© W. David Ashley and Andrew Krause 2019
W. David Ashley and Andrew KrauseFoundations of PyGTK Developmenthttps://doi.org/10.1007/978-1-4842-4179-0_3

3. Some Simple GTK+ Applications

W. David Ashley1  and Andrew Krause2
(1)
AUSTIN, TX, USA
(2)
Leesburg, VA, USA
 

This chapter introduces some simple GTK+ applications and a few GTK+ widgets. We cover topics that are utilized in the upcoming chapters and example applications.

The following concepts are covered in this chapter.
  • The basic function and method calls used by all GTK+ Python applications

  • The object-oriented nature of the GTK+ widget system

  • The role that signals, callbacks, and events play in your application

  • How to alter textual styles with the Pango Text Markup Language

  • Some useful functions and methods for widgets

  • How to make a clickable label

  • How to get and set properties (attributes) using the widget methods

It is important that you grasp the concepts presented so that you have a proper foundation.

Hello World

Practically every programming language book in the world starts with a Hello World example. While this book is no different, the example it uses is more complicated than most other language examples. This is because we base our example around the Gtk.Application and Gtk.ApplicationWindow classes. This makes the example program somewhat longer, and, at first glance, overblown for such a simple GTK+ window. But it also allows good explanations for how GTK+ works and how the Python bindings wrap the APIs into a very good object-oriented system.

Listing 3-1 is one of the simplest applications in this book, but it provides the basis for explaining how a GTK+ application should be organized and how the GTK+ hierarchy of widgets work. This is the basic code that every GTK+ application you create in Python should have!
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Main Window")
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 3-1

HelloWorld.py

Figure 3-1 contains everything you need for a complete GTK+ 3.x Python program.
../images/142357_2_En_3_Chapter/142357_2_En_3_Fig1_HTML.jpg
Figure 3-1

HelloWorld.py

If you have previous experience with GTK+, you may notice some GTK+ 2.x common elements are missing. We explicitly make this a Python 3 program at line 1. This is necessary because the GTK+ 3.x modules are only available in Python version 3.x. This declaration allows lines 4–6 to properly establish the GTK+ 3.x environment.

Lines 8–11 support the visible GTK+ window. The only activity we need to support for this application is calling the super class to initialize it. But there seems to be some missing activities! All of those missing elements are either contained in the Gtk.ApplicationWindow superclass or they are supported in the Gtk.Application class. One of the default supporting actions connects the delete-event to a default method to quit the application.

Lines 13–23 support the application logic. One of the four default methods for the Gtk.Application class are defined in our subclass. The do_activate method performs the activation activities needed.

do_activate is called when the application is activated (after startup). In this case, two functions are needed. First, we check to see if this is the initial call to the method, and if it is, we create the Application GTK+ window instance. Second, we activate and show (present) the main application window.

Lines 25–27 are the only Python statements needed to start our application. No other statements are necessary, and in fact, none should be added. All the application work should take place in the Gtk.Application class or the Gtk.ApplicationWindow class or their subclasses. This prevents any unnecessary work taking place for a “single instance” application that has attempted to start up another application instance.

GTK+ Widget Hierarchy

The GTK+ application programming interface is actually a C language API. However, it is organized in such a way that an object-oriented language like Python can wrap the C API so that the entire set of APIs are turned into a set of classes organized in a hierarchy.

The transition from GTK+ 2.x to 3.x made changes that have helped other languages create object-oriented bindings that are easier to maintain and easier to implement. For instance, while Python 2.x supported abstract classes, they were buried in the collection classes and were hard to implement in your own code. Python 3.3 supplies the collections.abc module, which makes it easy for you to subclass classes in the module to create your own abstract classes. Also, the GTK+ 3.x API drastically reduces the number of abstract classes. In the future, all of them will probably be eliminated.

