some wxpython memory issue

Hi everybody,

I've found some similar topics in the archive but none of them could give me a clue. So I'll try again=). I have a massive memory problem with my wxpython script. Instead of posting my code (because it is pretty large) I'll ask my questions more common:

1) How does wxpython behave with releasing unused memory?

For example: The script has a notebook (or wx.aui.notebook) which is filled with some pages. Depending on the content of the page the memory increases as expected. BUT: closing a page does NOT release the memory. Instead it just wastes even more memory! I also noticed this behaviour in the wxpython demo (AUI_MDI.py).

2) Why is the memory used by my wx.lib.sheet.CSheet much higher than the same data in excel?

For Example: 'test.xls' is an excel file of size 25kB containing some text (no images or graphs) in 70 lines and 20 columns. Importing this file in excel takes about 100kB. My excel handler for the GUI uses the xlrd package to read all the data, then creates a wx.lib.sheet.CSheet and uses SetTable to provide the data to the grid. After that the memory usage of my GUI has increased by 2MB.
Another example: 'test2.xls' has a size of 600kB (20 subsheets with an average of 100 lines and 20 columns). In excel it takes 2.7 MB of memory, in my wxpython GUI about 35MB. Closing and opening this file again increases the memory usage again without releasing the previously used memory.

I achived the memory usage with the windows taskmanager AND win32process.GetProcessMemoryUsage(win32process.GetCurrentProcess())["WorkingSetSize"].

I'm sorry for such a long issue description but a solution is very important for me. I hope someone could help me or at least give me a hint about programming more memory efficient with wxpython. I'm looking forward to the answers.

Greetz
Tim

Tim Kraemer wrote:

Hi everybody,

I've found some similar topics in the archive but none of them could give me a clue. So I'll try again=). I have a massive memory problem with my wxpython script. Instead of posting my code (because it is pretty large) I'll ask my questions more common:

1) How does wxpython behave with releasing unused memory?

Like any other Python code. For the wx C++ code the typical behavior is to destroy widgets when their parent is destroyed.

For example: The script has a notebook (or wx.aui.notebook) which is filled with some pages. Depending on the content of the page the memory increases as expected. BUT: closing a page does NOT release the memory. Instead it just wastes even more memory! I also noticed this behaviour in the wxpython demo (AUI_MDI.py).

2) Why is the memory used by my wx.lib.sheet.CSheet much higher than the same data in excel?

Keep in mind that there is a lot more in play than just the data values. There is corresponding overhead for all the Python objects (including those that you use to hold the data values you put in the grid) and also the wx C++ objects and the system overhead for the UI objects.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

Tim Kraemer wrote:
> Hi everybody,
>
> I've found some similar topics in the archive but none of them could give me a
clue. So I'll try again=). I have a massive memory problem with my wxpython
script. Instead of posting my code (because it is pretty large) I'll ask my
questions more common:
>
> 1) How does wxpython behave with releasing unused memory?

Like any other Python code. For the wx C++ code the typical behavior is to
destroy widgets when their parent is destroyed.

So I guess destroying a notebook page should also destroy the contained grid or further pages. But why does the memory usage increase instead of decreasing? That's what I'm expecting when a widget is destroyed. Is there any possibility to check whether the grid is destroyed or not?

>
> For example: The script has a notebook (or wx.aui.notebook) which is filled
with some pages. Depending on the content of the page the memory increases as
expected. BUT: closing a page does NOT release the memory. Instead it just
wastes even more memory! I also noticed this behaviour in the wxpython demo
(AUI_MDI.py).
>
> 2) Why is the memory used by my wx.lib.sheet.CSheet much higher than the same
data in excel?

Keep in mind that there is a lot more in play than just the data values.
There is corresponding overhead for all the Python objects (including those that
you use to hold the data values you put in the grid) and also the wx C++ objects
and the system overhead for the UI objects.

I understand:-). That's a fact I can accept if all this overhead (or at least the stuff that isn't existing anymore) would be released from memory when closing a page (see above).

Thanks and greetz,
Tim

Tim Kraemer wrote:

Robin Dunn wrote:

Tim Kraemer wrote:

Hi everybody,

I've found some similar topics in the archive but none of them could give me a

clue. So I'll try again=). I have a massive memory problem with my wxpython script. Instead of posting my code (because it is pretty large) I'll ask my questions more common:

1) How does wxpython behave with releasing unused memory?

