Python GTK 3 Tutorial PDF
Python GTK 3 Tutorial PDF
Release 3.4
Sebastian Plsterl
Contents
ii
Release 3.4 Date February 25, 2014 Copyright GNU Free Documentation License 1.3 with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts This tutorial gives an introduction to writing GTK+ 3 applications in Python. Prior to working through this tutorial, it is recommended that you have a reasonable grasp of the Python programming language. GUI programming introduces new problems compared to interacting with the standard output (console / terminal). It is necessary for you to know how to create and run Python les, understand basic interpreter errors, and work with strings, integers, oats and Boolean values. For the more advanced widgets in this tutorial, good knowledge of lists and tuples will be needed. Although this tutorial describes the most important classes and methods within GTK+ 3, it is not supposed to serve as an API reference. Please refer to the GTK+ 3 Reference Manual for a detailed description of the API. Contents:
Contents
Contents
CHAPTER 1
Installation
The rst step before we start with actual coding consists of setting up PyGObject and its dependencies. PyGObject is a Python module that enables developers to access GObject-based libraries such as GTK+ within Python. It exclusively supports GTK+ version 3 or later. If you want to use GTK+ 2 in your application, use PyGTK, instead.
1.1 Dependencies
GTK+3 Python 2 (2.6 or later) or Python 3 (3.1 or later) gobject-introspection
It will print any applications and libraries that are currently missing on your system but required for building. You should install those using your distributions package repository. A list of package names for different distributions is maintained on the GNOME wiki. Run the command above again to ensure the required tools are present. Executing the following command will build PyGObject and all its dependencies:
1
[Link]
Chapter 1. Installation
CHAPTER 2
Getting Started
1 2 3 4 5 6 7
#!/usr/bin/python from [Link] import Gtk win = [Link]() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
The rst line of all Python programs should start with #! followed by the path to the Python interpreter you want to invoke. 5
In order to access GTK+ classes and functions we rst must import the Gtk module. The next line creates an empty window.
win = [Link]()
Followed by connecting to the windows delete event to ensure that the application is terminated if we click on the x to close the window.
[Link]("delete-event", Gtk.main_quit)
Finally, we start the GTK+ processing loop which we quit when the window is closed (see line 5).
[Link]()
To run the program, open a terminal, change to the directory of the le, and enter:
python simple_example.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#!/usr/bin/python from [Link] import Gtk class MyWindow([Link]): def __init__(self): [Link].__init__(self, title="Hello World") [Link] = [Link](label="Click Here") [Link]("clicked", self.on_button_clicked) [Link]([Link]) def on_button_clicked(self, widget): print("Hello World") win = MyWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
This example differs from the simple example as we sub-class [Link] to dene our own MyWindow class.
class MyWindow([Link]):
In the classs constructor we have to call the constructor of the super class. In addition, we tell it to set the value of the property title to Hello World.
[Link].__init__(self, title="Hello World")
The next three lines are used to create a button widget, connect to its clicked signal and add it as child to the top-level window.
[Link] = [Link](label="Click Here") [Link]("clicked", self.on_button_clicked) [Link]([Link])
Accordingly, the method on_button_clicked() will be called if you click on the button.
def on_button_clicked(self, widget): print("Hello World")
The last block, outside of the class, is very similar to the simple example above, but instead of creating an instance of the generic [Link] class, we create an instance of MyWindow.
CHAPTER 3
Basics
This section will introduce some of the most important aspects of GTK+.
Firstly, widget is an instance of a widget we created earlier. Next, the event we are interested in. Each widget has its own particular events which can occur. For instance, if you have a button you usually want to connect to the clicked event. This means that when the button is clicked, the signal is issued. Thirdly, the callback argument is the name of the callback function. It contains the code which runs when signals of the specied type are issued. Finally, the data argument includes any data which should be passed when the signal is issued. However, this argument is completely optional and can be left out if not required. The function returns a number that identies this particular signal-callback pair. It is required to disconnect from a signal such that the callback function will not be called during any future or currently ongoing emissions of the signal it has been connected to.
[Link](handler_id)
If you have lost the handler_id for some reason (for example the handlers were installed using [Link].connect_sinals()), you can still disconnect a specic callback using the function disconnect_by_func():
widget.disconnect_by_func(callback)
Almost all applications will connect to the delete-event signal of the top-level window. It is emitted if a user requests that a toplevel window is closed. The default handler for this signal destroys the window, but does not terminate the application. Connecting the delete-event signal to the function Gtk.main_quit() will result in the desired behaviour. 9
[Link]("delete-event", Gtk.main_quit)
3.2 Properties
Properties describe the conguration and state of widgets. As for signals, each widget has its own particular set of properties. For example, a button has the property label which contains the text of the label widget inside the button. You can specify the name and value of any number of properties as keyword arguments when creating an instance of a widget. To create a label aligned to the right with the text Hello World and an angle of 25 degrees, use:
label = [Link](label="Hello World", angle=25, halign=[Link])
which is equivalent to
label = [Link]() label.set_label("Hello World") label.set_angle(25) label.set_halign([Link])
you and
can also get and set the properties with widget.set_property("prop-name", value),
10
Chapter 3. Basics
CHAPTER 4
This section explains how strings are represented in Python 2.x, Python 3.x and GTK+ and discusses common errors that arise when working with strings.
4.1 Denitions
Conceptionally, a string is a list of characters such as A, B, C or . Characters are abstract representations and their meaning depends on the language and context they are used in. The Unicode standard describes how characters are represented by code points. For example the characters above are represented with the code points U+0041, U+0042, U+0043, and U+00C9, respectively. Basically, code points are numbers in the range from 0 to 0x10FFFF. As mentioned earlier, the representation of a string as a list of code points is abstract. In order to convert this abstract representation into a sequence of bytes the Unicode string must be encoded. The simplest from of encoding is ASCII and is performed as follows: 1. If the code point is < 128, each byte is the same as the value of the code point. 2. If the code point is 128 or greater, the Unicode string cant be represented in this encoding. (Python raises a UnicodeEncodeError exception in this case.) Although ASCII encoding is simple to apply it can only encode for 128 different characters which is hardly enough. One of the most commonly used encodings that addresses this problem is UTF-8 (it can handle any Unicode code point). UTF stands for Unicode Transformation Format, and the 8 means that 8-bit numbers are used in the encoding.
4.2 Python 2
4.2.1 Python [Link] Unicode Support
Python 2 comes with two different kinds of objects that can be used to represent strings, str and unicode. Instances of the latter are used to express Unicode strings, whereas instances of the str type are byte representations (the encoded string). Under the hood, Python represents Unicode strings as either 16- or 32-bit integers, depending on how the Python interpreter was compiled. Unicode strings can be converted to 8-bit strings with [Link]():
>>> unicode_string = u"Fu\u00dfb\u00e4lle" >>> print unicode_string Fublle >>> type(unicode_string) <type unicode>
11
Pythons 8-bit strings have a [Link]() method that interprets the string using the given encoding:
>>> utf8_string = unicode_string.encode("utf-8") >>> type(utf8_string) <type str> >>> u2 = utf8_string.decode("utf-8") >>> unicode_string == u2 True
Unfortunately, Python 2.x allows you to mix unicode and str if the 8-bit string happened to contain only 7-bit (ASCII) bytes, but would get UnicodeDecodeError if it contained non-ASCII values:
>>> utf8_string = " sind rund" >>> unicode_string + utf8_string uFu\xdfb\xe4lle sind rund >>> utf8_string = " k\xc3\xb6nnten rund sein" >>> print utf8_string knnten rund sein >>> unicode_string + utf8_string Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: ascii codec cant decode byte 0xc3 in position 2: ordinal not in range(128)
Note the warning at the end. Although we called [Link].set_text() with a unicode instance as argument, [Link].get_text() will always return a str instance. Accordingly, txt and unicode_string are not equal. This is especially important if you want to internationalize your program using gettext. You have to make sure that gettext will return UTF-8 encoded 8-bit strings for all languages. In general it is recommended to not use unicode objects in GTK+ applications at all and only use UTF-8 encoded str objects since GTK+ does not fully integrate with unicode objects. Otherwise, you would have to decode the return values to Unicode strings each time you call a GTK+ method:
12
4.3 Python 3
4.3.1 Python [Link] Unicode support
Since Python 3.0, all strings are stored as Unicode in an instance of the str type. Encoded strings on the other hand are represented as binary data in the form of instances of the bytes type. Conceptionally, str refers to text, whereas bytes refers to data. Use [Link]() to go from str to bytes, and [Link]() to go from bytes to str. In addition, it is no longer possible to mix Unicode strings with encoded strings, because it will result in a TypeError:
>>> text = "Fu\u00dfb\u00e4lle" >>> data = b" sind rund" >>> text + data Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Cant convert bytes object to str implicitly >>> text + [Link]("utf-8") Fublle sind rund >>> [Link]("utf-8") + data bFu\xc3\x9fb\xc3\xa4lle sind rund
4.4 References
Whats new in Python 3.0 describes the new concepts that clearly distinguish between text and data. The Unicode HOWTO discusses Python [Link] support for Unicode, and explains various problems that people commonly encounter when trying to work with Unicode. The Unicode HOWTO for Python 3.x discusses Unicode support in Python 3.x.
4.3. Python 3
13
UTF-8 encoding table and Unicode characters contains a list of Unicode code points and their respective UTF-8 encoding.
14
CHAPTER 5
Layout Containers
While many GUI toolkits require you to precisely place widgets in a window, using absolute positioning, GTK+ uses a different approach. Rather than specifying the position and size of each widget in the window, you can arrange your widgets in rows, columns, and/or tables. The size of your window can be determined automatically, based on the sizes of the widgets it contains. And the sizes of the widgets are, in turn, determined by the amount of text they contain, or the minimum and maximum sizes that you specify, and/or how you have requested that the available space should be shared between sets of widgets. You can perfect your layout by specifying padding distance and centering values for each of your widgets. GTK+ then uses all this information to resize and reposition everything sensibly and smoothly when the user manipulates the window. GTK+ arranges widgets hierarchically, using containers. They are invisible to the end user and are inserted into a window, or placed within each other to layout components. There are two avours of containers: single-child containers, which are all descendants of [Link], and multiple-child containers, which are descendants of [Link]. The most commonly used are vertical or horizontal boxes ([Link]), tables ([Link]) and grids ([Link]).
5.1 Boxes
Boxes are invisible containers into which we can pack our widgets. When packing widgets into a horizontal box, the objects are inserted horizontally from left to right or right to left depending on whether [Link].pack_start() or [Link].pack_end() is used. In a vertical box, widgets are packed from top to bottom or vice versa. You may use any combination of boxes inside or beside other boxes to create the desired effect.
5.1.1 Example
Lets take a look at a slightly modied version of the extended example with two buttons.
1 2 3 4 5 6
from [Link] import Gtk class MyWindow([Link]): def __init__(self): [Link].__init__(self, title="Hello World")
15
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
[Link] = [Link](spacing=6) [Link]([Link]) self.button1 = [Link](label="Hello") [Link]("clicked", self.on_button1_clicked) [Link].pack_start(self.button1, True, True, 0) self.button2 = [Link](label="Goodbye") [Link]("clicked", self.on_button2_clicked) [Link].pack_start(self.button2, True, True, 0) def on_button1_clicked(self, widget): print("Hello") def on_button2_clicked(self, widget): print("Goodbye") win = MyWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
First, we create a horizontally orientated box container where 6 pixels are placed between children. This box becomes the child of the top-level window.
[Link] = [Link](spacing=6) [Link]([Link])
While with [Link].pack_start() widgets are positioned from left to right, [Link].pack_end() positions them from right to left.
5.2 Grid
[Link] is a container which arranges its child widgets in rows and columns, but you do not need to specify the dimensions in the constructor. Children are added using [Link](). They can span multiple rows or columns. It is also possible to add a child next to an existing child, using [Link].attach_next_to(). [Link] can be used like a [Link] by just using [Link](), which will place children next to each other in the direction determined by the orientation property (defaults to [Link]).
16
5.2.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
from [Link] import Gtk class GridWindow([Link]): def __init__(self): [Link].__init__(self, title="Grid Example") grid = [Link]() [Link](grid) button1 button2 button3 button4 button5 button6 = = = = = = [Link](label="Button [Link](label="Button [Link](label="Button [Link](label="Button [Link](label="Button [Link](label="Button 1") 2") 3") 4") 5") 6")
[Link](button1) [Link](button2, 1, 0, 2, 1) grid.attach_next_to(button3, button1, [Link], 1, 2) grid.attach_next_to(button4, button3, [Link], 2, 1) [Link](button5, 1, 2, 1, 1) grid.attach_next_to(button6, button5, [Link], 1, 1) win = GridWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
5.3 Table
Tables allows us to place widgets in a grid similar to [Link]. The grids dimensions need to be specied in the [Link] constructor. To place a widget into a box, use [Link](). [Link].set_row_spacing() and [Link].set_col_spacing() set the spacing between the rows at the specied row or column. Note that for columns, the space goes to the right of the column, and for rows, the space goes below the row. You can also set a consistent spacing for all rows and/or columns with [Link].set_row_spacings() and 5.3. Table 17
[Link].set_col_spacings(). Note that with these calls, the last row and last column do not get any spacing.
5.3.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
from [Link] import Gtk class TableWindow([Link]): def __init__(self): [Link].__init__(self, title="Table Example") table = [Link](3, 3, True) [Link](table) button1 button2 button3 button4 button5 button6 = = = = = = [Link](label="Button [Link](label="Button [Link](label="Button [Link](label="Button [Link](label="Button [Link](label="Button 0, 1, 0, 1, 1, 2, 1, 3, 1, 3, 2, 3, 0, 0, 1, 1, 2, 2, 1) 1) 3) 2) 3) 3) 1") 2") 3") 4") 5") 6")
5.4 ListBox
A [Link] is a vertical container that contains [Link] children. These rows can by dynamically sorted and ltered, and headers can be added dynamically depending on the row content. It also allows keyboard and mouse navigation and selection like a typical list.
18
Using [Link] is often an alternative to [Link], especially when the list contents has a more complicated layout than what is allowed by a [Link], or when the contents is interactive (i.e. has a button in it). Although a [Link] must have only [Link] children you can add any kind of widget to it via [Link](), and a [Link] widget will automatically be inserted between the list and the widget.
5.4.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
from [Link] import Gtk class ListBoxWindow([Link]): def __init__(self): [Link].__init__(self, title="ListBox Demo") self.set_border_width(10) hbox = [Link](spacing=6) [Link](hbox) listbox = [Link]() listbox.set_selection_mode([Link]) hbox.pack_start(listbox, True, True, 0) row = [Link]() hbox = [Link](orientation=[Link], spacing=50) [Link](hbox) vbox = [Link](orientation=[Link]) hbox.pack_start(vbox, True, True, 0) label1 = [Link]("Automatic Date & Time", xalign=0) label2 = [Link]("Requires internet access", xalign=0) vbox.pack_start(label1, True, True, 0) vbox.pack_start(label2, True, True, 0) switch = [Link]() [Link] = [Link] hbox.pack_start(switch, False, True, 0) [Link](row)
5.4. ListBox
19
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
row = [Link]() hbox = [Link](orientation=[Link], spacing=50) [Link](hbox) label = [Link]("Enable Automatic Update", xalign=0) check = [Link]() hbox.pack_start(label, True, True, 0) hbox.pack_start(check, False, True, 0) [Link](row) row = [Link]() hbox = [Link](orientation=[Link], spacing=50) [Link](hbox) label = [Link]("Date Format", xalign=0) combo = [Link]() [Link](0, "0", "24-hour") [Link](1, "1", "AM/PM") hbox.pack_start(label, True, True, 0) hbox.pack_start(combo, False, True, 0) [Link](row)
20
5.5.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
from [Link] import Gtk class StackWindow([Link]): def __init__(self): [Link].__init__(self, title="Stack Demo") self.set_border_width(10) vbox = [Link](orientation=[Link], spacing=6) [Link](vbox) stack = [Link]() stack.set_transition_type([Link].SLIDE_LEFT_RIGHT) stack.set_transition_duration(1000) checkbutton = [Link]("Click me!") stack.add_titled(checkbutton, "check", "Check Button") label = [Link]() label.set_markup("<big>A fancy label</big>") stack.add_titled(label, "label", "A label") stack_switcher = [Link]() stack_switcher.set_stack(stack) vbox.pack_start(stack_switcher, True, True, 0) vbox.pack_start(stack, True, True, 0) win = StackWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
5.6 HeaderBar
A [Link] is similar to a horizontal [Link], it allows to place children at the start or the end. In addition, it allows a title to be displayed. The title will be centered with respect to the width of the box, even if the children at either side take up different amounts of space. Since GTK+ now supports Client Side Decoration, a [Link] can be used in place of the title bar (which is rendered by the Window Manager). A [Link] is usually located across the top of a window and should contain commonly used controls which
5.6. HeaderBar
21
affect the content below. They also provide access to window controls, including the close window button and window menu.
5.6.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
from [Link] import Gtk, Gio class HeaderBarWindow([Link]): def __init__(self): [Link].__init__(self, title="Stack Demo") self.set_border_width(10) self.set_default_size(400, 200) hb = [Link]() [Link].show_close_button = True [Link] = "HeaderBar example" self.set_titlebar(hb) button = [Link]() icon = [Link](name="mail-send-receive-symbolic") image = [Link].new_from_gicon(icon, [Link]) [Link](image) hb.pack_end(button) box = [Link](orientation=[Link]) [Link].add_class(box.get_style_context(), "linked") button = [Link]() [Link]([Link]([Link], [Link])) [Link](button) button = [Link]() [Link]([Link]([Link], [Link])) [Link](button) hb.pack_start(box) [Link]([Link]())
22
36 37 38 39
5.6. HeaderBar
23
24
CHAPTER 6
Label
Labels are the main method of placing non-editable text in windows, for instance to place a title next to a [Link] widget. You can specify the text in the constructor, or later with the [Link].set_text() or [Link].set_markup() methods. The width of the label will be adjusted automatically. You can produce multi-line labels by putting line breaks (\n) in the label string. Labels can be made selectable with [Link].set_selectable(). Selectable labels allow the user to copy the label contents to the clipboard. Only labels that contain useful-to-copy information such as error messages should be made selectable. The label text can be justied using the [Link].set_justify() method. The widget is also capable of word-wrapping, which can be activated with [Link].set_line_wrap(). [Link] support some simple formatting, for instance allowing you to make some text bold, colored, or larger. You can do this by providing a string to [Link].set_markup(), using the Pango Markup syntax 1 . For instance, <b>bold text</b> and <s>strikethrough text</s>. In addition, [Link] supports clickable hyperlinks. The markup for links is borrowed from HTML, using the a with href and title attributes. GTK+ renders links similar to the way they appear in web browsers, with colored, underlined text. The title attribute is displayed as a tooltip on the link.
label.set_markup("Go to <a href=\"[Link] " "title=\"Our website\">GTK+ website</a> for more")
Labels may contain mnemonics. Mnemonics are underlined characters in the label, used for keyboard navigation. Mnemonics are created by providing a string with an underscore before the mnemonic character, such as _File, to the functions [Link].new_with_mnemonic() or [Link].set_text_with_mnemonic(). Mnemonics automatically activate any activatable widget the label is inside, such as a [Link]; if the label is not inside the mnemonics target widget, you have to tell the label about the target using [Link].set_mnemonic_widget().
1
25
6.1 Example
1 2 3 4 5 6 7 8
from [Link] import Gtk class LabelWindow([Link]): def __init__(self): [Link].__init__(self, title="Label Example") hbox = [Link](spacing=10)
26
Chapter 6. Label
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
hbox.set_homogeneous(False) vbox_left = [Link](orientation=[Link], spacing=10) vbox_left.set_homogeneous(False) vbox_right = [Link](orientation=[Link], spacing=10) vbox_right.set_homogeneous(False) hbox.pack_start(vbox_left, True, True, 0) hbox.pack_start(vbox_right, True, True, 0) label = [Link]("This is a normal label") vbox_left.pack_start(label, True, True, 0) label = [Link]() label.set_text("This is a left-justified label.\nWith multiple lines.") label.set_justify([Link]) vbox_left.pack_start(label, True, True, 0) label = [Link]( "This is a right-justified label.\nWith multiple lines.") label.set_justify([Link]) vbox_left.pack_start(label, True, True, 0) label = [Link]("This is an example of a line-wrapped label. It " "should not be taking up the entire " "width allocated to it, but automatically " "wraps the words to fit.\n" " It supports multiple paragraphs correctly, " "and correctly adds " "many extra spaces. ") label.set_line_wrap(True) vbox_right.pack_start(label, True, True, 0) label = [Link]("This is an example of a line-wrapped, filled label. " "It should be taking " "up the entire width allocated to it. " "Here is a sentence to prove " "my point. Here is another sentence. " "Here comes the sun, do de do de do.\n" " This is a new paragraph.\n" " This is another newer, longer, better " "paragraph. It is coming to an end, " "unfortunately.") label.set_line_wrap(True) label.set_justify([Link]) vbox_right.pack_start(label, True, True, 0) label = [Link]() label.set_markup("Text can be <small>small</small>, <big>big</big>, " "<b>bold</b>, <i>italic</i> and even point to " "somewhere in the <a href=\"[Link] " "title=\"Click to find out more\">internets</a>.") label.set_line_wrap(True) vbox_left.pack_start(label, True, True, 0) label = [Link].new_with_mnemonic( "_Press Alt + P to select button to the right") vbox_left.pack_start(label, True, True, 0) label.set_selectable(True)
6.1. Example
27
67 68 69 70 71 72 73 74 75 76 77
button = [Link](label="Click at your own risk") label.set_mnemonic_widget(button) vbox_right.pack_start(button, True, True, 0) [Link](hbox) window = LabelWindow() [Link]("delete-event", Gtk.main_quit) window.show_all() [Link]()
28
Chapter 6. Label
CHAPTER 7
Entry
Entry widgets allow the user to enter text. You can change the contents with the [Link].set_text() method, and read the current contents with the [Link].get_text() method. You can also limit the number of characters the Entry can take by calling [Link].set_max_length(). Occasionally you might want to make an Entry widget read-only. This can be done by passing False to the [Link].set_editable() method. Entry widgets can also be used to retrieve passwords from the user. It is common practice to hide the characters typed into the entry to prevent revealing the password to a third party. Calling [Link].set_visibility() with False will cause the text to be hidden. [Link] has the ability to display progress or activity information behind the text. This is similar to [Link] widget and is commonly found in web browsers to indicate how much of a page download has been completed. To make an entry display such information, use [Link].set_progress_fraction(), [Link].set_progress_pulse_step(), or [Link].progress_pulse(). Additionally, an Entry can show icons at either side of the entry. These icons can be activatable by clicking, can be set up as drag source and can have tooltips. To add an icon, use [Link].set_icon_from_stock() or one of the various other functions that set an icon from an icon name, a pixbuf, or icon theme. To set a tooltip on an icon, use [Link].set_icon_tooltip_text() or the corresponding function for markup.
7.1 Example
1 2 3
29
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
def __init__(self): [Link].__init__(self, title="Entry Demo") self.set_size_request(200, 100) self.timeout_id = None vbox = [Link](orientation=[Link], spacing=6) [Link](vbox) [Link] = [Link]() [Link].set_text("Hello World") vbox.pack_start([Link], True, True, 0) hbox = [Link](spacing=6) vbox.pack_start(hbox, True, True, 0) self.check_editable = [Link]("Editable") self.check_editable.connect("toggled", self.on_editable_toggled) self.check_editable.set_active(True) hbox.pack_start(self.check_editable, True, True, 0) self.check_visible = [Link]("Visible") self.check_visible.connect("toggled", self.on_visible_toggled) self.check_visible.set_active(True) hbox.pack_start(self.check_visible, True, True, 0) [Link] = [Link]("Pulse") [Link]("toggled", self.on_pulse_toggled) [Link].set_active(False) hbox.pack_start([Link], True, True, 0) [Link] = [Link]("Icon") [Link]("toggled", self.on_icon_toggled) [Link].set_active(False) hbox.pack_start([Link], True, True, 0) def on_editable_toggled(self, button): value = button.get_active() [Link].set_editable(value) def on_visible_toggled(self, button): value = button.get_active() [Link].set_visibility(value) def on_pulse_toggled(self, button): if button.get_active(): [Link].set_progress_pulse_step(0.2) # Call self.do_pulse every 100 ms self.timeout_id = GObject.timeout_add(100, self.do_pulse, None) else: # Dont call self.do_pulse anymore GObject.source_remove(self.timeout_id) self.timeout_id = None [Link].set_progress_pulse_step(0) def do_pulse(self, user_data): [Link].progress_pulse()
30
Chapter 7. Entry
62 63 64 65 66 67 68 69 70 71 72 73 74 75
return True def on_icon_toggled(self, button): if button.get_active(): stock_id = Gtk.STOCK_FIND else: stock_id = None [Link].set_icon_from_stock([Link], stock_id) win = EntryWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
7.1. Example
31
32
Chapter 7. Entry
CHAPTER 8
Button Widgets
8.1 Button
The Button widget is another commonly used widget. It is generally used to attach a function that is called when the button is pressed. The [Link] widget can hold any valid child widget. That is it can hold most any other standard [Link]. The most commonly used child is the [Link]. Usually, you want to connect to the buttons clicked signal which is emitted when the button has been pressed and released.
8.1.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
from [Link] import Gtk class ButtonWindow([Link]): def __init__(self): [Link].__init__(self, title="Button Demo") self.set_border_width(10) hbox = [Link](spacing=6) [Link](hbox) button = [Link]("Click Me") [Link]("clicked", self.on_click_me_clicked) hbox.pack_start(button, True, True, 0) button = [Link](stock=Gtk.STOCK_OPEN) [Link]("clicked", self.on_open_clicked)
33
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
hbox.pack_start(button, True, True, 0) button = [Link]("_Close", use_underline=True) [Link]("clicked", self.on_close_clicked) hbox.pack_start(button, True, True, 0) def on_click_me_clicked(self, button): print("\"Click me\" button was clicked") def on_open_clicked(self, button): print("\"Open\" button was clicked") def on_close_clicked(self, button): print("Closing application") Gtk.main_quit() win = ButtonWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
8.2 ToggleButton
A [Link] is very similar to a normal [Link], but when clicked they remain activated, or pressed, until clicked again. When the state of the button is changed, the toggled signal is emitted. To retrieve the state of the [Link], you can use the [Link].get_active() method. This returns True if the button is down. You can also set the toggle buttons state, with [Link].set_active(). Note that, if you do this, and the state actually changes, it causes the toggled signal to be emitted.
8.2.1 Example
1 2 3 4 5 6 7 8 9 10 11
from [Link] import Gtk class ToggleButtonWindow([Link]): def __init__(self): [Link].__init__(self, title="ToggleButton Demo") self.set_border_width(10) hbox = [Link](spacing=6) [Link](hbox)
34
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
button = [Link]("Button 1") [Link]("toggled", self.on_button_toggled, "1") hbox.pack_start(button, True, True, 0) button = [Link]("B_utton 2", use_underline=True) button.set_active(True) [Link]("toggled", self.on_button_toggled, "2") hbox.pack_start(button, True, True, 0) def on_button_toggled(self, button, name): if button.get_active(): state = "on" else: state = "off" print("Button", name, "was turned", state) win = ToggleButtonWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
8.3 CheckButton
[Link] inherits from [Link]. The only real difference between the two is [Link] appearance. A [Link] places a discrete [Link] next to a widget, (usually a [Link]). The toggled signal, [Link].set_active() and [Link].get_active() are inherited.
8.4 RadioButton
Like checkboxes, radio buttons also inherit from [Link], but these work in groups, and only one [Link] in a group can be selected at any one time. Therefore, a [Link] is one way of giving the user a choice from many options.
Radio buttons can be created with one of the static methods [Link].new_from_widget(), [Link].new_with_label_from_widget() or [Link].new_with_mnemonic_from_widget The rst radio button in a group will be created passing None as the group argument. In subsequent calls, the group you wish to add this button to should be passed as an argument. When rst run, the rst radio button in the group will be active. [Link].set_active() with True as rst argument. Changing a [Link] widget [Link].join_group(). group after its creation This can be changed by calling can be achieved by calling
8.3. CheckButton
35
8.4.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
from [Link] import Gtk class RadioButtonWindow([Link]): def __init__(self): [Link].__init__(self, title="RadioButton Demo") self.set_border_width(10) hbox = [Link](spacing=6) [Link](hbox) button1 = [Link].new_with_label_from_widget(None, "Button 1") [Link]("toggled", self.on_button_toggled, "1") hbox.pack_start(button1, False, False, 0) button2 = [Link].new_from_widget(button1) button2.set_label("Button 2") [Link]("toggled", self.on_button_toggled, "2") hbox.pack_start(button2, False, False, 0) button3 = [Link].new_with_mnemonic_from_widget(button1, "B_utton 3") [Link]("toggled", self.on_button_toggled, "3") hbox.pack_start(button3, False, False, 0) def on_button_toggled(self, button, name): if button.get_active(): state = "on" else: state = "off" print("Button", name, "was turned", state) win = RadioButtonWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
8.5 LinkButton
A [Link] is a [Link] with a hyperlink, similar to the one used by web browsers, which triggers an action when clicked. It is useful to show quick links to resources. The URI bound to a [Link] can be set specically using [Link].set_uri(), and retrieved using [Link].get_uri().
36
8.5.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
from [Link] import Gtk class LinkButtonWindow([Link]): def __init__(self): [Link].__init__(self, title="LinkButton Demo") self.set_border_width(10) button = [Link]("[Link] "Visit GTK+ Homepage") [Link](button) win = LinkButtonWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
8.6 SpinButton
A [Link] is an ideal way to allow the user to set the value of some attribute. Rather than having to directly type a number into a [Link], [Link] allows the user to click on one of two arrows to increment or decrement the displayed value. A value can still be typed in, with the bonus that it can be checked to ensure it is in a given range. The main properties of a [Link] are set through [Link]. To change the value that [Link] is showing, use [Link].set_value(). The value entered can either be an integer or oat, depending on your requirements, use [Link].get_value() or [Link].get_value_as_int(), respectively. When you allow the displaying of oat values in the spin button, you may wish to adjust the number of decimal spaces displayed by calling [Link].set_digits(). By default, [Link] accepts textual data. If you wish to limit this to numerical values only, call [Link].set_numeric() with True as argument. We can also adjust the update policy of [Link]. There are two options here; by default the spin button updates the value even if the data entered is invalid. Alternatively, we can set the policy to only update when the value entered is valid by calling [Link].set_update_policy().
8.6. SpinButton
37
8.6.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
from [Link] import Gtk class SpinButtonWindow([Link]): def __init__(self): [Link].__init__(self, title="SpinButton Demo") self.set_border_width(10) hbox = [Link](spacing=6) [Link](hbox) adjustment = [Link](0, 0, 100, 1, 10, 0) [Link] = [Link]() [Link].set_adjustment(adjustment) hbox.pack_start([Link], False, False, 0) check_numeric = [Link]("Numeric") check_numeric.connect("toggled", self.on_numeric_toggled) hbox.pack_start(check_numeric, False, False, 0) check_ifvalid = [Link]("If Valid") check_ifvalid.connect("toggled", self.on_ifvalid_toggled) hbox.pack_start(check_ifvalid, False, False, 0) def on_numeric_toggled(self, button): [Link].set_numeric(button.get_active()) def on_ifvalid_toggled(self, button): if button.get_active(): policy = [Link].IF_VALID else: policy = [Link] [Link].set_update_policy(policy) win = SpinButtonWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
8.7 Switch
A [Link] is a widget that has two states: on or off. The user can control which state should be active by clicking the empty area, or by dragging the handle.
38
You shouldnt use the activate signal on the [Link] which is an action signal and emitting it causes the switch to animate. Applications should never connect to this signal, but use the notify::active signal, see the example here below.
8.7.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
from [Link] import Gtk class SwitcherWindow([Link]): def __init__(self): [Link].__init__(self, title="Switch Demo") self.set_border_width(10) hbox = [Link](spacing=6) [Link](hbox) switch = [Link]() [Link]("notify::active", self.on_switch_activated) switch.set_active(False) hbox.pack_start(switch, True, True, 0) switch = [Link]() [Link]("notify::active", self.on_switch_activated) switch.set_active(True) hbox.pack_start(switch, True, True, 0) def on_switch_activated(self, switch, gparam): if switch.get_active(): state = "on" else: state = "off" print("Switch was turned", state) win = SwitcherWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
8.7. Switch
39
40
CHAPTER 9
ProgressBar
The [Link] is typically used to display the progress of a long running operation. It provides a visual clue that processing is underway. The [Link] can be used in two different modes: percentage mode and activity mode. When an application can determine how much work needs to take place (e.g. read a xed number of bytes from a le) and can monitor its progress, it can use the [Link] in percentage mode and the user sees a growing bar indicating the percentage of the work that has been completed. In this mode, the application is required to call [Link].set_fraction() periodically to update the progress bar, passing a oat between 0 and 1 to provide the new percentage value. When an application has no accurate way of knowing the amount of work to do, it can use activity mode, which shows activity by a block moving back and forth within the progress area. In this mode, the application is required to call [Link]() periodically to update the progress bar. You can also choose the step size, with the [Link].set_pulse_step() method. By default, [Link] is horizontal and left-to-right, but you can change it to a vertical progress bar by using the [Link].set_orientation() method. Changing the direction the progress bar grows can be done using [Link].set_inverted(). [Link] can also contain text which can be set by calling [Link].set_text() and [Link].set_show_text().
9.1 Example
41
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
from [Link] import Gtk, GObject class ProgressBarWindow([Link]): def __init__(self): [Link].__init__(self, title="ProgressBar Demo") self.set_border_width(10) vbox = [Link](orientation=[Link], spacing=6) [Link](vbox) [Link] = [Link]() vbox.pack_start([Link], True, True, 0) button = [Link]("Show text") [Link]("toggled", self.on_show_text_toggled) vbox.pack_start(button, True, True, 0) button = [Link]("Activity mode") [Link]("toggled", self.on_activity_mode_toggled) vbox.pack_start(button, True, True, 0) button = [Link]("Right to Left") [Link]("toggled", self.on_right_to_left_toggled) vbox.pack_start(button, True, True, 0) self.timeout_id = GObject.timeout_add(50, self.on_timeout, None) self.activity_mode = False def on_show_text_toggled(self, button): show_text = button.get_active() if show_text: text = "some text" else: text = None [Link].set_text(text) [Link].set_show_text(show_text) def on_activity_mode_toggled(self, button): self.activity_mode = button.get_active() if self.activity_mode: [Link]() else: [Link].set_fraction(0.0) def on_right_to_left_toggled(self, button): value = button.get_active() [Link].set_inverted(value) def on_timeout(self, user_data): """ Update value on the progress bar """ if self.activity_mode: [Link]() else: new_value = [Link].get_fraction() + 0.01
42
Chapter 9. ProgressBar
59 60 61 62 63 64 65 66 67 68 69 70 71
if new_value > 1: new_value = 0 [Link].set_fraction(new_value) # As this is a timeout function, return True so that it # continues to get called return True win = ProgressBarWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
9.1. Example
43
44
Chapter 9. ProgressBar
CHAPTER 10
Spinner
The [Link] displays an icon-size spinning animation. It is often used as an alternative to a GtkProgressBar for displaying indenite activity, instead of actual progress. To start the animation, use [Link](), to stop it use [Link]().
10.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from [Link] import Gtk class SpinnerAnimation([Link]): def __init__(self): [Link].__init__(self, title="Spinner") self.set_border_width(3) [Link]("delete-event", Gtk.main_quit) [Link] = [Link]("Start Spinning") [Link]("toggled", self.on_button_toggled) [Link].set_active(False) [Link] = [Link]() [Link] = [Link](3, 2, True) [Link]([Link], 0, 2, 0, 1) [Link]([Link], 0, 2, 2, 3)
45
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
[Link]([Link]) self.show_all() def on_button_toggled(self, button): if button.get_active(): [Link]() [Link].set_label("Stop Spinning") else: [Link]() [Link].set_label("Start Spinning")
46
CHAPTER 11
A [Link] and its associated widgets are an extremely powerful way of displaying data. They are used in conjunction with a [Link] or [Link] and provide a way of displaying and manipulating data in many ways, including: Automatically updates when data added, removed or edited Drag and drop support Sorting of data Support embedding widgets such as check boxes, progress bars, etc. Reorderable and resizable columns Filtering of data With the power and exibility of a [Link] comes complexity. It is often difcult for beginner developers to be able to utilize correctly due to the number of methods which are required.
This creates a list store with three columns, two string columns, and a oat column. Adding data to the model is done using [Link]() or [Link](), depending upon which sort of model was created.
treeiter = [Link](["The Art of Computer Programming", "Donald E. Knuth", 25.46])
47
Both methods return a [Link] instance, which points to the location of the newly inserted row. You can retrieve a [Link] by calling [Link].get_iter(). Once, data has been inserted you can retrieve or modify data using the tree iter and column index.
print store[treeiter][2] # Prints value of third column store[treeiter][2] = 42.15
As with Pythons built-in list object you can use len() to get the number of rows and use slices to retrieve or set values.
# Print number of rows print len(store) # Print all but first column print store[treeiter][1:] # Print last column print store[treeiter][-1] # Set first two columns store[treeiter][:2] = ["Donald Ervin Knuth", 41.99]
Keep in mind, that if you use [Link], the above code will only iterate over the rows of the top level, but not the children of the nodes. To iterate over all rows and its children, use the print_tree_store function.
def print_tree_store(store): rootiter = store.get_iter_first() print_rows(store, rootiter, "") def print_rows(store, treeiter, indent): while treeiter != None: print indent + str(store[treeiter][:]) if store.iter_has_child(treeiter): childiter = store.iter_children(treeiter) print_rows(store, childiter, indent + "\t") treeiter = store.iter_next(treeiter)
Apart from accessing values stored in a [Link] with the list-like method mentioned above, it is also possible to either use [Link] or [Link] instances. Both reference a particular row in a tree model. One can convert a path to an iterator by calling [Link].get_iter(). As [Link] contains only one level, i.e. nodes do not have any child nodes, a path is essentially the index of the row you want to access.
# Get path pointing to 6th row in list store path = [Link](5) treeiter = liststore.get_iter(path) # Get value at 2nd column value = liststore.get_value(treeiter, 1)
In the case of [Link], a path is a list of indexes or a string. The string form is a list of numbers separated by a colon. Each number refers to the offset at that level. Thus, the path 0 refers to the root node and the path 2:4 refers to the fth child of the third node.
# Get path pointing to 5th child of 3rd row in tree store path = [Link]([2, 4]) treeiter = treestore.get_iter(path)
48
Instances of [Link] can be accessed like lists, i.e. len(treepath) returns the depth of the item treepath is pointing to, and treepath[i] returns the childs index on the i-th level.
Once the [Link] widget has a model, it will need to know how to display the model. It does this with columns and cell renderers. Cell renderers are used to draw the data in the tree model in a way. There are a number of cell renderers that come with GTK+, for instance [Link], [Link] and [Link]. In addition, it is relatively easy to write a custom renderer yourself. A [Link] is the object that [Link] uses to organize the vertical columns in the tree view. It needs to know the name of the column to label for the user, what type of cell renderer to use, and which piece of data to retrieve from the model for a given row.
renderer = [Link]() column = [Link]("Title", renderer, text=0) tree.append_column(column)
To render more than one model column in a view column, you need to create a [Link] instance and use [Link].pack_start() to add the model columns to it.
column = [Link]("Title and Author") title = [Link]() author = [Link]() column.pack_start(title, True) column.pack_start(author, True) column.add_attribute(title, "text", 0) column.add_attribute(author, "text", 1) tree.append_column(column)
Then to retrieve data for the row selected: 11.2. The View 49
def on_tree_selection_changed(selection): model, treeiter = selection.get_selected() if treeiter != None: print "You selected", model[treeiter][0]
You can control what selections are allowed by calling [Link].set_mode(). [Link].get_selected() does not work if the selection mode is set to [Link], use [Link].get_selected_rows() instead.
11.4 Sorting
Sorting is an important feature for tree views and is supported by the standard tree models ([Link] and [Link]), which implement the [Link] interface.
The next step is to enable sorting. Note that the column_id (0 in the example) refers to the column of the model and not to the TreeViews column.
column.set_sort_column_id(0)
50
david David
First of all a comparison function is needed. This function gets two rows and has to return a negative integer if the rst one should come before the second one, zero if they are equal and a positive integer if the second one should come before the second one.
def compare(model, row1, row2, user_data): sort_column, _ = model.get_sort_column_id() value1 = model.get_value(row1, sort_column) value2 = model.get_value(row2, sort_column) if value1 < value2: return -1 elif value1 == value2: return 0 else: return 1
11.4. Sorting
51
52
CHAPTER 12
CellRenderers
[Link] widgets are used to display information within widgets such as the [Link] or [Link]. They work closely with the associated widgets and are very powerful, with lots of conguration options for displaying a large amount of data in different ways. There are seven [Link] widgets which can be used for different purposes: [Link] [Link] [Link] [Link] [Link] [Link] [Link] [Link]
12.1 CellRendererText
A [Link] renders a given text in its cell, using the font, color and style information provided by its properties. The text will be ellipsized if it is too long and the ellipsize property allows it. By default, text in [Link] widgets is not editable. This can be changed by setting the value of the editable property to True:
cell.set_property("editable", True)
You can then connect to the edited signal and update your [Link] accordingly.
53
12.1.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
from [Link] import Gtk class CellRendererTextWindow([Link]): def __init__(self): [Link].__init__(self, title="CellRendererText Example") self.set_default_size(200, 200) [Link] = [Link](str, str) [Link](["Fedora", "[Link] [Link](["Slackware", "[Link] [Link](["Sidux", "[Link] treeview = [Link](model=[Link]) renderer_text = [Link]() column_text = [Link]("Text", renderer_text, text=0) treeview.append_column(column_text) renderer_editabletext = [Link]() renderer_editabletext.set_property("editable", True) column_editabletext = [Link]("Editable Text", renderer_editabletext, text=1) treeview.append_column(column_editabletext) renderer_editabletext.connect("edited", self.text_edited) [Link](treeview) def text_edited(self, widget, path, text): [Link][path][1] = text
54
34 35 36 37 38
12.2 CellRendererToggle
[Link] renders a toggle button in a cell. The button is drawn as a radio- or checkbutton, depending on the radio property. When activated, it emits the toggled signal. As a [Link] can have two states, active and not active, you most likely want to bind the active property on the cell renderer to a boolean value in the model, thus causing the check button to reect the state of the model.
12.2.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from [Link] import Gtk class CellRendererToggleWindow([Link]): def __init__(self): [Link].__init__(self, title="CellRendererToggle Example") self.set_default_size(200, 200) [Link] = [Link](str, bool, bool) [Link](["Debian", False, True]) [Link](["OpenSuse", True, False]) [Link](["Fedora", False, False])
12.2. CellRendererToggle
55
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
treeview = [Link](model=[Link]) renderer_text = [Link]() column_text = [Link]("Text", renderer_text, text=0) treeview.append_column(column_text) renderer_toggle = [Link]() renderer_toggle.connect("toggled", self.on_cell_toggled) column_toggle = [Link]("Toggle", renderer_toggle, active=1) treeview.append_column(column_toggle) renderer_radio = [Link]() renderer_radio.set_radio(True) renderer_radio.connect("toggled", self.on_cell_radio_toggled) column_radio = [Link]("Radio", renderer_radio, active=2) treeview.append_column(column_radio) [Link](treeview) def on_cell_toggled(self, widget, path): [Link][path][1] = not [Link][path][1] def on_cell_radio_toggled(self, widget, path): selected_path = [Link](path) for row in [Link]: row[2] = ([Link] == selected_path) win = CellRendererToggleWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
12.3 CellRendererPixbuf
A [Link] can be used to render an image in a cell. It allows to render either a given [Link] (set via the pixbuf property) or a stock item (set via the stock-id property).
56
12.3.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
from [Link] import Gtk class CellRendererPixbufWindow([Link]): def __init__(self): [Link].__init__(self, title="CellRendererPixbuf Example") self.set_default_size(200, 200) [Link] = [Link](str, str) [Link](["New", Gtk.STOCK_NEW]) [Link](["Open", Gtk.STOCK_OPEN]) [Link](["Save", Gtk.STOCK_SAVE]) treeview = [Link](model=[Link]) renderer_text = [Link]() column_text = [Link]("Text", renderer_text, text=0) treeview.append_column(column_text) renderer_pixbuf = [Link]() column_pixbuf = [Link]("Image", renderer_pixbuf, stock_id=1) treeview.append_column(column_pixbuf) [Link](treeview) win = CellRendererPixbufWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
12.3. CellRendererPixbuf
57
12.4 CellRendererCombo
[Link] renders text in a cell like [Link] from which it is derived. But while the latter offers a simple entry to edit the text, [Link] offers a [Link] widget to edit the text. The values to display in the combo box are taken from the [Link] specied in the model property. The combo cell renderer takes care of adding a text cell renderer to the combo box and sets it to display the column specied by its text-column property. A [Link] can operate in two modes. It can be used with and without an associated [Link] widget, depending on the value of the has-entry property.
12.4.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from [Link] import Gtk class CellRendererComboWindow([Link]): def __init__(self): [Link].__init__(self, title="CellRendererCombo Example") self.set_default_size(200, 200) liststore_manufacturers = [Link](str) manufacturers = ["Sony", "LG", "Panasonic", "Toshiba", "Nokia", "Samsung"] for item in manufacturers: liststore_manufacturers.append([item]) self.liststore_hardware = [Link](str, str) self.liststore_hardware.append(["Television", "Samsung"]) self.liststore_hardware.append(["Mobile Phone", "LG"])
58
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
self.liststore_hardware.append(["DVD Player", "Sony"]) treeview = [Link](model=self.liststore_hardware) renderer_text = [Link]() column_text = [Link]("Text", renderer_text, text=0) treeview.append_column(column_text) renderer_combo = [Link]() renderer_combo.set_property("editable", True) renderer_combo.set_property("model", liststore_manufacturers) renderer_combo.set_property("text-column", 0) renderer_combo.set_property("has-entry", False) renderer_combo.connect("edited", self.on_combo_changed) column_combo = [Link]("Combo", renderer_combo, text=1) treeview.append_column(column_combo) [Link](treeview) def on_combo_changed(self, widget, path, text): self.liststore_hardware[path][1] = text win = CellRendererComboWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
12.5 CellRendererProgress
[Link] renders a numeric value as a progress bar in a cell. Additionally, it can display a text on top of the progress bar. The percentage value of the progress bar can be modied by changing the value property. Similar to [Link], you can enable the activity mode by incrementing the pulse property instead of the value property.
12.5. CellRendererProgress
59
12.5.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
from [Link] import Gtk, GObject class CellRendererProgressWindow([Link]): def __init__(self): [Link].__init__(self, title="CellRendererProgress Example") self.set_default_size(200, 200) [Link] = [Link](str, int, bool) self.current_iter = [Link](["Sabayon", 0, False]) [Link](["Zenwalk", 0, False]) [Link](["SimplyMepis", 0, False]) treeview = [Link](model=[Link]) renderer_text = [Link]() column_text = [Link]("Text", renderer_text, text=0) treeview.append_column(column_text) renderer_progress = [Link]() column_progress = [Link]("Progress", renderer_progress, value=1, inverted=2) treeview.append_column(column_progress) renderer_toggle = [Link]() renderer_toggle.connect("toggled", self.on_inverted_toggled) column_toggle = [Link]("Inverted", renderer_toggle, active=2) treeview.append_column(column_toggle) [Link](treeview)
60
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
self.timeout_id = GObject.timeout_add(100, self.on_timeout, None) def on_inverted_toggled(self, widget, path): [Link][path][2] = not [Link][path][2] def on_timeout(self, user_data): new_value = [Link][self.current_iter][1] + 1 if new_value > 100: self.current_iter = [Link].iter_next(self.current_iter) if self.current_iter == None: self.reset_model() new_value = [Link][self.current_iter][1] + 1 [Link][self.current_iter][1] = new_value return True def reset_model(self): for row in [Link]: row[1] = 0 self.current_iter = [Link].get_iter_first() win = CellRendererProgressWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
12.6 CellRendererSpin
[Link] renders text in a cell like [Link] from which it is derived. But while the latter offers a simple entry to edit the text, [Link] offers a [Link] widget. Of course, that means that the text has to be parseable as a oating point number. The range of the spinbutton is taken from the adjustment property of the cell renderer, which can be set explicitly or mapped to a column in the tree model, like all properties of cell renders. [Link] also has properties for the climb rate and the number of digits to display.
12.6. CellRendererSpin
61
12.6.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
from [Link] import Gtk class CellRendererSpinWindow([Link]): def __init__(self): [Link].__init__(self, title="CellRendererSpin Example") self.set_default_size(200, 200) [Link] = [Link](str, int) [Link](["Oranges", 5]) [Link](["Apples", 4]) [Link](["Bananas", 2]) treeview = [Link](model=[Link]) renderer_text = [Link]() column_text = [Link]("Fruit", renderer_text, text=0) treeview.append_column(column_text) renderer_spin = [Link]() renderer_spin.connect("edited", self.on_amount_edited) renderer_spin.set_property("editable", True) adjustment = [Link](0, 0, 100, 1, 10, 0) renderer_spin.set_property("adjustment", adjustment) column_spin = [Link]("Amount", renderer_spin, text=1) treeview.append_column(column_spin) [Link](treeview) def on_amount_edited(self, widget, path, value):
62
34 35 36 37 38 39
12.6. CellRendererSpin
63
64
CHAPTER 13
ComboBox
A [Link] allows for the selection of an item from a dropdown menu. They are preferable to having many radio buttons on screen as they take up less room. If appropriate, it can show extra information about each item, such as text, a picture, a checkbox, or a progress bar. [Link] is very similar to [Link], as both use the model-view pattern; the list of valid choices is specied in the form of a tree model, and the display of the choices can be adapted to the data in the model by using cell renderers. If the combo box contains a large number of items, it may be better to display them in a grid rather than a list. This can be done by calling [Link].set_wrap_width(). The [Link] widget usually restricts the user to the available choices, but it can optionally have an [Link], allowing the user to enter arbitrary text if none of the available choices are suitable. To do this, use one of the static methods [Link].new_with_entry() or [Link].new_with_model_and_entry() to create an [Link] instance. For a simple list of textual choices, the model-view API of [Link] can be a bit overwhelming. In this case, [Link] offers a simple alternative. Both [Link] and [Link] can contain an entry.
13.1 Example
1 2 3 4
65
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
def __init__(self): [Link].__init__(self, title="ComboBox Example") self.set_border_width(10) name_store = [Link](int, str) name_store.append([1, "Billy Bob"]) name_store.append([11, "Billy Bob Junior"]) name_store.append([12, "Sue Bob"]) name_store.append([2, "Joey Jojo"]) name_store.append([3, "Rob McRoberts"]) name_store.append([31, "Xavier McRoberts"]) vbox = [Link](orientation=[Link], spacing=6) name_combo = [Link].new_with_model_and_entry(name_store) name_combo.connect("changed", self.on_name_combo_changed) name_combo.set_entry_text_column(1) vbox.pack_start(name_combo, False, False, 0) country_store = [Link](str) countries = ["Austria", "Brazil", "Belgium", "France", "Germany", "Switzerland", "United Kingdom", "United States of America", "Uruguay"] for country in countries: country_store.append([country]) country_combo = [Link].new_with_model(country_store) country_combo.connect("changed", self.on_country_combo_changed) renderer_text = [Link]() country_combo.pack_start(renderer_text, True) country_combo.add_attribute(renderer_text, "text", 0) vbox.pack_start(country_combo, False, False, True) currencies = ["Euro", "US Dollars", "British Pound", "Japanese Yen", "Russian Ruble", "Mexican peso", "Swiss franc"] currency_combo = [Link]() currency_combo.set_entry_text_column(0) currency_combo.connect("changed", self.on_currency_combo_changed) for currency in currencies: currency_combo.append_text(currency) vbox.pack_start(currency_combo, False, False, 0) [Link](vbox) def on_name_combo_changed(self, combo): tree_iter = combo.get_active_iter() if tree_iter != None: model = combo.get_model() row_id, name = model[tree_iter][:2] print("Selected: ID=%d , name=%s" % (row_id, name)) else: entry = combo.get_child() print("Entered: %s" % entry.get_text()) def on_country_combo_changed(self, combo): tree_iter = combo.get_active_iter()
66
63 64 65 66 67 68 69 70 71 72 73 74 75 76
if tree_iter != None: model = combo.get_model() country = model[tree_iter][0] print("Selected: country=%s" % country) def on_currency_combo_changed(self, combo): text = combo.get_active_text() if text != None: print("Selected: currency=%s" % text) win = ComboBoxWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
13.1. Example
67
68
CHAPTER 14
IconView
A [Link] is a widget that displays a collection of icons in a grid view. It supports features such as drag and drop, multiple selections and item reordering. Similarly to [Link], [Link] uses a [Link] for its model. Instead of using cell renderers, [Link] requires that one of the columns in its [Link] contains [Link] objects. [Link] supports numerous selection modes to allow for either selecting multiple icons at a time, restricting selections to just one item or disallowing selecting items completely. To specify a selection mode, the [Link].set_selection_mode() method is used with one of the [Link] selection modes.
14.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
from [Link] import Gtk from [Link] import Pixbuf icons = ["gtk-cut", "gtk-paste", "gtk-copy"] class IconViewWindow([Link]): def __init__(self): [Link].__init__(self) self.set_default_size(200, 200) liststore = [Link](Pixbuf, str) iconview = [Link]() iconview.set_model(liststore) iconview.set_pixbuf_column(0) iconview.set_text_column(1) for icon in icons: pixbuf = [Link].get_default().load_icon(icon, 64, 0) [Link]([pixbuf, "Label"]) [Link](iconview) win = IconViewWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
69
70
CHAPTER 15
The [Link] widget can be used to display and edit large amounts of formatted text. Like the [Link], it has a model/view design. In this case the [Link] is the model which represents the text being edited. This allows two or more [Link] widgets to share the same [Link], and allows those text buffers to be displayed slightly differently. Or you could maintain several text buffers and choose to display each one at different times in the same [Link] widget.
71
Many methods exist to retrieve a [Link]. For instance, [Link].get_start_iter() returns an iterator pointing to the rst position in the text buffer, whereas [Link].get_end_iter() returns an iterator pointing past the last valid character. Retrieving the bounds of the selected text can be achieved by calling [Link].get_selection_bounds(). To insert text at a specic position use [Link](). Another useful method is [Link].insert_at_cursor() which inserts text wherever the cursor may be currently positioned. To remove portions of the text buffer use [Link](). In addition, [Link] can be used to locate textual matches in the buffer using [Link].forward_search() and [Link].backward_search(). The start and end iters are used as the starting point of the search and move forwards/backwards depending on requirements.
15.3 Tags
Text in a buffer can be marked with tags. A tag is an attribute that can be applied to some range of text. For example, a tag might be called bold and make the text inside the tag bold. However, the tag concept is more general than that; tags dont have to affect appearance. They can instead affect the behaviour of mouse and key presses, lock a range of text so the user cant edit it, or countless other things. A tag is represented by a [Link] object. One [Link] can be applied to any number of text ranges in any number of buffers. Each tag is stored in a [Link]. A tag table denes a set of tags that can be used together. Each buffer has one tag table associated with it; only tags from that tag table can be used with the buffer. A single tag table can be shared between multiple buffers, however. To specify that some text in the buffer should have specic formatting, you must dene a tag to hold that formatting information, and then apply that tag to the region of text using [Link].create_tag() and [Link].apply_tag():
tag = textbuffer.create_tag("orange_bg", background="orange") textbuffer.apply_tag(tag, start_iter, end_iter)
The following are some of the common styles applied to text: Background colour (foreground property) Foreground colour (background property) Underline (underline property) Bold (weight property) Italics (style property) Strikethrough (strikethrough property) Justication (justication property) Size (size and size-points properties) Text wrapping (wrap-mode property) You can also delete particular tags later using [Link].remove_tag() or delete all tags in a given region by calling [Link].remove_all_tags().
72
15.4 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from [Link] import Gtk, Pango class SearchDialog([Link]): def __init__(self, parent): [Link].__init__(self, "Search", parent, [Link], buttons=( Gtk.STOCK_FIND, [Link], Gtk.STOCK_CANCEL, [Link])) box = self.get_content_area() label = [Link]("Insert text you want to search for:") [Link](label) [Link] = [Link]() [Link]([Link]) self.show_all()
15.4. Example
73
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
class TextViewWindow([Link]): def __init__(self): [Link].__init__(self, title="TextView Example") self.set_default_size(-1, 350) [Link] = [Link]() [Link]([Link]) self.create_textview() self.create_toolbar() self.create_buttons() def create_toolbar(self): toolbar = [Link]() [Link](toolbar, 0, 0, 3, 1) button_bold = [Link].new_from_stock(Gtk.STOCK_BOLD) [Link](button_bold, 0) button_italic = [Link].new_from_stock(Gtk.STOCK_ITALIC) [Link](button_italic, 1) button_underline = [Link].new_from_stock(Gtk.STOCK_UNDERLINE) [Link](button_underline, 2) button_bold.connect("clicked", self.on_button_clicked, self.tag_bold) button_italic.connect("clicked", self.on_button_clicked, self.tag_italic) button_underline.connect("clicked", self.on_button_clicked, self.tag_underline) [Link]([Link](), 3) radio_justifyleft = [Link]() radio_justifyleft.set_stock_id(Gtk.STOCK_JUSTIFY_LEFT) [Link](radio_justifyleft, 4) radio_justifycenter = [Link].new_with_stock_from_widget( radio_justifyleft, Gtk.STOCK_JUSTIFY_CENTER) [Link](radio_justifycenter, 5) radio_justifyright = [Link].new_with_stock_from_widget( radio_justifyleft, Gtk.STOCK_JUSTIFY_RIGHT) [Link](radio_justifyright, 6) radio_justifyfill = [Link].new_with_stock_from_widget( radio_justifyleft, Gtk.STOCK_JUSTIFY_FILL) [Link](radio_justifyfill, 7) radio_justifyleft.connect("toggled", self.on_justify_toggled, [Link]) radio_justifycenter.connect("toggled", self.on_justify_toggled, [Link]) radio_justifyright.connect("toggled", self.on_justify_toggled, [Link])
74
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
radio_justifyfill.connect("toggled", self.on_justify_toggled, [Link]) [Link]([Link](), 8) button_clear = [Link].new_from_stock(Gtk.STOCK_CLEAR) button_clear.connect("clicked", self.on_clear_clicked) [Link](button_clear, 9) [Link]([Link](), 10) button_search = [Link].new_from_stock(Gtk.STOCK_FIND) button_search.connect("clicked", self.on_search_clicked) [Link](button_search, 11) def create_textview(self): scrolledwindow = [Link]() scrolledwindow.set_hexpand(True) scrolledwindow.set_vexpand(True) [Link](scrolledwindow, 0, 1, 3, 1) [Link] = [Link]() [Link] = [Link].get_buffer() [Link].set_text("This is some text inside of a [Link]. " + "Select text and click one of the buttons bold, italic, " + "or underline to modify the text accordingly.") [Link]([Link]) self.tag_bold = [Link].create_tag("bold", weight=[Link]) self.tag_italic = [Link].create_tag("italic", style=[Link]) self.tag_underline = [Link].create_tag("underline", underline=[Link]) self.tag_found = [Link].create_tag("found", background="yellow") def create_buttons(self): check_editable = [Link]("Editable") check_editable.set_active(True) check_editable.connect("toggled", self.on_editable_toggled) [Link](check_editable, 0, 2, 1, 1) check_cursor = [Link]("Cursor Visible") check_cursor.set_active(True) check_editable.connect("toggled", self.on_cursor_toggled) [Link].attach_next_to(check_cursor, check_editable, [Link], 1, 1) radio_wrapnone = [Link].new_with_label_from_widget(None, "No Wrapping") [Link](radio_wrapnone, 0, 3, 1, 1) radio_wrapchar = [Link].new_with_label_from_widget( radio_wrapnone, "Character Wrapping") [Link].attach_next_to(radio_wrapchar, radio_wrapnone, [Link], 1, 1)
15.4. Example
75
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
radio_wrapword = [Link].new_with_label_from_widget( radio_wrapnone, "Word Wrapping") [Link].attach_next_to(radio_wrapword, radio_wrapchar, [Link], 1, 1) radio_wrapnone.connect("toggled", self.on_wrap_toggled, [Link]) radio_wrapchar.connect("toggled", self.on_wrap_toggled, [Link]) radio_wrapword.connect("toggled", self.on_wrap_toggled, [Link]) def on_button_clicked(self, widget, tag): bounds = [Link].get_selection_bounds() if len(bounds) != 0: start, end = bounds [Link].apply_tag(tag, start, end) def on_clear_clicked(self, widget): start = [Link].get_start_iter() end = [Link].get_end_iter() [Link].remove_all_tags(start, end) def on_editable_toggled(self, widget): [Link].set_editable(widget.get_active()) def on_cursor_toggled(self, widget): [Link].set_cursor_visible(widget.get_active()) def on_wrap_toggled(self, widget, mode): [Link].set_wrap_mode(mode) def on_justify_toggled(self, widget, justification): [Link].set_justification(justification) def on_search_clicked(self, widget): dialog = SearchDialog(self) response = [Link]() if response == [Link]: cursor_mark = [Link].get_insert() start = [Link].get_iter_at_mark(cursor_mark) if start.get_offset() == [Link].get_char_count(): start = [Link].get_start_iter() self.search_and_mark([Link].get_text(), start) [Link]() def search_and_mark(self, text, start): end = [Link].get_end_iter() match = start.forward_search(text, 0, end) if match != None: match_start, match_end = match [Link].apply_tag(self.tag_found, match_start, match_end) self.search_and_mark(text, match_end) win = TextViewWindow()
76
15.4. Example
77
78
CHAPTER 16
Menus
GTK+ comes with two different types of menus, [Link] and [Link]. [Link] is a standard menu bar which contains one or more [Link] instances or one of its subclasses. [Link] widgets are used for quick accessibility to commonly used functions of an application. Examples include creating a new document, printing a page or undoing an operation. It contains one or more instances of [Link] or one of its subclasses.
16.1 Actions
Although, there are specic APIs to create menus and toolbars, you should use [Link] and create [Link] instances. Actions are organised into groups. A [Link] is essentially a map from names to [Link] objects. All actions that would make sense to use in a particular context should be in a single group. Multiple action groups may be used for a particular user interface. In fact, it is expected that most non-trivial applications will make use of multiple groups. For example, in an application that can edit multiple documents, one group holding global actions (e.g. quit, about, new), and one group per document holding actions that act on that document (eg. save, cut/copy/paste, etc). Each windows menus would be constructed from a combination of two action groups. Different classes representing different types of actions exist: [Link]: An action which can be triggered by a menu or toolbar item [Link]: An action which can be toggled between two states [Link]: An action of which only one in a group can be active [Link]: An action of which represents a list of recently used les Actions represent operations that the user can be perform, along with some information how it should be presented in the interface, including its name (not for display), its label (for display), an accelerator, whether a label indicates a stock item, a tooltip, as well as the callback that is called when the action gets activated. You can create actions by either calling one of the constructors directly and adding them to a [Link] by calling [Link].add_action() or [Link].add_action_with_accel(), or by calling one of the convenience functions: [Link].add_actions(), [Link].add_toggle_actions() [Link].add_radio_actions(). Note that you must specify actions for sub menus as well as menu items.
79
16.2 UI Manager
[Link] provides an easy way of creating menus and toolbars using an XML-like description. First of all, you should add the [Link] to the UI Manager with [Link].insert_action_group(). At this point is also a good idea to tell the parent window to respond to the specied keyboard shortcuts, by using [Link].get_accel_group() and [Link].add_accel_group(). Then, you can dene the actual visible layout of the menus and toolbars, and add the UI layout. This ui string uses an XML format, in which you should mention the names of the actions that you have already created. Remember that these names are just the identiers that we used when creating the actions. They are not the text that the user will see in the menus and toolbars. We provided those human-readable names when we created the actions. Finally, you retrieve the root widget with [Link].get_widget() and add the widget to a container such as [Link].
16.3 Example
1 2 3 4 5 6 7 8 9 10 11 12 13
from [Link] import Gtk, Gdk UI_INFO = """ <ui> <menubar name=MenuBar> <menu action=FileMenu> <menu action=FileNew> <menuitem action=FileNewStandard /> <menuitem action=FileNewFoo /> <menuitem action=FileNewGoo /> </menu> <separator /> <menuitem action=FileQuit />
80
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
</menu> <menu action=EditMenu> <menuitem action=EditCopy /> <menuitem action=EditPaste /> <menuitem action=EditSomething /> </menu> <menu action=ChoicesMenu> <menuitem action=ChoiceOne/> <menuitem action=ChoiceTwo/> <separator /> <menuitem action=ChoiceThree/> </menu> </menubar> <toolbar name=ToolBar> <toolitem action=FileNewStandard /> <toolitem action=FileQuit /> </toolbar> <popup name=PopupMenu> <menuitem action=EditCopy /> <menuitem action=EditPaste /> <menuitem action=EditSomething /> </popup> </ui> """ class MenuExampleWindow([Link]): def __init__(self): [Link].__init__(self, title="Menu Example") self.set_default_size(200, 200) action_group = [Link]("my_actions") self.add_file_menu_actions(action_group) self.add_edit_menu_actions(action_group) self.add_choices_menu_actions(action_group) uimanager = self.create_ui_manager() uimanager.insert_action_group(action_group) menubar = uimanager.get_widget("/MenuBar") box = [Link](orientation=[Link]) box.pack_start(menubar, False, False, 0) toolbar = uimanager.get_widget("/ToolBar") box.pack_start(toolbar, False, False, 0) eventbox = [Link]() [Link]("button-press-event", self.on_button_press_event) box.pack_start(eventbox, True, True, 0) label = [Link]("Right-click to see the popup menu.") [Link](label) [Link] = uimanager.get_widget("/PopupMenu")
16.3. Example
81
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
[Link](box) def add_file_menu_actions(self, action_group): action_filemenu = [Link]("FileMenu", "File", None, None) action_group.add_action(action_filemenu) action_filenewmenu = [Link]("FileNew", None, None, Gtk.STOCK_NEW) action_group.add_action(action_filenewmenu) action_new = [Link]("FileNewStandard", "_New", "Create a new file", Gtk.STOCK_NEW) action_new.connect("activate", self.on_menu_file_new_generic) action_group.add_action_with_accel(action_new, None) action_group.add_actions([ ("FileNewFoo", None, "New Foo", None, "Create new foo", self.on_menu_file_new_generic), ("FileNewGoo", None, "_New Goo", None, "Create new goo", self.on_menu_file_new_generic), ]) action_filequit = [Link]("FileQuit", None, None, Gtk.STOCK_QUIT) action_filequit.connect("activate", self.on_menu_file_quit) action_group.add_action(action_filequit) def add_edit_menu_actions(self, action_group): action_group.add_actions([ ("EditMenu", None, "Edit"), ("EditCopy", Gtk.STOCK_COPY, None, None, None, self.on_menu_others), ("EditPaste", Gtk.STOCK_PASTE, None, None, None, self.on_menu_others), ("EditSomething", None, "Something", "<control><alt>S", None, self.on_menu_others) ]) def add_choices_menu_actions(self, action_group): action_group.add_action([Link]("ChoicesMenu", "Choices", None, None)) action_group.add_radio_actions([ ("ChoiceOne", None, "One", None, None, 1), ("ChoiceTwo", None, "Two", None, None, 2) ], 1, self.on_menu_choices_changed) three = [Link]("ChoiceThree", "Three", None, None) [Link]("toggled", self.on_menu_choices_toggled) action_group.add_action(three) def create_ui_manager(self): uimanager = [Link]() # Throws exception if something went wrong uimanager.add_ui_from_string(UI_INFO) # Add the accelerator group to the toplevel window accelgroup = uimanager.get_accel_group() self.add_accel_group(accelgroup)
82
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
return uimanager def on_menu_file_new_generic(self, widget): print("A File|New menu item was selected.") def on_menu_file_quit(self, widget): Gtk.main_quit() def on_menu_others(self, widget): print("Menu item " + widget.get_name() + " was selected") def on_menu_choices_changed(self, widget, current): print(current.get_name() + " was selected.") def on_menu_choices_toggled(self, widget): if widget.get_active(): print(widget.get_name() + " activated") else: print(widget.get_name() + " deactivated") def on_button_press_event(self, widget, event): # Check if right mouse button was preseed if [Link] == [Link].BUTTON_PRESS and [Link] == 3: [Link](None, None, None, None, [Link], [Link]) return True # event has been handled window = MenuExampleWindow() [Link]("delete-event", Gtk.main_quit) window.show_all() [Link]()
16.3. Example
83
84
CHAPTER 17
Dialogs
Dialog windows are very similar to standard windows, and are used to provide or retrieve information from the user. They are often used to provide a preferences window, for example. The major difference a dialog has is some prepacked widgets which layout the dialog automatically. From there, we can simply add labels, buttons, check buttons, etc. Another big difference is the handling of responses to control how the application should behave after the dialog has been interacted with. There are several derived Dialog classes which you might nd useful. [Link] is used for most simple notications. But at other times you might need to derive your own dialog class to provide more complex functionality.
85
17.1.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
from [Link] import Gtk class DialogExample([Link]): def __init__(self, parent): [Link].__init__(self, "My Dialog", parent, 0, (Gtk.STOCK_CANCEL, [Link], Gtk.STOCK_OK, [Link])) self.set_default_size(150, 100) label = [Link]("This is a dialog to display additional information") box = self.get_content_area() [Link](label) self.show_all() class DialogWindow([Link]): def __init__(self): [Link].__init__(self, title="Dialog Example") self.set_border_width(6) button = [Link]("Open dialog") [Link]("clicked", self.on_button_clicked) [Link](button) def on_button_clicked(self, widget): dialog = DialogExample(self) response = [Link]() if response == [Link]: print("The OK button was clicked") elif response == [Link]: print("The Cancel button was clicked") [Link]() win = DialogWindow() [Link]("delete-event", Gtk.main_quit) win.show_all()
86
44
[Link]()
17.2 MessageDialog
[Link] is a convenience class, used to create simple, standard message dialogs, with a message, an icon, and buttons for user response You can specify the type of message and the text in the [Link] constructor, as well as specifying standard buttons. In some dialogs which require some further explanation of what has happened, a secondary text can be added. In this case, the primary message entered when creating the message dialog is made bigger and set to bold text. The secondary message can be set by calling [Link].format_secondary_text().
17.2.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
from [Link] import Gtk class MessageDialogWindow([Link]): def __init__(self): [Link].__init__(self, title="MessageDialog Example") box = [Link](spacing=6) [Link](box) button1 = [Link]("Information") [Link]("clicked", self.on_info_clicked) [Link](button1) button2 = [Link]("Error") [Link]("clicked", self.on_error_clicked) [Link](button2) button3 = [Link]("Warning") [Link]("clicked", self.on_warn_clicked) [Link](button3) button4 = [Link]("Question") [Link]("clicked", self.on_question_clicked)
17.2. MessageDialog
87
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
[Link](button4) def on_info_clicked(self, widget): dialog = [Link](self, 0, [Link], [Link], "This is an INFO MessageDialog") dialog.format_secondary_text( "And this is the secondary text that explains things.") [Link]() print("INFO dialog closed") [Link]() def on_error_clicked(self, widget): dialog = [Link](self, 0, [Link], [Link], "This is an ERROR MessageDialog") dialog.format_secondary_text( "And this is the secondary text that explains things.") [Link]() print("ERROR dialog closed") [Link]() def on_warn_clicked(self, widget): dialog = [Link](self, 0, [Link], [Link].OK_CANCEL, "This is an WARNING MessageDialog") dialog.format_secondary_text( "And this is the secondary text that explains things.") response = [Link]() if response == [Link]: print("WARN dialog closed by clicking OK button") elif response == [Link]: print("WARN dialog closed by clicking CANCEL button") [Link]() def on_question_clicked(self, widget): dialog = [Link](self, 0, [Link], [Link].YES_NO, "This is an QUESTION MessageDialog") dialog.format_secondary_text( "And this is the secondary text that explains things.") response = [Link]() if response == [Link]: print("QUESTION dialog closed by clicking YES button") elif response == [Link]: print("QUESTION dialog closed by clicking NO button") [Link]() win = MessageDialogWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
88
17.3 FileChooserDialog
The [Link] is suitable for use with File/Open or File/Save menu items. You can use all of the [Link] methods on the le chooser dialog as well as those for [Link]. When creating a [Link] you have to dene the dialogs purpose: To select a le for opening, as for a File/Open command, use [Link] To save a le for the rst time, as for a File/Save command, use [Link], and suggest a name such as Untitled with [Link].set_current_name(). To save a le under a different use [Link], [Link].set_filename(). name, and as set for a File/Save As command, the existing lename with
To choose a folder instead of a le, use [Link].SELECT_FOLDER. [Link] inherits from [Link], so buttons have response IDs such as [Link] and [Link] which can be specied in the [Link] constructor. In contrast to [Link], you can not use custom response codes with [Link]. It expects that at least one button will have of the following response IDs: [Link] [Link] [Link] [Link] When the user is nished selecting les, your program can get the selected names either as lenames ([Link].get_filename()) or as URIs ([Link].get_uri()). By default, [Link] only allows a single le to be selected at a time. To enable multiple les to be selected, use [Link].set_select_multiple(). Retrieving a list of selected les is possible with either [Link].get_filenames() or [Link].get_uris(). [Link] also supports a variety of options which make the les and folders more congurable and accessible. [Link].set_local_only(): Only local les can be selected. [Link].show_hidden(): Hidden les and folders are displayed. [Link].set_do_overwrite_confirmation(): If the le chooser was congured in [Link] mode, it will present a conrmation dialog if the user types a le name that already exists. Furthermore, you can specify which kind of les are displayed by creating [Link] objects and calling [Link].add_filter(). The user can then select one of the added lters from a combo box at the bottom of the le chooser.
17.3. FileChooserDialog
89
17.3.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
from [Link] import Gtk class FileChooserWindow([Link]): def __init__(self): [Link].__init__(self, title="FileChooser Example") box = [Link](spacing=6) [Link](box) button1 = [Link]("Choose File") [Link]("clicked", self.on_file_clicked) [Link](button1) button2 = [Link]("Choose Folder") [Link]("clicked", self.on_folder_clicked) [Link](button2) def on_file_clicked(self, widget): dialog = [Link]("Please choose a file", self, [Link], (Gtk.STOCK_CANCEL, [Link],
90
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
Gtk.STOCK_OPEN, [Link])) self.add_filters(dialog) response = [Link]() if response == [Link]: print("Open clicked") print("File selected: " + dialog.get_filename()) elif response == [Link]: print("Cancel clicked") [Link]() def add_filters(self, dialog): filter_text = [Link]() filter_text.set_name("Text files") filter_text.add_mime_type("text/plain") dialog.add_filter(filter_text) filter_py = [Link]() filter_py.set_name("Python files") filter_py.add_mime_type("text/x-python") dialog.add_filter(filter_py) filter_any = [Link]() filter_any.set_name("Any files") filter_any.add_pattern("*") dialog.add_filter(filter_any) def on_folder_clicked(self, widget): dialog = [Link]("Please choose a folder", self, [Link].SELECT_FOLDER, (Gtk.STOCK_CANCEL, [Link], "Select", [Link])) dialog.set_default_size(800, 400) response = [Link]() if response == [Link]: print("Select clicked") print("Folder selected: " + dialog.get_filename()) elif response == [Link]: print("Cancel clicked") [Link]() win = FileChooserWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
17.3. FileChooserDialog
91
92
CHAPTER 18
Clipboard
[Link] provides a storage area for a variety of data, including text and images. Using a clipboard allows this data to be shared between applications through actions such as copying, cutting, and pasting. These actions are usually done in three ways: using keyboard shortcuts, using a [Link], and connecting the functions to [Link] widgets. There are multiple clipboard selections for different purposes. In most circumstances, the selection named CLIPBOARD is used for everyday copying and pasting. PRIMARY is another common selection which stores text selected by the user with the cursor.
18.1 Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from [Link] import Gtk, Gdk class ClipboardWindow([Link]): def __init__(self): [Link].__init__(self, title="Clipboard Example") table = [Link](3, 2) [Link] = [Link](Gdk.SELECTION_CLIPBOARD) [Link] = [Link]() [Link] = [Link].new_from_stock(Gtk.STOCK_STOP, [Link]) button_copy_text = [Link]("Copy Text") button_paste_text = [Link]("Paste Text") button_copy_image = [Link]("Copy Image") button_paste_image = [Link]("Paste Image")
93
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
[Link]([Link], 0, 1, 0, 1) [Link]([Link], 0, 1, 1, 2) [Link](button_copy_text, 1, 2, 0, 1) [Link](button_paste_text, 2, 3, 0, 1) [Link](button_copy_image, 1, 2, 1, 2) [Link](button_paste_image, 2, 3, 1, 2) button_copy_text.connect("clicked", self.copy_text) button_paste_text.connect("clicked", self.paste_text) button_copy_image.connect("clicked", self.copy_image) button_paste_image.connect("clicked", self.paste_image) [Link](table) def copy_text(self, widget): [Link].set_text([Link].get_text(), -1) def paste_text(self, widget): text = [Link].wait_for_text() if text != None: [Link].set_text(text) else: print("No text on the clipboard.") def copy_image(self, widget): if [Link].get_storage_type() == [Link]: [Link].set_image([Link].get_pixbuf()) else: print("No image has been pasted yet.") def paste_image(self, widget): image = [Link].wait_for_image() if image != None: [Link].set_from_pixbuf(image)
94
CHAPTER 19
Note: Versions of PyGObject < 3.0.3 contain a bug which does not allow drag and drop to function correctly. Therefore a version of PyGObject >= 3.0.3 is required for the following examples to work. Setting up drag and drop between widgets consists of selecting a drag source (the widget which the user starts the drag from) with the [Link].drag_source_set() method, selecting a drag destination (the widget which the user drops onto) with the [Link].drag_dest_set() method and then handling the relevant signals on both widgets. Instead of using [Link].drag_source_set() and [Link].drag_dest_set() some specialised widgets require the use of specic functions (such as [Link] and [Link]). A basic drag and drop only requires the source to connect to the drag-data-get signal and the destination to connect to the drag-data-received signal. More complex things such as specic drop areas and custom drag icons will require you to connect to additional signals and interact with the [Link] object it supplies. In order to transfer data between the source and destination, you must interact with the [Link] variable supplied in the drag-data-get and drag-data-received signals using the [Link] get and set methods.
95
19.4 Example
1 2 3 4 5
from [Link] import Gtk, Gdk, GdkPixbuf (TARGET_ENTRY_TEXT, TARGET_ENTRY_PIXBUF) = range(2) (COLUMN_TEXT, COLUMN_PIXBUF) = range(2)
96
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
DRAG_ACTION = [Link] class DragDropWindow([Link]): def __init__(self): [Link].__init__(self, title="Drag and Drop Demo") vbox = [Link](orientation=[Link], spacing=6) [Link](vbox) hbox = [Link](spacing=12) vbox.pack_start(hbox, True, True, 0) [Link] = DragSourceIconView() self.drop_area = DropArea() hbox.pack_start([Link], True, True, 0) hbox.pack_start(self.drop_area, True, True, 0) button_box = [Link](spacing=6) vbox.pack_start(button_box, True, False, 0) image_button = [Link].new_with_label_from_widget(None, "Images") image_button.connect("toggled", self.add_image_targets) button_box.pack_start(image_button, True, False, 0) text_button = [Link].new_with_label_from_widget(image_button, "Text") text_button.connect("toggled", self.add_text_targets) button_box.pack_start(text_button, True, False, 0) self.add_image_targets() def add_image_targets(self, button=None): targets = [Link]([]) targets.add_image_targets(TARGET_ENTRY_PIXBUF, True) self.drop_area.drag_dest_set_target_list(targets) [Link].drag_source_set_target_list(targets) def add_text_targets(self, button=None): self.drop_area.drag_dest_set_target_list(None) [Link].drag_source_set_target_list(None) self.drop_area.drag_dest_add_text_targets() [Link].drag_source_add_text_targets() class DragSourceIconView([Link]): def __init__(self): [Link].__init__(self) self.set_text_column(COLUMN_TEXT) self.set_pixbuf_column(COLUMN_PIXBUF) model = [Link](str, [Link]) self.set_model(model) self.add_item("Item 1", "image")
19.4. Example
97
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
self.add_item("Item 2", "gtk-about") self.add_item("Item 3", "edit-copy") self.enable_model_drag_source([Link].BUTTON1_MASK, [], DRAG_ACTION) [Link]("drag-data-get", self.on_drag_data_get) def on_drag_data_get(self, widget, drag_context, data, info, time): selected_path = self.get_selected_items()[0] selected_iter = self.get_model().get_iter(selected_path) if info == TARGET_ENTRY_TEXT: text = self.get_model().get_value(selected_iter, COLUMN_TEXT) data.set_text(text, -1) elif info == TARGET_ENTRY_PIXBUF: pixbuf = self.get_model().get_value(selected_iter, COLUMN_PIXBUF) data.set_pixbuf(pixbuf) def add_item(self, text, icon_name): pixbuf = [Link].get_default().load_icon(icon_name, 16, 0) self.get_model().append([text, pixbuf])
class DropArea([Link]): def __init__(self): [Link].__init__(self, "Drop something on me!") self.drag_dest_set([Link], [], DRAG_ACTION) [Link]("drag-data-received", self.on_drag_data_received) def on_drag_data_received(self, widget, drag_context, x,y, data,info, time): if info == TARGET_ENTRY_TEXT: text = data.get_text() print("Received text: %s" % text) elif info == TARGET_ENTRY_PIXBUF: pixbuf = data.get_pixbuf() width = pixbuf.get_width() height = pixbuf.get_height() print("Received pixbuf with width %spx and height %spx" % (width, height)) win = DragDropWindow() [Link]("delete-event", Gtk.main_quit) win.show_all() [Link]()
98
CHAPTER 20
The [Link] class offers you the opportunity to design user interfaces without writing a single line of code. This is possible through describing the interface by a XML le and then loading the XML description at runtime and create the objects automatically, which the Builder class does for you. For the purpose of not needing to write the XML manually the Glade application lets you create the user interface in a WYSIWYG (what you see is what you get) manner This method has several advantages: Less code needs to be written. UI changes can be seen more quickly, so UIs are able to improve. Designers without programming skills can create and edit UIs. The description of the user interface is independent from the programming language being used. There is still code required for handling interface changes triggered by the user, but [Link] allows you to focus on implementing that functionality.
99
The second line loads all objects dened in [Link] into the Builder object. It is also possible to load only some of the objects. The following line would add only the objects (and their child objects) given in the tuple.
# we dont really have two buttons here, this is just an example builder.add_objects_from_file("[Link]", ("button1", "button2"))
These two methods exist also for loading from a string rather than a le. Their corresponding names are [Link].add_from_string() and [Link].add_objects_from_string() and they simply take a XML string instead of a le name.
Every widget can be retrieved from the builder by the [Link].get_object() method and the widgets id. It is really that simple. It is also possible to get a list of all objects with
builder.get_objects()
100
Now we have to dene the handler functions in our code. The onDeleteWindow should simply result in a call to Gtk.main_quit(). When the button is pressed we would like to print the string Hello World!, so we dene the handler as follows
def hello(button): print "Hello World!"
Next, we have to connect the signals and the handler functions. The easiest way to do this is to dene a dict with a mapping from the names to the handlers and then pass it to the [Link].connect_signals() method.
handlers = { "onDeleteWindow": Gtk.main_quit, "onButtonPressed": hello } builder.connect_signals(handlers)
An alternative approach is to create a class which has methods that are called like the signals. In our example the last code snippet could be rewritten as:
1 2 3 4 5 6 7 8 9 10
class Handler: def onDeleteWindow(self, *args): Gtk.main_quit(*args) def onButtonPressed(self, button): print("Hello World!") builder = [Link]() builder.add_from_file("builder_example.glade") builder.connect_signals(Handler())
20.4 Example
The nal code of the example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
from [Link] import Gtk class Handler: def onDeleteWindow(self, *args): Gtk.main_quit(*args) def onButtonPressed(self, button): print("Hello World!") builder = [Link]() builder.add_from_file("builder_example.glade") builder.connect_signals(Handler()) window = builder.get_object("window1") window.show_all() [Link]()
20.4. Example
101
102
CHAPTER 21
Objects
GObject is the fundamental type providing the common attributes and methods for all object types in GTK+, Pango and other libraries based on GObject. The [Link] class provides methods for object construction and destruction, property access methods, and signal support. This section will introduce some important aspects about the GObject implementation in python.
21.2 Signals
Signals connect arbitrary application-specic events with any number of listeners. For example, in GTK+, every user event (keystroke or mouse move) is received from the X server and generates a GTK+ event under the form of a signal emission on a given object instance. Each signal is registered in the type system together with the type on which it can be emitted: users of the type are said to connect to the signal on a given type instance when they register a function to be invoked upon the signal emission. Users can also emit the signal by themselves or stop the emission of the signal from within one of the functions connected to the signal.
103
GObject.SIGNAL_RUN_FIRST indicates that this signal will invoke the object method handler (do_my_signal() here) in the rst emission stage. Alternatives are GObject.SIGNAL_RUN_LAST (the method handler will be invoked in the third emission stage) and GObject.SIGNAL_RUN_CLEANUP (invoke the method handler in the last emission stage). The second part, None, indicates the return type of the signal, usually None. (int,) indicates the signal arguments, here, the signal will only take one argument, whose type is int. This argument type list must end with a comma. Signals can be emitted using [Link]():
my_obj.emit("my_signal", 42) # emit the signal "my_signal", with the # argument 42
21.3 Properties
One of GObjects nice features is its generic get/set mechanism for object properties. Each class inherited from [Link] can dene new properties. Each property as a type which never changes (e.g. str, oat, int...). For instance, they are used for [Link] where there is a label property which contains the text of the button.
Some properties also have functions dedicated to them, called getter and setter. For the property label of a button, there are two functions to get and set them, [Link].get_label() and [Link].set_label().
104
Properties can also be read-only, if you want some properties to be readable but not writable. To do so, you can add some ags to the property denition, to control read/write access. Flags are GObject.PARAM_READABLE (only read access for external code), GObject.PARAM_WRITABLE (only write access), GObject.PARAM_READWRITE (public):
foo = [Link](type=str, flags = GObject.PARAM_READABLE) # not writable bar = [Link](type=str, flags = GObject.PARAM_WRITABLE) # not readable
You can also dene new read-only properties with a new method decorated with [Link]():
from [Link] import GObject class MyObject([Link]): def __init__(self): [Link].__init__(self) @[Link] def readonly(self): return This is read-only.
There is also a way to dene minimum and maximum values for numbers, using a more verbose form:
from [Link] import GObject class MyObject([Link]): __gproperties__ = { "int-prop": (int, # type "integer prop", # nick "A property that contains an integer", # blurb 1, # min 5, # max 2, # default GObject.PARAM_READWRITE # flags ), } def __init__(self): [Link].__init__(self) self.int_prop = 2 def do_get_property(self, prop): if [Link] == int-prop: return self.int_prop else: raise AttributeError, unknown property %s % [Link] def do_set_property(self, prop, value):
21.3. Properties
105
if [Link] == int-prop: self.int_prop = value else: raise AttributeError, unknown property %s % [Link]
Properties must be dened in [Link].__gproperties__, a dictionary, and handled in do_get_property and do_set_property.
21.4 API
class [Link] get_property(property_name) Retrieves a property value. set_property(property_name, value) Set property property_name to value. emit(signal_name, ...) Emit signal signal_name. Signal arguments must follow, e.g. if your signal is of type (int,), it must be emitted with:
[Link](signal_name, 42)
freeze_notify() This method freezes all the notify:: signals (which are emitted when any property is changed) until the thaw_notify() method is called. It recommended to use the with statement when calling freeze_notify(), that way it is ensured that thaw_notify() is called implicitly at the end of the block:
with an_object.freeze_notify(): # Do your work here ...
thaw_notify() Thaw all the notify:: signals which were thawed by freeze_notify(). It is recommended to not call thaw_notify() explicitly but use freeze_notify() together with the with statement.
106
handler_block(handler_id) Blocks a handler of an instance so it will not be called during any signal emissions unless handler_unblock() is called for that handler_id. Thus blocking a signal handler means to temporarily deactivate it, a signal handler has to be unblocked exactly the same amount of times it has been blocked before to become active again. It is recommended to use handler_block() in conjunction with the with statement which will call handler_unblock() implicitly at the end of the block:
with an_object.handler_block(handler_id): # Do your work here ...
handler_unblock(handler_id) Undoes the effect of handler_block(). A blocked handler is skipped during signal emissions and will not be invoked until it has been unblocked exactly the amount of times it has been blocked before. It is recommended to not call handler_unblock() explicitly but use handler_block() together with the with statement. __gsignals__ A dictionary where inherited class can dene new signals. Each element in the dictionary is a new signal. The key is the signal name. The value is a tuple, with the form:
(GObject.SIGNAL_RUN_FIRST, None, (int,))
GObject.SIGNAL_RUN_FIRST can be replaced with GObject.SIGNAL_RUN_LAST or GObject.SIGNAL_RUN_CLEANUP. None is the return type of the signal. (int,) is the list of the parameters of the signal, it must end with a comma. __gproperties__ The __gproperties__ dictionary is a class property where you dene the properties of your object. This is not the recommend way to dene new properties, the method written above is much less verbose. The benets of this method is that a property can be dened with more settings, like the minimum or the maximum for numbers. The key is the name of the property The value is a tuple which describe the property. The number of elements of this tuple depends on its rst element but the tuple will always contain at least the following items: The rst element is the propertys type (e.g. int, float...). The second element is the propertys nick name, which is a string with a short description of the property. This is generally used by programs with strong introspection capabilities, like the graphical user interface builder Glade. The third one is the propertys description or blurb, which is another string with a longer description of the property. Also used by Glade and similar programs. The last one (which is not necessarily the forth one as we will see later) is the propertys ags: GObject.PARAM_READABLE, GObject.PARAM_WRITABLE, GObject.PARAM_READWRITE. The absolute length of the tuple depends on the property type (the rst element of the tuple). Thus we have the following situations: If the type is bool or str, the forth element is the default value of the property.
21.4. API
107
If the type is int or float, the forth element is the minimum accepted value, the fth element is the maximum accepted value and the sixth element is the default value. If the type is not one of these, there is no extra element. GObject.SIGNAL_RUN_FIRST Invoke the object method handler in the rst emission stage. GObject.SIGNAL_RUN_LAST Invoke the object method handler in the third emission stage. GObject.SIGNAL_RUN_CLEANUP Invoke the object method handler in the last emission stage. GObject.PARAM_READABLE The property is readable. GObject.PARAM_WRITABLE The property is writable. GObject.PARAM_READWRITE The property is readable and writable.
108
CHAPTER 22
Stock Items
Stock items represent commonly-used menu or toolbar items such as Open or Exit. Each stock item is identied by a stock ID; stock IDs are just strings, but constants such as Gtk.STOCK_OPEN are provided to avoid typing mistakes in the strings. Gtk.STOCK_ABOUT
Gtk.STOCK_ADD
Gtk.STOCK_APPLY
Gtk.STOCK_BOLD
Gtk.STOCK_CANCEL
Gtk.STOCK_CAPS_LOCK_WARNING
Gtk.STOCK_CDROM
Gtk.STOCK_CLEAR
Gtk.STOCK_CLOSE
Gtk.STOCK_COLOR_PICKER
109
Gtk.STOCK_CONNECT
Gtk.STOCK_CONVERT
Gtk.STOCK_COPY
Gtk.STOCK_CUT
Gtk.STOCK_DELETE
Gtk.STOCK_DIALOG_AUTHENTICATION
Gtk.STOCK_DIALOG_INFO
Gtk.STOCK_DIALOG_WARNING
Gtk.STOCK_DIALOG_ERROR
Gtk.STOCK_DIALOG_QUESTION
Gtk.STOCK_DISCARD
Gtk.STOCK_DISCONNECT
110
Gtk.STOCK_DND
Gtk.STOCK_DND_MULTIPLE
Gtk.STOCK_EDIT
Gtk.STOCK_EXECUTE
Gtk.STOCK_FILE
Gtk.STOCK_FIND
Gtk.STOCK_FIND_AND_REPLACE
Gtk.STOCK_FLOPPY
Gtk.STOCK_FULLSCREEN
Gtk.STOCK_GOTO_BOTTOM
RTL variant:
Gtk.STOCK_GOTO_LAST
111
LTR variant:
RTL variant:
Gtk.STOCK_GOTO_TOP
RTL variant:
Gtk.STOCK_GO_DOWN
RTL variant:
Gtk.STOCK_GO_UP
Gtk.STOCK_HARDDISK
Gtk.STOCK_HELP
Gtk.STOCK_HOME
Gtk.STOCK_INDEX
Gtk.STOCK_INDENT
112
LTR variant:
RTL variant:
Gtk.STOCK_INFO
Gtk.STOCK_ITALIC
RTL variant:
Gtk.STOCK_JUSTIFY_CENTER
Gtk.STOCK_JUSTIFY_FILL
Gtk.STOCK_JUSTIFY_LEFT
Gtk.STOCK_JUSTIFY_RIGHT
Gtk.STOCK_LEAVE_FULLSCREEN
Gtk.STOCK_MISSING_IMAGE
113
RTL variant:
RTL variant:
Gtk.STOCK_MEDIA_PAUSE
RTL variant:
RTL variant:
Gtk.STOCK_MEDIA_RECORD
RTL variant:
Gtk.STOCK_MEDIA_STOP
Gtk.STOCK_NETWORK
114
Gtk.STOCK_NEW
Gtk.STOCK_NO
Gtk.STOCK_OK
Gtk.STOCK_OPEN
Gtk.STOCK_ORIENTATION_PORTRAIT
Gtk.STOCK_ORIENTATION_LANDSCAPE
Gtk.STOCK_ORIENTATION_REVERSE_LANDSCAPE
Gtk.STOCK_ORIENTATION_REVERSE_PORTRAIT
Gtk.STOCK_PAGE_SETUP
Gtk.STOCK_PASTE
Gtk.STOCK_PREFERENCES
Gtk.STOCK_PRINT
Gtk.STOCK_PRINT_ERROR
Gtk.STOCK_PRINT_PAUSED
115
Gtk.STOCK_PRINT_PREVIEW
Gtk.STOCK_PRINT_REPORT
Gtk.STOCK_PRINT_WARNING
Gtk.STOCK_PROPERTIES
Gtk.STOCK_QUIT
RTL variant:
Gtk.STOCK_REFRESH
Gtk.STOCK_REMOVE
RTL variant:
Gtk.STOCK_SAVE
Gtk.STOCK_SAVE_AS
Gtk.STOCK_SELECT_ALL
116
Gtk.STOCK_SELECT_COLOR
Gtk.STOCK_SELECT_FONT
Gtk.STOCK_SORT_ASCENDING
Gtk.STOCK_SORT_DESCENDING
Gtk.STOCK_SPELL_CHECK
Gtk.STOCK_STOP
Gtk.STOCK_STRIKETHROUGH
RTL variant:
Gtk.STOCK_UNDERLINE
RTL variant:
Gtk.STOCK_UNINDENT
117
LTR variant:
RTL variant:
Gtk.STOCK_YES
Gtk.STOCK_ZOOM_100
Gtk.STOCK_ZOOM_FIT
Gtk.STOCK_ZOOM_IN
118
CHAPTER 23
search
119
Gtk.CellRendererText provides a straightforward text entry for editing, while Gtk.CellRendererCombo enhances the editing capability by displaying a Gtk.ComboBox widget that allows the selection of predefined options. This combo box is backed by a Gtk.TreeModel, which determines the values available for selection, and can operate in two modes: with or without an associated Gtk.Entry widget, depending on the 'has-entry' property.
Gtk.CellRendererPixbuf is used to render images in a Gtk.TreeView. To integrate it, a tree view column is created with the renderer_pixbuf and it's set up to display a particular column in the model using the 'pixbuf' or 'stock-id' properties. This allows the images to be displayed alongside text or other renderers in the tree view.
The main loop and signals allow GTK+ applications to efficiently handle events and user interactions asynchronously. This mechanism lets developers register functions with signals that are triggered by specific events, enabling responsive interfaces without polling. The separation of logic using signals promotes clean, modular code and eases the maintenance and extension of applications.
In a Gtk.ListStore, data is updated by using a signal connection that listens for changes. For a Gtk.CellRendererSpin, the 'edited' signal is connected to a callback function, which updates the data in the ListStore when the user changes the spinner value. The callback receives the path as an argument, which indicates the row to update, and sets the new value accordingly.
To create a new signal in GObject, you define it in the __gsignals__ dictionary with a name and a tuple containing the signal flags, return type, and argument types. For instance, 'my_signal': (GObject.SIGNAL_RUN_FIRST, None, (int,)) creates a signal 'my_signal' with no return value and an integer argument. This setup specifies when the signal handler is invoked, the argument types the signal takes, and the execution order during signal emission.
Gtk.CellRendererToggle is ideal in scenarios where a binary state (checked/unchecked) is needed, such as toggling options on and off. It presents as a checkbox or radio button based on the 'radio' property. Conversely, Gtk.CellRendererSpin is used where precise numeric input is required, offering a Gtk.SpinButton for selecting from a numerical range, useful in adjusting quantities or values incrementally. Each renderer is suited to different input types, toggle for binary and spin for continuous numeric.
A read-only property in GObject is defined using the GObject.property decorator with the GObject.PARAM_READABLE flag, restricting external code from modifying it. This can be useful for exposing values that applications need to access without being altered, maintaining data integrity and stabilizing application states.
The 'toggled' signal in a Gtk.CellRendererToggle is emitted when the toggle button is activated, allowing developers to modify the underlying boolean value in the tree model that represents the active state of the renderer. This can be effectively used to synchronize UI components with the underlying data model, ensuring that the toggle button reflects the current model state.
Gtk.ComboBox can be customized to handle many selections by using the Gtk.ComboBox.set_wrap_width() method to layout items in a grid instead of a list, making the interface more manageable. Additionally, adding a Gtk.Entry allows users to input text directly, expanding the usability if standard options aren't sufficient. These adjustments ensure a streamlined user experience despite a large dataset.
The 'inverted' property in a Gtk.CellRendererProgress affects whether the progress is displayed in a normal or reversed direction. When set to 'true', it reverses the filling direction of the progress bar, providing an alternative visual output useful in certain UI designs where reverse progress sensation is necessary, such as countdowns or tasks focused on retreating metrics.