Search code examples
pythonwxpython

How to kill a WxPython application when user clicks a frame's close


The application is supposed to close when I click the close button for the main frame. But the way I implemented it, it quits with a Segmentation fault when I click the button.

I worry about safe shutdown of the program, because I will need to persist stuff to disk later.

What is the proper non-violent way to terminate a WxPython application though a close button?


Here is the "main" loop of the program I implemented:

if __name__ == "__main__":
    app = wx.App(False)
    mf = MainFrame(None, title='Spectrum Checker') #subclasses frame
    mf.register_close_callback( app.Destroy) #what is the apt func?
    app.MainLoop()

Here is how it is implemented the callback within the MainFrame:

def __init__(self, parent, title):
    ...
    self.Bind(wx.EVT_CLOSE, self._when_closed)

...

def _when_closed(self, event):
if self.__close_callback__:
    self.__close_callback__()

Solution

  • Here's the normal method of closing a frame:

    import wx
    
    ########################################################################
    class MyFrame(wx.Frame):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self):
            """Constructor"""
            wx.Frame.__init__(self, None, title="Close Me")
            panel = wx.Panel(self)
    
            closeBtn = wx.Button(panel, label="Close")
            closeBtn.Bind(wx.EVT_BUTTON, self.onClose)
    
        #----------------------------------------------------------------------
        def onClose(self, event):
            """"""
            self.Close()
    
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyFrame()
        frame.Show()
        app.MainLoop()
    

    Now if you have a binding to wx.EVT_CLOSE, then you'll end up in an infinite loop until you get a segfault. The main reason to bind to EVT_CLOSE is so you can catch the close event and ask the user to save their information. If you want to do that, then you need to use self.Destroy instead of "self.Close" so you don't keep firing that event.

    As has already been mentioned, in your close handler you need to make sure you end threads, destroy taskbar icons, close open files, etc so nothing hangs. See also: http://wxpython.org/docs/api/wx.CloseEvent-class.html


    OP's addition

    I was facing two issues, I thank all three answers for helping me find them:

    First, the frame does not respond to self.Close() or self.Destroy() because it has an attribute self.stuff that has a running thread. This thread must be closed first.

    Second, self.Close() in a handler that responds to close events and causes an infinite recursion when called. This causes a runtime error (segmentation fault, recursion depth exceeded). The solution is to use self.Destroy() instead.