The GTK+ 3.x object hierarchy is documented by the PyGObject API Reference ( http://lazka.github.io / pgi-docs/#Gtk-3.0) document. This is the Python GTK+ 3.x reference document. It covers everything you need to know about the Python object bindings to GTK+, including the object hierarchy, supported classes, interfaces, functions, methods, and properties. While the document is mostly comprehensive, it lacks information concerning some new classes. We hope that this book provides that information, as well excellent examples on how to use all the widgets and classes.

While it is important to have an understanding of the GTK+ hierarchy, it is still possible to create good GUI applications with only a superficial understanding. But the more you understand the hierarchy, the better control you have over your application.

Extending HelloWorld.py

Even though Listing 3-1 is a complete application, obviously it is not very useful. So let’s add useful features and method calls to provide visible information and visual appeal to our application (see Listing 3-2).
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        label = Gtk.Label.new("Hello World!")
        label.set_selectable(True)
        self.add(label)
        self.set_size_request(200, 100)
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Hello World!")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 3-2

HelloWorld with Label

Figure 3-2 is the result of running Listing 3-2. Note that the label is already highlighted.
../images/142357_2_En_3_Chapter/142357_2_En_3_Fig2_HTML.jpg
Figure 3-2

HelloWorld with label

We now have an application that displays data, and thus is a little more useful. Let’s take a look at the changes we made to the program to achieve this result.

Lines 12–15 are where most of the changes have been made. On line 12, we create Gtk.Label with text “Hello World!” contained within it. On line 13, we make that text selectable. This allows the user to select the text and copy it to the clipboard. On line 14, we add the label to the Gtk.ApplicationWindow default container. All main windows in GTK+ derive from Gtk.Container, so it is possible to add widgets to that container. Line 15 resizes Gtk.ApplicationWindow.

Line 27 shows all the widgets contained by Gtk.ApplicationWindow. We need this method call because the present method does not perform that function. It only shows the main window.

These are the only changes made to Listing 3-1. As you can see, it does not take a lot of effort to add new functionality to a Python GTK+ application.

The GTK.Label Widget

A GTK.Label widget was created in Listing 3-2. This was accomplished with the following invocation.
label = Gtk.Label.new("Hello World!")

This call creates a new label with the specified text included. The text may include Python escape sequences (such as " "), which GTK+ uses to format your text on the screen.

There are lots of useful methods that GTK.Label supports. The following is list of some of the more useful ones.
  • set_selectable: This method turns on/off the text’s selectability. The default is off. This is very useful for things like error messages, where the user may wish to copy the text to the clipboard.

  • set_text: This method replaces the current label text with the specified new text.

  • set_text_with_mnemonic: This method replaces the current label text with the specified new text. The new text may or may not have a mnemonic contained within it. If characters in the text are preceded by an underscore, they are underlined, which indicates that they represent a keyboard accelerator called a mnemonic. The mnemonic key can be used to activate another widget, chosen automatically, or explicitly using Gtk.Label.set_mnemonic_widget.

  • get_text: This method retrieves the current label text.

Layout Containers

The Gtk.ApplicationWindow and Gtk.Window classes both indirectly derive from the Gtk.Container widget. This means that all the methods in the Gtk.Container are available to the derived windows.

By using the add method, widgets or other container types can be added to a main window. That is how GTK.Label is added to the main window. It follows when you add a widget to a container that a parent/child relationship is formed; the container becomes the parent and the label becomes a child of the container.

The parent/child relationship between widgets is very important in GTK+ for many reasons. For example, when a parent widget is destroyed, GTK+ recursively destroys all the child widgets, no matter how deeply nested they are.

Containers also have a default sizing algorithm. This can be both good and bad news. In many cases, the default sizing is just what you want; but in many cases, it is not. You can override the default sizing by resizing the main window.

Another sizing helper for the container is the set_border_width method. It allows you to create a border around the text so that when the user shrinks the window manually, the window has a minimum size determined by the size of text and the border width.

There is more information on containers and layouts in Chapter 4.

Signals and Callbacks

GTK+ is a system that relies on signals and callback methods. A signal is a notification to your application that the user has performed some action. You can tell GTK+ to run a method or function when the signal is emitted. These are called callback methods/functions.

Caution

GTK+ signals are not the same as POSIX signals! Signals in GTK+ are propagated by events from the X Window System. Each provides separate methods. These two signal types should not be used interchangeably.

After you initialize your user interface, control is given to the gtk_main() function through the Gtk.Application class instance, which sleeps until a signal is emitted. At this point, control is passed to other methods/functions.

As the programmer, you connect signals to their methods/callback functions. The callback method/function is called when the action has occurred and the signal is emitted, or when you have explicitly emitted the signal. You also have the capability of stopping signals from being emitted at all.

Note

It is possible to connect signals at any point within your applications. For example, new signals can be connected within callback methods/functions. However, you should try to initialize mission-critical callbacks before calling gtk_main() or the present() method in the Gtk.Application instance.

There are many types of signals, and just like functions, they are inherited from parent structures. Many signals are generic to all widgets, such as "hide" and "grab-focus" or specific to the widget such as the Gtk.RadioButton signal "group-changed". In any case, widgets derived from a class can use all the signals available to all of its ancestors.

Connecting the Signal

Our first example of connecting to a signal intercepts the "destroy" signal from a main window so that we can choose how to handle that signal. One of the main reasons for handling this signal ourselves is to perform an action prior to having the window automatically destroyed by the GTK+ system.
widget.connect("destroy", self.on_window_destroy, extra_param)

GTK+ emits the "destroy" signal when widget.destroy() is called on the widget or when False is returned from a delete_event() callback method/function. If you reference the API documentation, you see that the destroy signal belongs to the Gtk.Object class. This means that every class in GTK+ inherits the signal. You can be notified of the destruction of any GTK+ structure/instance.

There are two required parameters to every connect() call. The first is the name of the signal you want to track. Each widget has many possible signals, all of which are found in the API documentation. Remember that widgets are free to use the signals of their ancestors, since each widget is actually an implementation of each of its ancestors. You can use the “Object Hierarchy” section of the API to reference parent classes.
widget.connect("signal_name", function_name, extra_param)

When typing the signal name, the underscore and dash characters are interchangeable. They are parsed as the same character, so it does not make any difference which one you choose. I use the underscore character in all the examples in this book.

The second parameter in the connect() method is the callback method/function which is called when the signal is emitted. The format of the callback method/function depends on the function prototype requirements of each specific signal. An example callback method is shown in the next section.

The last parameter in the connect() method allows you to send extra parameters to the callback method/function. Unlike the C version of the g_signal_connect() function, the Python version of the connect() method call allows you to pass as many extra parameters as you need for the callback method/function. This is very useful because it prevents the artificial creation of a single variable container that wraps a number of variables/classes that you wish to pass to a callback/method.

In this instance of connect(), a single label is passed to the callback method.
widget.connect("destroy", self.on_window_destroy, label)

The return value for connect() is the handler identifier of the signal. You can use this with GObject.signal_handler_block(), GObject.signal_handler_unblock(), and GObject.signal_handler_disconnect(). These functions stop a callback method/function from being called, re-enable the callback function, and remove the signal handler from a widget’s handler list, respectively. More information is in the API documentation.

Callback Methods/Functions

Callback methods/functions specified in connect() are called when the signal is emitted on the widget to which it was connected. For all signals, with the exception of events, callback methods/functions are in the following form.
# a callback function
def on_window_destroy(widget, extra_arg)
# a callback method
def on_window_destroy(self, widget, extra_arg)

You can find an example format of a callback method/function for each signal in the API documentation, but this is the generic format. The widget parameter is the object that performed the connect() call.

There are other possible required arguments that may appear in the middle as well, although this is not always the case. For these parameters, you need to reference the documentation of the signal you are utilizing.

The last parameter of your callback method/function corresponds to the last parameter of connect(). Remember that there can be as many of these optional arguments as you need, but the number of extra parameters from the connect() call and the number of extra arguments in the callback method/ function definition must be the same.

You should also note that the first argument to the method version of the callback is the self argument required by Python in method definitions; otherwise, the function and method definitions are the same.

Events

Events are special types of signals that are emitted by the X Window System. They are initially emitted by the X Window System and then sent from the window manager to your application to be interpreted by the signal system provided by GLib. For example, the "destroy" signal is emitted on the widget, but the "delete-event" event is first recognized by the underlying Gdk.Window of the widget, and then emitted as a signal of the widget.

The first instance of an event you encountered was the "delete-event". The "delete-event" signal is emitted when the user tries to close the window. The window can be exited by clicking the Close button on the title bar, using the Close pop-up menu item in the taskbar, or by any other means provided by the window manager.

Connecting events to a callback function is done in the same manner with connect() as with other GTK + signals. However, your callback function is set up slightly differently.
# an event callback function
def on_window_destroy(widget, event, extra_arg)
# an event callback method
def on_window_destroy(self, widget, event, extra_arg)

The first difference in the callback method/function is the boolean return value. If True is returned from an event callback, GTK+ assumes the event has already been handled and it does not continue. By returning False, you are telling GTK+ to continue handling the event. False is the default return value for the function, so you do not need to use the "delete-event" signal in most cases. This is only useful if you want to override the default signal handler.

For example, in many applications, you may want to confirm the exit of the program. By using the following code, you can prevent the application from exiting if the user does not want to quit.
# an event callback method
def on_delete_event(self, widget, event, extra_arg):
    answer = # Ask the user if exiting is desired.
    if answer:
        return False
    else:
        return True

By returning False from the "delete-event" callback function, widget.destroy() is automatically called on the widget. This signal automatically continues with the action, so there is no need to connect to it unless you want to override the default.

In addition, the callback function includes the Gdk.Event parameter. Gdk.Event is a union of the Gdk.EventType enumeration and all the available event structures. Let’s first look at the Gdk.EventType enumeration.

Event Types

The Gdk.EventType enumeration provides a list of available event types. These can be used to determine the type of event that has occurred, since you may not always know what has happened.

For example, if you connect the "button-press-event" signal to a widget, there are three different types of events that can cause the signal’s callback function to be run: Gdk.EventType.BUTTON_PRESS, Gdk.EventType.2BUTTON_PRESS, and Gdk.EventType.3BUTTON_PRESS. Double-clicks and triple-clicks emit the Gdk.EventType.BUTTON_PRESS as a second event as well, so being able to distinguish between different types of events is necessary.

Appendix B provides see a complete list of the events available to you. It shows the signal name that is passed to connect(), the Gdk.EventType enumeration value, and a description of the event.

Let’s look at the "delete-event" callback function. We already know that "delete-event" is of the type Gdk.EventType.DELETE, but let’s assume for a moment that we did not know that. We can easily test this by using the following conditional statement.
def delete_event(self, window, event, data):
    if event.type == Gdk.EventType.DELETE:
       return False
    return True

In this example, if the event type is Gdk.EventType.DELETE, False is returned, and widget.destroy() is called on the widget; otherwise, True is returned, and no further action is taken.

Using Specific Event Structures

Sometimes, you may already know which type of event has been emitted. In the following example, we know that a "key-press-event" is always emitted.
widget.connect("key-press-event", on_key_press)
In this case, it is safe to assume that the type of event is always Gdk.EventType.KEY_PRESS, and the callback function can be declared as such.
def on_key_press(widget, event):
Since we know that the type of event is a Gdk.EventType.KEY_PRESS, we do not need access to all of the structures in Gdk.Event. We only have use for Gdk.EventKey, which we can use instead of Gdk.Event in the callback method/function. Since the event is already cast as Gdk.EventKey, we have direct access to only the elements in that structure.
Gdk.EventKey.type              # GDK_KEY_PRESS or GDK_KEY_RELEASE
Gdk.EventKey.window            # The window that received the event
Gdk.EventKey.send_event        # TRUE if the event used XSendEvent
Gdk.EventKey.time              # The length of the event in milliseconds
Gdk.EventKey.state             # The state of Control, Shift, and Alt
Gdk.EventKey.keyval            # The key that was pressed
Gdk.EventKey.length            # The length of string
Gdk.EventKey.string            # A string approximating the entered text
Gdk.EventKey.hardware_keycode  # Raw code of the key that was pressed or released
Gdk.EventKey.group             # The keyboard group
Gdk.EventKey.is_modifier       # Whether hardware_keycode was mapped

There are many useful properties in the Gdk.EventKey structure that we use throughout the book. At some point, it would be useful for you to browse some of the Gdk.Event structures in the API documentation. We cover a few of the most important structures in this book, including Gdk.EventKey and Gdk.EventButton.

The only variable that is available in all the event structures is the event type, which defines the type of event that has occurred. It is a good idea to always check the event type to avoid handling it in the wrong way.

Further GTK+ Methods

Before continuing on to further examples, I would like to draw your attention to a few functions that will come in handy in later chapters and when you create your own GTK+ applications.

Gtk.Widget Methods

The Gtk.Widget structure contains many useful functions that you can use with any widget. This section outlines a few that you need in a lot of your applications.

It is possible to destroy a widget by explicitly calling widget.destroy() on the object. When invoked, widget.destroy() drops the reference count on the widget and all of its children recursively. The widget, along with its children, are then destroyed and all memory is freed.
widget.destroy()

Generally, this is only called on top-level widgets. It is usually only used to destroy dialog windows and to implement menu items that quit the application. It is used in the next example to quit the application when a button is clicked.

You can use widget.set_size_request() to set the minimum size of a widget. It forces the widget to be smaller or larger than it would normally be. It does not, however, resize the widget so that it is too small to be functional or able to draw itself on the screen.
widget.set_size_request(width, height)

By passing –1 to either parameter, you are telling GTK+ to use its natural size, or the size that the widget would normally be allocated to if you do not define a custom size. This is used if you want to specify only the height or only the width parameter. It also allows you to reset the widget to its original size.

There is no way to set a widget with a width or height of less than 1 pixel, but by passing 0 to either parameter, GTK+ makes the widget as small as possible. Again, it is not resized so small that it’s non-functional or unable to draw itself.

Because of internationalization, there is a danger in setting the size of any widget. The text may look great on your computer, but on a computer using a German translation of your application, the widget may be too small or large for the text. Themes also present issues with widget sizing, because widgets are defaulted to different sizes, depending on the theme. Therefore, it is best to allow GTK+ to choose the size of widgets and windows in most cases.

You can use widget.grab_focus() to force a widget to grab the keyboard focus. This only work on widgets that can handle keyboard interaction. One example of a use for widget.grab_focus() is sending the cursor to a text entry when the search toolbar is shown in Firefox. It could also be used to give focus to a Gtk.Label that is selectable.
widget.grab_focus()
Often, you want to set a widget as inactive. By calling widget.set_sensitive(), the specified widget and all of its children are disabled or enabled. By setting a widget as inactive, the user is prevented from interacting with the widget. Most widgets are also grayed out when set as inactive.
widget.set_sensitive(boolean)

If you want to re-enable a widget and its children, you need only to call this method on the same widget. Children are affected by the sensitivity of their parents, but they only reflect the parents’ settings, instead of changing their properties.

Gtk.Window Methods

You have now seen two examples using the Gtk.Window class. You learned how to set the title of a window and add a child widget. Now, let’s explore a few more functions that allow you to further customize windows.

All windows are set as resizable by default. This is desirable in most applications, because each user has different size preferences. However, if there is a specific reason for doing so, you can use window.set_resizable() to prevent the user from resizing the window.
window.set_resizable(boolean)

Caution

You should note that the ability to resize is controlled by the window manager, so this setting may not be honored in all cases!

The preceding caution brings up an important point. Much of what GTK+ does interacts with the functionality provided by the window manager. Because of this, not all of your window settings may be followed on all window managers. This is because your settings are merely hints that are either used or ignored. You should keep in mind that your requests may or may not be honored when designing applications with GTK+.

The default size of Gtk.Window can be set with window.set_default_size(), but there are a few things to watch out for when using this method. If the minimum size of the window is larger than the size you specify, this method is ignored by GTK+. It is also ignored if you have previously set a larger size request.
window.set_default_size(width, height)

Unlike widget.set_size_request(), window.set_default_size() only sets the initial size of the window; it does not prevent the user from resizing it to a larger or smaller size. If you set a height or width parameter to 0 , the window’s height or width is set to the minimum possible size. If you pass –1 to either parameter, the window is set to its natural size.

You can request that the window manager move the window to the specified location with window.move(); however, the window manager is free to ignore this request. This is true of all functions that require action from the window manager.
window.move(x, y)
By default, the position of the window on the screen is calculated with respect to the top-left corner of the screen, but you can use window.set_gravity() to change this assumption.
window.set_gravity(gravity)

This function defines the gravity of the widget, which is the point that layout calculations consider (0, 0). Possible values for the Gdk.Gravity enumeration include Gdk.Gravity.NORTH_WEST, Gdk.Gravity.NORTH, Gdk.Gravity.GRAVITY_NORTH_EAST, Gdk.Gravity.WEST, Gdk.Gravity.CENTER, Gdk.Gravity.EAST, Gdk.Gravity.SOUTH_WEST, Gdk.Gravity.SOUTH, Gdk.Gravity.SOUTH_EAST, and Gdk.Gravity.STATIC.

North, south, east, and west refer to the top, bottom, right, and left edges of the screen. They are used to construct multiple gravity types. Gdk.Gravity.STATIC refers to the top-left corner of the window itself, ignoring window decorations.

If your application has more than one window, you can set one as the parent with window.set_transient_for(). This allows the window manager to do things such as center the child above the parent or make sure one window is always on top of the other. We explore the idea of multiple windows and transient relationships in Chapter 6 when discussing dialogs.
window.set_transient_for(parent)
You can set the icon that appears in the taskbar and title bar of the window by calling window.set_icon_from_file(). The size of the icon does not matter, because it is resized when the desired size is known. This allows the scaled icon to have best quality.
window.set_icon_from_file(filename)

True is returned if the icon was successfully loaded and set.

Caution

Icons are a complex topic and have many behavioral complexities, including icon sets, scaling, and interactions with themes. See the GTK+ documentation for more information.

Process Pending Events

At times, you may want to process all pending events in an application. This is extremely useful when you are running a piece of code that takes a long time to process. This causes your application to appear frozen, because widgets are not redrawn if the CPU is taken up by another process. For example, in an integrated development environment that I created called OpenLDev, I have to update the user interface while a build command is being processed; otherwise, the window would lock up and no build output would be shown until the build was complete.

The following loop is the solution for this problem. It is the answer to a great number of questions from new GTK+ programmers.
while Gtk.events_pending():
    Gtk.main_iteration()

The loop calls Gtk.main_iteration(), which processes the first pending event for your application. This continues while Gtk.events_pending() returns True, which tells you whether there are events waiting to be processed.

Using this loop is an easy solution to the freezing problem, but a better solution is to use coding strategies that avoid the problem altogether. For example, you can use idle functions found in GLib to call a function only when there are no actions of greater importance to process.

Buttons

Gtk.Button is a special kind of container that can only contain a single child. However, that child can be a container itself, thus allowing a button to contain multiple widgets. The Gtk.Button class is a clickable entity. It can be connected to a defined method of the owning container or window.

Gtk.Button is an action widget. That is, when it is clicked, an action is expected to be taken. The programmer has full control of that action by processing the signal emitted when the button is clicked. So let’s take a look at how Gtk.Button works in another simple example (see Listing 3-3).
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_border_width(25)
        button = Gtk.Button.new_with_mnemonic("_Close")
        button.connect("clicked", self.on_button_clicked)
        button.set_relief(Gtk.ReliefStyle.NORMAL)
        self.add(button)
        self.set_size_request(200, 100)
    def on_button_clicked(self, button):
        self.destroy()
class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.myapp",
                         **kwargs)
        self.window = None
    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self,
                                     title="Hello World!")
        self.window.show_all()
        self.window.present()
