I am writing a wxPython application that needs to be long-running.
Actually, continuously running: it is a data monitor for an electric
power trading system.
Now, I have found out that the application leaks memory, and grows
from an 8MB address space to a 100 MB space within 3 hours.
Monitoring it with windows' task manager, it appears that the number
of gdi objects grows continuously.
My application does not accumulate data: it builds its UI (a fairly
elaborate one)
and then one thread acquires data from a socket connection, creates
wxEvents,
passes them to the wxPyton UI, which updates text and graphics in widgets.
So
the only allocation is in the wxPens, wxFonts, wxBrushes that are created to
update the widgets. These are in local variable of the Draw methods of the
widgets',
and I would assume that they fall out of scope when the Draw methods
terminate,
and are the collected by the Python interpereter. If the underlying GDI
objects
are not reclaimed when the wxPython objects are collected, then accumulation
of GDI objects I observe could occur.
Is there any way to determine where the memory is leaking, or is there some
secret protocol for drawing in widgets that I am not observing? If someone
can
provide some clue leading me to a solution, I would be very appreciative.
Following are a couple of representative code extracts to show what
kinds of things my widgets do.
- Parzival
···
----------------------------
class Clock(wxControl,Widget):
def __init__(self, parent, ID,
colours=None,
pos = wxDefaultPosition, size = (40,40),
style = wxNO_BORDER, name=None):
if not name: name = str(ID)
wxControl.__init__(self, parent, ID, pos=pos, size=size,
style=style, name=name)
if colours:
self.SetColours(colours)
else:
self.SetColours(("grey","black","medium grey","medium red"))
self.SetValue(time.time(),())
EVT_SIZE(self, self.OnSize)
EVT_PAINT(self, self.OnPaint)
def __OnPaint(self, event):
''' Double bufferred paint '''
cw, ch = self.GetClientSizeTuple()
pdc = wxPaintDC(self)
ddc = wxMemoryDC()
ddc.SelectObject(wxEmptyBitmap(cw, ch))
ddc.SetBackground(pdc.GetBackground())
ddc.Clear()
self.Draw(ddc)
pdc.BeginDrawing()
pdc.Blit(0, 0, cw, ch, ddc, 0, 0)
pdc.EndDrawing()
def OnPaint(self, event):
self.Draw(wxPaintDC(self))
def OnSize(self,event):
pass
def SetValue(self, value, args):
self.timeTuple = time.localtime(int(value + 0.5))
self.Refresh()
def Draw(self,dc):
cw, ch = self.GetClientSizeTuple()
w = min(cw,ch)
h = w
dc.BeginDrawing()
colr = self.GetColour(0)
dc.SetPen(wxPen(colr))
bw = w/20
if bw > 1:
dc.SetBrush(wxBrush(colr))
dc.DrawRoundedRectangle(0, 0, w, h, max(1,w/5))
w -= bw
h -= bw
facecolr = self.GetDefinedColour(4)
if facecolr:
dc.SetBrush(wxBrush(facecolr))
else:
dc.SetBrush(dc.GetBackground())
dc.DrawRoundedRectangle(bw/3, bw/3, w, h, max(1,w/5))
r1 = w/2 - 1
r2 = r1 - max(1, r1/6)
x = ch*0.5
y = cw*0.5
for hour in range(0,12):
self.__DrawHand(dc,hour*5.0,self.GetColour(0),1,r1,r1-bw,x,y)
hour, minute, second = self.timeTuple[3:6]
rsec = r1
rmin = r2
rhour = r2 - (r1 - r2)
self.__DrawHand(dc,hour*5.0+minute/12.0,self.GetColour(1),3,rhour,0,x,y)
self.__DrawHand(dc,minute,self.GetColour(2),2,rmin,-rmin/16,x,y)
self.__DrawHand(dc,second,self.GetColour(3),1,rsec,-rsec/8,x,y)
dc.EndDrawing()
def __DrawHand(self,dc,minute,colour,width,r1,r2,x,y):
fpen = wxPen(colour)
fpen.SetWidth(width)
dc.SetPen(fpen)
a = minute*3.141592654/30.0
cosa = math.cos(a)
sina = math.sin(a)
x1 = int(x + r1*sina + 0.5)
x2 = int(x + r2*sina + 0.5)
y1 = int(y - r1*cosa + 0.5)
y2 = int(y - r2*cosa + 0.5)
dc.DrawLine(x1,y1, x2,y2)
#---------------------------------------------------------------------------
----
class Gauge(_GaugeBase):
''' A horizontal gauge with an origin. '''
def __init__(self, parent, ID,
colours=None,
pos = wxDefaultPosition, size = (100,10),
style = wxSUNKEN_BORDER, name=None):
_GaugeBase.__init__(self, parent, ID, colours, pos, size, style,
name)
def SetBounds(self,lo,hi,mid=None):
if lo >= hi:
raise ValueError, ("invalid range",lo,hi)
if mid == None:
if hi <= 0:
mid = hi
elif lo >= 0:
mid = lo
else:
mid = 0
elif mid < lo or mid > hi:
raise ValueError, ("mid out of range", lo, hi, mid)
self.lo = lo
self.range = float(hi-lo)
self.rMid = (mid-lo)/self.range
self.rValue = self.rMid
self.txtlo = sstr(lo)
self.txthi = sstr(hi)
self.txtmid = sstr(mid)
self.Refresh()
def SetValue(self, value, args):
self.rValue = (value-self.lo)/self.range
self.Refresh()
def Draw(self,dc):
w, h = self.GetClientSizeTuple()
dc.BeginDrawing()
dc.SetFont(self.GetFont())
if self.dolabels:
ch = dc.GetCharHeight()
hbar = max(4, h - ch)
twlo,th = dc.GetTextExtent(self.txtlo)
twhi,th = dc.GetTextExtent(self.txthi)
xhitext = max(0, w-twhi)
ytext = hbar
else:
hbar = h
#dc.Clear()
rValue = max(0.0, min(1.0,self.rValue))
wr = int(w*rValue)
wz = int(w*self.rMid)
colour = self.GetColour(rValue < self.rMid)
if rValue != self.rValue:
br = wxBrush(colour,wxBDIAGONAL_HATCH)
else:
br = wxBrush(colour)
dc.SetBrush(br)
rr = max(2,min(20,int(hbar/4.0+0.5)))
if rValue < self.rMid:
#dc.DrawRectangle(wr,0, wz-wr, hbar)
dc.DrawRoundedRectangle(wr,0, wz-wr, hbar, rr)
else:
#dc.DrawRectangle(wz,0, wr-wz, hbar)
dc.DrawRoundedRectangle(wz,0, wr-wz, hbar, rr)
if self.dolabels:
twzr,th = dc.GetTextExtent(self.txtmid)
if twlo < wz-th and wz+th < xhitext:
dc.DrawText(self.txtmid, int(w*self.rMid - twzr*0.5), ytext)
dc.DrawText(self.txthi, xhitext, ytext)
if xhitext > twlo + ch:
dc.DrawText(self.txtlo, 0, ytext)
dc.EndDrawing()
#---------------------------------------------------------------------------
----
class Gauge(_GaugeBase):
''' A horizontal gauge with an origin. '''
def __init__(self, parent, ID,
colours=None,
pos = wxDefaultPosition, size = (100,10),
style = wxSUNKEN_BORDER, name=None):
_GaugeBase.__init__(self, parent, ID, colours, pos, size, style,
name)
def SetBounds(self,lo,hi,mid=None):
if lo >= hi:
raise ValueError, ("invalid range",lo,hi)
if mid == None:
if hi <= 0:
mid = hi
elif lo >= 0:
mid = lo
else:
mid = 0
elif mid < lo or mid > hi:
raise ValueError, ("mid out of range", lo, hi, mid)
self.lo = lo
self.range = float(hi-lo)
self.rMid = (mid-lo)/self.range
self.rValue = self.rMid
self.txtlo = sstr(lo)
self.txthi = sstr(hi)
self.txtmid = sstr(mid)
self.Refresh()
def SetValue(self, value, args):
self.rValue = (value-self.lo)/self.range
self.Refresh()
def Draw(self,dc):
w, h = self.GetClientSizeTuple()
dc.BeginDrawing()
dc.SetFont(self.GetFont())
if self.dolabels:
ch = dc.GetCharHeight()
hbar = max(4, h - ch)
twlo,th = dc.GetTextExtent(self.txtlo)
twhi,th = dc.GetTextExtent(self.txthi)
xhitext = max(0, w-twhi)
ytext = hbar
else:
hbar = h
#dc.Clear()
rValue = max(0.0, min(1.0,self.rValue))
wr = int(w*rValue)
wz = int(w*self.rMid)
colour = self.GetColour(rValue < self.rMid)
if rValue != self.rValue:
br = wxBrush(colour,wxBDIAGONAL_HATCH)
else:
br = wxBrush(colour)
dc.SetBrush(br)
rr = max(2,min(20,int(hbar/4.0+0.5)))
if rValue < self.rMid:
#dc.DrawRectangle(wr,0, wz-wr, hbar)
dc.DrawRoundedRectangle(wr,0, wz-wr, hbar, rr)
else:
#dc.DrawRectangle(wz,0, wr-wz, hbar)
dc.DrawRoundedRectangle(wz,0, wr-wz, hbar, rr)
if self.dolabels:
twzr,th = dc.GetTextExtent(self.txtmid)
if twlo < wz-th and wz+th < xhitext:
dc.DrawText(self.txtmid, int(w*self.rMid - twzr*0.5), ytext)
dc.DrawText(self.txthi, xhitext, ytext)
if xhitext > twlo + ch:
dc.DrawText(self.txtlo, 0, ytext)
dc.EndDrawing()
_______________________________________________
wxPython-users mailing list
wxPython-users@lists.sourceforge.net
http://lists.sourceforge.net/lists/listinfo/wxpython-users