Like any other Python code. For the wx C++ code the typical behavior is to destroy widgets when their parent is destroyed.

So I guess destroying a notebook page should also destroy the contained grid or further pages. But why does the memory usage increase instead of decreasing? That's what I'm expecting when a widget is destroyed. Is there any possibility to check whether the grid is destroyed or not?

Keep a reference to the grid and then try to access one of its attributes. If the C++ object has been destroyed then you'll get a wx.PyDeadObjectError exception. There is code in the wxPython wrappers that is invoked when the C++ part of the widget is destroyed and it resets the __class__ of the Python proxy if there is more than one reference remaining. That new class raises an exception on any attribute access except for __nonzero__, which simply returns False.

For example: The script has a notebook (or wx.aui.notebook) which is filled

with some pages. Depending on the content of the page the memory increases as expected. BUT: closing a page does NOT release the memory. Instead it just wastes even more memory! I also noticed this behaviour in the wxpython demo (AUI_MDI.py).

2) Why is the memory used by my wx.lib.sheet.CSheet much higher than the same

data in excel?

Keep in mind that there is a lot more in play than just the data values. There is corresponding overhead for all the Python objects (including those that you use to hold the data values you put in the grid) and also the wx C++ objects and the system overhead for the UI objects.

I understand:-). That's a fact I can accept if all this overhead (or at least the stuff that isn't existing anymore) would be released from memory when closing a page (see above).

What platform and Python version are you on? It's possible that you are simply seeing a side effect of the way that Python interacts with the system for memory allocation. IIRC Python 2.4 and earlier would not actually release memory that it had allocated from the system, but would instead hold on to all of it for later reuse. However even if this is part of what you are seeing it still should not be growing without bounds unless you are actually creating and holding that many objects.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Just as a hint. I met with a trouble alike (wx.PyDeadObjectError exception) and Tim Roberts found out that it arose from my mistaken approach to mutable objects. If the widgets that give you this bellyache are referenced to in a mutable object (a list or a dictionary), try googling “Tim Roberts + mutable + static variable + Raffaello” or ask me for the enlightening answer he gave me. Thanks again, Tim.

···

2009/2/2 Robin Dunn robin@alldunn.com

Tim Kraemer wrote:

Robin Dunn wrote:

Tim Kraemer wrote:

Hi everybody,

I’ve found some similar topics in the archive but none of them could give me a
clue. So I’ll try again=). I have a massive memory problem with my wxpython script. Instead of posting my code (because it is pretty large) I’ll ask my questions more common:

  1. How does wxpython behave with releasing unused memory?
    Like any other Python code. For the wx C++ code the typical behavior is to destroy widgets when their parent is destroyed.

So I guess destroying a notebook page should also destroy the contained grid or further pages. But why does the memory usage increase instead of decreasing? That’s what I’m expecting when a widget is destroyed. Is there any possibility to check whether the grid is destroyed or not?

Keep a reference to the grid and then try to access one of its attributes. If the C++ object has been destroyed then you’ll get a wx.PyDeadObjectError exception. There is code in the wxPython wrappers that is invoked when the C++ part of the widget is destroyed and it resets the class of the Python proxy if there is more than one reference remaining. That new class raises an exception on any attribute access except for nonzero, which simply returns False.

For example: The script has a notebook (or wx.aui.notebook) which is filled
with some pages. Depending on the content of the page the memory increases as expected. BUT: closing a page does NOT release the memory. Instead it just wastes even more memory! I also noticed this behaviour in the wxpython demo (AUI_MDI.py).
2) Why is the memory used by my wx.lib.sheet.CSheet much higher than the same
data in excel?

Keep in mind that there is a lot more in play than just the data values. There is corresponding overhead for all the Python objects (including those that you use to hold the data values you put in the grid) and also the wx C++ objects and the system overhead for the UI objects.

I understand:-). That’s a fact I can accept if all this overhead (or at least the stuff that isn’t existing anymore) would be released from memory when closing a page (see above).

What platform and Python version are you on? It’s possible that you are simply seeing a side effect of the way that Python interacts with the system for memory allocation. IIRC Python 2.4 and earlier would not actually release memory that it had allocated from the system, but would instead hold on to all of it for later reuse. However even if this is part of what you are seeing it still should not be growing without bounds unless you are actually creating and holding that many objects.

Robin Dunn

Software Craftsman

