wxPython: Keyboard Shortcuts (Accelerators)

Almost any computer power user will want to use keyboard shortcuts (AKA: accelerators) to get their work done. Fortunately for us, wxPython provides a way to accomplish this very easily using an Accelerator Table via the wx.AcceleratorTable class. In this article we will look at a couple examples to see how this is accomplished.

Getting Started

For our first trick, we’ll begin with a really simple example. Check out the code below!

import wx
 
class MyForm(wx.Frame):
 
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial", size=(500,500))
 
        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)

        randomId = wx.NewId()
        self.Bind(wx.EVT_MENU, self.onKeyCombo, id=randomId)
        accel_tbl = wx.AcceleratorTable([(wx.ACCEL_CTRL,  ord('Q'), randomId )])
        self.SetAcceleratorTable(accel_tbl)
        
    #----------------------------------------------------------------------
    def onKeyCombo(self, event):
        """"""
        print "You pressed CTRL+Q!"
        
# Run the program
if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()

The code above is just a panel with an wx.AcceleratorTable object that contains one shortcut key combination in it, namely CTRL+Q. Keyboard shortcuts in wxPython are actually menu events (i.e. wx.EVT_MENU), probably because the shortcuts are usually also a menu item. Thus, they rely on some sort of id. If we had a menu item that we wanted to give a shortcut to, we would use the menu item’s id. Here we just create a new id. Note that we have to use wx.ACCEL_CTRL to “catch” the CTRL keypress. Finally, after creating the wx.AcceleratorTable, we need to add it to the wx.Frame by calling the frame object’s SetAcceleratorTable method and passing in the AcceleratorTable instance. Phew! Did you get all that? Good! Let’s continue then.

Learning About Multiple Shortcuts and More

In this example, we will learn how to add multiple keyboard shortcuts and we’ll also learn about how to create a multi-key shortcut. Let’s take a look:

import wx
 
class MyForm(wx.Frame):
 
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Tutorial")

        panel = wx.Panel(self, wx.ID_ANY)
        
        # Create a menu
        menuBar = wx.MenuBar()
        fileMenu = wx.Menu()
        
        refreshMenuItem = fileMenu.Append(wx.NewId(), "Refresh",
                                          "Refresh app")
        self.Bind(wx.EVT_MENU, self.onRefresh, refreshMenuItem)
        
        exitMenuItem = fileMenu.Append(wx.NewId(), "E&xit\tCtrl+X", "Exit the program")
        self.Bind(wx.EVT_MENU, self.onExit, exitMenuItem)
        
        menuBar.Append(fileMenu, "File")
        self.SetMenuBar(menuBar)
        
        # Create an accelerator table
        xit_id = wx.NewId()
        yit_id = wx.NewId()
        self.Bind(wx.EVT_MENU, self.onAltX, id=xit_id)
        self.Bind(wx.EVT_MENU, self.onShiftAltY, id=yit_id)
        
        self.accel_tbl = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord('R'), refreshMenuItem.GetId()),
                                              (wx.ACCEL_ALT, ord('X'), xit_id),
                                              (wx.ACCEL_SHIFT|wx.ACCEL_ALT, ord('Y'), yit_id)
                                             ])
        self.SetAcceleratorTable(self.accel_tbl)
        
    #----------------------------------------------------------------------
    def onRefresh(self, event):
        print "refreshed!"
        
    #----------------------------------------------------------------------
    def onAltX(self, event):
        """"""
        print "You pressed ALT+X!"
        
    #----------------------------------------------------------------------
    def onShiftAltY(self, event):
        """"""
        print "You pressed SHIFT+ALT+Y!"
        
    #----------------------------------------------------------------------
    def onExit(self, event):
        """"""
        self.Close()

#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = MyForm().Show()
    app.MainLoop()

In this example, we create a super simple menu with one menu item. As you can see, we use the refresh menu item’s id when we create the shortcut in the accelerator table. We also bind the menu item itself to the same event handler. The other two items in the accelerator table are bound in the same manner as before. However, note that the last item in the table as two flags: wx.ACCEL_SHIFT and wx.ACCEL_ALT. What does that mean? It means that we will have to press SHIFT+ALT+SomeKey to fire the event. In this case, the key we want is “Y”.

If you were paying close attention, you will have noticed that there’s an exit menu item too that claims that if you press CTRL+X, the application will close. However, we don’t have that shortcut in our accelerator table. Luckily for us, wxPython will do this shortcut automatically because of the way it’s added to the menu item: “\tCtrl+X”. The “\t” is a tab character (in case you didn’t know) and because of that, it tells wxPython to parse what comes after it and add it to an accelerator table. Isn’t that neat?

Note: I was unable to confirm whether or not wxPython adds that shortcut to a separate accelerator table or if it adds it to the one the programmer creates, but it doesn’t really matter either way since it “just works”.

Wrapping Up

By now you should know how to create keyboard shortcuts in wxPython. Congratulations! You’re that much closer to creating a cool application! I use keyboard shortcuts a lot and really appreciate it when an application has good, intuitive shortcuts that are easy to remember. If you’re a keyboard junkie too, then you will know what I mean. Have fun!

Further Reading

Note: This code was tested on Windows with Python 2.5.4 and wxPython 2.8.10.1