Statusbar issue

Ignore the previous post.

I am trying to understand some Statusbar behavior.
In the below code, if I click" Destroy Statusbar" followed by “Create Statusbar”, my code works as expected.

If I click “Destroy Statusbar” twice in a row, my program abruptly exits. The exception block does not catch any errors.

If I click "Create Statusbar twice in a row, my program reports the following error message.
wx._core.wxAssertionError: C++ assertion “!m_frameStatusBar” failed at …\src\common \framecmn.cpp(381) in wxFrameBase::CreateStatusBar(): recreating status bar in wxFrame

I am using wxPython 4.0.7 post 2, Python 3.6.5, Win 10

Any idea how to fix the above?

Thanks,
Bruce

import wx

class TestFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title='Statusbar testing')
        panel = wx.Panel(self)

        destroyStatusbar = wx.Button(panel, label='Destroy StatusBar')
        destroyStatusbar.Bind(wx.EVT_BUTTON, self.destroySB)

        createStatusbar = wx.Button(panel, label='Create StatusBar')
        createStatusbar.Bind(wx.EVT_BUTTON, self.createSB)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(destroyStatusbar, 0, wx.ALL, 20)
        sizer.Add(createStatusbar,  0, wx.ALL, 20)
        panel.SetSizer(sizer)

        self.statusbar = self.CreateStatusBar(2)
        self.statusbar.SetStatusText('111111')
        self.statusbar.SetStatusText('222222', 1)

        self.Show()

    def destroySB(self, event):
        print ("Trying to destroy Statusbar")
        try:
            self.statusbar.Destroy()
            print ("Statusbar destroyed")
        except:
            print ("Unable to destory Statusbar")

    def createSB(self, event):
        print ("Trying to create Statusbar")
        try:
            self.statusbar = self.CreateStatusBar(2)
            self.statusbar.SetStatusText('111111AAAA')
            self.statusbar.SetStatusText('222222AAAA', 1)  
            print ("Statusbar created")
        except:
            print ("Unable to create Statusbar")

if __name__ == '__main__':
    app = wx.App(False)
    frame = TestFrame()
    app.MainLoop()

Keep in mind that most of wxPython is wrappers around C++ classes, and using the C++ objects incorrectly can result in fatal runtime errors. Lots of those things are caught by C++ assertions that wxPython turns into wxAssertionError exceptions. But not everything can be caught and so we still need to do some defensive programming.

Normally we could use a bool() trick to check if a widget or window still exists or has been destroyed, like:

    if widget: 
        do_something()

But since the statusbar is created by the frame in your example then it doesn’t get the extra wrapper code that makes that possible. However, you can still ask the frame if there is a statusbar or not. Like this:

    def destroySB(self, event):
        print ("Trying to destroy Statusbar")
        try:
            if self.GetStatusBar():
                self.statusbar.Destroy()
                print ("Statusbar destroyed")
            else:
                print("Statusbar is already gone")
        except:
            print ("Unable to destory Statusbar")

Hi Robin,

Thank you for answering my questions and your solution does work.
I am surprised the try/except block was not able to catch the exception when destroying the status bar.

Thank you again,

Bruce

It’s because it’s not a Python exception, but rather a C segmentation fault, due to dereferencing a pointer with an invalid address.

Hi Robin,

In the below example I use .Destroy() on a button widget.
The try/except block does catch the exception.

Why the difference between destroying a statusbar and a button?

Thanks again,
Bruce

import wx

class TestFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title='Button Destroy')
        panel = wx.Panel(self)

        self.testButton = wx.Button(panel, label='Press to Destroy')
        self.testButton.Bind(wx.EVT_BUTTON, self.destroyTestButton)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.testButton, 0, wx.ALL, 20)

        panel.SetSizer(sizer)
        self.Show()

    def destroyTestButton(self, event):
        try:
            self.testButton.Destroy()
            print ("testButton destroyed  #1")
            self.testButton.Destroy()  #This should cause an exception
        except:
            print ("testButton destroyed, exception caught  #2")
            print ("Worked as expected")

        try:
            self.testButton.Destroy()  #This should cause an exception
            print ("this message should not be displayed")
        except:
            print ("testButton destroyed, exception caught  #3")
            print ("Worked as expected")        

if __name__ == '__main__':
    app = wx.App(False)
    frame = TestFrame()
    app.MainLoop()

The difference is the same as the difference between an
engine breakdown (while driving to Rome) and a flat tire
(while driving to Athens).

They need fixes at different conceptual levels.

Karsten

Hi Karsten,

In sample code #1, I had a few questions.

Why does my program abruptly exit if the destroy button in clicked twice in a row? The try/except block did not catch the error.

Why does my program generate an wx.core.wxAssertionError: C++ assertion error is the create button is clocked twice in a row? The try/except block did catch the error.

Robin’s answer was to do some defensive programming by checking if a widget still exists. His solution worked.

I still had a few more questions so I created sample code #2.

This code destroys a button widget multiple times within a try/except block. The code worked as expected, the try/except block did catch the error.

My current questions are:

Why the different behavior between destroying a statusbar and a button?

With respect to using .Destroy(), when should I do some defensive programming as suggested by Robin or make use of try/except code blocks?

Thanks,

Bruce

Like I said before, since it’s the frame that creates the statusbar in CreateStatusBar instead of creating a wx.StatusBar from Python code, then we are not able to wrap it in the extra code that would catch the attempted use of it after it has been destroyed and turn it into a Python exception.

Hi Robin,

Got it.
Success, I modified my test program to use wx.StatusBar instead of CreateStatusBar.

Thank you,
Bruce