dcsimg

GUI Building With GTK

While developing the GIMP (GNU Image Manipulation Program) a few years back, Spencer Kimball and Peter Mattis decided that it might be fun to write a user interface toolkit to go along with it. And so they created GTK+ (the GIMP Toolkit) -- a library of "widgets" that makes it easier for developers to build GUI-based application. A widget is one of the various components of a graphical application; a toolbar, for example. The widgets provide programmers with pre-built dialog boxes, windows, menu bars and other GUI components. Having a pre-built foundation for these user interface components greatly speeds development of GUI applications and also enables applications built with those widgets to share a common "look and feel".

While developing the GIMP
(GNU Image Manipulation Program) a few years back, Spencer Kimball and
Peter Mattis decided that it might be fun to write a user interface
toolkit to go along with it. And so they created GTK+ (the GIMP Toolkit)
– a library of “widgets” that makes it easier for developers to build
GUI-based application. A widget is one of the various components of a
graphical application; a toolbar, for example. The widgets provide
programmers with pre-built dialog boxes, windows, menu bars and other
GUI components. Having a pre-built foundation for these user interface
components greatly speeds development of GUI applications and also
enables applications built with those widgets to share a common “look
and feel”.

For those of us seeking a toolkit that does not
restrict us to any one operating system or language, GTK+ is worth
checking out.

While the GTK+ toolkit is written in C, there are
language bindings available for a variety of languages including C++,
Pascal, Guile, Perl, and Python. And the fact that the toolkit is
released under an Open Source license makes de-bugging applications and
learning about GTK+ easier than it would be with a proprietary
toolkit.

GTK+ has been ported to a variety of platforms,
including Solaris, HP-UX, MS Windows, and Irix, as well as Linux. It
includes a library of routines (called GLIB) to support non-graphical
elements required in an event-driven application such as linked lists,
trees,sockets, and memory allocation to help make applications more
portable.

GTK+ comes with a large collection of widgets, from
simple components like the standard button and text
widget,
to more complicated components like the color
selection
and file selection widgets. GTK+ also makes it easy
to extend existing widgets and create new ones.

While it would be impossible to teach GTK+
development in a single article, the basics of writing and implementing
a GTK+ application are simple. Spencer, Peter, and the literally
hundreds of contributors to the project have provided a wide range of
widgets to make even the most complex graphical application easier to
manage. As mentioned earlier, a widget is a GUI component. Text boxes,
buttons, labels, and windows are all widgets. By organizing these
widgets together and writing a few functions to process the messages the
widgets send to each other (called “signals”), applications can be
easily created. (Okay, it’s not quite that simple, but let’s not go
there just yet.) Each widget also has at least one widget creation
function and usually several properties that can be modified to change
the characteristics of that widget.

Container Widgets

Some widgets can act as containers for other widgets.
The standard button widget, for example, (GtkButton) usually has a
label in it. This label is, in fact, a label widget (GtkLabel). The label
widget
can be replaced with a graphic image instead of a text label
to create an image button.Container widgets can only have one
widget placed within them, but this limitation can be circumvented by
using packing widgets.

Packing Widgets

Packing widgets can hold more than one widget.
They are not visible, so they’re usually placed within a container
widget,
giving the container widget the appearance of holding
many widgets. Packing widgets come in three varieties: The
vertical packing box allows widgets to be stacked vertically; the
horizontal packing widget allows widgets to lie next to each
other from left to right; finally, the packing tableprovides a
grid to place widgets on in any direction.

Packing widgets can be placed within other
packing widgets for more elaborate effects than the basic
top/down or left/right displays. The packing widgetseliminate the
use of absolute x and y coordinates when placing widgets. Most
applications don’t care about the position of the widget as long as one
widget is on top of another or next to another. Manually keeping track
of the height of a widget and the position of the one above it as the
screen resizes can be an unnecessary pain.

Inheritance

Using inheritance, the simple widgets in GTK+ can be
combined to make more complex widgets. Since GTK+ is written in C, which
is not a language that has inheritance built in to it, a fair bit of
work was done in the toolkit to make inheritance to work. All widgets
inherit from the base widget class (GtkWidget) and many
widgets have multiple levels of inheritance.

The Radio Button in GTK+ (GtkRadioButton) is
inherited from the Check Button (GtkCheckButton) which is
in turn based on the Toggle Button (GtkToggleButton) which
inherits some of it’s behavior from the Button Widget (GtkButton). This
inheritance goes all the way back to the basic widget. This allows
widgets to reuse code instead of duplicating the same code for several
identical widgets.








Compile GTK 1
Button: The result of the Simple Button
Example.

Properties

Widgets employ a number of properties. Frequently
used properties include the visible property which can be used to
display or hide a widget. The gtk_widget_show function
and the gtk_widget_hide function are used to manipulate the visible
property.

Other properties are less frequently used or are more
specific to particular widgets. For example, the toggle button
widget
has a property that indicates the state of the button (up or
down) and the state can only be set or queried by the toggle
button
or any widgets that inherit from the toggle
button
.