http://wxPython.org Java give you jitters? Relax with wxPython!


wxpython-users mailing list

wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

Hi Robin,

I've just tried to access a temporary reference to a grid after closing its corresponding file and I achieve the wx.PyDeadObjectError exception. So I guess everything should be fine. But I still observe that the memory usage does not decrease to a former level or something slightly above. Instead it increases again and again after each open and close of the file.

I am on a WinXP box with Python 2.4 and wxpython 2.8.9.1. Because you wrote that Python 2.4 would hold the memory once used I've tried it with Python 2.5 but observed the same issue.

Thanks for your time and suggestions. If there is no way to avoid the increase of memory usage I'll have to check if I get stuck in more serious trouble when the script runs for several hours=).

Tim

Robin Dunn wrote:

···

Tim Kraemer wrote:
>
> Robin Dunn wrote:
>
>> Tim Kraemer wrote:
>>> Hi everybody,
>>>
>>> I've found some similar topics in the archive but none of them could give me
a
>> clue. So I'll try again=). I have a massive memory problem with my wxpython
script. Instead of posting my code (because it is pretty large) I'll ask my
questions more common:
>>> 1) How does wxpython behave with releasing unused memory?
>> Like any other Python code. For the wx C++ code the typical behavior is to
destroy widgets when their parent is destroyed.
>>
>
> So I guess destroying a notebook page should also destroy the contained grid
or further pages. But why does the memory usage increase instead of decreasing?
That's what I'm expecting when a widget is destroyed. Is there any possibility
to check whether the grid is destroyed or not?
>

Keep a reference to the grid and then try to access one of its attributes. If
the C++ object has been destroyed then you'll get a wx.PyDeadObjectError
exception. There is code in the wxPython wrappers that is invoked when the C++
part of the widget is destroyed and it resets the __class__ of the Python proxy
if there is more than one reference remaining. That new class raises an
exception on any attribute access except for __nonzero__, which simply returns
False.

>>> For example: The script has a notebook (or wx.aui.notebook) which is filled
>> with some pages. Depending on the content of the page the memory increases as
expected. BUT: closing a page does NOT release the memory. Instead it just
wastes even more memory! I also noticed this behaviour in the wxpython demo
(AUI_MDI.py).
>>> 2) Why is the memory used by my wx.lib.sheet.CSheet much higher than the
same
>> data in excel?
>>
>> Keep in mind that there is a lot more in play than just the data values.
There is corresponding overhead for all the Python objects (including those that
you use to hold the data values you put in the grid) and also the wx C++ objects
and the system overhead for the UI objects.
>>
>
> I understand:-). That's a fact I can accept if all this overhead (or at least
the stuff that isn't existing anymore) would be released from memory when
closing a page (see above).
>

What platform and Python version are you on? It's possible that you are simply
seeing a side effect of the way that Python interacts with the system for memory
allocation. IIRC Python 2.4 and earlier would not actually release memory that
it had allocated from the system, but would instead hold on to all of it for
later reuse. However even if this is part of what you are seeing it still
should not be growing without bounds unless you are actually creating and
holding that many objects.

-- Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

_______________________________________________
wxpython-users mailing list
wxpython-users@lists.wxwidgets.org
http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

Are you really sure you’re not storing any references to the notebook pages elsewhere, that might be keeping them alive after destruction?

–S

···

On Mon, Feb 2, 2009 at 11:57 PM, Tim Kraemer kraemertim@ymail.com wrote:

Hi Robin,

I’ve just tried to access a temporary reference to a grid after closing its corresponding file and I achieve the wx.PyDeadObjectError exception. So I guess everything should be fine. But I still observe that the memory usage does not decrease to a former level or something slightly above. Instead it increases again and again after each open and close of the file.

I am on a WinXP box with Python 2.4 and wxpython 2.8.9.1. Because you wrote that Python 2.4 would hold the memory once used I’ve tried it with Python 2.5 but observed the same issue.

Thanks for your time and suggestions. If there is no way to avoid the increase of memory usage I’ll have to check if I get stuck in more serious trouble when the script runs for several hours=).

Stephen Hansen wrote:

Are you *really* sure you're not storing any references to the notebook pages elsewhere, that might be keeping them alive after destruction?

Or maybe some other data that you create at the same time as the notebook pages.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Hi,

Robin Dunn wrote:

Stephen Hansen wrote:
>
> Are you *really* sure you're not storing any references to the notebook pages
elsewhere, that might be keeping them alive after destruction?