if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)
Listing 3-3

HelloWorld with Button

Figure 3-3 shows the result of running Listing 3-3. Note how the button is centered by default.
../images/142357_2_En_3_Chapter/142357_2_En_3_Fig3_HTML.jpg
Figure 3-3

HelloWorld with button

Those of you who are experienced GTK+ 2.x developers may wonder why we did not use a stock button instead. Stock buttons have been deprecated since GTK+ 3.1 and should not be used in new code. This may come as a huge surprise because this causes a lot of work when upgrading a 2.x application to a 3.x application. But all is not as bad as it first seems. By converting to non-stock buttons, your application becomes more portable for all supported platforms.

Let's take a detailed look at the button code. There is some interesting code.

Line 12 sets the border width around the button to be created later. Lines 13–16 create the button and connect it to a method in the Gtk.Application instance. Line 13 creates a button with the mnemonic label "_Close". The underline indicates that the letter C is the mnemonic. When the user presses Alt+C, the "clicked" signal is emitted by the button.

Line 14 connects the "clicked" signal produced by the button to the on_button_clicked method in the Gtk.ApplicationWindow instance. It does this by obtaining the instance from the kwargs argument. The dictionary name application was assigned a value on line 28, and that value was fetched on line 14 to point to the correct Gtk.Application instance method.

