Chapter 3: GTK


3.1 What Is GTK+?

GTK+ is a toolkit for programming graphical user interfaces. In the earlier days of the X Window System (version 11, or X11), the only toolkit that looked halfway decent and had any sort of popularity was Motif . Other toolkits came and went, but when Spencer Kimball and Peter Mattis decided to write an image-processing program in 1995, Motif was handy, so they used it.

This program (later to be called The GIMP) was distributed as free software and soon gained a small following. However, Motif was a commercial library, preventing widespread use. Therefore, Kimball and Mattis decided to do what John Bradley had done with XV: write their own toolkit, GTK (The GIMP Toolkit). GTK debuted in July 1996 [Bunks]. At the beginning, it had three library components : GLib as a fundamental library, GDK as an interface to X11, and GTK on top of these.

Somewhere along the line, GTK acquired object-oriented capabilities. Graphical components could now inherit from others, and the basic signal system that we have today was introduced. In honor of the new object-oriented features, the developers decided to rename the toolkit GTK+ [Amundson].

In GTK+ version 2.0 (March 2002), the object-oriented pieces left GTK+, forming the more general GObject system. In addition, GTK+ became platform independent by adding more back ends to GDK. Two new components appeared, Pango , a powerful library for text rendering, and ATK , an accessibility toolkit. At that point, GTK+ had no reason to be shy in comparisons to any other toolkit.

GTK+ has been free software from day one, distributed under the terms of the GNU LGPL. This is part of the reason that it was chosen as the toolkit for the GNOME project. Not every GTK+ application is a GNOME application (see Section 4.1), but all GNOME applications use GTK+. Therefore, this chapter is a point-by-point explanation of GTK+'s features; the later chapters show how GNOME builds on GTK+.

This chapter does not cover GTK+ components with an equivalent in the GNOME libraries. In addition, the material and examples follow GNOME Usability Project guidelines [GUP].

GUI programming has one principal concept regardless of the particular toolkit: Widgets are pieces that the user normally manipulates or views (for instance, scrollbars and buttons are widgets; however, not all widgets are visible). All widgets are GObjects and expose many of their features with the help of GObject properties. Containers organize widgets into groups. Furthermore, events are emitted as signals in response to user input. In GTK+, you can attach signal handlers to trap these events.

Note  

This book tries to cover as few API functions as possible, explaining only the functions that do something that you cannot otherwise achieve by manipulating properties. GTK+ is full of access functions that do nothing other than change properties, and you can do that with the GObject API.