Or maybe some other data that you create at the same time as the notebook pages.

-- Robin Dunn

Because it's driving me crazy I've extracted a small working example out of my program that illustrates the problem I have: a wx.Frame with an AuiNotebook. Via the menu you can add new pages to the notebook or delete the currently selected page. A new page contains a wx.lib.sheet.CSheet with 13 columns and 100 rows. The data is stored in a PyGridTableBase derived class and consists of a randomly generated value and colour (CellAttr). Additionally a thread is started which observes the memory usage and displays it (together with the change state to the previous value) in the statusbar of the frame.

What you can see: each new page increases the memory usage by ~400kB. That's ok. But deleting a page also increases the usage by ~150kB instead of releasing the 400kB.

Could someone please review the code and/or test it and give me the arbitrative hint what's going wrong in my script:-).

Thanks a lot,
Tim

Sorry, I forgot the code.

Hi,

Robin Dunn wrote:
>
> Stephen Hansen wrote:
> >
> > Are you *really* sure you're not storing any references to the notebook
pages
> elsewhere, that might be keeping them alive after destruction?
>
> Or maybe some other data that you create at the same time as the notebook
pages.
>
> -- Robin Dunn

Because it's driving me crazy I've extracted a small working example out of my
program that illustrates the problem I have: a wx.Frame with an AuiNotebook. Via
the menu you can add new pages to the notebook or delete the currently selected
page. A new page contains a wx.lib.sheet.CSheet with 13 columns and 100 rows.
The data is stored in a PyGridTableBase derived class and consists of a randomly
generated value and colour (CellAttr). Additionally a thread is started which
observes the memory usage and displays it (together with the change state to the
previous value) in the statusbar of the frame.

What you can see: each new page increases the memory usage by ~400kB. That's ok.
But deleting a page also increases the usage by ~150kB instead of releasing the
400kB.

Could someone please review the code and/or test it and give me the arbitrative
hint what's going wrong in my script:-).

Thanks a lot,
Tim

import wx,win32process, thread, time, random
import wx.aui

import wx.grid as Grid
import wx.lib.sheet as Sheet

class MyTable(Grid.PyGridTableBase):
    def __init__(self, data, colnames):
        Grid.PyGridTableBase.__init__(self)
        self.data = data
        self.colnames = colnames
        
        self._rows = self.GetNumberRows()
        self._cols = self.GetNumberCols()

    def GetColLabelValue(self, col):
        return self.colnames[col]
    
    def GetNumberCols(self):
        return len(self.data[0])
        
    def GetNumberRows(self):
        return len(self.data)
    
    def GetRowLabelValue(self, row):
        return unicode(row+1)
    
    def GetValue(self, row, col):
        return unicode(self.data[row][col]["value"])
        
    def SetValue(self, row, col, value):
        self.data[row][col]["value"] = value

    def GetAttr(self, row, col, kind):
        attr = Grid.GridCellAttr()
        attr.SetBackgroundColour(self.data[row][col]["BG"])
            
        attr.IncRef()
        return attr

class MyGrid(Sheet.CSheet):
    def __init__(self, prnt):
        Sheet.CSheet.__init__(self, prnt)
        
        columns = ["Param1","Param2","Param3","Param4","Param5","Param6","Param7","Param8","Param9","Param10","Param11","Param12","Param13"]
        colours = [wx.BLUE, wx.GREEN, wx.RED, wx.WHITE, wx.CYAN]
        InitData =
        for row in range(100):
            d =
            for col in range(len(columns)):
                d.append({"value":str(random.randint(0,100)),"BG":random.choice(colours)})
            InitData.append(d)
        self.._table = MyTable(InitData, columns)
        
        self.SetTable(self._table, True)

