Chapter 13. Actions: menus, toolbars and accelerators

Table of Contents
Actions
Menus
Toolbars
Keyboard accelerators
Setting an application icon

In this chapter we investigate adding the command structure to the application framework we developed in Chapter 12. This consists of QAction objects that are added to toolbars and menu bars, and that can provide keyboard accelerators.

Creating QActions and populating your toolbars and menus with them is not an exciting task, but it is necessary for any GUI application. Let's take a deeper look at the possibilities of actions, toolbars and menus.

Actions

We first encountered the QAction class in Chapter 10. To recap briefly, QAction is a model of a well-defined action that a user can perpetrate against your application, or the data of your application models. This a very powerful concept. Previously, you would have to create menu items and connect them to slots, create toolbar items and connect them to slots, and create keyboard shortcuts and connect them to slots, too.

Keeping everything synchronized is difficult, and the whole process often leads to a horrible mess. Combine this with the possibility that you might want to disable some action—such as redo, when there's nothing to redo—and you're suddenly writing lots of duplicated code. And then, of course, you get to the point where your users want to modify the contents of the toolbar...

By bringing all the user-interface characteristics of actions together in the QAction class, most of the mess can be avoided. The QAction class tracks the following interface elements:

For each of these properties, there is a set function; although you can also set some properties in the constructor of QAction. Here is an annotated example of a complete QAction definition:

Example 13-1. Defining a complex toggle action

    planAction=QAction(self)                               (1)
    planAction.setIconSet(QIconSet(QPixmap(plan)))         (2)
    planAction.setText("Plan")                             (3)
    planAction.setMenuText("&Plan ...")                    (4)
    planAction.setOn(0)
    planAction.setStatusTip("Enable the cunning plan")     (6)
    planAction.setToolTip("Enables the cunning plan")      (7)
    planAction.setWhatsThis(                               (8)
"""Plan

Selecting plan enables the cunning plan 
for this window.""")
    planAction.setAccel(QAccel.stringToKey("CTRL+C"),)     (9)
    planAction.setToggleAction(1)                          (10)
        
(1)
There are three constructors that create a QAction with some of the properties already set. However, I find it nicer to create an empty QAction and set everything by hand. Besides, the next thing we would like to do is to read in the definition of a QAction from an XML file or a Python dictionary, and automatically generate the actions. If you want to do this, it is just as easy to set every property separately.

One important parameter is the parent to the QAction. You can create groups of QActions — QActionGroups. A QActionGroup is a type of QAction that functions like a type of QGroupBox: it can group actions into mutually exclusive choices. The whole group of actions can be added to a menu.

(2)
The set of icons you associate with an action is used to paint the toolbar buttons. They are also used to decorate the pull-down menus. QIconSet can generate icons in different sizes and with an active, normal or disabled look all by itself, based on one icon—or you can explicitly set icons of different sizes. You can provide a user option to toggle QMainWindow.setUsesBigPixmaps on or off. This decides whether the toolbars will be drawn with big icons, or small ones.
(3)
This is a generic text that can be used where a more specific text hasn't been set. For instance, if you enable captions in toolbars with QMainWindow.setUsesTextLabel(), then this text will be used. It will also be used for pulldown menu texts, unless you set those explicitly with setMenuText().
(4)
By setting the menutext explicitly, you can add keyboard shortcuts (like alt-p in this case), or the three dots that indicate that a dialog window will open. You wouldn't want the shortcut to show up on toolbar captions—they don't work there, so you can set them in the menu text, by prefixing an ampersand (&) to the shortcut letter.
(5)
Sometimes, a certain action toggles an application state on or off. Examples include setting toolbar captions of toolbar icon sizes, or bold or normal fonts. By grouping actions in action groups, you can create mutually exclusive groups (such as centered, justified or ragged text in a text editor).
(6)
This text appears in the statusbar of the mainwindow when the user presses but doesn't release the menu option or toolbar button associated with the action.
(8)
This is a longer text that appears in the form of a yellow note when the user presses the optional ‘what's this' button, which appears on the toolbar.
(9)
The keyboard accelerator that activates this action—for instance, pressing the control and the s key together—will trigger the activated() signal, which might be connected to the save() slot function. It is easiest to construct this using QAccel's stringToKey() function. Not only is this very convenient, it can also be translated to languages where a different shortcut key is preferred, by using either pygettext or PyQt's tr(). See Chapter 25 for more information on internationalizing an application.
(10)
Of course, if you create a toggle action, it is nice to be able to set the initial state—which is what this does.

The QAction class can emit two signals:

By connecting these signals to the correct slots (either directly in the document, or proxy slots defined in the application interface), you have encapsulated the entire behavior of your interface.

        self.connect(planaction,
                     SIGNAL("activated()"),
                     self.slotExecuteCunningPlan)
      

or, for a toggle action:

        self.connect(planaction,
                     SIGNAL("toggled(bool)"),
                     self.slotActivateCunningPlan)
      

All that remains, is to add the actions to pulldown menus or toolbars:

        self.planaction.addTo(self.planMenu)
        self.planaction.addTo(self.toolBar)