Table of Contents
Quite a few new widgets and support objects were added in PyGTK 2.4 including:
The Action and ActionGroup objects work together to provide the images, text, callbacks and accelerators for your application menus and toolbars. The UIManager uses Actions and ActionGroups to build the menubars and toolbars automatically based on a XML specification. It's much easier to create and populate menus and toolbars using the UIManager described in a later section. The following sections on the Action and ActionGroup objects describe how to directly apply these objects but I recommend using the UIManager whenever possible.
An Action object represents an action that the user can take using an application user interface. It contains information used by proxy UI elements (for example, MenuItems or Toolbar items) to present the action to the user. There are two subclasses of Action:
|ToggleAction||An Action that can be toggled between two states.|
|RadioAction||An Action that can be grouped so that only one can be active.|
For example, the standard-> menu item can be represented with an icon, mnemonic text and accelerator. When activated, the menu item triggers a callback that could exit the application. Likewise a Toolbar button could share the icon, mnemonic text and callback. Both of these UI elements could be proxies of the same Action.
Ordinary Button, ToggleButton and RadioButton widgets can also act as proxies for an Action though there is no support for these in the UIManager.
An Action can be created using the constructor:
action = gtk.Action(name, label, tooltip, stock_id)
name is a string used to identify the Action in an ActionGroup or in a UIManager specification. label and tooltip are strings used as the label and tooltip in proxy widgets. If label is None then the stock_id must be a string specifying a Stock Item to get the label from. If tooltip is None the Action will not have a tooltip.
As we'll see in Section 16.1.2, “ActionGroups” it's much easier to create Action objects using the ActionGroup convenience methods:
actiongroup.add_actions(entries, user_data=None) actiongroup.add_toggle_actions(entries, user_data=None) actiongroup.add_radio_actions(entries, value=0, on_change=None, user_data=None)
More about these later but first I'll describe how to use an Action with a Button to illustrate the basic operations of connecting an Action to a proxy widget.
The basic procedure for using an Action with a Button proxy is illustrated by the simpleaction.py example program. The Button is connected to the Action using the method:
where proxy is a MenuItem, ToolItem or Button widget.
An Action has one signal the "activate" signal that is triggered when the Action is activated usually as the result of a proxy widget being activated (for example a ToolButton is clicked). You just have connect a callback to this signal to handle the activation of any of the proxy widgets.
The source code for the simpleaction.py example program is:
1 #!/usr/bin/env python 2 3 import pygtk 4 pygtk.require('2.0') 5 import gtk 6 7 class SimpleAction: 8 def __init__(self): 9 # Create the toplevel window 10 window = gtk.Window() 11 window.set_size_request(70, 30) 12 window.connect('destroy', lambda w: gtk.main_quit()) 13 14 # Create an accelerator group 15 accelgroup = gtk.AccelGroup() 16 # Add the accelerator group to the toplevel window 17 window.add_accel_group(accelgroup) 18 19 # Create an action for quitting the program using a stock item 20 action = gtk.Action('Quit', None, None, gtk.STOCK_QUIT) 21 # Connect a callback to the action 22 action.connect('activate', self.quit_cb) 23 24 # Create an ActionGroup named SimpleAction 25 actiongroup = gtk.ActionGroup('SimpleAction') 26 # Add the action to the actiongroup with an accelerator 27 # None means use the stock item accelerator 28 actiongroup.add_action_with_accel(action, None) 29 30 # Have the action use accelgroup 31 action.set_accel_group(accelgroup) 32 33 # Connect the accelerator to the action 34 action.connect_accelerator() 35 36 # Create the button to use as the action proxy widget 37 quitbutton = gtk.Button() 38 # add it to the window 39 window.add(quitbutton) 40 41 # Connect the action to its proxy widget 42 action.connect_proxy(quitbutton) 43 44 window.show_all() 45 return 46 47 def quit_cb(self, b): 48 print 'Quitting program' 49 gtk.main_quit() 50 51 if __name__ == '__main__': 52 sa = SimpleAction() 53 gtk.main()
The example creates an Action (line 20) that uses a Stock Item to provide the label text with mnemonic, icon, accelerator and translation domain. If a Stock Item is not used you'll need to specify a label instead. Line 22 connects the "activate" signal of action to the self.quit_cb() method so that it is invoked when the Action is activated by quitbutton. Line 42 connects quitbutton to action as a proxy widget. When quitbutton is clicked it will activate action and thereby invoke the self.quit_cb() method. The simpleaction.py example uses quite a bit of code (lines 15, 17, 31 and 34 to setup the accelerator for the Button. The procedure is similar for MenuItems and Toolbar ToolItems.
In the previous section we saw that an existing widget could be connected to an Action as a proxy. In this section we'll see how a proxy widget can be created using the Action methods:
menuitem = action.create_menu_item() toolitem = action.create_tool_item()
The basicaction.py example illustrates a MenuItem, ToolButton and a Button sharing an Action. The MenuItem and the ToolButton are created using the above methods. The basicaction.py example program source code is:
1 #!/usr/bin/env python 2 3 import pygtk 4 pygtk.require('2.0') 5 import gtk 6 7 class BasicAction: 8 def __init__(self): 9 # Create the toplevel window 10 window = gtk.Window() 11 window.connect('destroy', lambda w: gtk.main_quit()) 12 vbox = gtk.VBox() 13 vbox.show() 14 window.add(vbox) 15 16 # Create an accelerator group 17 accelgroup = gtk.AccelGroup() 18 # Add the accelerator group to the toplevel window 19 window.add_accel_group(accelgroup) 20 21 # Create an action for quitting the program using a stock item 22 action = gtk.Action('Quit', '_Quit me!', 'Quit the Program', 23 gtk.STOCK_QUIT) 24 action.set_property('short-label', '_Quit') 25 # Connect a callback to the action 26 action.connect('activate', self.quit_cb) 27 28 # Create an ActionGroup named BasicAction 29 actiongroup = gtk.ActionGroup('BasicAction') 30 # Add the action to the actiongroup with an accelerator 31 # None means use the stock item accelerator 32 actiongroup.add_action_with_accel(action, None) 33 34 # Have the action use accelgroup 35 action.set_accel_group(accelgroup) 36 37 # Create a MenuBar 38 menubar = gtk.MenuBar() 39 menubar.show() 40 vbox.pack_start(menubar, False) 41 42 # Create the File Action and MenuItem 43 file_action = gtk.Action('File', '_File', None, None) 44 actiongroup.add_action(file_action) 45 file_menuitem = file_action.create_menu_item() 46 menubar.append(file_menuitem) 47 48 # Create the File Menu 49 file_menu = gtk.Menu() 50 file_menuitem.set_submenu(file_menu) 51 52 # Create a proxy MenuItem 53 menuitem = action.create_menu_item() 54 file_menu.append(menuitem) 55 56 # Create a Toolbar 57 toolbar = gtk.Toolbar() 58 toolbar.show() 59 vbox.pack_start(toolbar, False) 60 61 # Create a proxy ToolItem 62 toolitem = action.create_tool_item() 63 toolbar.insert(toolitem, 0) 64 65 # Create and pack a Label 66 label = gtk.Label(''' 67 Select File->Quit me! or 68 click the toolbar Quit button or 69 click the Quit button below or 70 press Control+q 71 to quit. 72 ''') 73 label.show() 74 vbox.pack_start(label) 75 76 # Create a button to use as another proxy widget 77 quitbutton = gtk.Button() 78 # add it to the window 79 vbox.pack_start(quitbutton, False) 80 81 # Connect the action to its proxy widget 82 action.connect_proxy(quitbutton) 83 # Have to set tooltip after toolitem is added to toolbar 84 action.set_property('tooltip', action.get_property('tooltip')) 85 tooltips = gtk.Tooltips() 86 tooltips.set_tip(quitbutton, action.get_property('tooltip')) 87 88 window.show() 89 return 90 91 def quit_cb(self, b): 92 print 'Quitting program' 93 gtk.main_quit() 94 95 if __name__ == '__main__': 96 ba = BasicAction() 97 gtk.main()
This example introduces an ActionGroup to hold the Actions used in the program. Section 16.1.2, “ActionGroups” will go into more detail on the use of ActionGroups.
The code in lines 9-14 sets up a top level window containing a VBox. Lines 16-35 set up the "Quit" Action similar to that in the simpleaction.py example program and add it with the gtk.STOCK_QUIT Stock Item accelerator (line 32) to the "BasicAction" ActionGroup (created in line 29). Note that, unlike the simpleaction.py example program, you don't have to call the connect_accelerator() method for the action since it is called automatically when the create_menu_item() method is called in line 53.
Lines 38-40 create a MenuBar and pack it into the VBox. Lines 43-44 create an Action (file_action) for themenu and add it to actiongroup. The and menu items are created in lines 45 and 53 and added to menubar and file_menu respectively in lines 46 and 54.
Likewise a Toolbar is created and added to the VBox in lines 57-59. The proxy ToolItem is created and added to toolbar in lines 62-63. Note the Action tooltip must be set (line 84) after the ToolItem is added to the Toolbar for it to be used. Also the Button tooltip must be added manually (lines 84-86).
A proxy widget can be disconnected from an Action by using the method:
An Action has a number of properties that control the display and function of its proxy widgets. The most important of these are the "sensitive" and "visible" properties. The "sensitive" property determines the sensitivity of the proxy widgets. If "sensitive" is FALSE the proxy widgets are not activatable and will usually be displayed "grayed out". Likewise, the "visible" property determines whether the proxy widgets will be visible. If an Action's "visible" property is FALSE its proxy widgets will be hidden.
As we'll see in the next section, an Action's sensitivity or visibility is also controlled by the sensitivity or visibility of the ActionGroup it belongs to. Therefore, for an Action to be sensitive (or visible) both it and its ActionGroup must be sensitive (or visible). To determine the effective sensitivity or visibility of an Action you should use the following methods:
result = action.is_sensitive() result = action.is_visible()
The name assigned to an Action is contained in its "name" property which is set when the Action is created. You can retrieve that name using the method:
name = action.get_name()
Other properties that control the display of the proxy widgets of an Action include:
|"hide-if-empty"||If TRUE, empty menu proxies for this action are hidden.|
|"is-important"||If TRUE, ToolItem proxies for this action show text in gtk.TOOLBAR_BOTH_HORIZ mode.|
|"visible-horizontal"||If TRUE, the ToolItem is visible when the toolbar is in a horizontal orientation.|
|"visible-vertical"||If TRUE, the ToolItem is visible when the toolbar is in a vertical orientation.|
Other properties of interest include:
|"label"||The label used for menu items and buttons that activate this action.|
|"short-label"||A shorter label that may be used on toolbar buttons and buttons.|
|"stock-id"||The Stock Item to be used to retrieve the icon, label and accelerator to be used in widgets representing this action.|
|"tooltip"||A tooltip for this action.|
Note that the basicaction.py example program overrides the gtk.STOCK_QUIT label with "_Quit me!" and sets the "short-label" property to "_Quit". The short label is used for the ToolButton and the Button labels but the full label is used for the MenuItem label. Also note that the tooltip cannot be set on a ToolItem until it is added to a Toolbar.
An Action has three methods that are used to set up an accelerator:
action.set_accel_group(accel_group) action.set_accel_path(accel_path) action.connect_accelerator()
These, in conjunction with the gtk.ActionGroup.add_action_with_accel() method, should cover most cases of accelerator set up.
An AccelGroup must always be set for an Action. The set_accel_path() method is called by the gtk.ActionGroup.add_action_with_accel() method. If set_accel_path() is used the accelerator path should match the default format: "<Actions>/actiongroup_name/action_name". Finally, the connect_accelerator() method is called to complete the accelerator set up.
An Action must have an AccelGroup and an accelerator path associated with it before connect_accelerator() is called.
Since the connect_accelerator() method can be called several times (i.e. once for each proxy widget), the number of calls is counted so that an equal number of disconnect_accelerator() calls must be made before removing the accelerator.
As illustrated in the previous example programs, an Action accelerator can be used by all the proxy widgets. An Action should be part of an ActionGroup in order to use the default accelerator path that has the format: "<Actions>/actiongroup_name/action_name". The easiest way to add an accelerator is to use the gtk.ActionGroup.add_action_with_accel() method and the following general procedure:
Any proxy widgets created by or connected to the Action will use the accelerator.
As mentioned previously a ToggleAction is a subclass of Action that can be toggled between two states. The constructor for a ToggleAction takes the same parameters as an Action:
toggleaction = gtk.ToggleAction(name, label, tooltip, stock_id)
In addition to the Action methods the following ToggleAction methods:
toggleaction.set_active(is_active) is_active = toggleaction.get_active()
set and get the current state of toggleaction. is_active is a boolean value.
You can connect to the "toggled" signal specifying a callback with the signature:
def toggled_cb(toggleaction, user_data)
The "toggled" signal is emitted when the ToggleAction changes state.
A MenuItem proxy widget of a ToggleAction will be displayed like a CheckMenuItem by default. To have the proxy MenuItem displayed like a RadioMenuItem set the "draw-as-radio" property to TRUE using the method:
You can use the following method to determine whether the ToggleAction MenuItems will be displayed like RadioMenuItems:
draw_as_radio = toggleaction.get_draw_as_radio()
A RadioAction is a subclass of ToggleAction that can be grouped so that only one RadioAction is active at a time. The corresponding proxy widgets are the RadioMenuItem and RadioToolButton.
The constructor for a RadioAction takes the same arguments as an Action with the addition of a unique integer value that is used to identify the active RadioAction in a group:
radioaction = gtk.RadioAction(name, label, tooltip, stock_id, value)
The group for a RadioAction can be set using the method:
where group is another RadioAction that radioaction should be grouped with. The group containing a RadioAction can be retrieved using the method:
group = radioaction.get_group()
that returns a list of the group of RadioAction objects that includes radioaction.
The value of the currently active group member can retrieved using the method:
active_value = radioaction.get_current_value()
You can connect a callback to the "changed" signal to be notified when the active member of the RadioAction group has been changed. Note that you only have to connect to one of the RadioAction objects to track changes. The callback signature is:
def changed_cb(radioaction, current, user_data)
where current is the currently active RadioAction in the group.
This example is similar enough to the basicaction.py example program that a detailed description is not necessary.
As mentioned in the previous section, related Action objects should be added to an ActionGroup to provide common control over their visibility and sensitivity. For example, in a text processing application the menu items and toolbar buttons for specifying the text justification could be contained in an ActionGroup. A user interface is expected to have multiple ActionGroup objects that cover various aspects of the application. For example, global actions like creating new documents, opening and saving a document and quitting the application likely form one ActionGroup while actions such as modifying the view of the document would form another.
An ActionGroup is created using the constructor:
actiongroup = gtk.ActionGroup(name)
where name is a unique name for the ActionGroup. The name should be unique because it is used to form the default accelerator path for its Action objects.
The ActionGroup name can be retrieved using the method:
name = actiongroup.get_name()
or by retrieving the contents of the "name" property.
As illustrated in Section 16.1.1, “Actions” an existing Action can be added to an ActionGroup using one of the methods:
actiongroup.add_action(action) actiongroup.add_action_with_accel(action, accelerator)
where action is the Action to be added and accelerator is a string accelerator specification acceptable to gtk.accelerator_parse(). If accelerator is None the accelerator (if any) associated with the "stock-id" property of action will be used. As previously noted the add_action_wih_accel() method is preferred if you want to use accelerators.
The ActionGroup offers three convenience methods that make the job of creating and adding Action objects to an ActionGroup much easier:
actiongroup.add_actions(entries, user_data=None) actiongroup.add_toggle_actions(entries, user_data=None) actiongroup.add_radio_actions(entries, value=0, on_change=None, user_data=None)
The entries parameter is a sequence of action entry tuples that provide the information used to create the actions that are added to the ActionGroup. The RadioAction with the value of value is initially set active. on_change is a callback that is connected to the "changed" signal of the first RadioAction in the group. The signature of on_changed is:
def on_changed_cb(radioaction, current, user_data)
The entry tuples for Action objects contain:
You must minimally specify a value for the name field and a value in either the stock id field or the label field. If you specify a label then you can specify None for the stock id if you aren't using one. For example the following method call:
actiongroup.add_actions([('quit', gtk.STOCK_QUIT, '_Quit me!', None, 'Quit the Program', quit_cb)])
adds an action to actiongroup for exiting a program.
The entry tuples for the ToggleAction objects are similar to the Action entry tuples except there is an additional optional flag field containing a boolean value indicating whether the action is active. The default value for the flag field is FALSE. For example the following method call:
actiongroup.add_toggle_actions([('mute, None, '_Mute', '<control>m', 'Mute the volume', mute_cb, True)])
adds a ToggleAction to actiongroup and sets it to be initially active.
The entry tuples for the RadioAction objects are similar to the Action entry tuples but specify a value field instead of a callback field:
For example the following code fragment:
radioactionlist = [('am', None, '_AM', '<control>a', 'AM Radio', 0) ('fm', None, '_FM', '<control>f', 'FM Radio', 1) ('ssb', None, '_SSB', '<control>s', 'SSB Radio', 2)] actiongroup.add_radio_actions(radioactionlist, 0, changed_cb)
creates three RadioAction objects and sets the initial active action to 'am' and the callback that is invoked when any of the actions is activated to changed_cb.
An Action can be retrieved by name from an ActionGroup by using the method:
action = actiongroup.get_action(action_name)
A list of all the Action objects contained in an ActionGroup can be retrieved using the method:
actionlist = actiongroup.list_actions()
The sensitivity and visibility of all Action objects in an ActionGroup can be controlled by setting the associated property values. The following convenience methods get and set the properties:
is_sensitive = actiongroup.get_sensitive() actiongroup.set_sensitive(sensitive) is_visible = actiongroup.get_visible() actiongroup.set_visible(visible)
Finally you can remove an Action from an ActionGroup using the method:
The actiongroup.py example program duplicates the menubar and toolbar of the actions.py example program using the ActionGroup methods. In addition the program provides buttons to control the sensitivity and visibility of the menu items and toolbar items. Figure 16.4, “ActionGroup Example” illustrates the program in operation:
Your application can track the connection and removal of proxy widgets to the Action objects in an ActionGroup using the "connect-proxy" and disconnect-proxy" signals. The signatures of your signal handler callbacks should be:
def connect_proxy_cb(actiongroup, action, proxy, user_params) def disconnect_proxy_cb(actiongroup, action, proxy, user_params)
For example, you might want to track these changes to make some additional changes to the properties of the new proxy widget when it is connected or to update some other part of the user interface when a proxy widget is disconnected.
The "pre-activate" and "post-activate" signals allow your application to do some additional processing immediately before or after an action is activated. The signatures of the signal handler callbacks should be:
def pre_activate_cb(actiongroup, action, user_params) def post_activate_cb(actiongroup, action, user_params)
These signals are mostly used by the UIManager to provide global notification for all Action objects in ActionGroup objects used by it.