1

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.

4

1 回答 1

3

我使用以下代码来计算对象:

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

发现你的程序每次事件都会增加 1000 个元组。所以当你调用props[key].SetValue()prev值时,gc还没有收集到。这可能是 wxpg 的一个 bug,我们可以通过使用([x],[y],[z])保存值来绕过这个 bug,这样你就可以在不调用 SetValue() 的情况下更新值:

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

这是完整的代码:

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()
于 2012-05-16T02:45:59.757 回答