Menus and Toolbars
The menu bar and the toolbar are the two standard action surfaces of an application window: the menu bar at the top with its cascading menus, and the toolbar with its icon buttons. Both are built from the same parts - the action items (the MenuItem in a menu, the ToolButton in a toolbar) wired to the same handlers - and both support the accelerators (the keyboard shortcuts, the Ctrl+O that both the menu label and the accelerator group display). This chapter builds the menu bar for the running example (File, Edit, Help), the accelerators that bind the shortcuts and label the items, the toolbar with its buttons and separators, and the tear-off and the stock-item conveniences.
Building the Menu Bar
import gtk
def build_menubar():
menubar = gtk.MenuBar()
# the File menu:
file_menu = gtk.Menu()
file_item = gtk.MenuItem("File")
file_item.set_submenu(file_menu)
# the items - each is a MenuItem wired to a handler
# (the user_data carries the action the handler dispatches)
new_item = gtk.MenuItem("_New") # the _ marks the
file_menu.append(new_item) # accelerator (Alt+N)
new_item.connect("activate", on_action, "new")
file_menu.append(gtk.SeparatorMenuItem()) # the divider
open_item = gtk.MenuItem("_Open...")
file_menu.append(open_item)
open_item.connect("activate", on_action, "open")
quit_item = gtk.MenuItem("_Quit")
file_menu.append(quit_item)
quit_item.connect("activate", on_action, "quit")
menubar.append(file_item)
# the Edit and Help menus: the same construction
edit_menu = gtk.Menu()
edit_item = gtk.MenuItem("_Edit")
edit_item.set_submenu(edit_menu)
find_item = gtk.MenuItem("_Find...")
edit_menu.append(find_item)
find_item.connect("activate", on_action, "find")
menubar.append(edit_item)
# the menu bar is packed at the top of the window's vbox:
# window_vbox.pack_start(menubar, False, False, 0)
return menubar
def on_action(item, action):
# the one dispatcher: the user_data names the action
print("action:", action)
# dispatch: if action == "open": open_dialog() ...
The menu bar is a MenuBar whose children are the top-level MenuItems, each with a submenu (a Menu) holding the action items. The construction is the repeat: a Menu for the cascade, the top-level MenuItem with set_submenu, the action items appended into the menu (with the SeparatorMenuItem as the visual divider between groups), and each action item connected to the handler with the user_data naming the action - the single on_action dispatcher that switches on the name, which keeps the menu construction declarative and the behavior in one place. The activate signal is the menu item's "clicked" - it fires when the item is chosen, and the handler receives the item and the user data. The _ in the label is the mnemonic marker (the underlined letter, the Alt+N access key), and the menu bar is packed at the top of the window's layout as the fixed, non-growing child the containers chapter's three-pack leaves for it.
Accelerators: The Keyboard Shortcuts
import gtk
import gdk
# the accel group - the object that binds shortcuts to items
accel = gtk.AccelGroup()
window.add_accel_group(accel)
# bind Ctrl+O to the open item (the LABEL shows the shortcut):
open_item.add_accelerator(
"activate", accel,
ord("o"), gdk.CONTROL_MASK,
gtk.ACCEL_VISIBLE) # VISIBLE: show "Ctrl+O" in the label
# bind Ctrl+Q to quit:
quit_item.add_accelerator(
"activate", accel,
ord("q"), gdk.CONTROL_MASK,
gtk.ACCEL_VISIBLE)
# the ACCEL_VISIBLE flag is what makes the shortcut appear
# in the menu label (the right-aligned "Ctrl+O" text); without
# it the shortcut works but is not shown
# the modifier masks: CONTROL_MASK, SHIFT_MASK, MOD1_MASK
# (the Alt key), combined with | for the multi-modifier shortcuts
The accelerator is the keyboard shortcut bound to a menu item's signal, and the AccelGroup is the object that owns the bindings - the window add_accel_groups it, and each item's add_accelerator binds a key (with its modifier mask) to the item's activate signal through that group. The ACCEL_VISIBLE flag is the detail that matters for the UI: it makes the shortcut render in the menu label (the right-aligned "Ctrl+O"), so the menu documents its own shortcuts, while a binding without the flag works but is invisible. The key is the gdk key constant (or ord of a letter), the mask is the modifier (CONTROL_MASK, SHIFT_MASK, MOD1_MASK for Alt, combined with |), and the signal is almost always activate - the same signal the mouse click fires, which is why the shortcut and the click are the same action to the handler. The accelerator group is the mechanism that makes Ctrl+O open the file exactly as the menu's Open item does, and the VISIBLE flag is what keeps the menu and the keyboard in agreement on the screen.
The Toolbar
import gtk
def build_toolbar():
tb = gtk.Toolbar()
# the tool buttons - the stock icons do the image + label:
tb.insert(gtk.ToolButton(stock_id=gtk.STOCK_NEW), 0)
tb.insert(gtk.ToolButton(stock_id=gtk.STOCK_OPEN), 1)
tb.insert(gtk.ToolButton(stock_id=gtk.STOCK_SAVE), 2)
tb.insert(gtk.SeparatorToolItem(), 3) # the divider
tb.insert(gtk.ToolButton(stock_id=gtk.STOCK_CUT), 4)
tb.insert(gtk.ToolButton(stock_id=gtk.STOCK_COPY), 5)
tb.insert(gtk.ToolButton(stock_id=gtk.STOCK_PASTE), 6)
# the same handlers as the menu: the button's "clicked"
tb.get_item(1).connect("clicked", on_action, "open")
# the icon size: the toolbar scales its icons to the
# theme's toolbar size
tb.set_icon_size(gtk.ICON_SIZE_LARGE_TOOLBAR)
# the arrow: when the toolbar is too narrow, the overflow
# items collapse into the dropdown arrow automatically
return tb
# the toolbar and the menu share the handlers: the same
# on_action dispatcher, the same action names - the two
# surfaces are two presentations of the same command set
The Toolbar is the icon-button surface, and its items are the ToolButtons (the SeparatorToolItem for the dividers), inserted at positions. The stock-item form - ToolButton(stock_id=gtk.STOCK_OPEN) - is the convenience that gives the button the theme's standard icon and label for the standard action (the new, open, save, cut, copy, paste set), which is the fast path for the common toolbar; the custom-icon form (a set_icon_name with a theme icon name) is the path for the actions that are not stock. The toolbar's clicked signal is wired to the same on_action dispatcher the menu uses - the toolbar and the menu are two presentations of the same command set, and the shared handlers are the discipline that keeps them from diverging. The overflow is automatic: when the window is too narrow for all the buttons, the toolbar collapses the trailing items into a dropdown arrow, and the set_icon_size scales the icons to the theme's toolbar size. The toolbar is packed in the window's layout below the menu bar, as the second fixed child of the three-pack.
The Working Rules
- One dispatcher for the commands. The menu items and the toolbar buttons all carry the action name as user data and call the same
on_action- the two surfaces are the same command set, and the divergence between them is the bug the shared dispatcher prevents. - The accelerators use
ACCEL_VISIBLE. The shortcut that works but is not shown in the menu is the half-finished binding; the visible flag is what documents the shortcut where the user looks for it. - The
AccelGroupis added to the window. A binding on a group the window does not own does not fire - theadd_accel_groupis the step that makes the shortcuts live. - The stock items for the standard actions, the icon names for the custom ones. The stock set (new, open, save, the clipboard) is the theme's canonical presentation; the
set_icon_nameis for the app-specific commands. - The menu bar and toolbar are the fixed, non-growing children of the window's top-level layout - they take their natural height and the main area grows around them.
Summary
The menu bar is the MenuBar of top-level MenuItems, each owning a Menu of action items (with the SeparatorMenuItem dividers), and the toolbar is the Toolbar of ToolButtons (the stock items for the standard actions, the icon names for the custom) - and the two are the same command set, wired to the one on_action dispatcher through the activate (menu) and clicked (toolbar) signals with the action name as user data. The accelerators are the keyboard bindings: the AccelGroup added to the window, the add_accelerator on each item with the key and modifier mask, and the ACCEL_VISIBLE flag that renders the shortcut in the menu label. The construction is the repeat - the menu cascade, the item, the connection - and the discipline is the shared handlers and the visible accelerators, which keep the two surfaces and the keyboard in agreement on the commands the app offers.