Search code examples
pythonwxpythonwxwidgets

ESC key in wx.Dialog doesn't trigger button with ID_CANCEL unless it has focus


According to the docs, dialogs created in wxPython and shown with ShowModal should handle the escape (ESC) key by searching for a button with ID_CANCEL and simulating a click (i.e. triggering its EVT_BUTTON event).

This was working in all but one of my dialogs. After a lot of debugging, I discovered that the cancel button - or any other button! - should have focus. In other words, as long as I call .SetFocus() on one of the buttons I create, the ESC key works fine.

Does anybody know what's going on here? Do dialogs displayed with ShowModal() not automatically get focus? Should they? Or am I misunderstanding something?

In the example code below, uncomment the line b.SetFocus() to see the difference:

import wx
class MainWindow(wx.Frame):
    def __init__(self, parent):
        super(MainWindow, self).__init__(parent)
        self.Show()
        d = SomeDialog(self)
        d.ShowModal()
        self.Destroy()
class SomeDialog(wx.Dialog):
    def __init__(self, parent):
        super(SomeDialog, self).__init__(parent)
        button = wx.Button(self, wx.ID_CANCEL, 'Cancel')
        button.Bind(wx.EVT_BUTTON, self.action_cancel)
        #button.SetFocus()
    def action_cancel(self, e):
        self.EndModal(wx.ID_CANCEL)
if __name__ == '__main__':
    app = wx.App(False)
    frame = MainWindow(None)
    app.MainLoop()

Update: This happens when running on linux (Fedora 20, Gnome)


Solution

  • You can call the dialog's SetFocus() method to set focus on itself:

    import wx
    
    class MainWindow(wx.Frame):
    
        def __init__(self, parent):
            super(MainWindow, self).__init__(parent)
            self.Show()
            d = SomeDialog(self)
            d.ShowModal()
            self.Destroy()
    
    class SomeDialog(wx.Dialog):
    
        def __init__(self, parent):
            super(SomeDialog, self).__init__(parent)
            button = wx.Button(self, wx.ID_CANCEL, 'Cancel')
            button.Bind(wx.EVT_BUTTON, self.action_cancel)
            self.SetFocus()
    
        def action_cancel(self, e):
            self.EndModal(wx.ID_CANCEL)
    
    if __name__ == '__main__':
        app = wx.App(False)
        frame = MainWindow(None)
        app.MainLoop()
    

    This works on Kubuntu 14.04 with wxPython 2.8.12.1 and Python 2.7.6. However, I suspect that when you set focus to the dialog, it is probably passing the focus to its first child just as a wx.Panel will do. I don't know why Linux is behaving in this manner, but I would agree with @nepix32 and @VZ that this should work without the SetFocus(). You can call it anyway as a workaround, but you should probably report it as a bug. There's a link on the wxPython website where you can submit your bug report.