You may be wondering why we did not connect the button signal to a method local to the Gtk.ApplicationWindow class. This is because the signal to quit the application rightly belongs to the Gtk.Application class, not the Gtk.ApplicationWindow class. This is one of those “gotchas” that can be very hard to understand and apply properly. You need to think carefully when connecting signals to methods to make sure that the correct class gets the signal. This is a roundabout method to process the "clicked" signal. The normal way is to create your own method, like on_button_clicked, in the Gtk.ApplicationWindow class and connect the signal to that method. We are only showing this example to make the point that you can send signals to either the Gtk.ApplicationWindow instance or the Gtk.Application instance.

Line 14 sets the relief style for the button. You should always use the Gtk.ReliefStyle.NORMAL style unless you have good reasons for doing otherwise.

Line 16 adds the button to the Gtk.ApplicationWindow container. This works just like adding a label, as shown in Listing 3-2.

Lines 19–20 process the "clicked" signal emitted from our button. Our only action is to destroy the Gtk.ApplicationWindow instance.

We should note that when the last Gtk.ApplicationWindow instance is destroyed, the Gtk.Application causes the application to exit.

Test Your Understanding

In this chapter, you learned about the window, button, and label widgets. It is time to put that knowledge into practice. In the following exercise, you employ your knowledge of the structure of GTK+ applications, signals, and the GObject property system.