Signals

Signals occur in GTK+ any time something interesting
happens to a widget. Most widgets can send and receive many signals, but
they normally only use a few of those. For instance, the button
widget
has a signal to indicate that the mouse is moving over the
button. Nice, but who really cares? Most of the time, the only signal
that is important to a button is the one that indicates that the button
was clicked.

Let’s step through a sample program and see how
simple it is to write a quick GTK+ application. The application will be
a simple one — it will display a window with a button in it.




Figure 1: Simple Button
Example

1 /* — Need to include the GTK+ header file — */
2 #include <gtk/gtk.h>
3
4 int main (int argc, char *argv[])
5 {
6 GtkWidget *window; /* — The application window — */
7 GtkWidget *button; /* — The button widget — */
8
9 /* — Initialize GTK+. (Required) — */
10 gtk_init (&argc, &argv);
11
12 /* — Create the application window — */
13 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
14
15 /*
16 * The “delete_event” indicates that the user wants to close
17 * the window. We forward the request to the EndApp
18 * function.
19 */
20 gtk_signal_connect (
21 GTK_OBJECT (window), /* — window — */
22 “delete_event”, /* — signal to handle — */
23 GTK_SIGNAL_FUNC (EndApp),/* — function to call — */
24 NULL); /* — parameters — */
25
26 /*— Modify the applicationwindowborderwidthproperty— */
27 gtk_container_border_width (GTK_CONTAINER (window), 15);
28
29 /* — Create the button — */
30 button = gtk_button_new_with_label (“Button”);
31
32 /* — Add the button to the window — */
33 gtk_container_add (GTK_CONTAINER (window), button);
34
35 /* — Make the button visible — */
36 gtk_widget_show (button);
37
38 /* — Make the window visible — */
39 gtk_widget_show (window);
40
41 /* — Loop and process until the app exits — */
42 gtk_main ();
43 return (0);
44
45 /*
46 * EndApp
47 * Called when the application window is closed by the
48 * user. Returns FALSE to close the application window.
49 * A return of TRUE would prevent the window from closing.
50 */
51 gint EndApp (GtkWidget *widget, gpointer gdata)
52 {
53 /* — Exit out of the event loop (gtk_main) — */
54 gtk_main_quit ();
55
56 /* — Allow the window to close — */
57 return (FALSE);
58 }



The first step is to include the GTK+ include file, as in lines
1 and 2. The include file contains all the various types and prototypes
necessary to use the GTK+ library.

The application needs to define a pointer to a


GtkWidget to hold the
application window and it needs to define one for the button as well.
Although the application window and the other widgets are of different
types, lines 6 and 7 show how the object oriented nature of GTK+ allows
both to be defined as GtkWidgets.

The gtk_init function, line 10, initializes the toolkit and parses
any parameters passed to the GTK+ application from the command line.

The application window needs to be created next. As
shown in line 13, gtk_ window_new creates this window for us and returns the handle to
the window.

Applications should respond to signals that occur in
this window. The main window can usually ignore signals, but it should
pay attention to certain signals, like someone trying to close the
application. The window should at least respond to a user trying to
close the window. In GTK+, the delete_event is sent to a
window when the window is about to be closed. The return value indicates
whether the window will continue the closing process. This allows you to
pop up a dialog like “Are you sure you want to close the application?”
Returning TRUE will prevent the closing from continuing; returning FALSE
continues with the closing of the window.

The gtk_signal_connect
function associates a widget and a particular signal with a signal
handler, which gets called when the signal occurs. In this case, we want
to let the application close when the delete_event occurs. In
lines 20 through 24, the EndApp function gets called when the user tries to close the
application window.

Before the button is added to the application window,
it would look a little better if we defined a border within the window,
so the button itself doesn’t run all the way up to the edge of the
application window. We can define a border using gtk_container_border_width, as in line 27.

Of course, it helps to have something besides an
empty window. So we create a button widget with a text label in
line 30 and add it to the application. Next, a button (container)
is created and made visible, as in line 33 through 36, so that it can be
displayed within the application window.

Of course, while we’ve created the window and given
it a signal handler, the window is not yet visible. All windows in GTK+
are invisible when created. We have to make them visible so they can
appear using gtk_widget_show passing the window as the parameter to the function, as
in line 39.

After the initial windows are created, the gtk_main function should
be called, as illustrated in line 42. This starts GTK+’s event loop,
which means that events will begin to be processed. Whenever an event
occurs that has a signal handler associated with it, the function that
was associated with that window/signal combination will be called. In
this simple example, clicking the button will have no discernable
effect. Once the EndApp function is called by the delete_event handler, the
application will exit, returning 0, as shown in line 43.

The one missing piece is the EndApp function that we
introduced above, defined in lines 45 through 58. This function will be
called when the user closes the application window. All it does is call
the gtk_main_quit
function to exit out of the GTK+ event loop. If the gtk_main_quit function
was not called, then the window would close, but the application’s event
loop would still be trying to process events for the closed window.

Compiling the Example

In order to compile the example, you will need to
link in the GTK+ libraries. There are also a number of arguments specific
to GTK+ that you will need to pass
to the compiler. Rather than actually
remember all the command-line arguments, GTK+ provides you with a
utility program called gtk-config. Passing the argument `gtk-config–cflags` to
gcc will echo the
compile parameters GTK+ requires, and passing `gtk-config –libs` to
gcc will echo
back the required linking parameters. If the example was named “ex1.c”,
the command line to create an executable of “prog1″ would be:


gcc `gtk-config –cflags` \
`gtk-config –libs` ex1.c \
-o prog1




Figure 2: Advanced Example

#include <stdio.h>
#include <gtk/gtk.h>

/* — Function prototypes — */
gint EndApp (GtkWidget *widget, gpointer gdata);
void ButtonClicked (GtkWidget *widget, gpointer data);

int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *vbox;

/* — GTK+ initialization — */
gtk_init (&argc, &argv);

/* — Create the application window — */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

/* — Set the title of the window — */
gtk_window_set_title (GTK_WINDOW(window),”Pushbuttonexample”);

/*
* Connect signal handler to application window
* to gracefully exit the application.
*/
gtk_signal_connect (GTK_OBJECT (window), “delete_event”,
GTK_SIGNAL_FUNC (EndApp), NULL);

/* — Set the border width — */
gtk_container_border_width (GTK_CONTAINER (window), 15);

/* — Create a vertical packing box — */
vbox = gtk_vbox_new (FALSE, 0);

/* — Create 1st button and put it in the packing box — */
button = gtk_button_new_with_label (“Button 1″);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

/* — Signal handler for the first button — */
gtk_signal_connect (GTK_OBJECT (button), “clicked”
GTK_SIGNAL_FUNC(ButtonClicked),”Button 1″);

/* — Create 2nd button and put it in the packing box — */
button = gtk_button_new_with_label (“Button 2″);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

/* — Signal handler for the second button — */
gtk_signal_connect (GTK_OBJECT (button), “clicked”,
GTK_SIGNAL_FUNC (ButtonClicked), “Button 2″);
/* — Create 3rd button and put
* it in the packing box —
*/
button = gtk_button_new_with_label(
“Button 3″
);
gtk_box_pack_start (GTK_BOX (vbox),
button, FALSE, FALSE, 0);

/* — Signal handler for the third
* button —
*/
gtk_signal_connect (GTK_OBJECT (button),
“clicked”,
GTK_SIGNAL_FUNC (ButtonClicked),
“Button 3″);

/* —Put the packing boxand it’s
* widgets in the window—
*/
gtk_container_add (
GTK_CONTAINER (window), vbox);

/* — Make the window visible and all
* its widgets —
*/
gtk_widget_show_all (window);

/* — Start the gtk+ event loop — */
gtk_main ();
}

