2012-05-16 2 views
1

Я немного способ создать довольно сложное приложение wxPython с использованием ode для физического моделирования, openGL для рендеринга и wx для интерфейса. Все шла плавно, пока приложение не началось. Через несколько дней после того, как я не достиг прогресса, я наконец заметил, что мое приложение было утечкой памяти. Я был в состоянии отогнать в небольшой примере сценарий то, что просачивается в весьма необыкновенной скорости:Драматическая утечка памяти в приложении wxpython

#------------------------------------------------------------------------------- 
#------------------------------------------------------------------------------- 

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() 

пример создает рамку, и размещает wxPropertyGrid в него. Он получает свойство, отображающее трехмерное координатное значение, создает тысячу из них, а затем из таймера, работающего на частоте 100 Гц, каждый раз обновляет их до случайного значения. Это утечка где-то около 10 Мбит/с, и в конечном итоге сбой. Обычно он вылетает при выключении.

Я использую Python 2.7 & WX 2.9.3.1 МТО (классический) в ОС Windows 7.

Если я заменить свой производный CoordProperty со встроенным в собственности, такие как wxpg.FloatProperty и модифицировать код соответственно, утечка уходит.

Любые идеи? Или я должен отправить ошибку wx? Я даже могу удалить определение функции ValueToString в производном классе свойств, и приложение все еще течет.

+1

Это может быть немного разговор с ненавистью, но мне не нравится wxpython __lot__ (на основе личного опыта) - вы можете переключиться на что-то еще. – orlp

+0

Я второй момент выше меня, личные проекты заставили меня ненавидеть –

+0

Любые предложения? Пока не поздно переключаться - мне всего две недели. –

ответ

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, мы можем ходить вокруг этой ошибки с помощью ([x],[y],[z]) для сохранения значений, так что вы можете обновить значение без вызвать 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() 
+0

Это отличное решение! Он решил утечку и, похоже, тоже разрешил крах. Мне немного любопытно, почему требуются вложенные кортежи/списки, но просто изменять записи в кортеже, не делая эти записи одноэлементными списками, не работает. –