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))
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.
This is because Destroy() would be called instead of Close(). EVT_CLOSE would not be raised without Close(). You can try following code.
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)
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
def OnClose(self, event):
print 'In OnClose'
event.Skip()
def OnDestroy(self, event):
print 'In OnDestroy'
event.Skip()
if __name__ == '__main__':
app = wx.App(False)
frame = wx.Frame(None)
frame.Show()
dialog = TestDialog(frame)
result = dialog.ShowModal()
dialog.Close(True)
print 'Result: {}'.format(result)
app.MainLoop()
I just had this problem and based on the answer here I came up with the solution below. This was tested using wxPython 4.1.0 and python 3.8.5.
In essence, all wx.EVT_BUTTON
events in the wx.Dialog
window are bound to a method in the wx.Dialog
window. In the method, the ID of the clicked button is compared to the IDs used to create the buttons in the wx.Dialog
and actions are taken accordingly.
In the example below, clicking the OK button generates a random number. The wx.Dialog
only closes if the random number is > 0.5.
import random
import wx
class Example(wx.Frame):
"""Main window """
def __init__(self, parent):
super().__init__(
parent,
title = 'Intercept wx.Dialog button events',
size = (260, 180),
)
self.btn = wx.Button(self, label = 'Show Dialog Window')
self.Sizer = wx.BoxSizer()
self.Sizer.Add(self.btn, 0, wx.ALIGN_CENTER|wx.ALL, 5)
self.SetSizer(self.Sizer)
self.btn.Bind(wx.EVT_BUTTON, self.OnDialog)
self.Centre()
self.Show()
def OnDialog(self, event):
with MyDialog(self) as dlg:
if dlg.ShowModal() == wx.ID_OK:
print("Dialog closed with OK - Message from Main Window")
print("")
else:
print("Dialog closed with Cancel - Message from Main Window")
print("")
class MyDialog(wx.Dialog):
def __init__(self, parent):
super().__init__(
parent,
title = 'My Dialog',
size = (220, 90),
)
self.btnSizer = self.CreateStdDialogButtonSizer(
wx.OK|wx.CANCEL
)
self.Sizer = wx.BoxSizer()
self.Sizer.Add(self.btnSizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
self.SetSizer(self.Sizer)
self.CentreOnParent()
# HERE IS THE TRICK, Get all Button events
self.Bind(wx.EVT_BUTTON, self.OnButton)
def OnButton(self, event):
"""Process the OK button click"""
clicked_button_id = event.GetEventObject().GetId()
if clicked_button_id == wx.ID_OK:
print("Processing OK button click")
if (n := random.random()) > 0.5:
#--> Everything fine, window will close
print(n, "> 0.5. Ok, closing window")
pass
else:
#--> Error, window will remain open
print(n, "< 0.5. Not Ok, window remains open")
return False
else:
print("Processing Cancel button click")
# Skip event to make sure default behavior is executed
event.Skip()
if __name__ == '__main__':
app = wx.App()
Example(None)
app.MainLoop()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With