/*
* ButtonClicked
*
* Signal handler for the buttons. The
* fourth parameter on the
* gtk_signal_connect function will be
* passed into the signal handler as
* the data parameter. This allows a
* single signal handler to be used
* for all the button widgets.
*/
void ButtonClicked (GtkWidget *widget,
gpointer data)
{
printf (“%s clicked\n”, (char *) data);
}
/*
* EndApp
*
* Called when the application window is
* closed by the user. Returns FALSE to * close the application
window. A return * of TRUE would prevent the window from * closing.
*/
gintEndApp(GtkWidget *widget,gpointer gdata)
{
/* — Exit out of the event loop
* (gtk_main) —
*/
gtk_main_quit ();
/* — Allow the window to close — */
return (FALSE);
}



A Little Elaboration

A more elaborate example would have several widgets
in the window as well as some event, or signal, processing. The advanced
example shows a vertical packing box with several buttons placed
in it. Each button has a handler associated with it to handle a signal,
or event for that specific button. Rather than have a separate event or
signal handler for each button, a single handler can be used for all of
the buttons. This works because the handler can just be passed different
parameters depending on which button is pressed. Each button that’s
pressed will display a different message onto the console.








Compile GTK 2
Vertical Buttons: Each button outputs a separate
message.

Conclusion

As shown in the screen capture at left, clicking on
each of the buttons displays a different message on the console. Even
the largest applications such as word processors or spreadsheets simply
require more widgets and signal handlers.

As you can see from the
examples presented, graphical programming using the GTK+ libraries can
be simple and complete with just a bit of understanding. For more
information and examples, you should visit the GTK+ Web site at
http://www.gtk.org. There you can pick up the toolkit itself, and
perhaps have a look at one of their tutorials. There is even a
repository of GTK+ applications. The GTK+ Web site also links to a
couple of mailing lists for more interactive support.




Eric Harlow is a consultant for RDA Consultants in Timonium,
Maryland. In his spare time, he deprograms followers of a Redmond based
cult. He can be reached at
linuxgeek@yahoo.com.

Comments are closed.