class MyFrame(wx.Frame):
    def __init__(self, prnt):
        wx.Frame.__init__(self, prnt, wx.ID_ANY, "AuiNotebook Memory Test", size = (800,600))
        
        self.Statusbar = wx.StatusBar(self, -1)
        self.SetStatusBar(self.Statusbar)
        
        self.CreateMenu()
        
        self.myNB = wx.aui.AuiNotebook(self, wx.ID_ANY)
        
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        
        self.LastMemUsage = win32process.GetProcessMemoryInfo(win32process.GetCurrentProcess())["WorkingSetSize"]
        self.ThreadRunning = True
        thread.start_new_thread(self.MemObserver, ())
        
        self.pCnt = 1
        
    def CreateMenu(self):
        MyMenuBar = wx.MenuBar()
        FileMenu = wx.Menu()
        
        item = wx.MenuItem(FileMenu, -1, "New\tCtrl+N","Create a new Tab")
        self.Bind(wx.EVT_MENU, self.OnMenuNew, item)
        FileMenu.AppendItem(item)

        item = wx.MenuItem(FileMenu, -1, "Close\tCtrl+W","Close the current Tab")
        self.Bind(wx.EVT_MENU, self.OnMenuClose, item)
        FileMenu.AppendItem(item)
        
        MyMenuBar.Append(FileMenu, "&File")

        self.SetMenuBar(MyMenuBar)
        
    def OnMenuNew(self, evt):
        newGrid = MyGrid(self.myNB)
        self.myNB.AddPage(newGrid, "Grid"+str(self.pCnt))
        self.pCnt+=1
        evt.Skip()
        
    def OnMenuClose(self, evt):
        self.myNB.DeletePage(self.myNB.GetSelection())
        evt.Skip()
        
    def MemObserver(self):
        cnt = 0
        while self.ThreadRunning:
            if cnt%1000 == 0:
                info = win32process.GetProcessMemoryInfo(win32process.GetCurrentProcess())
                if info["WorkingSetSize"] > self.LastMemUsage:
                    state = " (increasing)"
                elif info["WorkingSetSize"] < self.LastMemUsage:
                    state = " (decreasing)"
                else:
                    state = " (equal)"
                self.Statusbar.SetStatusText("Memory usage: " + str(info["WorkingSetSize"]/1000) + " kB" + state)
                self.LastMemUsage = info["WorkingSetSize"]
                cnt = 0
            cnt+=1
            time.sleep(0.001)
                
    def OnClose(self, evt):
        self.ThreadRunning = False
        time.sleep(0.1)
        self.Destroy()
                
