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

前端 未结 4 488
情深已故
情深已故 2020-12-09 10:40

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 c

相关标签:
4条回答
  • 2020-12-09 10:46

    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.

    0 讨论(0)
  • 2020-12-09 11:00

    I am not sure why do you need the callback to close the application? If you do not bind anything to the MainFrame, the application should automatically be closed when you click the close button. Unless you have some other thread running which prevents the process to end. If you still want to bind to the close event and do something before closing you should use event.Skip() in the event handler if you still want to close the window. Otherwise the event is not propagated further and default handler is not executed. An example:

    import wx
    
    class MainFrame(wx.Frame):
        def __init__(self, *args, **kwargs):
            wx.Frame.__init__(self, *args, **kwargs)
            self.__close_callback = None
            self.Bind(wx.EVT_CLOSE, self._when_closed)
    
        def register_close_callback(self, callback):
            self.__close_callback = callback
    
        def _when_closed(self, event):
            doClose = True if not self.__close_callback else self.__close_callback()
            if doClose:
                event.Skip()
    
    if __name__ == "__main__":
        app = wx.App(False)
        mf = MainFrame(None, title='Test')
        mf.Show()
        mf.register_close_callback(lambda: True)
        app.MainLoop()
    

    When you execute the code and click close - the application is closed. When you change the callback function to lambda: False the window will not be closed since Skip is not called.

    0 讨论(0)
  • 2020-12-09 11:04

    One of the previous answers discusses using Destroy() rather than Close() in order to save user information before quitting the application. Here is some sample code asking the user if they intend to quit. If they respond with an affirmative, it "destroys" the frame and quits the application.

    # Bind our events from our menu item and from the close dialog 'x' on the frame
    def SetupEvents(self):
        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
        self.Bind(wx.EVT_MENU, self.OnCloseFrame, self.fileMenuExitItem)
    
    
    # Destroys the main frame which quits the wxPython application
    def OnExitApp(self, event):
        self.Destroy()
    
    
    # Makes sure the user was intending to quit the application
    def OnCloseFrame(self, event):
        dialog = wx.MessageDialog(self, message = "Are you sure you want to quit?", caption = "Caption", style = wx.YES_NO, pos = wx.DefaultPosition)
        response = dialog.ShowModal()
    
        if (response == wx.ID_YES):
            self.OnExitApp(event)
        else:
            event.StopPropagation()
    
    0 讨论(0)
  • 2020-12-09 11:04

    Closing all the top level frames in the app should end the application without needing to bind to close or anything else. You can close them yourself with .Close() if you have several, or some that are hidden.

    Also if you have a TaskBarIcon, that will need to be destroyed before the app will end.

    I think your seg fault could be because of another reason, to do with how the frames tear down and destroy their children. I'd try stepping through the code and find out at what point the seg fault is happening.

    0 讨论(0)
提交回复
热议问题