Search code examples
wxpythonwxwidgets

Why isn’t EVT_CLOSE fired when I click the OK or Cancel buttons in a wx.Dialog?


I have a wx.Dialog subclass that needs to perform a couple of cleanup operations when the user clicks the OK button. The wx.Dialog documentation says that clicking OK or Cancel should emit an EVT_CLOSE event:

EVT_CLOSE: The dialog is being closed by the user or programmatically (see Window.Close ). The user may generate this event clicking the close button (typically the ‘X’ on the top-right of the title bar) if it’s present (see the CLOSE_BOX style) or by clicking a button with the ID_CANCEL or ID_OK ids.

I’m using WX 2.9.5.0 (via wxPython), however, and when I click OK or Cancel in this test application the OnClose method is not called. OnClose is called when I click the system’s close button (I’m using OS X). Am I implementing this event-handling wrong or does wx.Dialog really not conform to its documentation? And in the latter case, what’s the best way to intercept a click on the OK button?

from __future__ import print_function
import wx

class TestDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, title='Test Dialog')

        sizer = wx.BoxSizer(wx.VERTICAL)

        message = wx.StaticText(self, wx.NewId(), 'This is some dummy text')
        sizer.Add(message)

        ok_button = wx.Button(self, wx.ID_OK, 'OK')
        cancel_button = wx.Button(self, wx.ID_CANCEL, 'Cancel')

        btn_sizer = self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL)
        btn_sizer.Add(cancel_button)
        btn_sizer.Add(ok_button)
        sizer.Add(btn_sizer)

        self.SetSizer(sizer)

        self.Bind(wx.EVT_CLOSE, self.OnClose)

    def OnClose(self, event):
        print('In OnClose')
        event.Skip()

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

    dialog = TestDialog(None)
    result = dialog.ShowModal()
    print('Result: {}'.format(result))

Solution

  • When you click the Ok or Cancel buttons on a modal dialog the dialog is not closed with Close, instead it is ended with EndModal so the EVT_CLOSE event is not sent. Code that needs to run when a modal dialog is completed normally is usually put after the call to ShowModal. I think in this case that the documentation is incorrect.

    OTOH, if the dialog is shown modeless (with Show instead of ShowModal) then they should be closed with Close and you will get the EVT_CLOSE event.