0

How do you define a destructor for a wx.Panel in wxpython?

META: After inheriting a code base which uses wxpython and PyPubSub I've discovered a huge number of pubsub subscriptions in the __init__ functions of wx.Panel's that are never unsubscribed and cause errors later on in the program.

Metalshark
  • 8,194
  • 7
  • 34
  • 49
  • You don't need to download PyPubSub separately from wxPython as it is included in wxPython under wx.lib.pubsub. – Mike Driscoll Sep 30 '14 at 13:15
  • @MikeDriscoll Am using wx.lib.pubsub rather than the separate download. – Metalshark Sep 30 '14 at 17:24
  • 1
    Standalone version of pypubsub (at https://github.com/schollii/pypubsub since 2016) is recommended as the version in wx.lib is just a starting point (actually I think in an upcoming version of wxPython, wx.lib.pubsub will just briefly explain how to obtain the standalone version). – Oliver Mar 24 '19 at 18:55

2 Answers2

2

You should be able to bind to EVT_WINDOW_DESTROY and do the unsub in the handler.

For example:

class MyPanel(wx.Panel):

def __init__(self, parent):
    wx.Panel.__init__(self, parent, wx.NewId())

    pub.subscribe(self.__handler, 'event')

    def __destroy(_):
        pub.unsubscribe(self.__handler, 'event')

    self.Bind(wx.EVT_WINDOW_DESTROY, __destroy)

If above is not working you can protect against the PyDeadObjectError exception, by adding the following in the code where you try to access ExtendedWxPanel:

if instanceOfExctendedWxPanel: then access it or methods of it.

Metalshark
  • 8,194
  • 7
  • 34
  • 49
Werner
  • 2,086
  • 1
  • 15
  • 14
  • I don't think I've ever had to unsubscribe either. By the way, you should put questions to the OP as a comment to the question and not in your official answer. – Mike Driscoll Sep 30 '14 at 13:06
  • The error is ````wx._core.PyDeadObjectError: The C++ part of the ExtendedWxPanel object has been deleted, attribute access no longer allowed.```` – Metalshark Sep 30 '14 at 17:23
  • @Metalshark what/how are you deleting/closing the ExtendedWxPanel instance before you get the PyDeadObjectError? – Werner Oct 01 '14 at 09:02
  • No threads - there is a subscription calling a handler which is an attribute of the wx.Panel which triggers and calls the handler once the wx.Panel is closed and the publisher fires. The handler cannot be called due to the PyDeadObjectError. Cannot guard against it. – Metalshark Oct 06 '14 at 08:09
0

I had the same issue, I solved it by doing the following:

My code (which gave the error) was:

import wx
from pubsub import pub

class My_panel(wx.panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.box = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.box)
        #
        pub.subscribe(self.do_something, 'Msg')

    def do_something(self):
        self.box.Layout()     

The above panel was in a wx.Notebook page. In my app the user has the possibility to add or delete page from this notebook.

When the pub.sendMessage('Msg') code line was run after that the user deleted the notebook page containing this panel, I had the following error:

RuntimeError: wrapped C/C++ object of type BoxSizer has been deleted

which seems to be ~~ the new error type of 'wx.PyDeadObjectError exception' according to wxPython: https://wxpython.org/Phoenix/docs/html/MigrationGuide.html What is explained in such documentation from wxPython is to use the nonzero() method which tests if the C++ object has been deleted.

Hence my working code is:

import wx
from pubsub import pub

class My_panel(wx.panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.box = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(self.box)
        #
        pub.subscribe(self.do_something, 'Msg')

    def do_something(self):
        if self.__nonzero__():
            self.box.Layout()     
roural
  • 1
  • 2