pygtk2tutorial/ 0040755 0001172 0000167 00000000000 10227357011 012665 5 ustar finlay fcf pygtk2tutorial/pygtk-tut-changelog.html 0100644 0001172 0000167 00000051121 10227357010 017444 0 ustar finlay fcf
============== 2.4 ==================
2005-04-13 John Finlay <finlay@moeraki.com>
* pygtk2-tut.xml: Set version number and pubdate.
* Copyright.xml: Update date.
* Replace gtk.TRUE and gtk.FALSE. Fix misc. other deprecations.
2005-03-31 John Finlay <finlay@moeraki.com>
* ComboBoxAndComboBoxEntry.xml: Convenience function is
gtk.combo_box_entry_new_text(). (brett@belizebotanic.org)
2005-02-28 John Finlay <finlay@moeraki.com>
* GettingStarted.xml (Stepping) add print statement to destroy
handler for illustrative purposes. (Rodolfo Gouveia)
============== 2.3 ==================
2004-12-24 John Finlay <finlay@moeraki.com>
* pygtk2-tut.xml: Set version number and pubdate. Add revhistory.
* UIManager.xml: Add.
2004-12-13 John Finlay <finlay@moeraki.com>
* MovingOn.xml: Remove reference to WINODW_DIALOG (Jens Knutson)
2004-12-08 John Finlay <finlay@moeraki.com>
* DragAndDrop.xml Patch from Rafael Villar Burke
2004-12-01 John Finlay <finlay@moeraki.com>
* Scribble.xml Patch by Rafael Villar Burke.
2004-11-29 John Finlay <finlay@moeraki.com>
* ComboBoxAndComboBoxEntry.xml Patch by Rafael Villar Burke.
* TimeoutsIOAndIdleFunctions.xml Patch by Rafael Villar Burke.
* AdvancedEventAndSignalHandling.xml Add parameter tags to function
and method defs. Patch by Rafael Villar Burke.
2004-11-20 John Finlay <finlay@moeraki.com>
* ColorButtonAndFontButton.xml:
* SettingWidgetAttributes.xml: Fix xml tags. (Rafael Villar Burke)
2004-10-31 John Finlay <finlay@moeraki.com>
* ExpanderWidget.xml
* GenericTreeModel.xml
* CellRenderers.xml Fixes by Rafael Villar Burke.
2004-10-28 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml Fixes by Rafael Villar Burke.
2004-10-24 John Finlay <finlay@moeraki.com>
* ContainerWidgets.xml Many fixes by Rafael Villar Burke.
* MiscellaneaousWidgets.xml Many fixes by Rafael Villar Burke.
2004-10-13 John Finlay <finlay@moeraki.com>
* PackingWidgets.xml ButtonWidget.xml Fix typos per kraai
Fixes #155318.
2004-09-20 John Finlay <finlay@moeraki.com>
* TextViewWidget.xml Minor fixes by Rafael Villar Burke.
* ActionsAndActionGroups.xml Add.
* NewInPyGTK2.4.xml Include ActionsAndActionGroups.xml
2004-09-12 John Finlay <finlay@moeraki.com>
* TreeModel.xml (sec-ManagingRowData) Minor fix. Patch by
Rafael Villar Burke
2004-09-08 John Finlay <finlay@moeraki.com>
* ContainerWidgets.xml (sec-AspectFrames)
(sec-Alignment) Fix link to Ref manual.
2004-08-31 John Finlay <finlay@moeraki.com>
* DrawingArea.xml Rewrite portions based on patch by Rafael Villar
Burke.
* DrawingArea.xml Add missing literal tags.
Patch by Rafael Villar Burke.
2004-08-21 John Finlay <finlay@moeraki.com>
* ColorButtonAndFontButton.xml Add.
* NewInPyGTK24.xml Include ColorButtonAndFontButton.xml.
2004-08-19 John Finlay <finlay@moeraki.com>
* Scribble.xml (sec-DrawingAreaWidgetAndDrawing)
Update example description.
2004-08-16 John Finlay <finlay@moeraki.com>
* CellRenderers.xml Add cellrenderer.py example program section
* Credits.xml Credit Steve George for cellrenderer.py example
program.
2004-08-15 John Finlay <finlay@moeraki.com>
* CellRenderers.xml (Activatable Toggle Cells) Add info about
setting the toggle from a column. (#150212) (Steve George)
2004-08-13 John Finlay <finlay@moeraki.com>
* TreeModel.xml Clean up Adding TreeStore rows section (Joey Tsai)
Add missing text in Large Data Stores section.. (Joey Tsai)
2004-08-08 John Finlay <finlay@moeraki.com>
* TreeModel.xml
* CellRenderers.xml
* GenericTreeModel.xml
* TreeViewWidget.xml
* NewWidgetsAndObjects.xml Minor rewording and addition of tags.
2004-08-06 John Finlay <finlay@moeraki.com>
* ButtonWidget.xml Fix errors in examples.
* DragAndDrop.xml Fix anchor.
* MenuWidget.xml Fix typo.
* PackingWidgets.xml Fix typo.
* MiscellaneousWidgets.xml (Dialogs) (Images) (Pixmaps) (Rulers)
(Progressbar) (Label)
Fix faulty wording and errors (All thanks to Marc Verney)
2004-08-04 John Finlay <finlay@moeraki.com>
* DrawingArea.xml Update example to use rulers and scrolled
window.
* pygtk2-tut.xml Bump version number and pubdate.
============== 2.2 ==================
2004-08-03 John Finlay <finlay@moeraki.com>
* ComboBoxAndComboBoxEntry.xml Add.
* EntryCompletion.xml Add.
* ExpanderWidget.xml Add.
* NewInPyGTK24.xml Add.
* NewWidgetsAndObject.xml Rearrange and make as a chapter.
* pygtk2-tut.xml Add NewInPyGTK24.xml. Update date.
2004-08-02 John Finlay <finlay@moeraki.com>
* MiscellaneousWidgets.xml Change Combo Box to Combo Widget to avoid
confusion with new ComboBox widget. Add deprecation note.
2004-07-28 John Finlay <finlay@moeraki.com>
* Credits.xml Add PyGTK section with credit to Nathan Durst and
Alex Roitman.
* FileChooser.xml Create.
* NewWidgetsAndObject.xml Add include for FileChooser.xml.
* NewWidgetsAndObject.xml Create.
* pygtk2-tut.xml Add NewWidgetsAndObject.xml file. Bump version
number and set date.
2004-07-20 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml (sec-ManagingCellRenderers) Fix title.
More detail on set_sort_column_id().
2004-07-12 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml (sec-CreatingTreeView) Fix faulty capitalization.
(thanks to Doug Quale)
2004-07-08 John Finlay <finlay@moeraki.com>
* Adjustments.xml AdvancedEventAndSignalHandling.xml
ButtonWidget.xml ChangeLog ContainerWidgets.xml
DragAndDrop.xml DrawingArea.xml GettingStarted.xml
ManagingSelections.xml MenuWidget.xml
MiscellaneousWidgets.xml MovingOn.xml
PackingWidgets.xml RangeWidgets.xml Scribble.xml
SettingWidgetAttributes.xml TextViewWidget.xml
TimeoutsIOAndIdleFunctions.xml WidgetOverview.xml
Update files with example programs.
2004-07-06 John Finlay <finlay@moeraki.com>
* examples/*.py Update examples to eliminate deprecated methods
and use import pygtk.
============== 2.1 ==================
2004-07-06 John Finlay <finlay@moeraki.com>
* pygtk2-tut.xml Bump version number to 2.1 and set
pubdate.
* TreeViewWidgets.xml Revise the treeviewdnd.py example to
illustrate row reordering with external drag and drop and
add explanation.
2004-07-03 John Finlay <finlay@moeraki.com>
* TimeoutsIOAndIdleFunctions.xml Update descriptions to use
the gobject functions.
2004-06-30 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml Extract the CellRenderers section into
CellRenderers.xml.
* CellRenderers.xml Create and add section on editable
CellRendererText.
* TreeViewWidget.xml Extract the TreeModel section and put into
new file TreeModel.xml. Add detail to the TreeViewColumn use of
its sort column ID.
* TreeModel.xml Create and add section on sorting TreeModel rows
using the TreeSortable interface.
2004-06-27 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml (Cell Data Function) Add filelisting example
using cell data functions. Add XInclude header to include generic
tree model and cell renderer subsections.
Fix typos and errors in links. Fix bugs in example listings.
Add section on TreeModel signals.
2004-06-22 John Finlay <finlay@moeraki.com>
* Introduction.xml Add note about pygtkconsole.py and gpython.py
programs do not work on Windows. Thanks to vector180.
2004-06-14 John Finlay <finlay@moeraki.com>
* DragAndDrop.xml Fix signal lists for drag source and dest.
Add detail to the overview drag cycle. Add detail about signal
handler operation.
* DragAndDrop.xml Add small example program dragtargets.py to
print out drag targets.
2004-05-31 John Finlay <finlay@moeraki.com>
* GettingStarted.xml Change wording in helloworld.py example
program - delete_event() comments confusing. Thanks to Ming Hua.
2004-05-28 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml (TreeModelFilter) Replace 'file' with 'filter'.
Thanks to Guilherme Salgado.
2004-05-27 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml (AccessingDataValues) Fix store.set example
column number wrong. Thanks to Rafael Villar Burke and Guilherme
Salgado.
(CellRendererAttributes) Fix error. Thanks to Doug Quale.
(TreeModelIntroduction)
(PythonProtocolSupport) Fix grammatical and spelling errors.
Thanks to Thomas Mills Hinkle.
2004-05-25 John Finlay <finlay@moeraki.com>
* Introduction.xml Add reference links to www.pygtk.org website
and describe some of its resources.
============ 2.0 ==============
2004-05-24 John Finlay <finlay@moeraki.com>
* TreeViewWidget.xml Add beginning of tutorial chapter.
* Introduction.xml Add reference to gpython.py program.
* pygtk2-tut.xml Bump release number to 2.0.
2004-03-31 John Finlay <finlay@moeraki.com>
* MiscellaneousWidgets.xml Fix bug in calendar.py example causing
date string to be off by one day in some time zones. Fixes #138487.
(thanks to Eduard Luhtonen)
2004-01-28 John Finlay <finlay@moeraki.com>
* DrawingArea.xml Modify description of DrawingArea to clarify that
drawing is done on the wrapped gtk.gdk.Window. Modify GC description
to clarify that new GCs created from drawables. (thanks to Antoon
Pardon)
* UndocumentedWidgets.xml Remove the section on Plugs and Sockets -
now in ContainerWidgets.xml.
* ContainerWidgets.xml Add section on Plugs and Sockets written
by Nathan Hurst.
* pygtk2-tut.xml Change date and version number.
2003-11-05 John Finlay <finlay@moeraki.com>
* Introduction.xml Add reference to the PyGTK 2.0 Reference Manual.
2003-11-04 John Finlay <finlay@moeraki.com>
* ContainerWidgets.xml
* RangeWidgets.xml
* WidgetOverview.xml Remove reference to testgtk.py since it doesn't
exist in PyGTK 2.0 (thanks to Steve Chaplin)
2003-10-07 John Finlay <finlay@moeraki.com>
* TextViewWidget.xml Change PANGO_ to pango. (thanks to Stephane Klein)
* pygtk2-tut.xml Change date and version number.
2003-10-06 John Finlay <finlay@moeraki.com>
* GettingStarted.xml Change third to second in description of signal
handler arguments. (thanks to Kyle Smith)
2003-09-26 John Finlay <finlay@moeraki.com>
* ContainerWidgets.xml Fix text layout error in frame shadow
description (thanks to Steve Chaplin)
2003-09-19 John Finlay <finlay@moeraki.com>
* ContainerWidgets.xml
* layout.py Use random module instead of whrandom in
layout.py example program (thanks to Steve Chaplin)
* PackingWidgets.xml
* packbox.py Use set_size_request() instead of set_usize() in
packbox.py example (thanks to Steve Chaplin)
2003-07-11 John Finlay <finlay@moeraki.com>
* ContainerWidgets.xml Fix link references to class-gtkalignment to
use a ulink instead of a link.
* ChangeLog Add this change log file
* pygtk2-tut.xml Change date and add a version number. Add ChangeLog
as an appendix.
The paned window widgets are useful when you want to divide an area into two parts, with the relative size of the two parts controlled by the user. A groove is drawn between the two portions with a handle that the user can drag to change the ratio. The division can either be horizontal (HPaned) or vertical (VPaned).
To create a new paned window, call one of:
hpane = gtk.HPaned() vpane = gtk.VPaned() |
After creating the paned window widget, you need to add child widgets to its two halves. To do this, use the methods:
paned.add1(child) paned.add2(child) |
The add1() method adds the child widget to the left or top half of the paned window. The add2() method adds the child widget to the right or bottom half of the paned window.
The paned.py example program creates part of the user interface of an imaginary email program. A window is divided into two portions vertically, with the top portion being a list of email messages and the bottom portion the text of the email message. Most of the program is pretty straightforward. A couple of points to note: text can't be added to a Text widget until it is realized. This could be done by calling the realize() method, but as a demonstration of an alternate technique, we connect a handler to the "realize" signal to add the text. Also, we need to add the SHRINK option to some of the items in the table containing the text window and its scrollbars, so that when the bottom portion is made smaller, the correct portions shrink instead of being pushed off the bottom of the window. Figure 10.6, “Paned Example” shows the result of running the program:
The source code of the paned.py program is:
1 #!/usr/bin/env python 2 3 # example paned.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk, gobject 8 9 class PanedExample: 10 # Create the list of "messages" 11 def create_list(self): 12 # Create a new scrolled window, with scrollbars only if needed 13 scrolled_window = gtk.ScrolledWindow() 14 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 15 16 model = gtk.ListStore(gobject.TYPE_STRING) 17 tree_view = gtk.TreeView(model) 18 scrolled_window.add_with_viewport (tree_view) 19 tree_view.show() 20 21 # Add some messages to the window 22 for i in range(10): 23 msg = "Message #%d" % i 24 iter = model.append() 25 model.set(iter, 0, msg) 26 27 cell = gtk.CellRendererText() 28 column = gtk.TreeViewColumn("Messages", cell, text=0) 29 tree_view.append_column(column) 30 31 return scrolled_window 32 33 # Add some text to our text widget - this is a callback that is invoked 34 # when our window is realized. We could also force our window to be 35 # realized with gtk.Widget.realize(), but it would have to be part of a 36 # hierarchy first 37 def insert_text(self, buffer): 38 iter = buffer.get_iter_at_offset(0) 39 buffer.insert(iter, 40 "From: pathfinder@nasa.gov\n" 41 "To: mom@nasa.gov\n" 42 "Subject: Made it!\n" 43 "\n" 44 "We just got in this morning. The weather has been\n" 45 "great - clear but cold, and there are lots of fun sights.\n" 46 "Sojourner says hi. See you soon.\n" 47 " -Path\n") 48 49 # Create a scrolled text area that displays a "message" 50 def create_text(self): 51 view = gtk.TextView() 52 buffer = view.get_buffer() 53 scrolled_window = gtk.ScrolledWindow() 54 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) 55 scrolled_window.add(view) 56 self.insert_text(buffer) 57 scrolled_window.show_all() 58 return scrolled_window 59 60 def __init__(self): 61 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 62 window.set_title("Paned Windows") 63 window.connect("destroy", lambda w: gtk.main_quit()) 64 window.set_border_width(10) 65 window.set_size_request(450, 400) 66 67 # create a vpaned widget and add it to our toplevel window 68 vpaned = gtk.VPaned() 69 window.add(vpaned) 70 vpaned.show() 71 72 # Now create the contents of the two halves of the window 73 list = self.create_list() 74 vpaned.add1(list) 75 list.show() 76 77 text = self.create_text() 78 vpaned.add2(text) 79 text.show() 80 window.show() 81 82 def main(): 83 gtk.main() 84 return 0 85 86 if __name__ == "__main__": 87 PanedExample() 88 main() |
Statusbars are simple widgets used to display a text message. They keep a stack of the messages pushed onto them, so that popping the current message will re-display the previous text message.
In order to allow different parts of an application to use the same statusbar to display messages, the statusbar widget issues Context Identifiers which are used to identify different "users". The message on top of the stack is the one displayed, no matter what context it is in. Messages are stacked in last-in-first-out order, not context identifier order.
A statusbar is created with a call to:
statusbar = gtk.Statusbar() |
A new Context Identifier is requested using a call to the following method with a short textual description of the context:
context_id = statusbar.get_context_id(context_description) |
There are three additional methods that operate on statusbars:
message_id = statusbar.push(context_id, text) statusbar.pop(context_id) statusbar.remove(context_id, message_id) |
The first, push(), is used to add a new message to the statusbar. It returns a message_id, which can be passed later to the remove() method to remove the message with the combination message_id and context_id from the statusbar's stack.
The pop() method removes the message highest in the stack with the given context_id.
The statusbar.py example program creates a statusbar and two buttons, one for pushing items onto the statusbar, and one for popping the last item back off. Figure 9.9, “Statusbar Example” illustrates the result:
The statusbar.py source code is:
1 #!/usr/bin/env python 2 3 # example statusbar.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class StatusbarExample: 10 def push_item(self, widget, data): 11 buff = " Item %d" % self.count 12 self.count = self.count + 1 13 self.status_bar.push(data, buff) 14 return 15 16 def pop_item(self, widget, data): 17 self.status_bar.pop(data) 18 return 19 20 def __init__(self): 21 self.count = 1 22 # create a new window 23 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 24 window.set_size_request(200, 100) 25 window.set_title("PyGTK Statusbar Example") 26 window.connect("delete_event", lambda w,e: gtk.main_quit()) 27 28 vbox = gtk.VBox(False, 1) 29 window.add(vbox) 30 vbox.show() 31 32 self.status_bar = gtk.Statusbar() 33 vbox.pack_start(self.status_bar, True, True, 0) 34 self.status_bar.show() 35 36 context_id = self.status_bar.get_context_id("Statusbar example") 37 38 button = gtk.Button("push item") 39 button.connect("clicked", self.push_item, context_id) 40 vbox.pack_start(button, True, True, 2) 41 button.show() 42 43 button = gtk.Button("pop last item") 44 button.connect("clicked", self.pop_item, context_id) 45 vbox.pack_start(button, True, True, 2) 46 button.show() 47 48 # always display the window as the last step so it all splashes on 49 # the screen at once. 50 window.show() 51 52 def main(): 53 gtk.main() 54 return 0 55 56 if __name__ == "__main__": 57 StatusbarExample() 58 main() |
The Expander widget is a fairly simple container widget that can reveal or hide its child widget by clicking on a triangle similar to the triangle in a TreeView. A new Expander is created using the constructor:
expander = gtk.Expander(label=None) |
where label is a string to be used as the expander label. If label is None or not specified, no label is created. Alternatively, you can use the function:
expander = gtk.expander_new_with_mnemonic(label=None) |
that sets the character in label preceded by an underscore as a mnemonic keyboard accelerator.
The Expander widget uses the Container API to add and remove its child widget:
expander.add(widget) expander.remove(widget) |
The child widget can be retrieved using the Bin "child" attribute or the get_child() method.
The setting that controls the interpretation of label underscores can be retrieved and changed using the methods:
use_underline = expander.get_use_underline() expander.set_use_underline(use_underline) |
If you want to use Pango markup (see the Pango Markup reference for more detail) in the label string, use the following methods to set and retrieve the setting of the "use-markup" property:
expander.set_use_markup(use_markup) use_markup = expander.get_use_markup() |
Finally, you can use any widget as the label widget using the following method:
expander.set_label_widget(label_widget) |
This allows you, for example, to use an HBox packed with an Image and a Label as the Expander label.
The state of the Expander can be retrieved and set using the methods:
expanded = expander.get_expanded() expander.set_expanded(expanded) |
If expanded is TRUE the child widget is revealed.
In most cases the Expander automatically does exactly what you want when revealing and hiding the child widget. In some cases your application might want to create a child widget at expansion time. The "notify::expanded" signal can be used to track changes in the state of the expander triangle. The signal handler can then create or change the child widget as needed.
The example program expander.py demonstrates the use of the Expander. Figure 16.11, “Expander Widget” illustrates the program in operation:
The program creates a Label containing the current time and shows it when the expander is expanded.
Images are data structures that contain pictures. These pictures can be used in various places.
Images can be created from Pixbufs, Pixmaps, image files (e.g. XPM, PNG, JPEG, TIFF, etc.) and even animation files.
Images are created using the function:
image = gtk.Image() |
The image is then loaded using one of the following methods:
image.set_from_pixbuf(pixbuf) image.set_from_pixmap(pixmap, mask) image.set_from_image(image) image.set_from_file(filename) image.set_from_stock(stock_id, size) image.set_from_icon_set(icon_set, size) image.set_from_animation(animation) |
Where pixbuf is a gtk.gdk.Pixbuf; pixmap and mask are gtk.gdk.Pixmaps; image is a gtk.gdk.Image; stock_id is the name of a gtk.StockItem; icon_set is a gtk.IconSet; and, animation is a gtk.gdk.PixbufAnimation. the size argument is one of:
ICON_SIZE_MENU ICON_SIZE_SMALL_TOOLBAR ICON_SIZE_LARGE_TOOLBAR ICON_SIZE_BUTTON ICON_SIZE_DND ICON_SIZE_DIALOG |
The easiest way to create an image is using the set_from_file() method which automatically determines the image type and loads it.
The program images.py illustrates loading various image types (goalie.gif, apple-red.png, chaos.jpg, important.tif, soccerball.gif) into images which are then put into buttons:
The source code is:
1 #!/usr/bin/env python 2 3 # example images.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class ImagesExample: 10 # when invoked (via signal delete_event), terminates the application. 11 def close_application(self, widget, event, data=None): 12 gtk.main_quit() 13 return False 14 15 # is invoked when the button is clicked. It just prints a message. 16 def button_clicked(self, widget, data=None): 17 print "button %s clicked" % data 18 19 def __init__(self): 20 # create the main window, and attach delete_event signal to terminating 21 # the application 22 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 23 window.connect("delete_event", self.close_application) 24 window.set_border_width(10) 25 window.show() 26 27 # a horizontal box to hold the buttons 28 hbox = gtk.HBox() 29 hbox.show() 30 window.add(hbox) 31 32 pixbufanim = gtk.gdk.PixbufAnimation("goalie.gif") 33 image = gtk.Image() 34 image.set_from_animation(pixbufanim) 35 image.show() 36 # a button to contain the image widget 37 button = gtk.Button() 38 button.add(image) 39 button.show() 40 hbox.pack_start(button) 41 button.connect("clicked", self.button_clicked, "1") 42 43 # create several images with data from files and load images into 44 # buttons 45 image = gtk.Image() 46 image.set_from_file("apple-red.png") 47 image.show() 48 # a button to contain the image widget 49 button = gtk.Button() 50 button.add(image) 51 button.show() 52 hbox.pack_start(button) 53 button.connect("clicked", self.button_clicked, "2") 54 55 image = gtk.Image() 56 image.set_from_file("chaos.jpg") 57 image.show() 58 # a button to contain the image widget 59 button = gtk.Button() 60 button.add(image) 61 button.show() 62 hbox.pack_start(button) 63 button.connect("clicked", self.button_clicked, "3") 64 65 image = gtk.Image() 66 image.set_from_file("important.tif") 67 image.show() 68 # a button to contain the image widget 69 button = gtk.Button() 70 button.add(image) 71 button.show() 72 hbox.pack_start(button) 73 button.connect("clicked", self.button_clicked, "4") 74 75 image = gtk.Image() 76 image.set_from_file("soccerball.gif") 77 image.show() 78 # a button to contain the image widget 79 button = gtk.Button() 80 button.add(image) 81 button.show() 82 hbox.pack_start(button) 83 button.connect("clicked", self.button_clicked, "5") 84 85 86 def main(): 87 gtk.main() 88 return 0 89 90 if __name__ == "__main__": 91 ImagesExample() 92 main() |
Pixmaps are data structures that contain pictures. These pictures can be used in various places, but most commonly as icons on the X desktop, or as cursors.
A pixmap which only has 2 colors is called a bitmap, and there are a few additional routines for handling this common special case.
To understand pixmaps, it would help to understand how X window system works. Under X, applications do not need to be running on the same computer that is interacting with the user. Instead, the various applications, called "clients", all communicate with a program which displays the graphics and handles the keyboard and mouse. This program which interacts directly with the user is called a "display server" or "X server." Since the communication might take place over a network, it's important to keep some information with the X server. Pixmaps, for example, are stored in the memory of the X server. This means that once pixmap values are set, they don't need to keep getting transmitted over the network; instead a command is sent to "display pixmap number XYZ here." Even if you aren't using X with GTK+ currently, using constructs such as Pixmaps will make your programs work acceptably under X.
To use pixmaps in PyGTK, we must first build a gtk.gdk.Pixmap using gtk.gdk functions in PyGTK. Pixmaps can either be created from in-memory data, or from data read from a file. We'll go through each of the calls to create a pixmap.
pixmap = gtk.gdk.pixmap_create_from_data(window, data, width, height, depth, fg, bg) |
This routine is used to create a pixmap from data in memory with the color depth given by depth. If depth is -1 the color depth is derived from the depth of window. Each pixel uses depth bits of data to represent the color. Width and height are in pixels. The window argument must refer to a realized gtk.gdk.Window, since a pixmap's resources are meaningful only in the context of the screen where it is to be displayed. fg and bg are the foreground and background colors of the pixmap.
Pixmaps can be created from XPM files using:
pixmap, mask = gtk.gdk.pixmap_create_from_xpm(window, transparent_color, filename) |
XPM format is a readable pixmap representation for the X Window System. It is widely used and many different utilities are available for creating image files in this format. In the pixmap_create_from_xpm() function the first argument is a gtk.gdk.Window type. (Most GTK+ widgets have an underlying gtk.gdk.Window which can be retrieved by using the widget's window attribute.) The file, specified by filename, must contain an image in the XPM format and the image is loaded into the pixmap structure. The mask is a bitmap that specifies which bits of pixmap are opaque; it is created by the function. All other pixels are colored using the color specified by transparent_color. An example using this function is below.
Pixmaps can also be created from data in memory using the function:
pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(window, transparent_color, data) |
Small images can be incorporated into a program as data in the XPM format using the above function. A pixmap is created using this data, instead of reading it from a file. An example of such data is:
xpm_data = [ "16 16 3 1", " c None", ". c #000000000000", "X c #FFFFFFFFFFFF", " ", " ...... ", " .XXX.X. ", " .XXX.XX. ", " .XXX.XXX. ", " .XXX..... ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " .XXXXXXX. ", " ......... ", " ", " " ] |
The final way to create a blank pixmap suitable for drawing operations is:
pixmap = gtk.gdk.Pixmap(window, width, height, depth=-1) |
window is either a gtk.gdk.Window. or None. If window is a gtk.gdk.Window then depth can be -1 to indicate that the depth should be determined from the window. If window is None then the depth must be specified.
The pixmap.py program is an example of using a pixmap in a button. Figure 9.6, “Pixmap in a Button Example” shows the result:
The source code is:
1 #!/usr/bin/env python 2 3 # example pixmap.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 # XPM data of Open-File icon 10 xpm_data = [ 11 "16 16 3 1", 12 " c None", 13 ". c #000000000000", 14 "X c #FFFFFFFFFFFF", 15 " ", 16 " ...... ", 17 " .XXX.X. ", 18 " .XXX.XX. ", 19 " .XXX.XXX. ", 20 " .XXX..... ", 21 " .XXXXXXX. ", 22 " .XXXXXXX. ", 23 " .XXXXXXX. ", 24 " .XXXXXXX. ", 25 " .XXXXXXX. ", 26 " .XXXXXXX. ", 27 " .XXXXXXX. ", 28 " ......... ", 29 " ", 30 " " 31 ] 32 33 class PixmapExample: 34 # when invoked (via signal delete_event), terminates the application. 35 def close_application(self, widget, event, data=None): 36 gtk.main_quit() 37 return False 38 39 # is invoked when the button is clicked. It just prints a message. 40 def button_clicked(self, widget, data=None): 41 print "button clicked" 42 43 def __init__(self): 44 # create the main window, and attach delete_event signal to terminating 45 # the application 46 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 47 window.connect("delete_event", self.close_application) 48 window.set_border_width(10) 49 window.show() 50 51 # now for the pixmap from XPM data 52 pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(window.window, 53 None, 54 xpm_data) 55 56 # an image widget to contain the pixmap 57 image = gtk.Image() 58 image.set_from_pixmap(pixmap, mask) 59 image.show() 60 61 # a button to contain the image widget 62 button = gtk.Button() 63 button.add(image) 64 window.add(button) 65 button.show() 66 67 button.connect("clicked", self.button_clicked) 68 69 def main(): 70 gtk.main() 71 return 0 72 73 if __name__ == "__main__": 74 PixmapExample() 75 main() |
A disadvantage of using pixmaps is that the displayed object is always rectangular, regardless of the image. We would like to create desktops and applications with icons that have more natural shapes. For example, for a game interface, we would like to have round buttons to push. The way to do this is using shaped windows.
A shaped window is simply a pixmap where the background pixels are transparent. This way, when the background image is multi-colored, we don't overwrite it with a rectangular, non-matching border around our icon. The wheelbarrow.py example program displays a full wheelbarrow image on the desktop. Figure 9.7, “Wheelbarrow Example Shaped Window” shows the wheelbarrow over a terminal window:
The source code for wheelbarrow.py is:
1 #!/usr/bin/env python 2 3 # example wheelbarrow.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 # XPM 10 WheelbarrowFull_xpm = [ 11 "48 48 64 1", 12 " c None", 13 ". c #DF7DCF3CC71B", 14 "X c #965875D669A6", 15 "o c #71C671C671C6", 16 "O c #A699A289A699", 17 "+ c #965892489658", 18 "@ c #8E38410330C2", 19 "# c #D75C7DF769A6", 20 "$ c #F7DECF3CC71B", 21 "% c #96588A288E38", 22 "& c #A69992489E79", 23 "* c #8E3886178E38", 24 "= c #104008200820", 25 "- c #596510401040", 26 "; c #C71B30C230C2", 27 ": c #C71B9A699658", 28 "> c #618561856185", 29 ", c #20811C712081", 30 "< c #104000000000", 31 "1 c #861720812081", 32 "2 c #DF7D4D344103", 33 "3 c #79E769A671C6", 34 "4 c #861782078617", 35 "5 c #41033CF34103", 36 "6 c #000000000000", 37 "7 c #49241C711040", 38 "8 c #492445144924", 39 "9 c #082008200820", 40 "0 c #69A618611861", 41 "q c #B6DA71C65144", 42 "w c #410330C238E3", 43 "e c #CF3CBAEAB6DA", 44 "r c #71C6451430C2", 45 "t c #EFBEDB6CD75C", 46 "y c #28A208200820", 47 "u c #186110401040", 48 "i c #596528A21861", 49 "p c #71C661855965", 50 "a c #A69996589658", 51 "s c #30C228A230C2", 52 "d c #BEFBA289AEBA", 53 "f c #596545145144", 54 "g c #30C230C230C2", 55 "h c #8E3882078617", 56 "j c #208118612081", 57 "k c #38E30C300820", 58 "l c #30C2208128A2", 59 "z c #38E328A238E3", 60 "x c #514438E34924", 61 "c c #618555555965", 62 "v c #30C2208130C2", 63 "b c #38E328A230C2", 64 "n c #28A228A228A2", 65 "m c #41032CB228A2", 66 "M c #104010401040", 67 "N c #492438E34103", 68 "B c #28A2208128A2", 69 "V c #A699596538E3", 70 "C c #30C21C711040", 71 "Z c #30C218611040", 72 "A c #965865955965", 73 "S c #618534D32081", 74 "D c #38E31C711040", 75 "F c #082000000820", 76 " ", 77 " .XoO ", 78 " +@#$%o& ", 79 " *=-;#::o+ ", 80 " >,<12#:34 ", 81 " 45671#:X3 ", 82 " +89<02qwo ", 83 "e* >,67;ro ", 84 "ty> 459@>+&& ", 85 "$2u+ ><ipas8* ", 86 "%$;=* *3:.Xa.dfg> ", 87 "Oh$;ya *3d.a8j,Xe.d3g8+ ", 88 " Oh$;ka *3d$a8lz,,xxc:.e3g54 ", 89 " Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ", 90 " Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ", 91 " Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ", 92 " Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ", 93 " Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ", 94 " OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ", 95 " 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ", 96 " :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo", 97 " +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g", 98 " *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en", 99 " p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>", 100 " OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ", 101 " 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ", 102 " @26MvzxNzvlbwfpdettttttttttt.c,n& ", 103 " *;16=lsNwwNwgsvslbwwvccc3pcfu<o ", 104 " p;<69BvwwsszslllbBlllllllu<5+ ", 105 " OS0y6FBlvvvzvzss,u=Blllj=54 ", 106 " c1-699Blvlllllu7k96MMMg4 ", 107 " *10y8n6FjvllllB<166668 ", 108 " S-kg+>666<M<996-y6n<8* ", 109 " p71=4 m69996kD8Z-66698&& ", 110 " &i0ycm6n4 ogk17,0<6666g ", 111 " N-k-<> >=01-kuu666> ", 112 " ,6ky& &46-10ul,66, ", 113 " Ou0<> o66y<ulw<66& ", 114 " *kk5 >66By7=xu664 ", 115 " <<M4 466lj<Mxu66o ", 116 " *>> +66uv,zN666* ", 117 " 566,xxj669 ", 118 " 4666FF666> ", 119 " >966666M ", 120 " oM6668+ ", 121 " *4 ", 122 " ", 123 " " 124 ] 125 126 class WheelbarrowExample: 127 # When invoked (via signal delete_event), terminates the application 128 def close_application(self, widget, event, data=None): 129 gtk.main_quit() 130 return False 131 132 def __init__(self): 133 # Create the main window, and attach delete_event signal to terminate 134 # the application. Note that the main window will not have a titlebar 135 # since we're making it a popup. 136 window = gtk.Window(gtk.WINDOW_POPUP) 137 window.connect("delete_event", self.close_application) 138 window.set_events(window.get_events() | gtk.gdk.BUTTON_PRESS_MASK) 139 window.connect("button_press_event", self.close_application) 140 window.show() 141 142 # Now for the pixmap and the image widget 143 pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d( 144 window.window, None, WheelbarrowFull_xpm) 145 image = gtk.Image() 146 image.set_from_pixmap(pixmap, mask) 147 image.show() 148 149 # To display the image, we use a fixed widget to place the image 150 fixed = gtk.Fixed() 151 fixed.set_size_request(200, 200) 152 fixed.put(image, 0, 0) 153 window.add(fixed) 154 fixed.show() 155 156 # This masks out everything except for the image itself 157 window.shape_combine_mask(mask, 0, 0) 158 159 # show the window 160 window.set_position(gtk.WIN_POS_CENTER_ALWAYS) 161 window.show() 162 163 def main(): 164 gtk.main() 165 return 0 166 167 if __name__ == "__main__": 168 WheelbarrowExample() 169 main() |
To make the wheelbarrow image sensitive, we attached the "button_press_event" signal to make the program exit. Lines 138-139 make the picture sensitive to a mouse button being pressed and connect the close_application() method.
What if you have a function which you want to be called when nothing else is happening ? Use the function:
source_id = gobject.idle_add(callback, ...) |
Any arguments beyond the first (indicated with ...) are passed to the callback in order. The source_id is returned to provide a reference to the handler.
This function causes GTK to call the specified callback function whenever nothing else is happening.
The callback signature is:
def callback(...): |
where the arguments passed to the callback are the same as those specified in the gobject.idle_add() function. As with the other callback functions, returning FALSE will stop the idle callback from being called and returning TRUE causes the callback function to be run at the next idle time.
An idle function can be removed from the queue by calling the function:
gobject.source_remove(source_id) |
with the source_id returned from the gobject.idle_add() function.
The following methods get and set the style associated with a widget:
widget.set_style(style) style = widget.get_style() |
The following function:
style = get_default_style() |
gets the default style.
A style contains the graphics information needed by a widget to draw itself in its various states:
STATE_NORMAL # The state during normal operation. STATE_ACTIVE # The widget is currently active, such as a button pushed STATE_PRELIGHT # The mouse pointer is over the widget. STATE_SELECTED # The widget is selected STATE_INSENSITIVE # The widget is disabled |
A style contains the following attributes:
fg # a list of 5 foreground colors - one for each state bg # a list of 5 background colors light # a list of 5 colors - created during set_style() method dark # a list of 5 colors - created during set_style() method mid # a list of 5 colors - created during set_style() method text # a list of 5 colors base # a list of 5 colors text_aa # a list of 5 colors halfway between text/base black # the black color white # the white color font_desc # the default pango font description xthickness # ythickness # fg_gc # a list of 5 graphics contexts - created during set_style() method bg_gc # a list of 5 graphics contexts - created during set_style() method light_gc # a list of 5 graphics contexts - created during set_style() method dark_gc # a list of 5 graphics contexts - created during set_style() method mid_gc # a list of 5 graphics contexts - created during set_style() method text_gc # a list of 5 graphics contexts - created during set_style() method base_gc # a list of 5 graphics contexts - created during set_style() method black_gc # a list of 5 graphics contexts - created during set_style() method white_gc # a list of 5 graphics contexts - created during set_style() method bg_pixmap # a list of 5 GdkPixmaps |
Each attribute can be accessed directly similar to style.black and style.fg_gc[gtk.STATE_NORMAL]. All attributes are read-only except for style.black, style.white, style.black_gc and style.white_gc.
An existing style can be copied for later modification by using the method:
new_style = style.copy() |
which copies the style attributes except for the graphics context lists and the light, dark and mid color lists.
The current style of a widget can be retrieved with:
style = widget.get_style() |
To change the style of a widget (e.g. to change the widget foreground color), the following widget methods should be used:
widget.modify_fg(state, color) widget.modify_bg(state, color) widget.modify_text(state, color) widget.modify_base(state, color) widget.modify_font(font_desc) widget.set_style(style) |
Setting the style will allocate the style colors and create the graphics contexts. Most widgets will automatically redraw themselves after the style is changed. If style is None then the widget style will revert to the default style.
Not all style changes will affect the widget. For example, changing the Label (see Section 9.1, “Labels”) widget background color will not change the label background color because the Label widget does not have its own gtk.gdk.Window. The background of the label is dependent on the background color of the label's parent. The use of an EventBox to hold a Label will allow the Label background color to be set. See Section 10.1, “The EventBox” for an example.
The color selection widget is, not surprisingly, a widget for interactive selection of colors. This composite widget lets the user select a color by manipulating RGB (Red, Green, Blue) and HSV (Hue, Saturation, Value) triples. This is done either by adjusting single values with sliders or entries, or by picking the desired color from a hue-saturation wheel/value bar. Optionally, the opacity of the color can also be set.
The color selection widget currently emits only one signal, "color_changed", which is emitted whenever the current color in the widget changes, either when the user changes it or if it's set explicitly through the set_color() method.
Lets have a look at what the color selection widget has to offer us. The widget comes in two flavors: gtk.ColorSelection and gtk.ColorSelectionDialog.
colorsel = gtk.ColorSelection() |
You'll probably not be using this constructor directly. It creates an orphan ColorSelection widget which you'll have to parent yourself. The ColorSelection widget inherits from the VBox widget.
colorseldlg = gtk.ColorSelectionDialog(title) |
where title is a string to be used in the titlebar of the dialog.
This is the most common color selection constructor. It creates a ColorSelectionDialog. It consists of a Frame containing a ColorSelection widget, an HSeparator and an HBox with three buttons,
, and . You can reach these buttons by accessing the ok_button, cancel_button and help_button attributes of the ColorSelectionDialog, (i.e. colorseldlg.ok_button). The ColorSelection widget is accessed using the attribute colorsel:colorsel = colorseldlg.colorsel |
The ColorSelection widget has a number of methods that change its characteristics or provide access to the color selection.
colorsel.set_has_opacity_control(has_opacity) |
The color selection widget supports adjusting the opacity of a color (also known as the alpha channel). This is disabled by default. Calling this method with has_opacity set to TRUE enables opacity. Likewise, has_opacity set to FALSE will disable opacity.
colorsel.set_current_color(color) colorsel.set_current_alpha(alpha) |
You can set the current color explicitly by calling the set_current_color() method with a gtk.gdk.Color. Setting the opacity (alpha channel) is done with the set_current_alpha() method. The alpha value should be between 0 (fully transparent) and 65636 (fully opaque).
color = colorsel.get_current_color() alpha = colorsel.get_current_alpha() |
When you need to query the current color, typically when you've received a "color_changed" signal, you use these methods.
The colorsel.py example program demonstrates the use of the ColorSelectionDialog. The program displays a window containing a drawing area. Clicking on it opens a color selection dialog, and changing the color in the color selection dialog changes the background color. Figure 9.13, “Color Selection Dialog Example” illustrates this program in action:
The source code for colorsel.py is:
1 #!/usr/bin/env python 2 3 # example colorsel.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class ColorSelectionExample: 10 # Color changed handler 11 def color_changed_cb(self, widget): 12 # Get drawingarea colormap 13 colormap = self.drawingarea.get_colormap() 14 15 # Get current color 16 color = self.colorseldlg.colorsel.get_current_color() 17 18 # Set window background color 19 self.drawingarea.modify_bg(gtk.STATE_NORMAL, color) 20 21 # Drawingarea event handler 22 def area_event(self, widget, event): 23 handled = False 24 25 # Check if we've received a button pressed event 26 if event.type == gtk.gdk.BUTTON_PRESS: 27 handled = True 28 29 # Create color selection dialog 30 if self.colorseldlg == None: 31 self.colorseldlg = gtk.ColorSelectionDialog( 32 "Select background color") 33 34 # Get the ColorSelection widget 35 colorsel = self.colorseldlg.colorsel 36 37 colorsel.set_previous_color(self.color) 38 colorsel.set_current_color(self.color) 39 colorsel.set_has_palette(True) 40 41 # Connect to the "color_changed" signal 42 colorsel.connect("color_changed", self.color_changed_cb) 43 # Show the dialog 44 response = self.colorseldlg.run() 45 46 if response -- gtk.RESPONSE_OK: 47 self.color = colorsel.get_current_color() 48 else: 49 self.drawingarea.modify_bg(gtk.STATE_NORMAL, self.color) 50 51 self.colorseldlg.hide() 52 53 return handled 54 55 # Close down and exit handler 56 def destroy_window(self, widget, event): 57 gtk.main_quit() 58 return True 59 60 def __init__(self): 61 self.colorseldlg = None 62 # Create toplevel window, set title and policies 63 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 64 window.set_title("Color selection test") 65 window.set_resizable(True) 66 67 # Attach to the "delete" and "destroy" events so we can exit 68 window.connect("delete_event", self.destroy_window) 69 70 # Create drawingarea, set size and catch button events 71 self.drawingarea = gtk.DrawingArea() 72 73 self.color = self.drawingarea.get_colormap().alloc_color(0, 65535, 0) 74 75 self.drawingarea.set_size_request(200, 200) 76 self.drawingarea.set_events(gtk.gdk.BUTTON_PRESS_MASK) 77 self.drawingarea.connect("event", self.area_event) 78 79 # Add drawingarea to window, then show them both 80 window.add(self.drawingarea) 81 self.drawingarea.show() 82 window.show() 83 84 def main(): 85 gtk.main() 86 return 0 87 88 if __name__ == "__main__": 89 ColorSelectionExample() 90 main() |
This document, like so much other great software out there, was created for free by volunteers. If you are at all knowledgeable about any aspect of PyGTK that does not already have documentation, please consider contributing to this document.
If you do decide to contribute, please mail your text to John Finlay (finlay@moeraki.com). Also, be aware that the entirety of this document is free, and any addition by you provide must also be free. That is, people may use any portion of your examples in their programs, and copies of this document may be distributed at will, etc.
Thank you.
Table of Contents
This describes the methods used to operate on widgets (and objects). These can be used to set style, padding, size, etc.
The method:
widget.activate() |
causes the widget to emit the "activate" signal.
The method:
widget.set_sensitive(sensitive) |
sets the sensitivity of the widget (i.e. does it react to events). if sensitive is TRUE the widget will receive events; if FALSE the widget will not receive events. A widget that is insensitive is usually displayed "grayed out".
The method:
widget.set_size_request(width, height) |
sets the widget size to the given width and height.
The methods:
widget.set_flags(flags) widget.unset_flags(flags) flags = widget.flags() |
set, unset and get the gtk.Object and gtk.Widget flags. flags can be any of the standard flags:
IN_DESTRUCTION FLOATING RESERVED_1 RESERVED_2 TOPLEVEL NO_WINDOW REALIZED MAPPED VISIBLE SENSITIVE PARENT_SENSITIVE CAN_FOCUS HAS_FOCUS CAN_DEFAULT HAS_DEFAULT HAS_GRAB RC_STYLE COMPOSITE_CHILD NO_REPARENT APP_PAINTABLE RECEIVES_DEFAULT DOUBLE_BUFFERED |
The method:
widget.grab_focus() |
allows a widget to grab the focus assuming that it has the CAN_FOCUS flag set.
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 = gtk.Toolbar() |
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=None) 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) |
If it's required, the orientation of a toolbar, its style and whether tooltips are available 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. The enable argument is either TRUE or FALSE.
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 pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class ToolbarExample: 10 # This method is connected to the Close button or 11 # closing the window from the WM 12 def delete_event(self, widget, event=None): 13 gtk.main_quit() 14 return False 15 |
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 (gtk.xpm)to serve as an icon for all of the buttons. Line 10 starts the ToolbarExample class and lines 12-14 define the callback method which will terminate the program.
16 # that's easy... when one of the buttons is toggled, we just 17 # check which one is active and set the style of the toolbar 18 # accordingly 19 def radio_event(self, widget, toolbar): 20 if self.text_button.get_active(): 21 toolbar.set_style(gtk.TOOLBAR_TEXT) 22 elif self.icon_button.get_active(): 23 toolbar.set_style(gtk.TOOLBAR_ICONS) 24 elif self.both_button.get_active(): 25 toolbar.set_style(gtk.TOOLBAR_BOTH) 26 27 # even easier, just check given toggle button and enable/disable 28 # tooltips 29 def toggle_event(self, widget, toolbar): 30 toolbar.set_tooltips(widget.get_active()) 31 |
Lines 19-30 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).
32 def __init__(self): 33 # Here is our main window (a dialog) and a handle for the handlebox 34 # Ok, we need a toolbar, an icon with a mask (one for all of 35 # the buttons) and an icon widget to put this icon in (but 36 # we'll create a separate widget for each button) 37 # create a new window with a given title, and nice size 38 dialog = gtk.Dialog() 39 dialog.set_title("GTKToolbar Tutorial") 40 dialog.set_size_request(450, 250) 41 dialog.set_resizable(True) 42 43 # typically we quit if someone tries to close us 44 dialog.connect("delete_event", self.delete_event) 45 46 # to make it nice we'll put the toolbar into the handle box, 47 # so that it can be detached from the main window 48 handlebox = gtk.HandleBox() 49 dialog.vbox.pack_start(handlebox, False, False, 5) 50 |
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.
51 # toolbar will be horizontal, with both icons and text, and 52 # with 5pxl spaces between items and finally, 53 # we'll also put it into our handlebox 54 toolbar = gtk.Toolbar() 55 toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL) 56 toolbar.set_style(gtk.TOOLBAR_BOTH) 57 toolbar.set_border_width(5) 58 handlebox.add(toolbar) 59 |
Well, what we do above is just a straightforward initialization of the toolbar widget.
60 # our first item is <close> button 61 iconw = gtk.Image() # icon widget 62 iconw.set_from_file("gtk.xpm") 63 close_button = toolbar.append_item( 64 "Close", # button label 65 "Closes this app", # this button's tooltip 66 "Private", # tooltip private info 67 iconw, # icon widget 68 self.delete_event) # a signal 69 toolbar.append_space() # space after item |
In the above code you see the simplest case: adding a button to toolbar. Just before appending a new item, we have to construct an image 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.
71 # now, let's make our radio buttons group... 72 iconw = gtk.Image() # icon widget 73 iconw.set_from_file("gtk.xpm") 74 icon_button = toolbar.append_element( 75 gtk.TOOLBAR_CHILD_RADIOBUTTON, # type of element 76 None, # widget 77 "Icon", # label 78 "Only icons in toolbar", # tooltip 79 "Private", # tooltip private string 80 iconw, # icon 81 self.radio_event, # signal 82 toolbar) # data for signal 83 toolbar.append_space() 84 self.icon_button = icon_button 85 |
Here we begin creating a radio buttons group. To do this we use the append_element() method. In fact, using this method one can also add simple items or even spaces (type = gtk.TOOLBAR_CHILD_SPACE or gtk.TOOLBAR_CHILD_BUTTON). 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 Section 6.4, “Radio Buttons” earlier in this tutorial). We also save a reference to the button in the ToolbarExample instance for later access.
86 # following radio buttons refer to previous ones 87 iconw = gtk.Image() # icon widget 88 iconw.set_from_file("gtk.xpm") 89 text_button = toolbar.append_element( 90 gtk.TOOLBAR_CHILD_RADIOBUTTON, 91 icon_button, 92 "Text", 93 "Only texts in toolbar", 94 "Private", 95 iconw, 96 self.radio_event, 97 toolbar) 98 toolbar.append_space() 99 self.text_button = text_button 100 101 iconw = gtk.Image() # icon widget 102 iconw.set_from_file("gtk.xpm") 103 both_button = toolbar.append_element( 104 gtk.TOOLBAR_CHILD_RADIOBUTTON, 105 text_button, 106 "Both", 107 "Icons and text in toolbar", 108 "Private", 109 iconw, 110 self.radio_event, 111 toolbar) 112 toolbar.append_space() 113 self.both_button = both_button 114 both_button.set_active(True) 115 |
We create the other radiobuttons the same way except we pass one of the created radio group buttons to the append_element() method to specify the radio group.
In the end we have to set the state of one of the buttons manually (otherwise they all stay in active state, preventing us from switching between them).
116 # here we have just a simple toggle button 117 iconw = gtk.Image() # icon widget 118 iconw.set_from_file("gtk.xpm") 119 tooltips_button = toolbar.append_element( 120 gtk.TOOLBAR_CHILD_TOGGLEBUTTON, 121 None, 122 "Tooltips", 123 "Toolbar with or without tips", 124 "Private", 125 iconw, 126 self.toggle_event, 127 toolbar) 128 toolbar.append_space() 129 tooltips_button.set_active(True) 130 |
A toggle button can be created in the obvious way (if one knows how to create radio buttons already).
131 # to pack a widget into toolbar, we only have to 132 # create it and append it with an appropriate tooltip 133 entry = gtk.Entry() 134 toolbar.append_widget(entry, "This is just an entry", "Private") 135 136 # well, it isn't created within the toolbar, so we must still show it 137 entry.show() 138 |
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 will be shown together with the toolbar).
139 # that's it ! let's show everything. 140 toolbar.show() 141 handlebox.show() 142 dialog.show() 143 144 def main(): 145 # rest in gtk_main and wait for the fun to begin! 146 gtk.main() 147 return 0 148 149 if __name__ == "__main__": 150 ToolbarExample() 151 main() |
Line 142 ends the ToolbarExample class definition. Lines 144-147 define the main() function which just calls the gtk.main() function to start the event processing loop. Lines 149-151 arrange to create a ToolbarExample instance and then enter the event processing 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, gtk.xpm. Figure 10.8, “Toolbar Example” illustrates the resulting display:
Table of Contents
The Clipboard object was added in PyGTK 2.2. The GtkClipboard was available in GTK+ 2.0 but was not wrapped by PyGTK 2.0 because it was not a complete GObject. Some new objects were added to the gtk.gdk module in PyGTK 2.2 but they will not be described in this tutorial. See the PyGTK 2 Reference Manual for more information on the gtk.gdk.Display, gtk.gdk.DisplayManager and gtk.gdk.Screen objects.
A Clipboard provides a storage area for sharing data between processes or between different widgets in the same process. Each Clipboard is identified by a string name encoded as a gdk.Atom. You can use any name you want to identify a Clipboard and a new one will be created if it doesn't exist. If you want to share a Clipboard with other processes each process will need to know the Clipboard's name.
Clipboards are built on the SelectionData and selection interfaces. The default Clipboard used by the TextView, Label and Entry widgets is "CLIPBOARD". Other common clipboards are "PRIMARY" and "SECONDARY" that correspond to the primary and secondary selections (Win32 ignores these). These can also be specified using the gtk.gdk.Atom objects: gtk.gdk.SELECTION_CLIPBOARD, gtk.gdk.SELECTION_PRIMARY and gtk.gdk.SELECTION_SECONDARY. See the gtk.gdk.Atom reference documentation for more information.
A Clipboard is created using the constructor:
clipboard = gtk.Clipboard(display, selection) |
where display is the gtk.gdk.Display associated with the Clipboard named by selection. The following convenience function creates a Clipboard using the default gtk.gdk.Display:
clipboard = gtk.clipboard_get(selection) |
Finally, a Clipboard can also be created using the Widget method:
clipboard = widget.get_clipboard(selection) |
The widget must be realized and be part of a toplevel window hierarchy.
Entry, SpinButton and TextView widgets have popup menus that provide the ability to cut and copy the selected text to and paste from the "CLIPBOARD" clipboard. In addition key bindings are set to allow keyboard accelerators to cut, copy and paste. Cut is activated by Control+X; copy, by Control+C; and, paste, by Control+V.
The widgets (Entry and SpinButton) implement the Editable interface that has the following methods to cut, copy and paste to and from the "CLIPBOARD" clipboard:
editable.cut_clipboard() editable.copy_clipboard() editable.paste_clipboard() |
A Label that is selectable (the "selectable" property is TRUE) also supports copying the selected text to the "CLIPBOARD" clipboard using a popup menu or the Control+C keyboard accelerator.
TextBuffers have similar methods though they also allow specifying the clipboard to use:
textbuffer.copy_clipboard(clipboard) |
The selection text will be copied to the Clipboard specified by clipboard.
textbuffer.cut_clipboard(clipboard, default_editable) |
The selected text will be copied to clipboard. If default_editable is TRUE the selected text will also be deleted from the TextBuffer. Otherwise, cut_clipboard() will act like the copy_clipboard() method.
textbuffer.paste_clipboard(clipboard, override_location, default_editable) |
If default_editable is TRUE, the contents of clipboard will be inserted into the TextBuffer at the location specified by the TextIter override_location. If default_editable is FALSE, paste_clipboard() will not insert the contents of clipboard. If override_location is None the contents of clipboard will be inserted at the cursor location.
TextBuffers also have two methods to manage a set of Clipboards that are automatically set with the contents of the current selection:
textbuffer.add_selection_clipboard(clipboard) textbuffer.remove_selection_clipboard(clipboard) |
When a TextBuffer is added to a TextView the "PRIMARY" clipboard is automatically added to the selection clipboards. Your application can add other clipboards (for example, the "CLIPBOARD" clipboard).
You can set the Clipboard data programmatically using either of:
clipboard.set_with_data(targets, get_func, clear_func, user_data) clipboard.set_text(text, len=-1) |
The set_with_data() method indicates which selection data targets are supported and provides functions (get_func and clear_func) that are called when the data is asked for or the clipboard data is changed. user_data is passed to get_func or clear_func when called. targets is a list of 3-tuples containing:
The signatures of get_func and clear_func are:
def get_func(clipboard, selectiondata, info, data): def clear_func(clipboard, data): |
where clipboard is the Clipboard, selectiondata is a SelectionData object to set the data in, info is the application assigned integer associated with a target, and data is user_data.
set_text() is a convenience method that uses the set_with_data() method to set text data on a Clipboard with the targets: "STRING", "TEXT", "COMPOUND_TEXT", and "UTF8_STRING". It uses internal get and clear functions to manage the data. set_text() is equivalent to the following:
def my_set_text(self, text, len=-1): targets = [ ("STRING", 0, 0), ("TEXT", 0, 1), ("COMPOUND_TEXT", 0, 2), ("UTF8_STRING", 0, 3) ] def text_get_func(clipboard, selectiondata, info, data): selectiondata.set_text(data) return def text_clear_func(clipboard, data): del data return self.set_with_data(targets, text_get_func, text_clear_func, text) return |
Once data is set on a clipboard, it will be available until the application is finished or the clipboard data is changed.
To provide the behavior typical of cut to a clipboard, your application will have to delete the selected text or object after copying it to the clipboard.
The contents of a Clipboard can be retrieved using the following method:
clipboard.request_contents(target, callback, user_data=None) |
The contents specified by target are retrieved asynchronously in the function specified by callback which is called with user_data. The signature of callback is:
def callback(clipboard, selectiondata, data): |
where selectiondata is a SelectionData object containing the contents of clipboard. data is user_data. The request_contents() method is the most general way of retrieving the contents of a Clipboard. The following convenience method retrieves the text contents of a Clipboard:
clipboard.request_text(callback, user_data=None) |
The text string is returned to the callback function instead of a Selectiondata object. You can check which targets are available on the Clipboard by using the method:
clipboard.request_targets(callback, user_data=None) |
The targets are returned as a tuple of gtk.gdk.Atom objects to the callback function.
Two convenience methods are provided to return the Clipboard contents synchronously:
selectiondata = clipboard.wait_for_contents(target) text = clipboard.wait_for_text() |
To illustrate the use of a Clipboard the clipboard.py example program tracks the text items that are cut or copied to the "CLIPBOARD" clipboard and saves the last ten clipboard entries. There are ten buttons that provide access to the text of the saved entries. The button label display the first sixteen characters of the saved text and the tooltips display the targets that the entry originally had. When an entry button is clicked the text window is loaded with the associated saved text which is editable. The button below the text window saves the current text window contents to the clipboard.
Figure 15.1, “Clipboard Example Program” illustrates the clipboard.py example program in operation:
The example program polls the clipboard every 1.5 seconds to see if the contents have changed. The program could be changed to duplicate the complete set of target contents and then take ownership of the clipboard using the set_with_data() method. Later, when another program sets the contents of the clipboard, the clear_func will be called and it can be used to reload the clipboard contents and retake the clipboard ownership.
The testtext.py example program (derived from the testtext.c program included in the GTK+ 2.0.x distribution) demonstrates the use of the TextView widget and its associated objects: TextBuffers, TextIters, TextMarks, TextTags, TextTagTables. Figure 13.2, “TextView Example” illustrates its operation:
The testtext.py program defines a number of classes in addition to the application class TestText:
Buffer class, lines 99-496, is subclassed from the gtk.TextBuffer type. It provides the editing buffer capabilities used by the View objects.
View class, lines 498-1126, is subclassed from the gtk.Window type and wraps a gtk.TextView object that uses a Buffer object instead of a gtk.TextBuffer object. It provides a window and the visual display of the contents of a Buffer object as well as a menubar.
FileSel class, lines 73-97, is subclassed from the gtk.FileSelection type to provide selection of filenames for the Buffer contents.
Stack class to provide simple stack objects.
The color cycle display is implemented by using text tags applied to a section of text in a buffer. Lines 109-115 (in the __init__() method) create these tags and lines 763-784 (do_apply_colors() method) apply the color tags to a section of text two characters at a time. Lines 202-239 provide the methods (color_cycle_timeout(), set_colors() and cycle_colors()) that produce the color cycle display when enabled. Color cycling is enabled by setting (line 220) the foreground_gdk property of the individual color_tags (which also sets the foreground_set property). Color cycling is disabled by setting the foreground_set property to FALSE (line 222). The colors are periodically changed by shifting the start_hue (line 237)
A new Buffer is filled with example content when the
-> menu item is selected (the fill_example_buffer() method in lines 302-372). The example buffer contains text of various colors, styles and languages and pixbufs. The init_tags() method (lines 260-300) sets up a variety of TextTags for use with the example text. The event signal of these tags is connected to the tag_event_handler() method (lines 241-256) to illustrate button and motion event capture.The TextView wrap mode is set to WRAP_WORD (line 580) and the TextView border windows are displayed by setting their sizes in lines 587-588 and line 596-597. The left and right border windows are used to display line numbers and the top and bottom border windows display the tab locations when custom tabs are set. The border windows are updated when an "expose-event" signal is received by the TextView (lines 590 and 599). The line_numbers_expose() method (lines 1079-1116) determines whether the left or right border window has an expose event and if so calculates the size of the expose area. Then the location of the line start and the line number for each line in the exposed area is calculated in the get_lines() method (lines 1057-1077). The line numbers are then drawn in the border window at the location (transformed by line 1109).
The custom tab locations are displayed in the top and bottom border windows in a similar fashion (lines 1013-1055). They are displayed only when the cursor is moved inside a range of text that has the custom tab attribute set. This is detected by handling the "mark-set" signal in the cursor_set_handler() method (lines 999-1011) and invalidating the top and bottom border windows if the mark set is the insert mark.
Movable widgets are added to a View with the do_add_children() method (lines 892-899) which calls the add_movable_children() method (lines 874-890). The children are gtk.Labels that can be dragged around inside the various windows that are part of a TextView widget.
Likewise, widgets are added to the TextView windows of a View and the Buffer by using the do_add_focus_children() method (lines 901-949).
Progress bars are used to show the status of an operation. They are pretty easy to use, as you will see with the code below. But first lets start out with the call to create a new progress bar.
progressbar = gtk.ProgressBar(adjustment=None) |
The adjustment argument specifies an adjustment to use with the progressbar. If not specified an adjustment will be created. Now that the progress bar has been created we can use it.
progressbar.set_fraction(fraction) |
The progressbar object is the progress bar you wish to operate on, and the argument (fraction) is the amount "completed", meaning the amount the progress bar has been filled from 0-100%. This is passed to the method as a real number ranging from 0 to 1.
A progress bar may be set to one of a number of orientations using the method:
progressbar.set_orientation(orientation) |
The orientation argument may take one of the following values to indicate the direction in which the progress bar moves:
PROGRESS_LEFT_TO_RIGHT PROGRESS_RIGHT_TO_LEFT PROGRESS_BOTTOM_TO_TOP PROGRESS_TOP_TO_BOTTOM |
As well as indicating the amount of progress that has occurred, the progress bar may be set to just indicate that there is some activity. This can be useful in situations where progress cannot be measured against a value range. The following function indicates that some progress has been made.
progressbar.pulse() |
The step size of the activity indicator is set using the following function where fraction is between 0.0 and 1.0.
progressbar.set_pulse_step(fraction) |
When not in activity mode, the progress bar can also display a configurable text string within its trough, using the following method:
progressbar.set_text(text) |
Note that set_text() doesn't support the printf()-like formatting of the GTK+ 1.2 Progressbar.
You can turn off the display of the string by calling set_text() again with no argument.
The current text setting of a progressbar can be retrieved with the following method:
text = progressbar.get_text() |
Progress Bars are usually used with timeouts or other such functions (see Chapter 19, Timeouts, IO and Idle Functions) to give the illusion of multitasking. All will employ the set_fraction() or pulse() methods in the same manner.
The progressbar.py program provides an example of the progress bar, updated using timeouts. This code also shows you how to reset the Progress Bar. Figure 9.4, “ProgressBar Example” illustrates the resulting display:
The source code for progressbar.py is:
1 #!/usr/bin/env python 2 3 # example progressbar.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk, gobject 8 9 # Update the value of the progress bar so that we get 10 # some movement 11 def progress_timeout(pbobj): 12 if pbobj.activity_check.get_active(): 13 pbobj.pbar.pulse() 14 else: 15 # Calculate the value of the progress bar using the 16 # value range set in the adjustment object 17 new_val = pbobj.pbar.get_fraction() + 0.01 18 if new_val > 1.0: 19 new_val = 0.0 20 # Set the new value 21 pbobj.pbar.set_fraction(new_val) 22 23 # As this is a timeout function, return TRUE so that it 24 # continues to get called 25 return True 26 27 class ProgressBar: 28 # Callback that toggles the text display within the progress 29 # bar trough 30 def toggle_show_text(self, widget, data=None): 31 if widget.get_active(): 32 self.pbar.set_text("some text") 33 else: 34 self.pbar.set_text("") 35 36 # Callback that toggles the activity mode of the progress 37 # bar 38 def toggle_activity_mode(self, widget, data=None): 39 if widget.get_active(): 40 self.pbar.pulse() 41 else: 42 self.pbar.set_fraction(0.0) 43 44 # Callback that toggles the orientation of the progress bar 45 def toggle_orientation(self, widget, data=None): 46 if self.pbar.get_orientation() == gtk.PROGRESS_LEFT_TO_RIGHT: 47 self.pbar.set_orientation(gtk.PROGRESS_RIGHT_TO_LEFT) 48 elif self.pbar.get_orientation() == gtk.PROGRESS_RIGHT_TO_LEFT: 49 self.pbar.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT) 50 51 # Clean up allocated memory and remove the timer 52 def destroy_progress(self, widget, data=None): 53 gobject.source_remove(self.timer) 54 self.timer = 0 55 gtk.main_quit() 56 57 def __init__(self): 58 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 59 self.window.set_resizable(True) 60 61 self.window.connect("destroy", self.destroy_progress) 62 self.window.set_title("ProgressBar") 63 self.window.set_border_width(0) 64 65 vbox = gtk.VBox(False, 5) 66 vbox.set_border_width(10) 67 self.window.add(vbox) 68 vbox.show() 69 70 # Create a centering alignment object 71 align = gtk.Alignment(0.5, 0.5, 0, 0) 72 vbox.pack_start(align, False, False, 5) 73 align.show() 74 75 # Create the ProgressBar 76 self.pbar = gtk.ProgressBar() 77 78 align.add(self.pbar) 79 self.pbar.show() 80 81 # Add a timer callback to update the value of the progress bar 82 self.timer = gobject.timeout_add (100, progress_timeout, self) 83 84 separator = gtk.HSeparator() 85 vbox.pack_start(separator, False, False, 0) 86 separator.show() 87 88 # rows, columns, homogeneous 89 table = gtk.Table(2, 2, False) 90 vbox.pack_start(table, False, True, 0) 91 table.show() 92 93 # Add a check button to select displaying of the trough text 94 check = gtk.CheckButton("Show text") 95 table.attach(check, 0, 1, 0, 1, 96 gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 97 5, 5) 98 check.connect("clicked", self.toggle_show_text) 99 check.show() 100 101 # Add a check button to toggle activity mode 102 self.activity_check = check = gtk.CheckButton("Activity mode") 103 table.attach(check, 0, 1, 1, 2, 104 gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 105 5, 5) 106 check.connect("clicked", self.toggle_activity_mode) 107 check.show() 108 109 # Add a check button to toggle orientation 110 check = gtk.CheckButton("Right to Left") 111 table.attach(check, 0, 1, 2, 3, 112 gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 113 5, 5) 114 check.connect("clicked", self.toggle_orientation) 115 check.show() 116 117 # Add a button to exit the program 118 button = gtk.Button("close") 119 button.connect("clicked", self.destroy_progress) 120 vbox.pack_start(button, False, False, 0) 121 122 # This makes it so the button is the default. 123 button.set_flags(gtk.CAN_DEFAULT) 124 125 # This grabs this button to be the default button. Simply hitting 126 # the "Enter" key will cause this button to activate. 127 button.grab_default () 128 button.show() 129 130 self.window.show() 131 132 def main(): 133 gtk.main() 134 return 0 135 136 if __name__ == "__main__": 137 ProgressBar() 138 main() |
Table of Contents
These all require authors! :) Please consider contributing to our tutorial.
If you must use one of these widgets that are undocumented, I strongly suggest you take a look at the *.c files in the PyGTK distribution. PyGTK's method names are very descriptive. Once you have an understanding of how things work, it's not difficult to figure out how to use a widget simply by looking at its method definitions. This, along with a few examples from others' code, and it should be no problem.
When you do come to understand all the methods of a new undocumented widget, please consider writing a tutorial on it so others may benefit from your time.
A TextMark indicates a location in a TextBuffer between two characters that is preserved across buffer modifications. TextMarks are created, moved and deleted using the TextBuffer methods as described in the TextBuffer section.
A TextBuffer has two built-in TextMarks named: insert and selection_bound which refer to the insertion point and the boundary of the selection (these may refer to the same location).
The name of a TextMark can be retrieved using the method:
name = textmark.get_name() |
By default marks other than insert are not visible (displayed as a vertical bar). The visibility of a mark can be set and retrieved using the methods:
setting = textmark.get_visible() textmark.set_visible(setting) |
where setting is TRUE if the mark is visible.
The TextBuffer that contains a TextMark can be obtained using the method:
buffer = textmark.get_buffer() |
You can determine whether a TextMark has been deleted using the method:
setting = textmark.get_deleted() |
The left gravity of a TextMark can be retrieved using the method:
setting = textmark.get_left_gravity() |
The left gravity of a TextMark indicates where the mark will end up after an insertion. If left gravity is TRUE the mark will be to the left of the insertion; if FALSE, to the right of the insertion.
child-attached(handle_box, widget, data) child-detached(handle_box, widget, data) |
It is unlikely that you will ever need to use the Viewport widget directly. You are much more likely to use the ScrolledWindow widget *see Section 10.9, “Scrolled Windows”) which in turn uses the Viewport.
A viewport widget allows you to place a larger widget within it such that you can view a part of it at a time. It uses Adjustment object (see Chapter 7, Adjustments) to define the area that is currently in view.
A Viewport is created with the function:
viewport = gtk.Viewport(hadjustment=None, vadjustment=None) |
As you can see you can specify the horizontal and vertical Adjustment objects that the widget is to use when you create the widget. It will create its own if you pass None as the value of the arguments or pass no arguments.
You can get and set the adjustments after the widget has been created using the following four methods:
viewport.get_hadjustment() viewport.get_vadjustment() viewport.set_hadjustment(adjustment) viewport.set_vadjustment(adjustment) |
The only other viewport method is used to alter its appearance:
viewport.set_shadow_type(type) |
Possible values for the type parameter are:
SHADOW_NONE SHADOW_IN SHADOW_OUT SHADOW_ETCHED_IN SHADOW_ETCHED_OUT |
Tooltips are the little text strings that pop up when you leave your pointer over a button or other widget for a few seconds.
Widgets that do not receive events (widgets that do not have their own window) will not work with tooltips.
The first call you will use creates a new tooltip. You only need to do this once for a set of tooltips as the gtk.Tooltips object this function returns can be used to create multiple tooltips.
tooltips = gtk.Tooltips() |
Once you have created a new tooltip, and the widget you wish to use it on, simply use this call to set it:
tooltips.set_tip(widget, tip_text, tip_private=None) |
The object tooltips is the tooltip you've already created. The first argument (widget) is the widget you wish to have this tooltip pop up for; the second (tip_text), the text you wish it to display. The last argument (tip_private) is a text string that can be used as an identifier.
The tooltip.py example program modifies the arrow.py program to add a tooltip for each button. Figure 9.3, “Tooltips Example” illustrates the resulting display with the tooltip for the second arrow button displayed:
The source code for tooltip.py is:
1 #!/usr/bin/env python 2 3 # example tooltip.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 # Create an Arrow widget with the specified parameters 10 # and pack it into a button 11 def create_arrow_button(arrow_type, shadow_type): 12 button = gtk.Button() 13 arrow = gtk.Arrow(arrow_type, shadow_type) 14 button.add(arrow) 15 button.show() 16 arrow.show() 17 return button 18 19 class Tooltips: 20 def __init__(self): 21 # Create a new window 22 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 23 24 window.set_title("Tooltips") 25 26 # It's a good idea to do this for all windows. 27 window.connect("destroy", lambda w: gtk.main_quit()) 28 29 # Sets the border width of the window. 30 window.set_border_width(10) 31 32 # Create a box to hold the arrows/buttons 33 box = gtk.HBox(False, 0) 34 box.set_border_width(2) 35 window.add(box) 36 37 # create a tooltips object 38 self.tooltips = gtk.Tooltips() 39 40 # Pack and show all our widgets 41 box.show() 42 43 button = create_arrow_button(gtk.ARROW_UP, gtk.SHADOW_IN) 44 box.pack_start(button, False, False, 3) 45 self.tooltips.set_tip(button, "SHADOW_IN") 46 47 button = create_arrow_button(gtk.ARROW_DOWN, gtk.SHADOW_OUT) 48 box.pack_start(button, False, False, 3) 49 self.tooltips.set_tip(button, "SHADOW_OUT") 50 51 button = create_arrow_button(gtk.ARROW_LEFT, gtk.SHADOW_ETCHED_IN) 52 box.pack_start(button, False, False, 3) 53 self.tooltips.set_tip(button, "SHADOW_ETCHED_IN") 54 55 button = create_arrow_button(gtk.ARROW_RIGHT, gtk.SHADOW_ETCHED_OUT) 56 box.pack_start(button, False, False, 3) 57 self.tooltips.set_tip(button, "SHADOW_ETCHED_OUT") 58 59 window.show() 60 61 def main(): 62 gtk.main() 63 return 0 64 65 if __name__ == "__main__": 66 tt = Tooltips() 67 main() |
There are other methods that can be used with tooltips. I will just list them with a brief description of what they do.
tooltips.enable() |
Enable a disabled set of tooltips.
tooltips.disable() |
Disable an enabled set of tooltips.
tooltips.set_delay(delay) |
Sets how many milliseconds you have to hold your pointer over the widget before the tooltip will pop up. The default is 500 milliseconds (half a second).
And that's all the methods associated with tooltips. More than you'll ever want to know :-)
Table of Contents
TextView widgets and their associated objects (TextBuffers, TextMarks, TextIters, TextTags and TextTagTables) provide a powerful framework for multiline text editing.
A TextBuffer (see Section 13.3, “Text Buffers”) contains the text which is displayed by one or more TextView widgets.
Within GTK+ 2.0, text is encoded in UTF-8 which means that one character may be encoded as multiple bytes. Within a TextBuffer it is necessary to differentiate between the character counts (called offsets) and the byte counts (called indexes).
TextIters provide a volatile representation of the position in a TextBuffer between two characters. TextIters are valid until the number of characters in the TextBuffer changes; i.e. any time characters are inserted or deleted from a TextBuffer all TextIters will become invalid. TextIters are the primary way to specify locations in a TextBuffer for manipulating text.
TextMarks are provided to allow preservation of TextBuffer positions across buffer modifications. A mark is like a TextIter (see Section 13.4, “Text Iters”) in that it represents a position between two characters in a TextBuffer) but if the text surrounding the mark is deleted the mark remains where the deleted text once was. Likewise, if text is inserted at the mark the mark ends up either to the left or right of the inserted text depending on the gravity of the mark - right gravity leaves the mark to the right of the inserted text while left gravity leaves it to the left. TextMarks (see Section 13.5, “Text Marks”) may be named or anonymous if not given a name. Each TextBuffer has two predefined named TextMarks (see Section 13.5, “Text Marks”) called insert and selection_bound. These refer to the insertion point and the boundary of the selection (the selection is between the insert and the selection_bound marks).
TextTags (see Section 13.6.1, “Text Tags”) are objects that specify a set of attributes that can be applied to a range of text in a TextBuffer. Each TextBuffer has a TextTagTable (see Section 13.6.2, “Text Tag Tables”) which contains the tags that are available in that buffer. TextTagTables can be shared between TextBuffers to provide commonality. TextTags are generally used to change the appearance of a range of text but can also be used to prevent a range of text from being edited.
The methods:
widget.show() widget.show_all() widget.hide() widget.hide_all() widget.realize() widget.unrealize() widget.map() widget.unmap() |
manage the display of the widget.
The show() method arranges to display the widget by using the realize() and map() methods.
The hide() method arranges to remove the widget from the display and also unmaps it using the unmap() method if necessary.
The show_all() and hide_all() methods arrange to show or hide the widget and all its children.
The realize() method arranges to allocate resources to the widget including its window.
The unrealize() method releases the widget window and other resources. Unrealizing a widget will also hide and unmap it.
The map() method arranges to allocate space on the display for the widget; this only applies to widgets that need to be handled by the window manager. Mapping a widget will also cause it to be realized if necessary.
The unmap() method removes a widget from the display and will also hide it if necessary.
The Calendar widget is an effective way to display and retrieve monthly date related information. It is a very simple widget to create and work with.
Creating a gtk.Calendar widget is as simple as:
calendar = gtk.Calendar() |
The calendar will display the current month and year by default.
There might be times where you need to change a lot of information within this widget and the following methods allow you to make multiple changes to a Calendar widget without the user seeing multiple on-screen updates.
calendar.freeze() calendar.thaw() |
They work just like the freeze/thaw methods of every other widget.
The Calendar widget has a few options that allow you to change the way the widget both looks and operates by using the method:
calendar.display_options(flags) |
The flags argument can be formed by combining either of the following five options using the logical bitwise OR (|) operation:
CALENDAR_SHOW_HEADING | this option specifies that the month and year should be shown when drawing the calendar. |
CALENDAR_SHOW_DAY_NAMES | this option specifies that the three letter descriptions should be displayed for each day (e.g. Mon,Tue, etc.). |
CALENDAR_NO_MONTH_CHANGE | this option states that the user should not and can not change the currently displayed month. This can be good if you only need to display a particular month such as if you are displaying 12 calendar widgets for every month in a particular year. |
CALENDAR_SHOW_WEEK_NUMBERS | this option specifies that the number for each week should be displayed down the left side of the calendar. (e.g. Jan 1 = Week 1,Dec 31 = Week 52). |
CALENDAR_WEEK_START_MONDAY | this option states that the calender week will start on Monday instead of Sunday which is the default. This only affects the order in which days are displayed from left to right. Note that in PyGTK 2.4 and above this option is deprecated. |
The following methods are used to set the the currently displayed date:
result = calendar.select_month(month, year) calendar.select_day(day) |
The return value from the select_month() method is a boolean value indicating whether the selection was successful.
With the select_day() method the specified day number is selected within the current month, if that is possible. A day value of 0 will deselect any current selection.
In addition to having a day selected, any number of days in the month may be "marked". A marked day is highlighted within the calendar display. The following methods are provided to manipulate marked days:
result = calendar.mark_day(day) result = calendar.unmark_day(day) calendar.clear_marks() |
mark_day() and unmark_day() return a boolean indicating whether the method was successful. Note that marks are persistent across month and year changes.
The final Calendar widget method is used to retrieve the currently selected date, month and/or year.
year, month, day = calendar.get_date() |
The Calendar widget can generate a number of signals indicating date selection and change. The names of these signals are self explanatory, and are:
month_changed day_selected day_selected_double_click prev_month next_month prev_year next_year |
That just leaves us with the need to put all of this together into the calendar.py example program. Figure 9.12, “Calendar Example” illustrates the program operation:
The calendar.py source code is:
1 #!/usr/bin/env python 2 3 # example calendar.py 4 # 5 # Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias Gronlund 6 # Copyright (C) 2000 Tony Gale 7 # Copyright (C) 2001-2004 John Finlay 8 # 9 # This program is free software; you can redistribute it and/or modify 10 # it under the terms of the GNU General Public License as published by 11 # the Free Software Foundation; either version 2 of the License, or 12 # (at your option) any later version. 13 # 14 # This program is distributed in the hope that it will be useful, 15 # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 # GNU General Public License for more details. 18 # 19 # You should have received a copy of the GNU General Public License 20 # along with this program; if not, write to the Free Software 21 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 23 import pygtk 24 pygtk.require('2.0') 25 import gtk, pango 26 import time 27 28 class CalendarExample: 29 DEF_PAD = 10 30 DEF_PAD_SMALL = 5 31 TM_YEAR_BASE = 1900 32 33 calendar_show_header = 0 34 calendar_show_days = 1 35 calendar_month_change = 2 36 calendar_show_week = 3 37 38 def calendar_date_to_string(self): 39 year, month, day = self.window.get_date() 40 mytime = time.mktime((year, month+1, day, 0, 0, 0, 0, 0, -1)) 41 return time.strftime("%x", time.localtime(mytime)) 42 43 def calendar_set_signal_strings(self, sig_str): 44 prev_sig = self.prev_sig.get() 45 self.prev2_sig.set_text(prev_sig) 46 47 prev_sig = self.last_sig.get() 48 self.prev_sig.set_text(prev_sig) 49 self.last_sig.set_text(sig_str) 50 51 def calendar_month_changed(self, widget): 52 buffer = "month_changed: %s" % self.calendar_date_to_string() 53 self.calendar_set_signal_strings(buffer) 54 55 def calendar_day_selected(self, widget): 56 buffer = "day_selected: %s" % self.calendar_date_to_string() 57 self.calendar_set_signal_strings(buffer) 58 59 def calendar_day_selected_double_click(self, widget): 60 buffer = "day_selected_double_click: %s" 61 buffer = buffer % self.calendar_date_to_string() 62 self.calendar_set_signal_strings(buffer) 63 64 year, month, day = self.window.get_date() 65 66 if self.marked_date[day-1] == 0: 67 self.window.mark_day(day) 68 self.marked_date[day-1] = 1 69 else: 70 self.window.unmark_day(day) 71 self.marked_date[day-1] = 0 72 73 def calendar_prev_month(self, widget): 74 buffer = "prev_month: %s" % self.calendar_date_to_string() 75 self.calendar_set_signal_strings(buffer) 76 77 def calendar_next_month(self, widget): 78 buffer = "next_month: %s" % self.calendar_date_to_string() 79 self.calendar_set_signal_strings(buffer) 80 81 def calendar_prev_year(self, widget): 82 buffer = "prev_year: %s" % self.calendar_date_to_string() 83 self.calendar_set_signal_strings(buffer) 84 85 def calendar_next_year(self, widget): 86 buffer = "next_year: %s" % self.calendar_date_to_string() 87 self.calendar_set_signal_strings(buffer) 88 89 def calendar_set_flags(self): 90 options = 0 91 for i in range(5): 92 if self.settings[i]: 93 options = options + (1<<i) 94 if self.window: 95 self.window.display_options(options) 96 97 def calendar_toggle_flag(self, toggle): 98 j = 0 99 for i in range(5): 100 if self.flag_checkboxes[i] == toggle: 101 j = i 102 103 self.settings[j] = not self.settings[j] 104 self.calendar_set_flags() 105 106 def calendar_font_selection_ok(self, button): 107 self.font = self.font_dialog.get_font_name() 108 if self.window: 109 font_desc = pango.FontDescription(self.font) 110 if font_desc: 111 self.window.modify_font(font_desc) 112 113 def calendar_select_font(self, button): 114 if not self.font_dialog: 115 window = gtk.FontSelectionDialog("Font Selection Dialog") 116 self.font_dialog = window 117 118 window.set_position(gtk.WIN_POS_MOUSE) 119 120 window.connect("destroy", self.font_dialog_destroyed) 121 122 window.ok_button.connect("clicked", 123 self.calendar_font_selection_ok) 124 window.cancel_button.connect_object("clicked", 125 lambda wid: wid.destroy(), 126 self.font_dialog) 127 window = self.font_dialog 128 if not (window.flags() & gtk.VISIBLE): 129 window.show() 130 else: 131 window.destroy() 132 self.font_dialog = None 133 134 def font_dialog_destroyed(self, data=None): 135 self.font_dialog = None 136 137 def __init__(self): 138 flags = [ 139 "Show Heading", 140 "Show Day Names", 141 "No Month Change", 142 "Show Week Numbers", 143 ] 144 self.window = None 145 self.font = None 146 self.font_dialog = None 147 self.flag_checkboxes = 5*[None] 148 self.settings = 5*[0] 149 self.marked_date = 31*[0] 150 151 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 152 window.set_title("Calendar Example") 153 window.set_border_width(5) 154 window.connect("destroy", lambda x: gtk.main_quit()) 155 156 window.set_resizable(False) 157 158 vbox = gtk.VBox(False, self.DEF_PAD) 159 window.add(vbox) 160 161 # The top part of the window, Calendar, flags and fontsel. 162 hbox = gtk.HBox(False, self.DEF_PAD) 163 vbox.pack_start(hbox, True, True, self.DEF_PAD) 164 hbbox = gtk.HButtonBox() 165 hbox.pack_start(hbbox, False, False, self.DEF_PAD) 166 hbbox.set_layout(gtk.BUTTONBOX_SPREAD) 167 hbbox.set_spacing(5) 168 169 # Calendar widget 170 frame = gtk.Frame("Calendar") 171 hbbox.pack_start(frame, False, True, self.DEF_PAD) 172 calendar = gtk.Calendar() 173 self.window = calendar 174 self.calendar_set_flags() 175 calendar.mark_day(19) 176 self.marked_date[19-1] = 1 177 frame.add(calendar) 178 calendar.connect("month_changed", self.calendar_month_changed) 179 calendar.connect("day_selected", self.calendar_day_selected) 180 calendar.connect("day_selected_double_click", 181 self.calendar_day_selected_double_click) 182 calendar.connect("prev_month", self.calendar_prev_month) 183 calendar.connect("next_month", self.calendar_next_month) 184 calendar.connect("prev_year", self.calendar_prev_year) 185 calendar.connect("next_year", self.calendar_next_year) 186 187 separator = gtk.VSeparator() 188 hbox.pack_start(separator, False, True, 0) 189 190 vbox2 = gtk.VBox(False, self.DEF_PAD) 191 hbox.pack_start(vbox2, False, False, self.DEF_PAD) 192 193 # Build the Right frame with the flags in 194 frame = gtk.Frame("Flags") 195 vbox2.pack_start(frame, True, True, self.DEF_PAD) 196 vbox3 = gtk.VBox(True, self.DEF_PAD_SMALL) 197 frame.add(vbox3) 198 199 for i in range(len(flags)): 200 toggle = gtk.CheckButton(flags[i]) 201 toggle.connect("toggled", self.calendar_toggle_flag) 202 vbox3.pack_start(toggle, True, True, 0) 203 self.flag_checkboxes[i] = toggle 204 205 # Build the right font-button 206 button = gtk.Button("Font...") 207 button.connect("clicked", self.calendar_select_font) 208 vbox2.pack_start(button, False, False, 0) 209 210 # Build the Signal-event part. 211 frame = gtk.Frame("Signal events") 212 vbox.pack_start(frame, True, True, self.DEF_PAD) 213 214 vbox2 = gtk.VBox(True, self.DEF_PAD_SMALL) 215 frame.add(vbox2) 216 217 hbox = gtk.HBox (False, 3) 218 vbox2.pack_start(hbox, False, True, 0) 219 label = gtk.Label("Signal:") 220 hbox.pack_start(label, False, True, 0) 221 self.last_sig = gtk.Label("") 222 hbox.pack_start(self.last_sig, False, True, 0) 223 224 hbox = gtk.HBox (False, 3) 225 vbox2.pack_start(hbox, False, True, 0) 226 label = gtk.Label("Previous signal:") 227 hbox.pack_start(label, False, True, 0) 228 self.prev_sig = gtk.Label("") 229 hbox.pack_start(self.prev_sig, False, True, 0) 230 231 hbox = gtk.HBox (False, 3) 232 vbox2.pack_start(hbox, False, True, 0) 233 label = gtk.Label("Second previous signal:") 234 hbox.pack_start(label, False, True, 0) 235 self.prev2_sig = gtk.Label("") 236 hbox.pack_start(self.prev2_sig, False, True, 0) 237 238 bbox = gtk.HButtonBox () 239 vbox.pack_start(bbox, False, False, 0) 240 bbox.set_layout(gtk.BUTTONBOX_END) 241 242 button = gtk.Button("Close") 243 button.connect("clicked", lambda w: gtk.main_quit()) 244 bbox.add(button) 245 button.set_flags(gtk.CAN_DEFAULT) 246 button.grab_default() 247 248 window.show_all() 249 250 def main(): 251 gtk.main() 252 return 0 253 254 if __name__ == "__main__": 255 CalendarExample() 256 main() |
In GTK+ version 2.0, the signal system has been moved from GTK to GLib. We won't go into details about the extensions which the GLib 2.0 signal system has relative to the GTK+ 1.2 signal system. The differences should not be apparent to PyGTK users.
Before we look in detail at helloworld.py, we'll discuss signals and callbacks. GTK+ is an event driven toolkit, which means it will sleep in gtk.main() until an event occurs and control is passed to the appropriate function.
This passing of control is done using the idea of "signals". (Note that these signals are not the same as the Unix system signals, and are not implemented using them, although the terminology is almost identical.) When an event occurs, such as the press of a mouse button, the appropriate signal will be "emitted" by the widget that was pressed. This is how GTK+ does most of its useful work. There are signals that all widgets inherit, such as "destroy", and there are signals that are widget specific, such as "toggled" on a toggle button.
To make a button perform an action, we set up a signal handler to catch these signals and call the appropriate function. This is done by using a GtkWidget (from the GObject class) method such as:
handler_id = object.connect(name, func, func_data) |
where object is the GtkWidget instance which will be emitting the signal, and the first argument name is a string containing the name of the signal you wish to catch. The second argument, func, is the function you wish to be called when it is caught, and the third, func_data, the data you wish to pass to this function. The method returns a handler_id that can be used to disconnect or block the handler.
The function specified in the second argument is called a "callback function", and should generally be of the form:
def callback_func(widget, callback_data): |
where the first argument will be a pointer to the widget that emitted the signal, and the second (callback_data) a pointer to the data given as the last argument to the connect() method as shown above.
If the callback function is an object method then it will have the general form:
def callback_meth(self, widget, callback_data): |
where self is the object instance invoking the method. This is the form used in the helloworld.py example program.
The above form for a signal callback function declaration is only a general guide, as some widget specific signals generate different calling parameters.
Another call used in the helloworld.py example is:
handler_id = object.connect_object(name, func, slot_object) |
connect_object() is the same as connect() except a callback function only uses one argument and a callback method, two arguments:
def callback_func(object) def callback_meth(self, object) |
where object is usually a widget. connect_object() allows the PyGTK widget methods that only take a single argument (self) to be used as signal handlers.
The following widgets do not have an associated window. If you want to capture events, you'll have to use the EventBox. See the section on the EventBox widget.
gtk.Alignment gtk.Arrow gtk.Bin gtk.Box gtk.Button gtk.CheckButton gtk.Fixed gtk.Image gtk.Label gtk.MenuItem gtk.Notebook gtk.Paned gtk.RadioButton gtk.Range gtk.ScrolledWindow gtk.Separator gtk.Table gtk.Toolbar gtk.AspectFrame gtk.Frame gtk.VBox gtk.HBox gtk.VSeparator gtk.HSeparator |
We'll further our exploration of PyGTK by examining each widget in turn, creating a few simple example programs to display them.
Table of Contents
PyGTK 2.0 is a set of Python modules which provide a Python interface to GTK+ 2.X. Throughout the rest of this document PyGTK refers to the 2.X version of PyGTK and GTK and GTK+ refer to the 2.X version of GTK+. The primary web site for PyGTK is www.pygtk.org. The primary author of PyGTK is:
who is assisted by the developers listed in the AUTHORS file in the PyGTK distribution and the PyGTK community.
Python is an extensible, object-oriented interpreted programming language which is provided with a rich set of modules providing access to a large number of operating system services, internet services (such as HTML, XML, FTP, etc.), graphics (including OpenGL, TK, etc.), string handling functions, mail services (IMAP, SMTP, POP3, etc.), multimedia (audio, JPEG) and cryptographic services. In addition there are many other modules available from third parties providing many other services. Python is licensed under terms similar to the LGPL license and is available for Linux, Unix , Windows and Macintosh operating systems. More information on Python is available at www.python.org . The primary Author of Python is:
GTK (GIMP Toolkit) is a library for creating graphical user interfaces. It is licensed using the LGPL license, so you can develop open software, free software, or even commercial non-free software using GTK without having to spend anything for licenses or royalties.
It's called the GIMP toolkit because it was originally written for developing the GNU Image Manipulation Program (GIMP), but GTK has now been used in a large number of software projects, including the GNU Network Object Model Environment (GNOME) project. GTK is built on top of GDK (GIMP Drawing Kit) which is basically a wrapper around the low-level functions for accessing the underlying windowing functions (Xlib in the case of the X windows system). The primary authors of GTK are:
GTK is currently maintained by:
GTK is essentially an object oriented application programmers interface (API). Although written completely in C, it is implemented using the idea of classes and callback functions (pointers to functions).
There is also a third component called GLib which contains a few replacements for some standard calls, as well as some additional functions for handling linked lists, etc. The replacement functions are used to increase GTK's portability, as some of the functions implemented here are not available or are nonstandard on other unixes such as g_strerror(). Some also contain enhancements to the libc versions, such as g_malloc that has enhanced debugging utilities.
In version 2.0, GLib has picked up the type system which forms the foundation for GTK's class hierarchy, the signal system which is used throughout GTK, a thread API which abstracts the different native thread APIs of the various platforms and a facility for loading modules.
As the last component, GTK uses the Pango library for internationalized text output.
This tutorial describes the Python interface to GTK+ and is based on the GTK+ 2.0 Tutorial written by Tony Gale and Ian Main. This tutorial attempts to document as much as possible of PyGTK, but is by no means complete.
This tutorial assumes some understanding of Python, and how to create and run Python programs. If you are not familiar with Python, please read the Python Tutorial first. This tutorial does not assume an understanding of GTK; if you are learning PyGTK to learn GTK, please comment on how you found this tutorial, and what you had trouble with. This tutorial does not describe how to compile or install Python, GTK+ or PyGTK.
This tutorial is based on:
The examples were written and tested on a RedHat 9.0 system.
This document is a "work in progress". Please look for updates on www.pygtk.org.
I would very much like to hear of any problems you have learning PyGTK from this document, and would appreciate input as to how it may be improved. Please see the section on Contributing for further information. If you encounter bugs please file a bug at bugzilla.gnome.org against the pygtk project. The information at www.pygtk.org about Bugzilla may help.
The PyGTK 2.0 Reference Manual is available at http://www.pygtk.org/pygtkreference. It describes in detail the PyGTK classes.
The PyGTK website (www.pygtk.org) contains other resources useful for learning about PyGTK including a link to the extensive FAQ and other articles and tutorials and an active maillist and IRC channel (see www.pygtk.org for details).
Johan Dahlin has written a small Python program (pygtkconsole.py) that runs on Linux and allows interactive exploration of PyGTK. The program provides a Python-like interactive interpreter interface that communicates with a child process that executes that entered commands. The PyGTK modules are loaded by default. A simple example session is:
moe: 96:1095$ pygtkconsole.py
Python 2.2.2, PyGTK 1.99.14 (Gtk+ 2.0.6)
Interactive console to manipulate GTK+ widgets.
>>> w=Window()
>>> b=Button('Hello')
>>> w.add(b)
>>> def hello(b):
... print "Hello, World!"
...
>>> b.connect('clicked', hello)
5
>>> w.show_all()
>>> Hello, World!
Hello, World!
Hello, World!
>>> b.set_label("Hi There")
>>>
|
This creates a window containing a button which prints a message ('Hello, World!') when clicked. This program makes it easy to try out various GTK widgets and PyGTK interfaces.
I also use a program that was developed by Brian McErlean as ActiveState recipe 65109 with some mods to make it run with PyGTK 2.X. I call it gpython.py. It works similar to the pygtkconsole.py program.
Both of these programs are known not to work on Microsoft Windows because they rely on Unix specific interfaces.
deactivate(menu_shell, data) selection-done(menu_shell, data) move-current(menu_shell, direction, data) activate-current(menu_shell, force_hide, data) cancel(menu_shell, data) |
Table of Contents
You may be wondering how you make GTK do useful work when in main(). Well, you have several options. Using the following gobject module function you can create a timeout function that will be called every "interval" milliseconds.
source_id = gobject.timeout_add(interval, function, ...) |
The interval argument is the number of milliseconds between calls to your function. The function argument is the callback you wish to have called. Any arguments after the second are passed to the function as data. The return value is an integer "source_id" which may be used to stop the timeout by calling:
gobject.source_remove(source_id) |
You may also stop the timeout callback function from being called again by returning zero or FALSE from your callback. If you want your callback to be called again, it should return TRUE.
Your callback should look something like this:
def timeout_callback(...): |
The number of arguments to the callback should match the number of data arguments specified in timeout_add().
A TextBuffer is the core component of the PyGTK text editing system. It contains the text, the TextTags in a TextTagTable and the TextMarks which together describe how the text is to be displayed and allow a user to interactively modify the text and text display. As noted in the previous section a TextBuffer is associated with one or more TextViews which display the TextBuffer contents.
A TextBuffer can be created automatically when a TextView is created or it can be created with the function:
textbuffer = TextBuffer(table=None) |
where table is a TextTagTable. If table is not specified (or is None) a TextTagTable will be created for the TextBuffer.
There are a large number of methods that can be used to:
You can retrieve the number of lines in a textbuffer by using the method:
line_count = textbuffer.get_line_count() |
Likewise you can get the number of characters in the textbuffer using:
char_count = textbuffer.get_char_count() |
When the textbuffer contents are changed the modified flag in the textbuffer is set. The status of the modified flag can be retrieved using the method:
modified = textbuffer.get_modified() |
If the program saves the contents of the textbuffer the following method can be used to reset the modified flag:
textbuffer.set_modified(setting) |
A TextIter is used to specify a location within a TextBuffer between two characters. TextBuffer methods that manipulate text use TextIters to specify where the method is to be applied. TextIters have a large number of methods that will be described in the >TextIters section.
The basic TextBuffer methods used to create TextIters are:
iter = textbuffer.get_iter_at_offset(char_offset) iter = textbuffer_get_iter_at_line(line_number) iter = textbuffer.get_iter_at_line_offset(line_number, line_offset) iter = textbuffer.get_iter_at_mark(mark) |
get_iter_at_offset() creates an iter that is just after char_offset chars from the start of the textbuffer.
get_iter_at_line() creates an iter that is just before the first character in line_number.
get_iter_at_line_offset() creates an iter that is just after the line_offset character in line_number.
get_iter_at_mark() creates an iter that is at the same position as the given mark.
The following methods create one or more TextIters at specific buffer locations:
startiter = textbuffer.get_start_iter() enditer = textbuffer_get_end_iter() startiter, enditer = textbuffer.get_bounds() start, end = textbuffer.get_selection_bounds() |
get_start_iter() creates an iter that is just before the first character in the textbuffer.
get_end_iter() creates an iter that is just after the last character in the textbuffer.
get_bounds() creates a tuple of two iters that are just before the first character and just after the last character in the textbuffer respectively.
get_selection_bounds() creates a tuple of two iters that have the same location as the insert and selection_bound marks in the textbuffer.
The text in a TextBuffer can be set using the method:
textbuffer.set_text(text) |
This method replaces the current contents of textbuffer with text.
The most general method to insert characters in a textbuffer is:
textbuffer.insert(iter, text) |
which inserts text at the textbuffer location specified by iter.
If you want to simulate the insertion of text by an interactive user use the method:
result = textbuffer.insert_interactive(iter, text, default_editable) |
which inserts text in the textbuffer at the location specified by iter but only if the location is editable (i.e. does not have a tag that specifies the text is non-editable) and the default_editable value is TRUE. The result indicates whether the text was inserted.
default_editable indicates the editability of text that doesn't have a tag affecting editability; default_editable is usually determined by a call to the TextView get_editable() method.
Other methods that insert text are:
textbuffer.insert_at_cursor(text) result = textbuffer.insert_at_cursor_interactive(text, default_editable) textbuffer.insert_range(iter, start, end) result = textbuffer.insert_range_interactive(iter, start, end, default_editable) |
insert_at_cursor() is a convenience method that inserts text at the current cursor (insert) location.
insert_range() copies text, pixbufs and tags between start and end from a TextBuffer (if different from textbuffer the tag table must be the same) and inserts the copy into textbuffer at iter's location.
The interactive versions of these methods operate the same way except they will only insert if the location is editable.
Finally, text can be inserted and have tags applied at the same time using the methods:
textbuffer.insert_with_tags(iter, text, tag1, tag2, ...) textbuffer.insert_with_tags_by_name(iter, text, tagname1, tagname2, ...) |
insert_with_tags() inserts the text in the textbuffer at the location specified by iter and applies the given tags.
insert_with_tags_by_name() does that same thing but allows you to specify the tags using the tag name.
The text in a textbuffer can be deleted by using the methods:
textbuffer.delete(start, end) result = textbuffer.delete_interactive(start, end, default_editable) |
delete() removes the text between the start and end TextIter locations in textbuffer.
delete_interactive() removes all the editable (as determined by the applicable text tags and the default_editable argument) text between start and end.
You can retrieve a copy of the text from a textbuffer by using the methods:
text = textbuffer.get_text(start, end, include_hidden_chars=TRUE) text = textbuffer.get_slice(start, end, include_hidden_chars=TRUE) |
get_text() returns a copy of the text in textbuffer between start and end; undisplayed text is excluded if include_hidden_chars is FALSE. Characters which represent embedded images or widgets are excluded.
get_slice() is the same as get_text() except that the returned text includes a 0xFFFC character for each embedded image or widget.
TextMarks are similar to TextIters in that they specify a location in a TextBuffer between two characters. However, TextMarks maintain their location information across buffer modifications. The TextMark methods will be described in the TextMarks section.
A textbuffer contains two built-in marks: the insert (cursor) mark and the selection_bound mark. The insert mark is the default location for the insertion of text and the selection_bound mark combines with the insert mark to define a selection range.
The built-in marks can be retrieved by using the methods:
insertmark = textbuffer.get_insert() selection_boundmark = textbuffer.get_selection_bound() |
The insert and selection_bound marks can be placed simultaneously at a location by using the method:
textbuffer.place_cursor(where) |
where is a textiter specifying the location. The place_cursor() method is needed to avoid temporarily creating a selection if the marks were moved individually.
TextMarks are created by using the method:
mark = textbuffer.create_mark(mark_name, where, left_gravity=FALSE) |
where mark_name is the name assigned to the created mark (can be None to create an anonymous mark), where is the textiter specifying the location of the mark in textbuffer and left_gravity indicates where the mark will be located after text is inserted at the mark (left if TRUE or right if FALSE).
A mark can be moved in the textbuffer by using the methods:
textbuffer.move_mark(mark, where) textbuffer.move_mark_by_name(name, where) |
mark specifies the mark to be moved. name specifies the name of the mark to be moved. where is a textiter specifying the new location.
A mark can be deleted from a textbuffer by using the methods:
textbuffer.delete_mark(mark) textbuffer.delete_mark_by_name(name) |
A mark can be retrieved by name using the method:
mark = textbuffer.get_mark(name) |
TextTags contain one or more attributes (e.g. foreground and background colors, font, editability) that can be applied to one or more ranges of text in a TextBuffer. The attributes that can be specified by TextTag properties will be described in Section 13.6.1, “Text Tags”.
A TextTag can be created with attributes and installed in the TextTagTable of a TextBuffer by using the convenience method:
tag = textbuffer.create_tag(name=None, attr1=val1, attr2=val2, ...) |
where name is a string specifying the name of the tag or None if the tag is an anonymous tag and the keyword-value pairs specify the attributes that the tag will have. See the TextTag> section for information on what attributes can be set by the TextTag properties.
A tag can be applied to a range of text in a textbuffer by using the methods:
textbuffer.apply_tag(tag, start, end) textbuffer.apply_tag_by_name(name, start, end) |
tag is the tag to be applied to the text. name is the name of the tag to be applied. start and end are textiters that specify the range of text that the tag is to be applied to.
A tag can be removed from a range of text by using the methods:
textbuffer.remove_tag(tag, start, end) textbuffer.remove_tag_by_name(name, start, end) |
All tags for a range of text can be removed by using the method:
textbuffer.remove_all_tags(start, end) |
In addition to text a TextBuffer can contain pixbuf images and an anchor location for widgets. A widget can be added to a TextView at an anchor location. A different widget can be added in each TextView which displays a buffer with an anchor.
A pixbuf can be inserted by using the method:
textbuffer.insert_pixbuf(iter, pixbuf) |
where iter specifies the location in the textbuffer to insert the pixbuf. The image will be counted as one character and will be represented in a get_slice() return (but left out of a get_text() return) as the Unicode character "0xFFFC".
A GTK+ widget can be inserted in a TextView at a buffer location specified with a TextChildAnchor. The TextChildAnchor will be counted as one character and represented as "0xFFFC" similar to a pixbuf.
The TextChildAnchor can be created and inserted in the buffer by using the convenience method:
anchor = text_buffer.create_child_anchor(iter) |
where iter is the location for the child_anchor.
A TextChildAnchor can also be created and inserted in two operations as:
anchor = gtk.TextChildAnchor() text_buffer.insert_child_anchor(iter, anchor) |
Then the widget can be added to the TextView at an anchor location using the method:
text_view.add_child_at_anchor(child, anchor) |
The list of widgets at a particular buffer anchor can be retrieved using the method:
widget_list = anchor.get_widgets() |
A widget can also be added to a TextView using the method:
text_view.add_child_in_window(child, which_window, xpos, ypos) |
where the child widget is placed in which_window at the location specified by xpos and ypos. which_window indicates in which of the windows that make up the TextView the widget is to be placed:
gtk.TEXT_WINDOW_TOP gtk.TEXT_WINDOW_BOTTOM gtk.TEXT_WINDOW_LEFT gtk.TEXT_WINDOW_RIGHT gtk.TEXT_WINDOW_TEXT gtk.TEXT_WINDOW_WIDGET |
The Layout container is similar to the Fixed container except that it implements an infinite (where infinity is less than 2^32) scrolling area. The X window system has a limitation where windows can be at most 32767 pixels wide or tall. The Layout container gets around this limitation by doing some exotic stuff using window and bit gravities, so that you can have smooth scrolling even when you have many child widgets in your scrolling area.
A Layout container is created using:
layout = gtk.Layout(hadjustment=None, vadjustment=None) |
As you can see, you can optionally specify the Adjustment objects (see Chapter 7, Adjustments) that the Layout widget will use for its scrolling. If you don't specify the Adjustment objects, new ones will be created.
You can add and move widgets in the Layout container using the following two methods:
layout.put(child_widget, x, y) layout.move(child_widget, x, y) |
The size of the Layout container can be set and retrieved using the next methods:
layout.set_size(width, height) size = layout.get_size() |
The final four methods for use with Layout widgets are for manipulating the horizontal and vertical adjustment widgets:
hadj = layout.get_hadjustment() vadj = layout.get_vadjustment() layout.set_hadjustment(adjustment) layout.set_vadjustment(adjustment) |
The layout.py example program creates three buttons and puts them in a layout widget. when a button is clicked, it is moved to a random location in the layout. Figure 10.3, “Layout Example” illustrates the starting display of the program:
The layout.py source code is:
1 #!/usr/bin/env python 2 3 # example layout.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 import random 9 10 class LayoutExample: 11 def WindowDeleteEvent(self, widget, event): 12 # return false so that window will be destroyed 13 return False 14 15 def WindowDestroy(self, widget, *data): 16 # exit main loop 17 gtk.main_quit() 18 19 def ButtonClicked(self, button): 20 # move the button 21 self.layout.move(button, random.randint(0,500), 22 random.randint(0,500)) 23 24 def __init__(self): 25 # create the top level window 26 window = gtk.Window(gtk.WINDOW_TOPLEVEL) 27 window.set_title("Layout Example") 28 window.set_default_size(300, 300) 29 window.connect("delete-event", self.WindowDeleteEvent) 30 window.connect("destroy", self.WindowDestroy) 31 # create the table and pack into the window 32 table = gtk.Table(2, 2, False) 33 window.add(table) 34 # create the layout widget and pack into the table 35 self.layout = gtk.Layout(None, None) 36 self.layout.set_size(600, 600) 37 table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND, 38 gtk.FILL|gtk.EXPAND, 0, 0) 39 # create the scrollbars and pack into the table 40 vScrollbar = gtk.VScrollbar(None) 41 table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK, 42 gtk.FILL|gtk.SHRINK, 0, 0) 43 hScrollbar = gtk.HScrollbar(None) 44 table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK, 45 gtk.FILL|gtk.SHRINK, 0, 0) 46 # tell the scrollbars to use the layout widget's adjustments 47 vAdjust = self.layout.get_vadjustment() 48 vScrollbar.set_adjustment(vAdjust) 49 hAdjust = self.layout.get_hadjustment() 50 hScrollbar.set_adjustment(hAdjust) 51 # create 3 buttons and put them into the layout widget 52 button = gtk.Button("Press Me") 53 button.connect("clicked", self.ButtonClicked) 54 self.layout.put(button, 0, 0) 55 button = gtk.Button("Press Me") 56 button.connect("clicked", self.ButtonClicked) 57 self.layout.put(button, 100, 0) 58 button = gtk.Button("Press Me") 59 button.connect("clicked", self.ButtonClicked) 60 self.layout.put(button, 200, 0) 61 # show all the widgets 62 window.show_all() 63 64 def main(): 65 # enter the main loop 66 gtk.main() 67 return 0 68 69 if __name__ == "__main__": 70 LayoutExample() 71 main() |
month-changed(calendar, data) day-selected(calendar, data) day-selected-double-click(calendar, data) prev-month(calendar, data) next-month(calendar, data) prev-year(calendar, data) next-year(calendar, data) |
We've almost seen all there is to see of the button widget. It's pretty simple. You can use the gtk.Button() function to create a button with a label by passing a string parameter, or to create a blank button by not specifying a label string. It's then up to you to pack a label or pixmap into this new button. To do this, create a new box, and then pack your objects into this box using the usual pack_start() method, and then use the add() method to pack the box into the button.
The function to create a button is:
button = gtk.Button(label=None, stock=None) |
if label text is specified it is used as the text on the button. If stock is specified it is used to select a stock icon and text label for the button. The stock items are:
STOCK_DIALOG_INFO STOCK_DIALOG_WARNING STOCK_DIALOG_ERROR STOCK_DIALOG_QUESTION STOCK_DND STOCK_DND_MULTIPLE STOCK_ADD STOCK_APPLY STOCK_BOLD STOCK_CANCEL STOCK_CDROM STOCK_CLEAR STOCK_CLOSE STOCK_CONVERT STOCK_COPY STOCK_CUT STOCK_DELETE STOCK_EXECUTE STOCK_FIND STOCK_FIND_AND_REPLACE STOCK_FLOPPY STOCK_GOTO_BOTTOM STOCK_GOTO_FIRST STOCK_GOTO_LAST STOCK_GOTO_TOP STOCK_GO_BACK STOCK_GO_DOWN STOCK_GO_FORWARD STOCK_GO_UP STOCK_HELP STOCK_HOME STOCK_INDEX STOCK_ITALIC STOCK_JUMP_TO STOCK_JUSTIFY_CENTER STOCK_JUSTIFY_FILL STOCK_JUSTIFY_LEFT STOCK_JUSTIFY_RIGHT STOCK_MISSING_IMAGE STOCK_NEW STOCK_NO STOCK_OK STOCK_OPEN STOCK_PASTE STOCK_PREFERENCES STOCK_PRINT STOCK_PRINT_PREVIEW STOCK_PROPERTIES STOCK_QUIT STOCK_REDO STOCK_REFRESH STOCK_REMOVE STOCK_REVERT_TO_SAVED STOCK_SAVE STOCK_SAVE_AS STOCK_SELECT_COLOR STOCK_SELECT_FONT STOCK_SORT_ASCENDING STOCK_SORT_DESCENDING STOCK_SPELL_CHECK STOCK_STOP STOCK_STRIKETHROUGH STOCK_UNDELETE STOCK_UNDERLINE STOCK_UNDO STOCK_YES STOCK_ZOOM_100 STOCK_ZOOM_FIT STOCK_ZOOM_IN STOCK_ZOOM_OUT |
The buttons.py program provides an example of using gtk.Button() to create a button with an image and a label in it. I've broken up the code to create a box from the rest so you can use it in your programs. There are further examples of using images later in the tutorial. Figure 6.1, “Button with Pixmap and Label” shows the window containing a button with both a pixmap and a label:
The source code for the buttons.py program is:
1 #!/usr/bin/env python 2 3 # example-start buttons buttons.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 # Create a new hbox with an image and a label packed into it 10 # and return the box. 11 12 def xpm_label_box(parent, xpm_filename, label_text): 13 # Create box for xpm and label 14 box1 = gtk.HBox(False, 0) 15 box1.set_border_width(2) 16 17 # Now on to the image stuff 18 image = gtk.Image() 19 image.set_from_file(xpm_filename) 20 21 # Create a label for the button 22 label = gtk.Label(label_text) 23 24 # Pack the pixmap and label into the box 25 box1.pack_start(image, False, False, 3) 26 box1.pack_start(label, False, False, 3) 27 28 image.show() 29 label.show() 30 return box1 31 32 class Buttons: 33 # Our usual callback method 34 def callback(self, widget, data=None): 35 print "Hello again - %s was pressed" % data 36 37 def __init__(self): 38 # Create a new window 39 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 40 41 self.window.set_title("Image'd Buttons!") 42 43 # It's a good idea to do this for all windows. 44 self.window.connect("destroy", lambda wid: gtk.main_quit()) 45 self.window.connect("delete_event", lambda a1,a2:gtk.main_quit()) 46 47 # Sets the border width of the window. 48 self.window.set_border_width(10) 49 50 # Create a new button 51 button = gtk.Button() 52 53 # Connect the "clicked" signal of the button to our callback 54 button.connect("clicked", self.callback, "cool button") 55 56 # This calls our box creating function 57 box1 = xpm_label_box(self.window, "info.xpm", "cool button") 58 59 # Pack and show all our widgets 60 button.add(box1) 61 62 box1.show() 63 button.show() 64 65 self.window.add(button) 66 self.window.show() 67 68 def main(): 69 gtk.main() 70 return 0 71 72 if __name__ == "__main__": 73 Buttons() 74 main() |
Lines 12-34 define the xpm_label_box() helper function which creates a horizontal box with a border width of 2 (lines 14-15), populates it with an image (lines 22-23) and a label (line 26).
Lines 36-70 define the Buttons class. Lines 41-70 define the instance initialization method which creates a window (line 43), sets the title (line 45), connects the "delete_event" and "destroy" signals (lines 48-49). Line 55 creates the button without a label. Its "clicked" signal gets connected to the callback() method in line 58. The xpm_label_box() function is called in line 61 to create the image and label to put in the button in line 64.
The xpm_label_box() function could be used to pack xpm's and labels into any widget that can be a container.
The Button widget has the following signals:
pressed - emitted when pointer button is pressed within Button widget released - emitted when pointer button is released within Button widget clicked - emitted when pointer button is pressed and then released within Button widget enter - emitted when pointer enters Button widget leave - emitted when pointer leaves Button widget |
The file selection widget is a quick and simple way to display a File dialog box. It comes complete with
, , and buttons, a great way to cut down on programming time.To create a new file selection box use:
filesel = gtk.FileSelection(title=None) |
To set the filename, for example to bring up a specific directory, or give a default filename, use this method:
filesel.set_filename(filename) |
To grab the filename text that the user has entered or clicked on, use this method:
filename = filesel.get_filename() |
There are also references to the widgets contained within the file selection widget. These are the filesel attributes:
filesel.dir_list filesel.file_list filesel.selection_entry filesel.selection_text filesel.main_vbox filesel.ok_button filesel.cancel_button filesel.help_button filesel.history_pulldown filesel.history_menu filesel.fileop_dialog filesel.fileop_entry filesel.fileop_file filesel.fileop_c_dir filesel.fileop_del_file filesel.fileop_ren_file filesel.button_area filesel.action_area |
Most likely you will want to use the ok_button, cancel_button, and help_button attributes to connect their widget signals to callbacks.
The filesel.py example program illustrates the use of the FileSelection widget. As you will see, there is nothing much to creating a file selection widget. While in this example the button appears on the screen, it does nothing as there is not a signal attached to it. Figure 9.14, “File Selection Example” shows the resulting display:
The source code for filesel.py is:
1 #!/usr/bin/env python 2 3 # example filesel.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class FileSelectionExample: 10 # Get the selected filename and print it to the console 11 def file_ok_sel(self, w): 12 print "%s" % self.filew.get_filename() 13 14 def destroy(self, widget): 15 gtk.main_quit() 16 17 def __init__(self): 18 # Create a new file selection widget 19 self.filew = gtk.FileSelection("File selection") 20 21 self.filew.connect("destroy", self.destroy) 22 # Connect the ok_button to file_ok_sel method 23 self.filew.ok_button.connect("clicked", self.file_ok_sel) 24 25 # Connect the cancel_button to destroy the widget 26 self.filew.cancel_button.connect("clicked", 27 lambda w: self.filew.destroy()) 28 29 # Lets set the filename, as if this were a save dialog, 30 # and we are giving a default filename 31 self.filew.set_filename("penguin.png") 32 33 self.filew.show() 34 35 def main(): 36 gtk.main() 37 return 0 38 39 if __name__ == "__main__": 40 FileSelectionExample() 41 main() |
Table of Contents
Labels are used a lot in GTK, and are relatively simple. Labels emit no signals as they do not have an associated X window. If you need to catch signals, or do clipping, place it inside a EventBox (see Section 10.1, “The EventBox”) widget or a Button (see Section 6.1, “Normal Buttons”) widget.
To create a new label, use:
label = gtk.Label(str) |
The sole argument is the string you wish the label to display. To change the label's text after creation, use the method:
label.set_text(str) |
label is the label you created previously, and str is the new string. The space needed for the new string will be automatically adjusted if needed. You can produce multi-line labels by putting line breaks in the label string.
To retrieve the current string, use:
str = label.get_text() |
label is the label you've created, and str is the return string. The label text can be justified using:
label.set_justify(jtype) |
Values for jtype are:
JUSTIFY_LEFT # the default JUSTIFY_RIGHT JUSTIFY_CENTER JUSTIFY_FILL # does not work |
The label widget is also capable of line wrapping the text automatically. This can be activated using:
label.set_line_wrap(wrap) |
The wrap argument takes a TRUE or FALSE value.
If you want your label underlined, then you can set a pattern on the label:
label.set_pattern(pattern) |
The pattern argument indicates how the underlining should look. It consists of a string of underscore and space characters. An underscore indicates that the corresponding character in the label should be underlined. For example, the string "__ __" would underline the first two characters and fourth and fifth characters. If you simply want to have an underlined accelerator ("mnemonic") in your label, you should use set_text_with_mnemonic(str), not set_pattern().
The label.py program is a short example to illustrate these methods. This example makes use of the Frame (see Section 10.5, “Frames”) widget to better demonstrate the label styles. You can ignore this for now as the Frame widget is explained later on.
In GTK+ 2.0, label text can contain markup for font and other text attribute changes, and labels may be selectable (for copy-and-paste). These advanced features won't be explained here.
Figure 9.1, “Label Examples” illustrates the result of running the example program:
The label.py source code is:
1 #!/usr/bin/env python 2 3 # example label.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class Labels: 10 def __init__(self): 11 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 12 self.window.connect("destroy", lambda w: gtk.main_quit()) 13 14 self.window.set_title("Label") 15 vbox = gtk.VBox(False, 5) 16 hbox = gtk.HBox(False, 5) 17 self.window.add(hbox) 18 hbox.pack_start(vbox, False, False, 0) 19 self.window.set_border_width(5) 20 21 frame = gtk.Frame("Normal Label") 22 label = gtk.Label("This is a Normal label") 23 frame.add(label) 24 vbox.pack_start(frame, False, False, 0) 25 26 frame = gtk.Frame("Multi-line Label") 27 label = gtk.Label("This is a Multi-line label.\nSecond line\n" 28 "Third line") 29 frame.add(label) 30 vbox.pack_start(frame, False, False, 0) 31 32 frame = gtk.Frame("Left Justified Label") 33 label = gtk.Label("This is a Left-Justified\n" 34 "Multi-line label.\nThird line") 35 label.set_justify(gtk.JUSTIFY_LEFT) 36 frame.add(label) 37 vbox.pack_start(frame, False, False, 0) 38 39 frame = gtk.Frame("Right Justified Label") 40 label = gtk.Label("This is a Right-Justified\nMulti-line label.\n" 41 "Fourth line, (j/k)") 42 label.set_justify(gtk.JUSTIFY_RIGHT) 43 frame.add(label) 44 vbox.pack_start(frame, False, False, 0) 45 46 vbox = gtk.VBox(False, 5) 47 hbox.pack_start(vbox, False, False, 0) 48 frame = gtk.Frame("Line wrapped label") 49 label = gtk.Label("This is an example of a line-wrapped label. It " 50 "should not be taking up the entire " 51 "width allocated to it, but automatically " 52 "wraps the words to fit. " 53 "The time has come, for all good men, to come to " 54 "the aid of their party. " 55 "The sixth sheik's six sheep's sick.\n" 56 " It supports multiple paragraphs correctly, " 57 "and correctly adds " 58 "many extra spaces. ") 59 label.set_line_wrap(True) 60 frame.add(label) 61 vbox.pack_start(frame, False, False, 0) 62 63 frame = gtk.Frame("Filled, wrapped label") 64 label = gtk.Label("This is an example of a line-wrapped, filled label. " 65 "It should be taking " 66 "up the entire width allocated to it. " 67 "Here is a sentence to prove " 68 "my point. Here is another sentence. " 69 "Here comes the sun, do de do de do.\n" 70 " This is a new paragraph.\n" 71 " This is another newer, longer, better " 72 "paragraph. It is coming to an end, " 73 "unfortunately.") 74 label.set_justify(gtk.JUSTIFY_FILL) 75 label.set_line_wrap(True) 76 frame.add(label) 77 vbox.pack_start(frame, False, False, 0) 78 79 frame = gtk.Frame("Underlined label") 80 label = gtk.Label("This label is underlined!\n" 81 "This one is underlined in quite a funky fashion") 82 label.set_justify(gtk.JUSTIFY_LEFT) 83 label.set_pattern( 84 "_________________________ _ _________ _ ______ __ _______ ___") 85 frame.add(label) 86 vbox.pack_start(frame, False, False, 0) 87 self.window.show_all () 88 89 def main(): 90 gtk.main() 91 return 0 92 93 if __name__ == "__main__": 94 Labels() 95 main() |
Note that the "Filled, wrapped label" is not fill justified.
Now that we know the theory behind this, let's clarify by walking through the example helloworld.py program.
Lines 9-76 define the HelloWorld class that contains all the callbacks as object methods and the object instance initialization method. Let's examine the callback methods.
Lines 13-14 define the hello() callback method that will be called when the button is "clicked". When called the method, prints "Hello World" to the console. We ignore the object instance, the widget and the data parameters in this example, but most callbacks use them. The data is defined with a default value of None because PyGTK will not pass a data value if it is not included in the connect() call; this would trigger an error since the callback is expecting three parameters and may receive only two. Defining a default value of None allows the callback to be called with two or three parameters without error. In this case the data parameter could have been left out since the hello() method will always be called with just two parameters (never called with user data). The next example will use the data argument to tell us which button was pressed.
def hello(self, widget, data=None): print "Hello World" |
The next callback (lines 16-26) is a bit special. The "delete_event" occurs when the window manager sends this event to the application. We have a choice here as to what to do about these events. We can ignore them, make some sort of response, or simply quit the application.
The value you return in this callback lets GTK+ know what action to take. By returning TRUE, we let it know that we don't want to have the "destroy" signal emitted, keeping our application running. By returning FALSE, we ask that "destroy" be emitted, which in turn will call our "destroy" signal handler. Note the comments have been removed for clarity.
def delete_event(widget, event, data=None): print "delete event occurred" return False |
The destroy() callback method (lines 28-30) causes the program to quit by calling gtk.main_quit() . This function tells GTK+ that it is to exit from gtk.main() when control is returned to it.
def destroy(widget, data=None): print "destroy signal occurred" gtk.main_quit() |
Lines 32-71 define the HelloWorld object instance initialization method __init__() that creates the window and widgets used by the program.
Line 34 creates a new window, but it is not displayed until we direct GTK+ to show the window near the end of our program. The window reference is saved in an object instance attribute (self.window) for later access.
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) |
Lines 41 and 46 illustrate two examples of connecting a signal handler to an object, in this case, the window. Here, the "delete_event" and "destroy" signals are caught. The first is emitted when we use the window manager to kill the window, or when we use the GtkWidget destroy() method call. The second is emitted when, in the "delete_event" handler, we return FALSE.
self.window.connect("delete_event", self.delete_event) self.window.connect("destroy", self.destroy) |
Line 49 sets an attribute of a container object (in this case the window) to have a blank area along the inside of it 10 pixels wide where no widgets will be placed. There are other similar methods that we will look at in Chapter 18, Setting Widget Attributes
self.window.set_border_width(10) |
Line 52 creates a new button and saves a reference to it in self.button. The button will have the label "Hello World" when displayed.
self.button = gtk.Button("Hello World") |
In line 57 we attach a signal handler to the button so when it emits the "clicked" signal, our hello() callback method is called. We are not passing any data to hello() so we just pass None as the data. Obviously, the "clicked" signal is emitted when we click the button with our mouse pointer. The user data parameter value None is not required and could be removed. The callback would then be called with one less parameter.
self.button.connect("clicked", self.hello, None) |
We are also going to use this button to exit our program. Line 62 illustrates how the "destroy" signal may come from either the window manager, or from our program. When the button is "clicked", same as above, it calls the hello() callback first, and then the following one in the order they are set up. You may have as many callbacks as you need, and all will be executed in the order you connected them.
Since we want to use the GtkWidget destroy() method that accepts one argument (the widget to be destroyed - in this case the window), we use the connect_object() method and pass it the reference to the window. The connect_object() method arranges to pass the window as the first callback argument instead of the button.
When the gtk.Widget destroy() method is called it will cause the "destroy" signal to be emitted from the window which will in turn cause the HelloWorld destroy() method to be called to end the program.
self.button.connect_object("clicked", gtk.Widget.destroy, self.window) |
Line 65 is a packing call, which will be explained in depth later on in Chapter 4, Packing Widgets . But it is fairly easy to understand. It simply tells GTK+ that the button is to be placed in the window where it will be displayed. Note that a GTK+ container can only contain one widget. There are other widgets, described later, that are designed to layout multiple widgets in various ways.
self.window.add(self.button) |
Now we have everything set up the way we want it to be. With all the signal handlers in place, and the button placed in the window where it should be, we ask GTK+ (lines 66 and 69) to "show" the widgets on the screen. The window widget is shown last so the whole window will pop up at once rather than seeing the window pop up, and then the button forming inside of it. Although with such a simple example, you'd never notice.
self.button.show() self.window.show() |
Lines 73-75 define the main() method which calls the gtk.main() function
def main(self): gtk.main() |
Lines 80-82 allow the program to run automatically if called directly or as an argument of the python interpreter. Line 81 creates an instance of the HelloWorld class and saves a reference to it in the hello variable. Line 82 calls the HelloWorld class main() method to start the GTK+ event processing loop.
if __name__ == "__main__": hello = HelloWorld() hello.main() |
Now, when we click the mouse button on a GTK+ button, the widget emits a "clicked" signal. In order for us to use this information, our program sets up a signal handler to catch that signal, which dispatches the function of our choice. In our example, when the button we created is "clicked", the hello() method is called with the None argument, and then the next handler for this signal is called. The next handler calls the widget destroy() function with the window as its argument thereby causing the window to emit the "destroy" signal, which is caught, and calls our HelloWorld destroy() method
Another course of events is to use the window manager to kill the window, which will cause the "delete_event" to be emitted. This will call our "delete_event" handler. If we return TRUE here, the window will be left as is and nothing will happen. Returning FALSE will cause GTK+ to emit the "destroy" signal that causes the HelloWorld "destroy" callback to be called, exiting GTK.
The GTK+ signals we have already discussed are for high-level actions, such as a menu item being selected. However, sometimes it is useful to learn about lower-level occurrences, such as the mouse being moved, or a key being pressed. There are also GTK+ signals corresponding to these low-level events. The handlers for these signals have an extra parameter which is a gtk.gdk.Event object containing information about the event. For instance, motion event handlers are passed a gtk.gdk.Event object containing EventMotion information which has (in part) attributes like:
type window time x y ... state ... |
window is the window in which the event occurred.
x and y give the coordinates of the event.
type will be set to the event type, in this case MOTION_NOTIFY. The types (in module gtk.gdk) are:
NOTHING a special code to indicate a null event. DELETE the window manager has requested that the toplevel window be hidden or destroyed, usually when the user clicks on a special icon in the title bar. DESTROY the window has been destroyed. EXPOSE all or part of the window has become visible and needs to be redrawn. MOTION_NOTIFY the pointer (usually a mouse) has moved. BUTTON_PRESS a mouse button has been pressed. _2BUTTON_PRESS a mouse button has been double-clicked (clicked twice within a short period of time). Note that each click also generates a BUTTON_PRESS event. _3BUTTON_PRESS a mouse button has been clicked 3 times in a short period of time. Note that each click also generates a BUTTON_PRESS event. BUTTON_RELEASE a mouse button has been released. KEY_PRESS a key has been pressed. KEY_RELEASE a key has been released. ENTER_NOTIFY the pointer has entered the window. LEAVE_NOTIFY the pointer has left the window. FOCUS_CHANGE the keyboard focus has entered or left the window. CONFIGURE the size, position or stacking order of the window has changed. Note that GTK+ discards these events for GDK_WINDOW_CHILD windows. MAP the window has been mapped. UNMAP the window has been unmapped. PROPERTY_NOTIFY a property on the window has been changed or deleted. SELECTION_CLEAR the application has lost ownership of a selection. SELECTION_REQUEST another application has requested a selection. SELECTION_NOTIFY a selection has been received. PROXIMITY_IN an input device has moved into contact with a sensing surface (e.g. a touchscreen or graphics tablet). PROXIMITY_OUT an input device has moved out of contact with a sensing surface. DRAG_ENTER the mouse has entered the window while a drag is in progress. DRAG_LEAVE the mouse has left the window while a drag is in progress. DRAG_MOTION the mouse has moved in the window while a drag is in progress. DRAG_STATUS the status of the drag operation initiated by the window has changed. DROP_START a drop operation onto the window has started. DROP_FINISHED the drop operation initiated by the window has completed. CLIENT_EVENT a message has been received from another application. VISIBILITY_NOTIFY the window visibility status has changed. NO_EXPOSE indicates that the source region was completely available when parts of a drawable were copied. This is not very useful. SCROLL ? WINDOW_STATE ? SETTING ? |
state specifies the modifier state when the event occurred (that is, it specifies which modifier keys and mouse buttons were pressed). It is the bitwise OR of some of the following (in module gtk.gdk):
SHIFT_MASK LOCK_MASK CONTROL_MASK MOD1_MASK MOD2_MASK MOD3_MASK MOD4_MASK MOD5_MASK BUTTON1_MASK BUTTON2_MASK BUTTON3_MASK BUTTON4_MASK BUTTON5_MASK |
As for other signals, to determine what happens when an event occurs we call the connect() method. But we also need to let GTK+ know which events we want to be notified about. To do this, we call the method:
widget.set_events(events) |
The events argument specifies the events we are interested in. It is the bitwise OR of constants that specify different types of events. For future reference, the event types (in module gtk.gdk) are:
EXPOSURE_MASK POINTER_MOTION_MASK POINTER_MOTION_HINT_MASK BUTTON_MOTION_MASK BUTTON1_MOTION_MASK BUTTON2_MOTION_MASK BUTTON3_MOTION_MASK BUTTON_PRESS_MASK BUTTON_RELEASE_MASK KEY_PRESS_MASK KEY_RELEASE_MASK ENTER_NOTIFY_MASK LEAVE_NOTIFY_MASK FOCUS_CHANGE_MASK STRUCTURE_MASK PROPERTY_CHANGE_MASK VISIBILITY_NOTIFY_MASK PROXIMITY_IN_MASK PROXIMITY_OUT_MASK SUBSTRUCTURE_MASK |
There are a few subtle points that have to be observed when calling the set_events() method. First, it must be called before the X window for a PyGTK widget is created. In practical terms, this means you should call it immediately after creating the widget. Second, the widget must be one which will be realized with an associated X window. For efficiency, many widget types do not have their own window, but draw in their parent's window. These widgets include:
gtk.Alignment gtk.Arrow gtk.Bin gtk.Box gtk.Image gtk.Item gtk.Label gtk.Layout gtk.Pixmap gtk.ScrolledWindow gtk.Separator gtk.Table gtk.AspectFrame gtk.Frame gtk.VBox gtk.HBox gtk.VSeparator gtk.HSeparator |
To capture events for these widgets, you need to use an EventBox widget. See Section 10.1, “The EventBox” widget for details.
The event attributes that are set by PyGTK for each type of event are:
every event type window send_event NOTHING DELETE DESTROY # no additional attributes EXPOSE area count MOTION_NOTIFY time x y pressure xtilt ytilt state is_hint source deviceid x_root y_root BUTTON_PRESS _2BUTTON_PRESS _3BUTTON_PRESS BUTTON_RELEASE time x y pressure xtilt ytilt state button source deviceid x_root y_root KEY_PRESS KEY_RELEASE time state keyval string ENTER_NOTIFY LEAVE_NOTIFY subwindow time x y x_root y_root mode detail focus state FOCUS_CHANGE _in CONFIGURE x y width height MAP UNMAP # no additional attributes PROPERTY_NOTIFY atom time state SELECTION_CLEAR SELECTION_REQUEST SELECTION_NOTIFY selection target property requestor time PROXIMITY_IN PROXIMITY_OUT time source deviceid DRAG_ENTER DRAG_LEAVE DRAG_MOTION DRAG_STATUS DROP_START DROP_FINISHED context time x_root y_root CLIENT_EVENT message_type data_format data VISIBILTY_NOTIFY state NO_EXPOSE # no additional attributes |
For our drawing program, we want to know when the mouse button is pressed and when the mouse is moved, so we specify POINTER_MOTION_MASK and BUTTON_PRESS_MASK. We also want to know when we need to redraw our window, so we specify EXPOSURE_MASK. Although we want to be notified via a Configure event when our window size changes, we don't have to specify the corresponding STRUCTURE_MASK flag, because it is automatically specified for all windows.
It turns out, however, that there is a problem with just specifying POINTER_MOTION_MASK. This will cause the server to add a new motion event to the event queue every time the user moves the mouse. Imagine that it takes us 0.1 seconds to handle a motion event, but the X server queues a new motion event every 0.05 seconds. We will soon get way behind the users drawing. If the user draws for 5 seconds, it will take us another 5 seconds to catch up after they release the mouse button! What we would like is to only get one motion event for each event we process. The way to do this is to specify POINTER_MOTION_HINT_MASK.
When we specify POINTER_MOTION_HINT_MASK, the server sends us a motion event the first time the pointer moves after entering our window, or after a button press or release event. Subsequent motion events will be suppressed until we explicitly ask for the position of the pointer using the gtk.gdk.Window method:
x, y, mask = window.get_pointer() |
window is a gtk.gdk.Window object. x and y are the coordinates of the pointer and mask is the modifier mask to detect which keys are pressed. (There is a gtk.Widget method, get_pointer() which provides the same information as the gtk.gdk.Window get_pointer() method but it does not return the mask information)
The scribblesimple.py example program demonstrates the basic use of events and event handlers. Figure 24.2, “Simple Scribble Example” illustrates the program in action:
The event handlers are connected to the drawing_area by the following lines:
92 # Signals used to handle backing pixmap 93 drawing_area.connect("expose_event", expose_event) 94 drawing_area.connect("configure_event", configure_event) 95 96 # Event signals 97 drawing_area.connect("motion_notify_event", motion_notify_event) 98 drawing_area.connect("button_press_event", button_press_event) 99 100 drawing_area.set_events(gtk.gdk.EXPOSURE_MASK 101 | gtk.gdk.LEAVE_NOTIFY_MASK 102 | gtk.gdk.BUTTON_PRESS_MASK 103 | gtk.gdk.POINTER_MOTION_MASK 104 | gtk.gdk.POINTER_MOTION_HINT_MASK) |
The button_press_event() and motion_notify_event() event handlers in scribblesimple.py are:
57 def button_press_event(widget, event): 58 if event.button == 1 and pixmap != None: 59 draw_brush(widget, event.x, event.y) 60 return True 61 62 def motion_notify_event(widget, event): 63 if event.is_hint: 64 x, y, state = event.window.get_pointer() 65 else: 66 x = event.x 67 y = event.y 68 state = event.state 69 70 if state & gtk.gdk.BUTTON1_MASK and pixmap != None: 71 draw_brush(widget, x, y) 72 73 return True |
The expose_event() and configure_event() handlers will be described later.
TreeViewColumns and CellRenderers work together to display a column of data in a TreeView. The TreeViewColumn provides the column title and a vertical space for the CellRenderers to render a portion of the data from the TreeView data store. A CellRenderer handles the rendering of each row and column data within the confines of the TreeViewColumn. A TreeViewColumn can contain more than one CellRenderer to provide a row display similar to an HBox. A common use of multiple CellRenderers is to combine a CellRendererPixbuf and a CellRendererText in one column.
An example illustrating the layout of two TreeViewColumns: one with two CellRenderers and one with one CellRenderer is shown in Figure 14.2, “TreeViewColumns with CellRenderers”:
The application of each CellRenderer is indicated with a different background color: yellow for the CellRendererPixbuf, cyan for one CellRendererText, and pink for the other CellRendererText. Note that the CellRendererPixbuf and the first CellRendererText are in the same column headed by the "Pixbuf and Text" header. The background color of the CellRendererText rendering "Print File" is the default color to show the application area in a single row.
Figure 14.2, “TreeViewColumns with CellRenderers” was created by the treeviewcolumn.py program.
The type of CellRenderer needed is determined by the type of tree model data display required; PyGTK has three pre-defined CellRenderers:
CellRendererPixbuf | renders pixbuf images either created by the program or one of the stock items. |
CellRendererText | renders text strings, and numbers that can be converted to a string (including ints, floats, booleans). |
CellRendererToggle | renders a boolean value as a toggle button or a radio button |
The properties of a CellRenderer determine how the data will be rendered:
"mode" | Read-Write | The editable mode of the CellRenderer. One of: gtk.CELL_RENDERER_MODE_INERT, gtk.CELL_RENDERER_MODE_ACTIVATABLE or gtk.CELL_RENDERER_MODE_EDITABLE |
"visible" | Read-Write | If TRUE the cell is displayed |
"xalign" | Read-Write | The fraction of free space to the left of the cell in the range 0.0 to 1.0. |
"yalign" | Read-Write | The fraction of free space above the cell in the range 0.0 to 1.0. |
"xpad" | Read-Write | The amount of padding to the left and right of the cell. |
"ypad" | Read-Write | The amount of padding above and below cell. |
"width" | Read-Write | The fixed width of the cell. |
"height" | Read-Write | The fixed height of the cell. |
"is-expander" | Read-Write | If TRUE the row has children |
"is-expanded" | Read-Write | If TRUE the row has children and it is expanded to show the children. |
"cell-background" | Write | The background color of the cell as a string. |
"cell-background-gdk" | Read-Write | The background color of the cell as a gtk.gdk.Color. |
"cell-background-set" | Read-Write | If TRUE the cell background color is set by this cellrenderer |
The above properties are available for all CellRenderer subclasses. The individual CellRenderer types also have their own properties.
The CellRendererPixbuf has these properties:
"pixbuf" | Read-Write | The pixbuf to render - overridden by "stock-id" |
"pixbuf-expander-open" | Read-Write | Pixbuf for open expander. |
"pixbuf-expander-closed" | Read-Write | Pixbuf for closed expander. |
"stock-id" | Read-Write | The stock ID of the stock icon to render |
"stock-size" | Read-Write | The size of the rendered icon |
"stock-detail" | Read-Write | Render detail to pass to the theme engine |
The CellRendererText has a large number of properties mostly dealing with style specification:
"text" | Read-Write | Text to render |
"markup" | Read-Write | Marked up text to render. |
"attributes" | Read-Write | A list of style attributes to apply to the text of the renderer. |
"background" | Write | Background color as a string |
"foreground" | Write | Foreground color as a string |
"background-gdk" | Read-Write | Background color as a gtk.gdk.Color |
"foreground-gdk" | Read-Write | Foreground color as a gtk.gdk.Color |
"font" | Read-Write | Font description as a string |
"font-desc" | Read-Write | Font description as a pango.FontDescription |
"family" | Read-Write | Name of the font family, e.g. Sans, Helvetica, Times, Monospace |
"style" | Read-Write | Font style |
"variant" | Read-Write | Font variant |
"weight" | Read-Write | Font weight |
"stretch" | Read-Write | Font stretch |
"size" | Read-Write | Font size |
"size-points" | Read-Write | Font size in points |
"scale" | Read-Write | Font scaling factor |
"editable" | Read-Write | If TRUE the text can be modified by the user |
"strikethrough" | Read-Write | If TRUE strike through the text |
"underline" | Read-Write | Style of underline for this text |
"rise" | Read-Write | Offset of text above the baseline (below the baseline if rise is negative) |
"language" | Read-Write | The language this text is in, as an ISO code. Pango can use this as a hint when rendering the text. If you don't understand this parameter, you probably don't need it. GTK+ 2.4 and above. |
"single-paragraph-mode" | Read-Write | If TRUE, keep all text in a single paragraph. GTK+ 2.4 and above. |
"background-set" | Read-Write | If TRUE apply the background color |
"foreground-set" | Read-Write | If TRUE apply the foreground color |
"family-set" | Read-Write | If TRUE apply the font family |
"style-set" | Read-Write | If TRUE apply the font style |
"variant-set" | Read-Write | If TRUE apply the font variant |
"weight-set" | Read-Write | If TRUE apply the font weight |
"stretch-set" | Read-Write | If TRUE apply the font stretch |
"size-set" | Read-Write | If TRUE apply the font size |
"scale-set" | Read-Write | If TRUE scale the font |
"editable-set" | Read-Write | If TRUE apply the text editability |
"strikethrough-set" | Read-Write | If TRUE apply the strikethrough |
"underline-set" | Read-Write | If TRUE apply the text underlining |
"rise-set" | Read-Write | If TRUE apply the rise |
"language-set" | Read-Write | If TRUE apply the language used to render the text. GTK+ 2.4 and above. |
Almost every CellRendererText property has an associated boolean property (with the "-set" suffix) that indicates if the property is to be applied. This allows you to set a property globally and selectively enable and disable its application.
The CellRendererToggle has the following properties:
"activatable" | Read-Write | If TRUE, the toggle button can be activated |
"active" | Read-Write | If TRUE, the button is active. |
"radio" | Read-Write | If TRUE, draw the toggle button as a radio button |
"inconsistent" | Read-Write | If TRUE, the button is in an inconsistent state. GTK+ 2.2 and above. |
The properties can be set for all rows by using the gobject.set_property() method. See the treeviewcolumn.py program for an example using this method.
An attribute associates a tree model column with a CellRenderer property; the CellRenderer sets the property from the row's column value before rendering the cell. This allows you to customize the cell display using tree model data. An attribute can be added to the current set by using:
treeviewcolumn.add_attribute(cell_renderer, attribute, column) |
where the property specified by attribute is set for the cell_renderer from column. For example:
treeviewcolumn.add_attribute(cell, "cell-background", 1) |
sets the CellRenderer background to the color specified by the string in the second column of the data store.
To clear all attributes and set several new attributes at once use:
treeviewcolumn.set_attributes(cell_renderer, ...) |
where the attributes of cell_renderer are set by key-value pairs: property=column. For example, for a CellRendererText:
treeviewcolumn.set_attributes(cell, text=0, cell_background=1, xpad=3) |
sets, for each row, the text from the first column, the background color from the second column and the horizontal padding from the fourth column. See the treeviewcolumn.py program for an example using these methods.
The attributes of a CellRenderer can be cleared using:
treeviewcolumn.clear_attributes(cell_renderer) |
If setting attributes is not sufficient for your needs you can set a function to be called for each row to set the properties for that CellRenderer using:
treeviewcolumn.set_cell_data_func(cell_renderer, func, data=None) |
where func has the signature:
def func(column, cell_renderer, tree_model, iter, user_data) |
where column is the TreeViewColumn containing cell_renderer, tree_model is the data store and iter is a TreeIter pointing at a row in tree_model. user_data is the value of data that was passed to set_cell_data_func().
In func you set whatever properties you want on cell_renderer. For example the following code fragment sets the text property to display PyGTK objects as an ID string.
... def obj_id_str(treeviewcolumn, cell_renderer, model, iter): pyobj = model.get_value(iter, 0) cell.set_property('text', str(pyobj)) return ... treestore = gtk.TreeStore(object) win = gtk.Window() treeview = gtk.TreeView(treestore) win.add(treeview) cell = CellRendererText() tvcolumn = gtk TreeViewColumn('Object ID', cell) treeview.append_column(tvcolumn) iter = treestore.append(None, [win]) iter = treestore.append(iter, [treeview]) iter = treestore.append(iter, [tvcolumn]) iter = treestore.append(iter, [cell]) iter = treestore.append(None, [treestore]) ... |
The resulting display should be something like Figure 14.3, “CellRenderer Data Function”:
Another use of a cell data function is to control the formatting of a numerical text display e.g. a float value. A CellRendererText will display and automatically convert a float to a string but with a default format "%f".
With cell data functions you can even generate the cell data for the columns from external data. For example the filelisting.py program uses a ListStore with just one column that holds a list of file names. The TreeView displays columns that include a pixbuf, the file name and the file's size, mode and time of last change. The data is generated by the following cell data functions:
def file_pixbuf(self, column, cell, model, iter): filename = os.path.join(self.dirname, model.get_value(iter, 0)) filestat = statcache.stat(filename) if stat.S_ISDIR(filestat.st_mode): pb = folderpb else: pb = filepb cell.set_property('pixbuf', pb) return def file_name(self, column, cell, model, iter): cell.set_property('text', model.get_value(iter, 0)) return def file_size(self, column, cell, model, iter): filename = os.path.join(self.dirname, model.get_value(iter, 0)) filestat = statcache.stat(filename) cell.set_property('text', filestat.st_size) return def file_mode(self, column, cell, model, iter): filename = os.path.join(self.dirname, model.get_value(iter, 0)) filestat = statcache.stat(filename) cell.set_property('text', oct(stat.S_IMODE(filestat.st_mode))) return def file_last_changed(self, column, cell, model, iter): filename = os.path.join(self.dirname, model.get_value(iter, 0)) filestat = statcache.stat(filename) cell.set_property('text', time.ctime(filestat.st_mtime)) return |
These cell data functions retrieve the file information using the name, extract the needed data and set the cell 'text' or 'pixbuf' property with the data. Figure 14.4, “File Listing Example Using Cell Data Functions” shows the example program in action:
A CellRendererText can use Pango markup (by setting the "markup" property) instead of a plain text string to encode various text attributes and provide a rich text display with multiple font style changes. See the Pango Markup reference in the PyGTK Reference Manual for details on the Pango markup language.
The following code fragment illustrates the use of the "markup" property:
... liststore = gtk.ListStore(str) cell = gtk.CellRendererText() tvcolumn = gtk.TreeViewColumn('Pango Markup', cell, markup=0) ... liststore.append(['<span foreground="blue"><b>Pango</b></span> markup can' ' change\n<i>style</i> <big>size</big>, <u>underline,' <s>strikethrough</s></u>,\n' 'and <span font_family="URW Chancery L"><big>font family ' 'e.g. URW Chancery L</big></span>\n<span foreground="red">red' ' foreground and <span background="cyan">cyan background</span></span>']) ... |
produces a display similar to Figure 14.5, “CellRendererText Markup”:
If you create pango markup on the fly you have to be careful to replace the characters that are special to the markup language: "<", ">", "&". The Python library function cgi.escape() can do these basic conversions.
CellRendererText cells can be made editable to allow a user to edit the contents of the cell that is selected by clicking it or pressing one of the Return, Enter, Space or Shift+Space keys. A CellRendererText is made editable for all rows by setting its "editable" property to TRUE as follows:
cellrenderertext.set_property('editable', True) |
Individual cells can be set editable by adding an attribute to the TreeViewColumn using the CellRendererText similar to:
treeviewcolumn.add_attribute(cellrenderertext, "editable", 2) |
which sets the "editable" property to the value contained in the third column of the data store.
Once the cell editing completes, your application should handle the "edited" signal to retrieve the new text and set the associated data store value. Otherwise the cell value reverts to its original value. The signature of the "edited" signal handler is:
def edited_cb(cell, path, new_text, user_data) |
where cell is the CellRendererText, path is the tree path (as a string) to the row containing the edited cell, new_text is the edited text and user_data is context data. Since the TreeModel is needed to use path to set new_text in the data store you probably want to pass the TreeModel as user_data in the connect() method:
cellrenderertext.connect('edited', edited_cb, model) |
If you have two or more editable cells in a row, you could pass the TreeModel column number as part of user_data as well as the TreeModel:
cellrenderertext.connect('edited', edited_cb, (model, col_num)) |
Then you can set the new text in the "edited" handler similar to this example using a ListStore:
def edited_cb(cell, path, new_text, user_data): liststore, column = user_data liststore[path][column] = new_text return |
CellRendererToggle buttons can be made activatable by setting the "activatable" property to TRUE. Similar to editable CellRendererText cells the "activatable" property can be set for the entire CellRendererToggle set of cells using the set_property() method or for individual cells by adding an attribute to the TreeViewColumn containing the CellRendererToggle.
cellrenderertoggle.set_property('activatable', True) treeviewcolumn.add_attribute(cellrenderertoggle, "activatable", 1) |
The setting of the individual toggle buttons can be derived from the values in a TreeModel column by adding an attribute, for example:
treeviewcolumn.add_attribute(cellrenderertoggle, "active", 2) |
You should connect to the "toggled" signal to get notification of user clicks on the toggle buttons so that your application can change the value in the data store. For example:
cellrenderertoggle.connect("toggled", toggled_cb, (model, column)) |
The callback has the signature:
def toggled_cb(cellrenderertoggle, path, user_data) |
where path is the tree path, as a string, pointing to the row containing the toggle that was clicked. You should pass the TreeModel and possibly the column index as part of user_data to provide the necessary context for setting the data store values. For example, your application can toggle the data store value as follows:
def toggled_cb(cell, path, user_data): model, column = user_data model[path][column] = not model[path][column] return |
If your application wants to display the toggle buttons as radio buttons and have only one be set, it will have to scan the data store to deactivate the active radio button and then set the toggled button. For example:
def toggled_cb(cell, path, user_data): model, column = user_data for row in model: row[column] = False model[path][column] = True return |
takes the lazy approach of setting all data store values to FALSE before setting the value to TRUE for the row specified by path.
The cellrenderer.py program illustrates the application of editable CellRendererText and activatable CellRendererToggle cells in a TreeStore.
1 #!/usr/bin/env python 2 # vim: ts=4:sw=4:tw=78:nowrap 3 """ Demonstration using editable and activatable CellRenderers """ 4 import pygtk 5 pygtk.require("2.0") 6 import gtk, gobject 7 8 tasks = { 9 "Buy groceries": "Go to Asda after work", 10 "Do some programming": "Remember to update your software", 11 "Power up systems": "Turn on the client but leave the server", 12 "Watch some tv": "Remember to catch ER" 13 } 14 15 class GUI_Controller: 16 """ The GUI class is the controller for our application """ 17 def __init__(self): 18 # setup the main window 19 self.root = gtk.Window(type=gtk.WINDOW_TOPLEVEL) 20 self.root.set_title("CellRenderer Example") 21 self.root.connect("destroy", self.destroy_cb) 22 # Get the model and attach it to the view 23 self.mdl = Store.get_model() 24 self.view = Display.make_view( self.mdl ) 25 # Add our view into the main window 26 self.root.add(self.view) 27 self.root.show_all() 28 return 29 def destroy_cb(self, *kw): 30 """ Destroy callback to shutdown the app """ 31 gtk.main_quit() 32 return 33 def run(self): 34 """ run is called to set off the GTK mainloop """ 35 gtk.main() 36 return 37 38 class InfoModel: 39 """ The model class holds the information we want to display """ 40 def __init__(self): 41 """ Sets up and populates our gtk.TreeStore """ 42 self.tree_store = gtk.TreeStore( gobject.TYPE_STRING, 43 gobject.TYPE_BOOLEAN ) 44 # places the global people data into the list 45 # we form a simple tree. 46 for item in tasks.keys(): 47 parent = self.tree_store.append( None, (item, None) ) 48 self.tree_store.append( parent, (tasks[item],None) ) 49 return 50 def get_model(self): 51 """ Returns the model """ 52 if self.tree_store: 53 return self.tree_store 54 else: 55 return None 56 57 class DisplayModel: 58 """ Displays the Info_Model model in a view """ 59 def make_view( self, model ): 60 """ Form a view for the Tree Model """ 61 self.view = gtk.TreeView( model ) 62 # setup the text cell renderer and allows these 63 # cells to be edited. 64 self.renderer = gtk.CellRendererText() 65 self.renderer.set_property( 'editable', True ) 66 self.renderer.connect( 'edited', self.col0_edited_cb, model ) 67 68 # The toggle cellrenderer is setup and we allow it to be 69 # changed (toggled) by the user. 70 self.renderer1 = gtk.CellRendererToggle() 71 self.renderer1.set_property('activatable', True) 72 self.renderer1.connect( 'toggled', self.col1_toggled_cb, model ) 73 74 # Connect column0 of the display with column 0 in our list model 75 # The renderer will then display whatever is in column 0 of 76 # our model . 77 self.column0 = gtk.TreeViewColumn("Name", self.renderer, text=0) 78 79 # The columns active state is attached to the second column 80 # in the model. So when the model says True then the button 81 # will show as active e.g on. 82 self.column1 = gtk.TreeViewColumn("Complete", self.renderer1 ) 83 self.column1.add_attribute( self.renderer1, "active", 1) 84 self.view.append_column( self.column0 ) 85 self.view.append_column( self.column1 ) 86 return self.view 87 def col0_edited_cb( self, cell, path, new_text, model ): 88 """ 89 Called when a text cell is edited. It puts the new text 90 in the model so that it is displayed properly. 91 """ 92 print "Change '%s' to '%s'" % (model[path][0], new_text) 93 model[path][0] = new_text 94 return 95 def col1_toggled_cb( self, cell, path, model ): 96 """ 97 Sets the toggled state on the toggle button to true or false. 98 """ 99 model[path][1] = not model[path][1] 100 print "Toggle '%s' to: %s" % (model[path][0], model[path][1],) 101 return 102 103 if __name__ == '__main__': 104 Store = InfoModel() 105 Display = DisplayModel() 106 myGUI = GUI_Controller() 107 myGUI.run() |
The program provides editable cells in the first column and activatable cells in the second column. Lines 64-66 create an editable CellRendererText and connect the "edited" signal to the col0_edited_cb() callback (lines 87-94) that changes the appropriate row column value in the TreeStore. Likewise lines 70-72 create an activatable CellRendererToggle and connect the "toggled" signal to the col1_toggled_cb() callback (lines 95-101) to change the appropriate row value. When an editable or activatable cell is changed, a message is printed to indicate what the change was.
Figure 14.6, “Editable and Activatable Cells” illustrates the cellrenderer.py program in operation.
TextIters represent a position between two characters in a TextBuffer. TextIters are usually created by using a TextBuffer method. TextIters are invalidated when the number of characters in a TextBuffer is changed (except for the TextIter that is used for the insertion or deletion). Inserting or deleting pixbufs or anchors also counts as a TextIter invalidating change.
There are a large number of methods associated with a TextIter object. They are grouped together in the following sections by similar function.
The TextBuffer that contains the TextIter can be retrieved using the method:
buffer = iter.get_buffer() |
The following methods can be used to get the location of the TextIter in the TextBuffer:
offset = iter.get_offset() # returns offset in buffer of iter line_number = iter.get_line() # returns number of line at iter line_offset = iter.get_line_offset() # returns iter offset in line numchars = iter.get_chars_in_line() # returns number of chars in line |
The PangoLanguage used at a given iter location in the TextBuffer is obtained by calling the method:
language = iter.get_language() |
The more general method used to get the text attributes at a TextIter's location is:
result = iter.get_attributes(values) |
where result indicates whether the given values (TextAttributes object) were modified. The given values are obtained by using the TextView method:
values = textview.get_default_attributes() |
The following attributes are accessible from a TextAttributes object (not implemented in PyGTK <= 1.99.15):
bg_color | background color |
fg_color | foreground color |
bg_stipple | background stipple bitmap |
fg_stipple | foreground stipple bitmap |
rise | offset of text above baseline |
underline | style of underline |
strikethrough | whether text is strikethrough |
draw_bg | TRUE if some tags affect the drawing of the background |
justification | style of justification |
direction | which direction the text runs |
font | PangoFontDescription in use |
font_scale | scale of the font in use |
left_margin | location of left margin |
right_margin | location of right margin |
pixels_above_lines | pixels spacing above a line |
pixels_below_lines | pixel spacing below a line |
pixels_inside_wrap | pixel spacing between wrapped lines |
tabs | PangoTabArray in use |
wrap_mode | mode of wrap in use |
language | PangoLanguage in use |
invisible | whether text is invisible (not implemented in GTK+ 2.0) |
bg_full_height | whether background is fit to full line height |
editable | whether the text is editable |
realized | text is realized |
pad1 | |
pad2 | |
pad3 | |
pad4 |
Various amounts of text and TextBuffer objects can be retrieved from a TextBuffer using the following methods:
char = iter.get_char() # returns char or 0 if at end of buffer text = start.get_slice(end) # returns the text between start and end iters text = start.get_text(end) # returns the text between start and end iters pixbuf = iter.get_pixbuf() # returns the pixbuf at the location (or None) anchor = iter.get_child_anchor() # returns the child anchor (or None) mark_list = iter.get_marks() # returns a list of marks tag_list = iter.get_toggled_tags() # returns a list of tags that are toggled on or off tag_list = iter.get_tags() # returns a prioritized list of tags |
Tag conditions at the TextIter location can be checked using the following methods:
result = iter.begins_tag(tag=None) # TRUE if tag is toggled on at iter result = iter.ends_tag(tag=None) # TRUE if tag is toggled off at iter result = iter.toggles_tag(tag=None) # TRUE if tag is toggled on or off at iter result = iter.has_tag(tag) # TRUE if tag is active at iter |
These methods return TRUE if the given tag satisfies the condition at iter. If the tag is None for the first three methods then the result is TRUE if any tag satisfies the condition at iter.
The following methods indicate whether the text at the TextIter location is editable or allows text insertion:
result = iter.editable() result = iter.can_insert(default_editability) |
The editable() method indicates whether the iter is in an editable range of text while the can_insert() method indicates whether text can be inserted at iter considering the default editability of the TextView, TextBuffer and applicable tags. The default_editability is usually determined by calling the method:
default_editability = textview.get_editable() |
The equivalence of two TextIters can be determined with the method:
are_equal = lhs.equal(rhs) |
Two TextIters can be compared with the method:
result = lhs.compare(rhs) |
result will be: -1 if lhs is less than rhs; 0 if lhs equals rhs; and, 1 if lhs is greater than rhs.
To determine whether a TextIter is located between two given TextIters use the method:
result = iter.in_range(start, end) |
result is TRUE if iter is between start and end. Note: start and end must be in ascending order. This can be guaranteed using the method:
first.order(second) |
which will reorder the TextIter offsets so that first is before second.
The location of a TextIter with respect to the text in a TextBuffer can be determined by the following methods:
result = iter.starts_word() result = iter.ends_word() result = iter.inside_word() result = iter.starts_sentence() result = iter.ends_sentence() result = iter.inside_sentence() result = starts_line() result = iter.ends_line() |
result returns TRUE if the TextIter is at the given text location. These methods are somewhat self-explanatory. The definition of the text components and their boundaries is determined by the language used at the TextIter. Note that a line is a collection of sentences similar to a paragraph.
The following methods can be used to determine if a TextIter is at the start or end of the TextBuffer:
result = iter.is_start() result = iter.is_end() |
result is TRUE if the TextIter is at the start or end of the TextBuffer.
Since a TextBuffer may contain multiple characters which are effectively viewed as one cursor position (e.g. carriage return-linefeed combination or letter with an accent mark) it's possible that a TextIter could be in a location which is not a cursor position. The following method indicates whether a TextIter is at a cursor position:
result = iter.is_cursor_position() |
TextIters can be moved through a TextBuffer in various text unit strides. The definition of the text units is set by the PangoLanguage in use at the TextIter location. The basic methods are:
result = iter.forward_char() # forward by one character result = iter.backward_char() # backward by one character result = iter.forward_word_end() # forward to the end of the word result = iter.backward_word_start() # backward to the start of the word result = iter.forward_sentence_end() # forward to the end of the sentence result = iter.backward_sentence_start() # backward to the start of the sentence result = iter.forward_line() # forward to the start of the next line result = iter.backward_line() # backward to the start of the previous line result = iter.forward_to_line_end() # forward to the end of the line result = iter.forward_cursor_position() # forward by one cursor position result = iter.backward_cursor_position() # forward by one cursor position |
result is TRUE if the TextIter was moved and FALSE if the TextIter is at the start or end of the TextBuffer.
All of the above methods (except forward_to_line_end()) have corresponding methods that take a count (that can be positive or negative) to move the TextIter in multiple text unit strides:
result = iter.forward_chars(count) result = iter.backward_chars(count) result = iter.forward_word_ends(count) result = iter.backward_word_starts(count) result = iter.forward_sentence_ends(count) result = iter.backward_sentence_starts(count) result = iter.forward_lines(count) result = iter.backward_lines(count) result = iter.forward_cursor_positions(count) result = iter.backward_cursor_positions(count) |
A TextIter can be moved to a specific location in the TextBuffer using the following methods:
iter.set_offset(char_offset) # move to given character offset iter.set_line(line_number) # move to start of given line iter.set_line_offset(char_on_line) # move to given character offset in current line iter.forward_to_end() # move to end of the buffer |
In addition, a TextIter can be moved to a location where a tag is toggled on or off by using the methods:
result = iter.forward_to_tag_toggle(tag) result = iter.backward_to_tag_taoggle(tag) |
result is TRUE if the TextIter was moved to a new location where tag is toggled. If tag is None then the TextIter will be moved to the next location where any tag is toggled.
A search for a string in a TextBuffer is done using the methods:
match_start, match_end = iter.forward_search(str, flags, limit=None) match_start, match_end = iter.backward_search(str, flags, limit=None) |
The return value is a tuple containing TextIters that indicate the location of the first character of the match and the first character after the match. str is the character string to be located. flags modifies the conditions of the search; flag values can be:
gtk.TEXT_SEARCH_VISIBLE_ONLY # invisible characters are ignored gtk.TEXT_SEARCH_TEXT_ONLY # pixbufs and child anchors are ignored |
limit is an optional TextIter that bounds the search range.
TreeSelections are objects that manage selections in a TreeView. When a TreeView is created a TreeSelection is automatically created as well. The TreeSelection can be retrieved from the TreeView using the method:
treeselection = treeview.get_selection() |
You can retrieve the TreeView associated with a TreeSelection by calling the method:
treeview = treeselection.get_treeview() |
The TreeSelection supports the following selection modes:
gtk.SELECTION_NONE | No selection is allowed. |
gtk.SELECTION_SINGLE | A single selection is allowed by clicking. |
gtk.SELECTION_BROWSE | A single selection allowed by browsing with the pointer. |
gtk.SELECTION_MULTIPLE | Multiple items can be selected at once. |
You can retrieve the current selection mode by calling the method:
mode = treeselection.get_mode() |
The mode can be set using:
treeselection.set_mode(mode) |
where mode is one of the above selection modes.
The method to use to retrieve the selection depends on the current selection mode. If the selection mode is gtk.SELECTION_SINGLE or gtk.SELECTION_BROWSE, you should use the following method:
(model, iter) = treeselection.get_selected() |
that returns a 2-tuple containing model, the TreeModel used by the TreeView associated with treeselection and iter, a TreeIter pointing at the selected row. If no row is selected then iter is None. If the selection mode is gtk.SELECTION_MULTIPLE a TypeError exception is raised.
If you have a TreeView using the gtk.SELECTION_MULTIPLE selection mode then you should use the method:
(model, pathlist) = treeselection.get_selected_rows() |
that returns a 2-tuple containing the tree model and a list of the tree paths of the selected rows. This method is not available in PyGTK 2.0 so you'll have to use a helper function to retrieve the list by using:
treeselection.selected_foreach(func, data=None) |
where func is a function that is called on each selected row with data. The signature of func is:
def func(model, path, iter, data) |
where model is the TreeModel, path is the tree path of the selected row and iter is a TreeIter pointing at the selected row.
This method can be used to simulate the get_selected_row() method as follows:
... def foreach_cb(model, path, iter, pathlist): list.append(path) ... def my_get_selected_rows(treeselection): pathlist = [] treeselection.selected_foreach(foreach_cb, pathlist) model = sel.get_treeview().get_model() return (model, pathlist) ... |
The selected_foreach() method cannot be used to modify the tree model or the selection though you can change the data in the rows.
If you want ultimate control over row selection you can set a function to be called before a row is selected or unselected by using the method:
treeselection.set_select_function(func, data) |
where func is a callback function and data is user data to be passed to func when it is called. func has the signature:
def func(selection, model, path, is_selected, user_data) |
where selection is the TreeSelection, model is the TreeModel used with the TreeView associated with selection, path is the tree path of the selected row, is_selected is TRUE if the row is currently selected and user_data is data. func should return TRUE if the row's selection status should be toggled.
Setting a select function is useful if:
You can change the selection programmatically using the following methods:
treeselection.select_path(path) treeselection.unselect_path(path) treeselection.select_iter(iter) treeselection.unselect_iter(iter) |
These methods select or unselect a single row that is specified by either path, a tree path or iter, a TreeIter pointing at the row. The following methods select or unselect several rows at once:
treeselection.select_all() treeselection.unselect_all() treeselection.select_range(start_path, end_path) treeselection.unselect_range(start_path, end_path) |
The select_all() method requires that the selection mode be gtk.SELECTION_MULTIPLE as does the select_range() method. The unselect_all() and unselect_range() methods will function with any selection mode. Note that the unselect_all() method is not available in PyGTK 2.0
You can check if a row is selected by using one of the methods:
result = treeselection.path_is_selected(path) result = treeselection.iter_is_selected(iter) |
that return TRUE if the row specified by path or iter is currently selected. You can retrieve a count of the number of selected rows using the method:
count = treeselection.count_selected_rows() |
This method is not available in PyGTK 2.0 so you'll have to simulate it using the selected_foreach() method similar to the simulation of the get_selected_rows() method in Section 21.2, “Retrieving the Selection”. For example:
... def foreach_cb(model, path, iter, counter): counter[0] += 1 ... def my_count_selected_rows(treeselection): counter = [0] treeselection.selected_foreach(foreach_cb, counter) return counter[0] ... |
Because of this flexibility, packing boxes in GTK can be confusing at first. There are a lot of options, and it's not immediately obvious how they all fit together. In the end, however, there are basically five different styles. Figure 4.1, “Packing: Five Variations” illustrates the result of running the program packbox.py with an argument of 1:
Each line contains one horizontal box (hbox) with several buttons. The call to pack is shorthand for the call to pack each of the buttons into the hbox. Each of the buttons is packed into the hbox the same way (i.e., same arguments to the pack_start() method).
This is an example of the pack_start() method.
box.pack_start(child, expand, fill, padding) |
box is the box you are packing the object into; the first argument is the child object to be packed. The objects will all be buttons for now, so we'll be packing buttons into boxes.
The expand argument to pack_start() and pack_end() controls whether the widgets are laid out in the box to fill in all the extra space in the box so the box is expanded to fill the area allotted to it (True); or the box is shrunk to just fit the widgets (False). Setting expand to False will allow you to do right and left justification of your widgets. Otherwise, they will all expand to fit into the box, and the same effect could be achieved by using only one of pack_start() or pack_end().
The fill argument to the pack methods control whether the extra space is allocated to the objects themselves (True), or as extra padding in the box around these objects (False). It only has an effect if the expand argument is also True.
Python allows a method or function to be defined with default argument values and argument keywords. Throughout this tutorial I'll show the definition of the functions and methods with defaults and keywords bolded as applicable. For example the pack_start() method is defined as:
box.pack_start(child, expand=True, fill=True, padding=0) box.pack_end(child, expand=True, fill=True, padding=0) |
child, expand, fill and padding are keywords. The expand, fill and padding arguments have the defaults shown. The child argument must be specified.
When creating a new box, the function looks like this:
hbox = gtk.HBox(homogeneous=False, spacing=0) vbox = gtk.VBox(homogeneous=False, spacing=0) |
The homogeneous argument to gtk.HBox() and gtk.VBox() controls whether each object in the box has the same size (i.e., the same width in an hbox, or the same height in a vbox). If it is set, the pack routines function essentially as if the expand argument was always turned on.
What's the difference between spacing (set when the box is created) and padding (set when elements are packed)? Spacing is added between objects, and padding is added on either side of an object. Figure 4.2, “Packing with Spacing and Padding” illustrates the difference; pass an argument of 2 to packbox.py :
Figure 4.3, “Packing with pack_end()” illustrates the use of the pack_end() method (pass an argument of 3 to packbox.py). The label "end" is packed with the pack_end() method. It will stick to the right edge of the window when the window is resized.
text-pushed(statusbar, context_id, text, data) text-popped(statusbar, context_id, text, data) |
Radio buttons are similar to check buttons except they are grouped so that only one may be selected/depressed at a time. This is good for places in your application where you need to select from a short list of options.
Creating a new radio button is done with this call:
radio_button = gtk.RadioButton(group=None, label=None) |
You'll notice the extra argument to this call. Radio buttons require a group to operate properly. The first call to gtk.RadioButton() should pass None as the first argument and a new radio button group will be created with the new radio button as its only member.
To add more radio buttons to a group, pass in a reference to a radio button in group in subsequent calls to gtk.RadioButton().
If a label argument is specified the text will be parsed for '_'-prefixed mnemonic characters.
It is also a good idea to explicitly set which button should be the default depressed button with:
radio_button.set_active(is_active) |
This is described in the section on toggle buttons, and works in exactly the same way. Once the radio buttons are grouped together, only one of the group may be active at a time. If the user clicks on one radio button, and then on another, the first radio button will first emit a "toggled" signal (to report becoming inactive), and then the second will emit its "toggled" signal (to report becoming active).
The example program radiobuttons.py creates a radio button group with three buttons. Figure 6.4, “Radio Buttons Example” illustrates the resulting window:
The source code for the example program is:
1 #!/usr/bin/env python 2 3 # example radiobuttons.py 4 5 import pygtk 6 pygtk.require('2.0') 7 import gtk 8 9 class RadioButtons: 10 def callback(self, widget, data=None): 11 print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) 12 13 def close_application(self, widget, event, data=None): 14 gtk.main_quit() 15 return False 16 17 def __init__(self): 18 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 19 20 self.window.connect("delete_event", self.close_application) 21 22 self.window.set_title("radio buttons") 23 self.window.set_border_width(0) 24 25 box1 = gtk.VBox(False, 0) 26 self.window.add(box1) 27 box1.show() 28 29 box2 = gtk.VBox(False, 10) 30 box2.set_border_width(10) 31 box1.pack_start(box2, True, True, 0) 32 box2.show() 33 34 button = gtk.RadioButton(None, "radio button1") 35 button.connect("toggled", self.callback, "radio button 1") 36 box2.pack_start(button, True, True, 0) 37 button.show() 38 39 button = gtk.RadioButton(button, "radio button2") 40 button.connect("toggled", self.callback, "radio button 2") 41 button.set_active(True) 42 box2.pack_start(button, True, True, 0) 43 button.show() 44 45 button = gtk.RadioButton(button, "radio button3") 46 button.connect("toggled", self.callback, "radio button 3") 47 box2.pack_start(button, True, True, 0) 48 button.show() 49 50 separator = gtk.HSeparator() 51 box1.pack_start(separator, False, True, 0) 52 separator.show() 53 54 box2 = gtk.VBox(False, 10) 55 box2.set_border_width(10) 56 box1.pack_start(box2, False, True, 0) 57 box2.show() 58 59 button = gtk.Button("close") 60 button.connect_object("clicked", self.close_application, self.window, 61 None) 62 box2.pack_start(button, True, True, 0) 63 button.set_flags(gtk.CAN_DEFAULT) 64 button.grab_default() 65 button.show() 66 self.window.show() 67 68 def main(): 69 gtk.main() 70 return 0 71 72 if __name__ == "__main__": 73 RadioButtons() 74 main() |
The code is fairly straight forward. Lines 63-64 make the "close" button the default widget so that pressing the "Enter" key when the window is active causes the "close" button to emit the "clicked" signal.