wxPython: All About Accelerators

The wxPython toolkit supports using keyboard shortcuts via the concept of Accelerators and Accelerator Tables. You can also bind directly to key presses, but in a lot of cases, you will want to go with Accelerators. The accelerator gives to the ability to add a keyboard shortcut to your application, such as the ubiquitous “CTRL+S” that most applications use for saving a file. As long as your application has focus, this keyboard shortcut can be added trivially.

Note that you will normally add an accelerator table to your wx.Frame instance. If you happen to have multiple frames in your application, then you may need to add an accelerator table to multiple frames depending on your design.

Let’s take a look at a simple example:

import wx
 
class MyForm(wx.Frame):
 
    def __init__(self):
        wx.Frame.__init__(self, None, title="Accelerator 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()

This can end up looking a bit ugly if you have a lot of keyboard shortcuts that you need to add to your application as you end upw tih a list of tuples that just looks kind of odd. You will find this way or writing an AcceleratorTable more often than not though. However there are other ways to add entries to your AcceleratorTable. Let’s take a look at an example from wxPython’s documentation:

entries = [wx.AcceleratorEntry() for i in xrange(4)]

entries[0].Set(wx.ACCEL_CTRL, ord('N'), ID_NEW_WINDOW)
entries[1].Set(wx.ACCEL_CTRL, ord('X'), wx.ID_EXIT)
entries[2].Set(wx.ACCEL_SHIFT, ord('A'), ID_ABOUT)
entries[3].Set(wx.ACCEL_NORMAL, wx.WXK_DELETE, wx.ID_CUT)

accel = wx.AcceleratorTable(entries)
frame.SetAcceleratorTable(accel)

Here we create a list of four wx.AcceleratorEntry() objects using a list comprehension. Then we access each of the entries in the list using the Python list’s index to call each entry’s Set method. The rest of the code is pretty similar to what you saw before. Let’s take a moment to make this code actually runnable:

import wx

class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title="AcceleratorEntry Tutorial", 
                          size=(500,500))

        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        
        exit_menu_item = wx.MenuItem(id=wx.NewId(), text="Exit",
                               helpString="Exit the application")
        about_menu_item = wx.MenuItem(id=wx.NewId(), text='About')

        ID_NEW_WINDOW = wx.NewId()
        ID_ABOUT = wx.NewId()
        
        self.Bind(wx.EVT_MENU, self.on_new_window, id=ID_NEW_WINDOW)
        self.Bind(wx.EVT_MENU, self.on_about, id=ID_ABOUT)
        
        entries = [wx.AcceleratorEntry() for i in range(4)]
        
        entries[0].Set(wx.ACCEL_CTRL, ord('N'),
                       ID_NEW_WINDOW, exit_menu_item)
        entries[1].Set(wx.ACCEL_CTRL, ord('X'), wx.ID_EXIT)
        entries[2].Set(wx.ACCEL_SHIFT, ord('A'), ID_ABOUT, 
                       about_menu_item)
        entries[3].Set(wx.ACCEL_NORMAL, wx.WXK_DELETE, wx.ID_CUT)
        
        accel_tbl = wx.AcceleratorTable(entries)
        self.SetAcceleratorTable(accel_tbl)
        
    def on_new_window(self, event):
        """"""
        print("You pressed CTRL+N!")
        
    def on_about(self, event):
        print('You pressed SHIFT+A')


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

First of all, I want to note that I don’t have all the accelerators hooked up. For example, “CTRL+X” won’t actually exit the program. But I did go ahead and hook up “CTRL+N” and “SHIFT+A”. Try running the code and see how it works.

You can also be slightly more explicit and create your AcceleratorEntry() objects one by one instead of using a list comprehension. Let’s modify our code a bit and see how that works:

import wx

class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, 
                          title="AcceleratorEntry Tutorial", 
                          size=(500,500))

        # Add a panel so it looks the correct on all platforms
        panel = wx.Panel(self, wx.ID_ANY)
        
        exit_menu_item = wx.MenuItem(id=wx.NewId(), text="Exit",
                               helpString="Exit the application")
        about_menu_item = wx.MenuItem(id=wx.NewId(), text='About')

        ID_NEW_WINDOW = wx.NewId()
        ID_ABOUT = wx.NewId()
        
        self.Bind(wx.EVT_MENU, self.on_new_window, id=ID_NEW_WINDOW)
        self.Bind(wx.EVT_MENU, self.on_about, id=ID_ABOUT)
        
        entry_one = wx.AcceleratorEntry(wx.ACCEL_CTRL, ord('N'),
                                        ID_NEW_WINDOW, 
                                        exit_menu_item)
        entry_two = wx.AcceleratorEntry(wx.ACCEL_SHIFT, ord('A'), 
                                        ID_ABOUT, 
                                        about_menu_item)
        entries = [entry_one, entry_two]
        
        accel_tbl = wx.AcceleratorTable(entries)
        self.SetAcceleratorTable(accel_tbl)
        

    def on_new_window(self, event):
        """"""
        print("You pressed CTRL+N!")
        
    def on_about(self, event):
        print('You pressed SHIFT+A')

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

Frankly I think like this version the best as it’s the most explicit. The “Zen of Python” is always about advocating doing things explicitly over implicitly so I think this also follows that paradigm well.


Wrapping Up

Now you know a couple of different ways to create keyboard shortcuts (accelerators) for your application. They are very handy and can enhance your application’s usefulness.


Related Reading

1 thought on “wxPython: All About Accelerators”

  1. Pingback: wxPython: All About Accelerators | The Mouse Vs. The Python - Codango.Com, Codango & Codango Labs

Comments are closed.