class BoaApp(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        self.main = MyFrame(None)
        self..main.Show()
        self.SetTopWindow(self.main)
        return True

if __name__ == '__main__':
    application = BoaApp(0)
    application.MainLoop()

I don't think it's the real root cause, but you are leaking
Grid.GridCellAttr objects. Because you are creating them each time and
not caching them, you shouldn't call attr.IncRef(). However, even when I
take this out, I still see the memory increasing.

By the way, I used Dowser (http://www.aminus.net/wiki/Dowser) to spot
this - I changed your __main__ block to this:

    import cherrypy
    import dowser
    cherrypy.config.update({'server.socket_port': 8088})
    cherrypy.tree.mount(dowser.Root())
    cherrypy.engine.autoreload.unsubscribe()
    cherrypy.engine.start()
    application = BoaApp(0)
    application.MainLoop()
    cherrypy.engine.exit()

And then point your web browser at http://localhost:8088 to get the
numbers of all the different kinds of objects in your application.

Hope that helps a little,

Simon

···

-----Original Message-----
From: wxpython-users-bounces@lists.wxwidgets.org
[mailto:wxpython-users-bounces@lists.wxwidgets.org] On Behalf
Of Tim Kraemer
Sent: 04 February 2009 14:48
To: wxpython-users@lists.wxwidgets.org
Subject: AW: AW: AW: [wxpython-users] some wxpython memory issue

>
> Because it's driving me crazy I've extracted a small
working example out of my
> program that illustrates the problem I have: a wx.Frame
with an AuiNotebook. Via
> the menu you can add new pages to the notebook or delete
the currently selected
> page. A new page contains a wx.lib.sheet.CSheet with 13
columns and 100 rows.
> The data is stored in a PyGridTableBase derived class and
consists of a randomly
> generated value and colour (CellAttr). Additionally a
thread is started which
> observes the memory usage and displays it (together with
the change state to the
> previous value) in the statusbar of the frame.
>
> What you can see: each new page increases the memory usage
by ~400kB. That's ok.
> But deleting a page also increases the usage by ~150kB
instead of releasing the
> 400kB.
>
> Could someone please review the code and/or test it and
give me the arbitrative
> hint what's going wrong in my script:-).
>
> Thanks a lot,
> Tim
>

import wx,win32process, thread, time, random
import wx.aui

import wx.grid as Grid
import wx.lib.sheet as Sheet

class MyTable(Grid.PyGridTableBase):
    def __init__(self, data, colnames):
        Grid.PyGridTableBase.__init__(self)
        self.data = data
        self.colnames = colnames
        
        self._rows = self.GetNumberRows()
        self._cols = self.GetNumberCols()

    def GetColLabelValue(self, col):
        return self.colnames[col]
    
    def GetNumberCols(self):
        return len(self.data[0])
        
    def GetNumberRows(self):
        return len(self.data)
    
    def GetRowLabelValue(self, row):
        return unicode(row+1)
    
    def GetValue(self, row, col):
        return unicode(self.data[row][col]["value"])
        
    def SetValue(self, row, col, value):
        self.data[row][col]["value"] = value

    def GetAttr(self, row, col, kind):
        attr = Grid.GridCellAttr()
        attr.SetBackgroundColour(self.data[row][col]["BG"])
            
        attr.IncRef()
        return attr

class MyGrid(Sheet.CSheet):
    def __init__(self, prnt):
        Sheet.CSheet.__init__(self, prnt)
        
        columns =
["Param1","Param2","Param3","Param4","Param5","Param6","Param7
","Param8","Param9","Param10","Param11","Param12","Param13"]
        colours = [wx.BLUE, wx.GREEN, wx.RED, wx.WHITE, wx.CYAN]
        InitData =
        for row in range(100):
            d =
            for col in range(len(columns)):
                
d.append({"value":str(random.randint(0,100)),"BG":random.choic
e(colours)})
            InitData.append(d)
        self.._table = MyTable(InitData, columns)
        
        self.SetTable(self._table, True)

class MyFrame(wx.Frame):
    def __init__(self, prnt):
        wx.Frame.__init__(self, prnt, wx.ID_ANY, "AuiNotebook
Memory Test", size = (800,600))
        
        self.Statusbar = wx.StatusBar(self, -1)
        self.SetStatusBar(self.Statusbar)
        
        self.CreateMenu()
        
        self.myNB = wx.aui.AuiNotebook(self, wx.ID_ANY)
        
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        
        self.LastMemUsage =
win32process.GetProcessMemoryInfo(win32process.GetCurrentProce
ss())["WorkingSetSize"]
        self.ThreadRunning = True
        thread.start_new_thread(self.MemObserver, ())
        
        self.pCnt = 1
        
    def CreateMenu(self):
        MyMenuBar = wx.MenuBar()
        FileMenu = wx.Menu()
        
        item = wx.MenuItem(FileMenu, -1,
"New\tCtrl+N","Create a new Tab")
        self.Bind(wx.EVT_MENU, self.OnMenuNew, item)
        FileMenu.AppendItem(item)

        item = wx.MenuItem(FileMenu, -1,
"Close\tCtrl+W","Close the current Tab")
        self.Bind(wx.EVT_MENU, self.OnMenuClose, item)
        FileMenu.AppendItem(item)
        
        MyMenuBar.Append(FileMenu, "&File")

        self.SetMenuBar(MyMenuBar)
        
    def OnMenuNew(self, evt):
        newGrid = MyGrid(self.myNB)
        self.myNB.AddPage(newGrid, "Grid"+str(self.pCnt))
        self.pCnt+=1
        evt.Skip()
        
    def OnMenuClose(self, evt):
        self.myNB.DeletePage(self.myNB.GetSelection())
        evt.Skip()
        
    def MemObserver(self):
        cnt = 0
        while self.ThreadRunning:
            if cnt%1000 == 0:
                info =
win32process.GetProcessMemoryInfo(win32process.GetCurrentProcess())
                if info["WorkingSetSize"] > self.LastMemUsage:
                    state = " (increasing)"
                elif info["WorkingSetSize"] < self.LastMemUsage:
                    state = " (decreasing)"
                else:
                    state = " (equal)"
                self.Statusbar.SetStatusText("Memory usage: "
+ str(info["WorkingSetSize"]/1000) + " kB" + state)
                self.LastMemUsage = info["WorkingSetSize"]
                cnt = 0
            cnt+=1
            time.sleep(0.001)
                
    def OnClose(self, evt):
        self.ThreadRunning = False
        time.sleep(0.1)
        self.Destroy()
                
class BoaApp(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        self.main = MyFrame(None)
        self..main.Show()
        self.SetTopWindow(self.main)
        return True

if __name__ == '__main__':
    application = BoaApp(0)
    application.MainLoop()