Exercise 1: Using Events and Properties

This exercise expands on the first two examples in this chapter by creating a Gtk.ApplicationWindow class that has the ability to destroy itself. You should set your first name as the title of the window. A selectable Gtk.Label with your last name as the default text string should be added as the child of the window.

Let’s consider other properties of this window: it should not be resizable and the minimum size should be 300×100 pixels. The methods to perform these tasks were discussed in this chapter.

Next, by looking at the API documentation, connect the key-press-event signal to the window. In the "key-press-event" callback function, switch the window title and the label text. For example, the first time the callback method is called, the window title should be set to your last name and the label text to your first name.

Once you have completed exercise 1, you can find a description of the solution in Appendix D. The solution’s complete source code can be downloaded from www.gtkbook.com .

Once you have completed this exercise, you are ready to move on to the next chapter, which covers container widgets. These widgets allow your main window to contain more than just a single widget, which was the case in all the examples in this chapter.

However, before you continue, you should know about www.gtkbook.com , which can supplement Foundations of PyGTK Development. This web site is filled with downloads, links to further GTK+ information, C and Python refresher tutorials, API documentation, and more. You can use it as you go through this book to aid in your quest to learn GTK+.

Summary

In this chapter, we introduced some simple GTK+ 3.x applications, along with some simple widgets, which also introduced concepts that will be beneficial in later chapters. Here are some of the concepts you learned in this chapter.
  • The Gtk.Label class was introduced with an example program.

  • The Gtk.Button class was introduced with an example program.

  • The signals and methods to catch the signals were introduced. This concept is covered in more depth in a later chapter.

  • The concept of containers was introduced. This concept is covered in more depth in Chapter 4.

In Chapter 4, we cover the Gtk.Container class and the vast array of container types available.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.139.169