Dramatic memory leak in wxpython application
Question: I'm some way into building a fairly complex wxPython app using ode for
physical modelling, openGL for rendering, and wx for UI. Everything was going
swimmingly until the application started to crash. After a few days of making
no progress I finally noticed that my application was leaking memory. I was
able to distill into a smallish example script something that leaks at a quite
extraordinary rate:

    
    
    #-------------------------------------------------------------------------------
    #-------------------------------------------------------------------------------
    
    import wx
    import wx.propgrid as wxpg
    import random
    
    class CoordProperty(wxpg.PyProperty):
        def __init__(self, label, name, value=(0,0,0)):
            wxpg.PyProperty.__init__(self, label, name)
            self.SetValue(value)
    
        def GetClassName(self):
            return "CoordProperty"
    
        def GetEditor(self):
            return "TextCtrl"
    
        def ValueToString(self, value, flags):
            x,y,z = value
            return "%f,%f,%f"%(x,y,z)
    
    
    app = wx.App(False)
    frame = wx.Frame(None, -1, "Test")
    pg = wxpg.PropertyGridManager(frame)
    props = {}
    
    for i in range(1000):
        prop_name = "prop_%d"%i
        prop = CoordProperty("Coord", prop_name)
        pg.Append(prop)
        props[prop_name] = prop
    
    def OnTimer(event):
        global props
        for key in props:
            props[key].SetValue((random.random(), random.random(), random.random()))
    
    timer = wx.Timer(frame, 1)
    frame.Bind(wx.EVT_TIMER, OnTimer)
    timer.Start(10) # 100Hz
    
    frame.Show()
    
    app.MainLoop()
    timer.Stop()
    

The example creates a frame, and places a wxPropertyGrid into it. It derives a
property that display a 3d co-ordinate value, creates a thousand of them, and
then from a timer running at 100Hz it updates each to a random value. This
leaks somewhere close to 10Mb/sec, and eventually crashes. It usually crashes
at shutdown too.

I'm using python 2.7 & wx 2.9.3.1 msw (classic) on Windows 7.

If I replace my derived CoordProperty with a built-in property, such as
wxpg.FloatProperty, and modify the code accordingly, the leak goes away.

Any ideas? Or should I submit a wx bug? I can even remove the definition of
the function ValueToString in the derived property class and the app still
leaks.


Answer: I use the following code to count objects:

    
    
    def output_memory():
        d = defaultdict(int) 
        for o in gc.get_objects():
            name = type(o).__name__  
            d[name] += 1
    
        items = d.items()
        items.sort(key=lambda x:x[1])
        for key, value in items:
            print key, value
    

and found that your program will increase 1000 tuples every time event. So
when you call `props[key].SetValue()` the prev value has not been collected by
gc. This may be a bug of wxpg, we can walk around this bug by using
`([x],[y],[z])` to save the values, so you can update the value without call
the SetValue():

    
    
    for name, prop in props.iteritems():
        value = prop.GetValue()
        value[0][0] = random()
        value[1][0] = random()
        value[2][0] = random()
    pg.Refresh()
    

Here is the full code:

    
    
    import wx
    import wx.propgrid as wxpg
    from random import random
    import gc
    
    from collections import defaultdict
    
    def output_memory():
        d = defaultdict(int) 
        for o in gc.get_objects():
            name = type(o).__name__  
            d[name] += 1
    
        items = d.items()
        items.sort(key=lambda x:x[1])
        for key, value in items:
            print key, value
    
    class CoordProperty(wxpg.PyProperty):
        def __init__(self, label, name):
            wxpg.PyProperty.__init__(self, label, name)
            self.SetValue(([0],[0],[0]))
    
        def GetClassName(self):
            return "CoordProperty"
    
        def GetEditor(self):
            return "TextCtrl"
    
        def GetValueAsString(self, flags):
            x,y,z = self.GetValue()   
            return "%f,%f,%f"%(x[0],y[0],z[0])   
    
    app = wx.App(False)
    frame = wx.Frame(None, -1, "Test")
    pg = wxpg.PropertyGridManager(frame)
    props = {}
    
    for i in range(1000):
        prop_name = "prop_%d"%i
        prop = CoordProperty("Coord", prop_name)
        pg.Append(prop)
        props[prop_name] = prop
    
    def OnTimer(event):
        for name, prop in props.iteritems():
            value = prop.GetValue()
            value[0][0] = random()
            value[1][0] = random()
            value[2][0] = random()
        pg.Refresh()
        #output_memory()
    
    timer = wx.Timer(frame, 1)
    frame.Bind(wx.EVT_TIMER, OnTimer)
    timer.Start(10) 
    
    frame.Show()
    
    app.MainLoop()
    timer.Stop()
    

