10.11. Toolbar

Toolbars are usually used to group some number of widgets in order to simplify customization of their look and layout. Typically a toolbar consists of buttons with icons, labels and tooltips, but any other widget can also be put inside a toolbar. Finally, items can be arranged horizontally or vertically and buttons can be displayed with icons, labels, or both.

Creating a toolbar is (as one may already suspect) done with the following function:
 
toolbar = GtkToolbar(orientation, style)

where orientation may be one of:
 
  ORIENTATION_HORIZONTAL    
  ORIENTATION_VERTICAL

and style one of:
 
  TOOLBAR_TEXT
  TOOLBAR_ICONS
  TOOLBAR_BOTH

The style applies to all the buttons created with the `item' methods (not to buttons inserted into toolbar as separate widgets).

After creating a toolbar one can append, prepend and insert items (that means simple text strings) or elements (that means any widget types) into the toolbar. To describe an item we need a label text, a tooltip text, a private tooltip text, an icon for the button and a callback for it. For example, to append or prepend an item you may use the following methods:
 
toolbar.append_item(text, tooltip_text, tooltip_private_text, icon, callback, user_data)

toolbar.prepend_item(text, tooltip_text, tooltip_private_text, icon, callback, user_data)

If you want to use the insert_item() method, the only additional parameter which must be specified is the position in which the item should be inserted, thus:
 
toolbar.insert_item(text, tooltip_text, tooltip_private_text, icon, callback, user_data, position)

To simplify adding spaces between toolbar items, you may use the following methods:
 
toolbar.append_space()

toolbar.prepend_space()

toolbar.insert_space(position)

While the size of the added space can be set globally for a whole toolbar with the method:
 
toolbar.set_space_size(space_size)

If it's required, the orientation of a toolbar and its style can be changed "on the fly" using the following methods:
 
toolbar.set_orientation(orientation)

toolbar.set_style(style)

toolbar.set_tooltips(enable)

Where orientation is one of ORIENTATION_HORIZONTAL or ORIENTATION_VERTICAL. The style is used to set appearance of the toolbar items by using one of TOOLBAR_ICONS, TOOLBAR_TEXT, or TOOLBAR_BOTH.

To show some other things that can be done with a toolbar, let's take the toolbar.py example program (we'll interrupt the listing with some additional explanations):
 
    1   #!/usr/bin/env python
    2   
    3   # example toolbar.py
    4   
    5   import gtk
    6   import gtkxpm
    7   
    8   class ToolbarExample:
    9       # This method is connected to the Close button or
   10       # closing the window from the WM
   11       def delete_event(self, widget, event=None):
   12           gtk.mainquit()
   13           return gtk.FALSE
   14   

The above beginning seems should be familiar to you if it's not your first PyGTK program. There is one additional thing though, we import a nice XPM picture to serve as an icon for all of the buttons. Line 8 starts the ToolbarExample class and lines 11-13 define the callback method which will terminate the program.
 
   15       # that's easy... when one of the buttons is toggled, we just
   16       # check which one is active and set the style of the toolbar
   17       # accordingly
   18       # But because we created and passed the radio buttons and toggle button
   19       # as widgets we have to explicitly hide() and show() the icons and
   20       # labels.  Wouldn't have to do this if the GtkToolbar.*_element methods
   21       # were available in PyGTK
   22       def radio_event(self, widget, data):
   23           widgets = (self.text_button, self.icon_button,
   24                      self.both_button, self.tooltips_button)
   25           if self.text_button.active: 
   26               self.toolbar.set_style(gtk.TOOLBAR_TEXT)
   27               for w in widgets:
   28                   w.children()[0].children()[0].hide()
   29                   w.children()[0].children()[1].show()
   30           elif self.icon_button.active:
   31               self.toolbar.set_style(gtk.TOOLBAR_ICONS)
   32               for w in widgets:
   33                   w.children()[0].children()[0].show()
   34                   w.children()[0].children()[1].hide()
   35           elif self.both_button.active:
   36               self.toolbar.set_style(gtk.TOOLBAR_BOTH)
   37               for w in widgets:
   38                   w.children()[0].children()[0].show()
   39                   w.children()[0].children()[1].show()
   40   
   41       # even easier, just check given toggle button and enable/disable 
   42       # tooltips
   43       def toggle_event(self, widget, data):
   44           self.toolbar.set_tooltips(widget.active)
   45
   46       # helper method to create a radiobutton that can be put in a toolbar
   47       # as a widget
   48       def create_radiobutton(self, toolbar, prevbutton, labeltext,
   49                              icon, cb, data):
   50           radiobutton = gtk.GtkRadioButton(prevbutton)
   51           radiobutton.set_relief(toolbar.get_button_relief())
   52           radiobutton.set_mode(gtk.FALSE)
   53           radiobutton.unset_flags(gtk.CAN_FOCUS)
   54           if cb:
   55               radiobutton.connect("clicked", cb, data)
   56           vbox = gtk.GtkVBox(gtk.FALSE, 0)
   57           radiobutton.add(vbox)
   58           vbox.show()
   59           if labeltext:
   60               label = gtk.GtkLabel(labeltext)
   61               vbox.pack_end(label)
   62               # we have no way to find out what the toolbar style is so we just
   63               # assume that the user set things right
   64               label.show()
   65           if icon:
   66               vbox.pack_end(icon)
   67               icon.show()
   68           radiobutton.show()
   69           return radiobutton
   70   

Lines 22-44 are two callback methods that will be called when one of the buttons on a toolbar is pressed. You should already be familiar with things like this if you've already used toggle buttons (and radio buttons). As noted in the comments, we have to explicitly hide or show the radio buttons and the toggle button because they were added to the toolbar as widgets. Lines 48-69 define a helper method (create_radiobutton()) that creates a radiobutton with an icon and a label and is connected to the given callback.
 
   71       def __init__(self):
   72           # Here is our main window (a dialog) and a handle for the handlebox
   73           # Ok, we need a toolbar, an icon with a mask (one for all of 
   74           # the buttons) and an icon widget to put this icon in (but 
   75           # we'll create a separate widget for each button)
   76           # create a new window with a given title, and nice size
   77           self.dialog = gtk.GtkDialog()
   78           self.dialog.set_title("GTKToolbar Tutorial")
   79           self.dialog.set_usize(600, 300)
   80           self.dialog.allow_shrink = gtk.TRUE
   81   
   82           # typically we quit if someone tries to close us
   83           self.dialog.connect("delete_event", self.delete_event)
   84   
   85           # we need to realize the window because we use pixmaps for 
   86           # items on the toolbar in the context of it
   87           self.dialog.realize()
   88   
   89           # to make it nice we'll put the toolbar into the handle box, 
   90           # so that it can be detached from the main window
   91           handlebox = gtk.GtkHandleBox()
   92           self.dialog.vbox.pack_start(handlebox, gtk.FALSE, gtk.FALSE, 5)
   93   

The above should be similar to any other PyGTK application. Just initialization of a ToolbarExample object instance creating the window, etc. There is only one thing that probably needs some explanation: a handle box. A handle box is just another box that can be used to pack widgets in to. The difference between it and typical boxes is that it can be detached from a parent window (or, in fact, the handle box remains in the parent, but it is reduced to a very small rectangle, while all of its contents are reparented to a new freely floating window). It is usually nice to have a detachable toolbar, so these two widgets occur together quite often.
 
   94           # toolbar will be horizontal, with both icons and text, and
   95           # with 5pxl spaces between items and finally, 
   96           # we'll also put it into our handlebox
   97           toolbar = gtk.GtkToolbar(gtk.ORIENTATION_HORIZONTAL,
   98                                    gtk.TOOLBAR_BOTH)
   99           toolbar.set_border_width(5)
  100           toolbar.set_space_size(5)
  101           handlebox.add(toolbar)
  102           self.toolbar = toolbar
  103   
  104           # now we create icon with mask: we'll reuse it to create
  105           # icon widgets for toolbar items
  106           icon, mask = gtk.create_pixmap_from_xpm_d(
  107               self.dialog.get_window(), self.dialog.get_style().white,
  108               gtkxpm.gtk_xpm)
  109   

Well, what we do above is just a straightforward initialization of the toolbar widget and creation of a GDK pixmap with its mask. If you want to know something more about using pixmaps, refer to GDK documentation or to the Pixmaps section earlier in this tutorial. We save a reference to the toolbar as self.toolbar for later access.
 
   110           # our first item is <close> button
  111           iconw = gtk.GtkPixmap(icon, mask) # icon widget
  112           close_button = toolbar.append_item(
  113               "Close",           # button label
  114               "Closes this app", # this button's tooltip
  115               "Private",         # tooltip private info
  116               iconw,             # icon widget
  117               self.delete_event) # a signal
  118           toolbar.append_space() # space after item
  119   

In the above code you see the simplest case: adding a button to toolbar. Just before appending a new item, we have to construct a pixmap widget to serve as an icon for this item; this step will have to be repeated for each new item. Just after the item we also add a space, so the following items will not touch each other. As you see the append_item() method returns a reference to our newly created button widget, so that we can work with it in the normal way.
 
  120           # now, let's make our radio buttons group... using the
  121           # create_radiobutton helper method
  122           iconw = gtk.GtkPixmap(icon, mask)
  123           icon_button = self.create_radiobutton(
  124               toolbar,
  125               None,                          # button in same radiobutton group
  126               "Icon",                        # label
  127               iconw,                         # icon
  128               self.radio_event,              # signal
  129               toolbar)                       # data for signal
  130           # add the button to the toolbar
  131           toolbar.append_widget(
  132               icon_button,                        # widget
  133               "Only icons in toolbar",       # tooltip
  134               "Private")                     # tooltip private string
  135           toolbar.append_space()
  136           self.icon_button = icon_button
  137   

Here we begin creating a radio buttons group. To do this we use the create_radiobutton() helper method and then add it to the toolbar using the append_widget() method. In the above case we start creating a radio group. In creating other radio buttons for this group a reference to the previous button in the group is required, so that a list of buttons can be easily constructed (see the section on Radio Buttons earlier in this tutorial). We also save a reference to the button in the ToolbarExample instance for later access.
 
  138           # following radio buttons refer to previous ones
  139           iconw = gtk.GtkPixmap(icon, mask)
  140           text_button = self.create_radiobutton(
  141               toolbar,
  142               icon_button,
  143               "Text",
  144               iconw,
  145               self.radio_event,
  146               toolbar)
  147           toolbar.append_widget(
  148               text_button,
  149               "Only texts in toolbar",
  150               "Private")
  151           toolbar.append_space()
  152           self.text_button = text_button
  153   
  154           iconw = gtk.GtkPixmap(icon, mask)
  155           both_button = self.create_radiobutton(
  156               toolbar,
  157               text_button,
  158               "Both",
  159               iconw,
  160               self.radio_event,
  161               toolbar)
  162           toolbar.append_widget(
  163               both_button,
  164               "Icons and text in toolbar",
  165               "Private")
  166           toolbar.append_space()
  167           self.both_button = both_button
  168   

We create the other radiobuttons the same way except we pass one of the create radio group buttons to the create_radiobutton() method to specify the radio group.
 
  169           # here we have just a simple toggle button
  170           iconw = gtk.GtkPixmap(icon, mask)
  171           tooltips_button = gtk.GtkToggleButton()
  172           self.tooltips_button = tooltips_button
  173           tooltips_button.connect("toggled", self.toggle_event, toolbar)
  174           vbox = gtk.GtkVBox(gtk.FALSE, 0)
  175           tooltips_button.add(vbox)
  176           vbox.pack_end(gtk.GtkLabel("Tooltips"))
  177           vbox.pack_end(iconw)
  178           vbox.show_all()
  179           toolbar.append_widget(
  180               tooltips_button,
  181               "Toolbar with or without tips",
  182               "Private")
  183           tooltips_button.show()
  184           toolbar.append_space()
  185   
  186           tooltips_button.set_active(gtk.TRUE)
  187           both_button.set_active(gtk.TRUE)
  188   

A toggle button can be created in the obvious way (if one knows how to create radio buttons already). In the end we have to set the state of one of the radio buttons manually (otherwise they all stay in active state, preventing us from switching between them).
 
  189           # to pack a widget into toolbar, we only have to 
  190           # create it and append it with an appropriate tooltip
  191           entry = gtk.GtkEntry()
  192           toolbar.append_widget(entry,  "This is just an entry", "Private")
  193   
  194           # well, it isn't created within the toolbar, so we must still show it
  195           entry.show()
  196   

As you see, adding any kind of widget to a toolbar is simple. The one thing you have to remember is that this widget must be shown manually (contrary to items which are added to the toolbar with the append_item(), prepend_item() and insert_item() methods).
 
  197           # that's it ! let's show everything.
  198           toolbar.show()
  199           handlebox.show()
  200           self.dialog.show()
  201   
  202   def main():
  203       # rest in gtk_main and wait for the fun to begin!
  204       gtk.mainloop()
  205       return 0
  206   
  207   if __name__ == "__main__":
  208       ToolbarExample()
  209       main()

Line 200 ends the ToolbarExample class definition. Lines 202-205 define the main() function which just calls the mainloop() function to start the event processing loop. Lines 207-209 arrange to create a ToolbarExample instance and then enter the event procressing loop. So, here we are at the end of toolbar tutorial. Of course, to appreciate it in full you need also this nice XPM icon, so here is the gtkxpm.py source:
 
# example gtkxpm.py

gtk_xpm = [
"32 39 5 1",
".      c none",
"+      c black",
"@      c #3070E0",
"#      c #F05050",
"$      c #35E035",
"................+...............",
"..............+++++.............",
"............+++++@@++...........",
"..........+++++@@@@@@++.........",
"........++++@@@@@@@@@@++........",
"......++++@@++++++++@@@++.......",
".....+++@@@+++++++++++@@@++.....",
"...+++@@@@+++@@@@@@++++@@@@+....",
"..+++@@@@+++@@@@@@@@+++@@@@@++..",
".++@@@@@@+++@@@@@@@@@@@@@@@@@@++",
".+#+@@@@@@++@@@@+++@@@@@@@@@@@@+",
".+##++@@@@+++@@@+++++@@@@@@@@$@.",
".+###++@@@@+++@@@+++@@@@@++$$$@.",
".+####+++@@@+++++++@@@@@+@$$$$@.",
".+#####+++@@@@+++@@@@++@$$$$$$+.",
".+######++++@@@@@@@++@$$$$$$$$+.",
".+#######+##+@@@@+++$$$$$$@@$$+.",
".+###+++##+##+@@++@$$$$$$++$$$+.",
".+###++++##+##+@@$$$$$$$@+@$$@+.",
".+###++++++#+++@$$@+@$$@++$$$@+.",
".+####+++++++#++$$@+@$$++$$$$+..",
".++####++++++#++$$@+@$++@$$$$+..",
".+#####+++++##++$$++@+++$$$$$+..",
".++####+++##+#++$$+++++@$$$$$+..",
".++####+++####++$$++++++@$$$@+..",
".+#####++#####++$$+++@++++@$@+..",
".+#####++#####++$$++@$$@+++$@@..",
".++####++#####++$$++$$$$$+@$@++.",
".++####++#####++$$++$$$$$$$$+++.",
".+++####+#####++$$++$$$$$$$@+++.",
"..+++#########+@$$+@$$$$$$+++...",
"...+++########+@$$$$$$$$@+++....",
".....+++######+@$$$$$$$+++......",
"......+++#####+@$$$$$@++........",
".......+++####+@$$$$+++.........",
".........++###+$$$@++...........",
"..........++##+$@+++............",
"...........+++++++..............",
".............++++..............."
]

Figure 10.9 illustrates the resulting display:

Figure 10.9 Toolbar Example