Keep in mind that properties always have some underlying code. When you change a widget property, the widget changes (as long as the property isn't write protected, that is).

3.1.1 Widgets and Containers

Containers are widgets that hold other widgets and are responsible for the layout of the user interface. The most obvious containers are windows ” most widgets in an application go into a window.

Other containers include box and table widgets that organize other widgets into a particular place or order. After nesting several containers, you get an application or dialog box look and feel. The act of putting a widget into a container is called packing , and a widget inside of a container is the container's child . An entire hierarchy of containers and widgets is a widget tree .

Note  

A widget may not be in more than one container at a single time. In other words, it may not be the child of several containers.

You can't see a widget when you create the object; you must explicitly request that it appear. You can manipulate and combine widgets without seeing them on your monitor. It isn't a good idea to show a window and then put in every widget; instead, you should work out the representation details when the window is invisible, and then show everything with one shot when you are finished.

It's also possible to hide visible widgets without losing their representation in memory. For example, you might want to hide tool palettes, property windows, and certain dialog boxes. If you hide rather than destroy these widgets, you won't have to worry about creating new instances when you want to show the windows again.

Note  

A widget's precise appearance depends on your current GTK+ theme. Themes vary greatly in implementation and details ” anything is possible, from a new set of colors to an entire new code module that controls what the window draws. Figures 3.1, 3.2, and 3.3 illustrate three different themes. (See Figure 3.3 on the next page.) All other screenshots in this book use the GTK+ Default theme.

click to expand
Figure 3.1: GNOME Run Program dialog box in the Default theme.
click to expand
Figure 3.2: GNOME Run Program dialog box in the Crux theme.
click to expand
Figure 3.3: GNOME Run Program dialog box in the Grand Canyon theme.

3.1.2 Event-Driven Programming

In contrast to the traditional top-to-bottom programs that you typically run on the command line, graphical programs usually consist of a collection of objects that wait for actions (from a user, the network, and so on). The actions trigger small pieces of code that often manipulate other objects.

This system is called event-driven programming . The windowing system transmits events such as key presses and mouse clicks when GTK+ is in a main event loop . GTK+ determines the corresponding widget and then emits an appropriate signal. The actual mechanism is GSignal, covered in Section 2.6.

It's important to recognize the difference between events and signals. Events come from outside GTK+ and typically enter through the main loop, where they reach a widget. Ordinary signals are internal to the application. Of course, every event usually leads to a signal emission. Certain signals that end with -event are called event signals; GTK+ emits these directly when it gets an event. A handler for an event signal returns a Boolean value; if this is TRUE , the signal emission immediately halts.

3.1.3 An Elementary Example

GTK+ resides primarily in a library that reflects the back end. For X11, the library is libgtk-x11-2.0 , with a number of auxiliary subsystem libraries. Sometimes it can be hard to tell what libraries you need, so use the pkg-config command with your compiler command to do all of the hard work for you (see Section 6.1). The only header file you need for GTK+ is gtk/gtk.h ; this file subsequently includes all of GLib, GObject, and anything else you need.

The classic first example, derived from the first program in [Kernighan], is a program that prints Hello , World . Your first GTK+ example should be as grandiose. Here it is:

 /* -*-coding: utf-8;-*- */ /* gtkhello.c -- traditional GTK+ Hello program */ #include <gtk/gtk.h> void hello(GtkWidget *widget, gpointer data) {   g_print("Hello, World!\n"); } gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) {   /* when this function returns FALSE, the delete-event      signal becomes a destroy signal */   return FALSE; } void end_program(GtkWidget *widget, gpointer data) {   /* End the main loop */   gtk_main_quit(); } int main(int argc, char **argv) {   GtkWindow *window;   GtkButton *button;   /* Initialize GTK+ */   gtk_init(&argc, &argv);   /* create window, set default height and width to 200 pixels */   window = g_object_new(GTK_TYPE_WINDOW,                         "default-height", 200,                         "default-width", 200,                         "border-width", 12,                         "title", "GtkHello",                         NULL);   /* add signal handlers for window */   g_signal_connect(window,                    "delete-event", G_CALLBACK(delete_event),                    NULL);   g_signal_connect(window,                    "destroy", G_CALLBACK(end_program),                    NULL);   /* create a button */   button = g_object_new(GTK_TYPE_BUTTON,                         "label", "_Hello, World!\nClick Here.",                         "use-underline", TRUE,                         NULL);   /* install signal handlers for button */   g_signal_connect(button,                    "clicked", G_CALLBACK(hello),                    NULL);   g_signal_connect_swapped(button,                            "clicked", G_CALLBACK(gtk_widget_destroy),                            window);   /* pack the button into the window, show all of its contents */   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(button));   gtk_widget_show_all(GTK_WIDGET(window));   /* start main event loop */   gtk_main();   return 0; } 

Compiling

To compile this simple program, use

 gcc -o gtkhello gtkhello.c `pkg-config --cflags --libs gtk+2.0` 

Naturally, you want to create a Makefile or use autoconf for larger applications.

Program Behavior

The gtkhello program opens a window, creates a big button labeled "Hello, World! Click Here" in the window and waits for events (not just button clicks, but also things like window resizing). When you click the button, gtkhello prints Hello, World! on the console, closes the window, and terminates. You can also close the window (and stop the program) with the window manager ” that is, click whatever button corresponds to Close in the title bar. See Figure 3.4 to see the final application.


Figure 3.4: Result of gtkhello program.

Program Structure

The gtk_init( argc_addr , argv_addr ) function performs several GTK+ initialization tasks , for example, it runs g_type_init() for you. The parameters are the addresses to argc and argv , the main program's command-line options. The gtk_init() function removes anything that it recognizes so that you don't have to deal with the standard GTK+ options yourself.

Then gtkhello does the following:

  1. Creates a new window by asking g_object_new() to create a GtkWindow widget (returned as a pointer to the window variable). This call sets several properties: default-width , default-height , border-width , and title ; these set the window size to 200 pixels square, supply a 12-pixel border, and set the title of the window to GtkHello.

  2. Attaches new signal handlers for delete-event and destroy : delete_event() and end_program() .

  3. Creates a new button object ( button ) of the GtkButton class, with the "Hello World" string as a label.

  4. Attaches hello() to the clicked signal handler for button .

  5. Uses g_signal_connect_swapped() to attach gtk_widget_destroy() to the same clicked signal, so that GSignal can pass window to the handler as a parameter. It also packs the button into the window with gtk_container_add() , displays the window and button with gtk_widget_show_all() , and calls gtk_main() to start the main event loop.

Binding Code to Signals

Some signal handler names are nearly identical to their signals: gtkhello binds delete_event() to the similarly named delete-event . GTK+ emits delete-event with a GtkWindow object when the window manager requests removal of the window. As mentioned in Section 3.1.2, this is an event signal, so the handler must return a Boolean value. If this return value is FALSE , the emission continues, and GTK+ destroys the window. The end_program() handler is also bound to delete-event ; this function calls gtk_main_quit() to stop the main event loop (and consequently, the whole program).

Note  

When you define handlers like this for delete-event , you can determine whether a window removal halts your program. For example, if your application contains unsaved data, you can ask users whether they really want to terminate the application.

GTK+ also offers gtk_widget_hide_on_delete() that you can employ as a handler for delete-event . With this function, GTK+ hides a window in case you want to use it again. This is ideal for tool palettes and similarly recurring windows.

Note  

GSignal treats - and _ as the same character, so you may see delete-event and delete_event used for the same purpose. This book follows the online API documentation, using -.

The gtkhello program attaches hello() as a handler to button 's clicked signal. It sends Hello, World! to the console and does nothing else. It is interesting that gtk_widget_destroy() is a second handler for this widget, receiving window as a parameter: Upon emission of this signal, gtkhello prints the message, destroys the window, and halts the program.

Note  

This function indirectly causes a destroy signal emission and therefore, a call to end_program() . In spite of this, it wouldn't be wise to attach end_program() directly to clicked , because a program with a GUI should shut down only when it has cleaned up after all of its widgets. By using this indirection, you don't have to override the destroy handler, and therefore, you won't have to worry about any trash on the beach (so to speak).

If you understood this small example and explanation, you probably also see that GTK+ programming is a fairly straightforward, clean matter, guided by clear concepts. Everything else is just a matter of knowing the details.

Character Encoding

You probably noticed this line at the very top of the program:

 /* -*-coding: utf-8;-*- */ 

This UTF-8 indicator is important if your program contains accents or other characters that aren't 7-bit ASCII, and you happen to use Emacs. Pango works on UTF-8 strings.

Most GTK+ and GNOME programs are in 7-bit ASCII and contain English strings. You can add translations for other languages; see Section 6.5.

3.1.4 Widget Fundamentals

All GTK+ widgets are GtkWidget class (type identifier: GTK_TYPE_WIDGET ) objects and are therefore also members of the GtkObject class ( GTK_TYPE_OBJECT ), the base class for all GTK+ classes.

One particular technical curiosity of GtkObject widgets is that the reference counters work a little differently than in normal GObject objects. A newly created widget object has a floating reference that the container widget takes over when you pack the widget. This reference ensures that GTK+ sweeps up the widget upon destruction of the container without removing extra references in your setup code.

The base GtkWidget class includes GTK+'s visible operational elements ( methods , properties, and signals). The next three sections outline the most important.

3.1.5 Methods

To show a widget, use

 GtkWidget *  widget  ; gtk_widget_show(  widget  ); 

However, it's much more practical to use

 gtk_widget_show_all(  widget  ) 

to show the widget and all of its children. You can see a widget only if all of its ancestors in the widget tree are also visible.

Note  

When you show a widget or perform any other sort of operation that alters the appearance of a widget, GTK+ doesn't change the widget on the screen immediately; instead, it normally waits until the program is in the main loop. If these miniscule fractions of sections actually matter to you, call

 gtk_widget_show_now(  widget  ) 

This function returns only after everything on the screen appears as it should.

Warning  

Calling gtk_widget_show_now() is akin to running the main loop, so GTK+ can process events and emit signals unrelated to the widget at hand during this time.

The converse of showing a widget is hiding the widget; use one of these two methods:

 gtk_widget_hide(  widget  ) gtk_widget_hide_all(  widget  ) 

To completely eradicate a widget, use

 gtk_widget_destroy(  widget  ) 

Widget destruction is an important part of several of this book's examples.

3.1.6 Properties

The most important gboolean widget properties are:

  • visible : TRUE if the widget is visible.

  • sensitive : FALSE if the widget is inactive (dimmed). When inactive, a widget does not respond to input.

  • can-focus : TRUE if the widget can grab the input focus .

  • has-focus : TRUE if the widget has the input focus.

  • can-default : TRUE if the widget is allowed to become its window's default widget.

  • has-default : TRUE if the widget is its window's default widget.

  • receives-default : TRUE if the widget becomes the default widget when its window gets the input focus.

The default widget receives the input after its window opens and should therefore be one of the least "dangerous" widgets. In dialog boxes, this is normally the button at the lower right. The default widget's purpose is to enable better keyboard operation; for example, the user can operate a default button on a dialog box by pressing the ENTER key.

3.1.7 Signals

GtkWidget objects have plenty of associated signals; most applications ignore the vast majority of them. Here are the three most practical signals, along with their handler prototypes :

  • delete-event

    gboolean handler(GtkWidget *widget, GdkEvent *event, gpointer data)

    This is an important signal for windows, because GTK+ emits this when the window manager wants to delete widget (for example, when you click a Close button in the title bar). As with other event signals, the handler gets the actual event through the event parameter. You can safely ignore the event. Your handler should return TRUE if you want the signal emission to stop immediately.

  • show

    void handler(GtkWidget *widget, gpointer data)

    GTK+ emits this signal when widget becomes visible.

  • hide

    void handler(GtkWidget *widget, gpointer data)

    GTK+ emits this signal when widget is hidden.




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net