Previous Next Contents

2. Getting Started

The first thing to do of course, is download the GTK source and install it. You can either get the distro with GIMP, or d/l it from Peter Mattis's home dir ftp.xcf.berkely.edu/pub/pmattis. Hopefully there will be a GTK distribution page up shortly. GTK uses GNU autoconf for configuration. Once untar'd, type configure --help to see a list of options.

To begin our introduction to GTK, we'll start with the simplest program possible. This program will create a 200x200 pixel window and has no way of exiting except to be killed using the shell.

#include <gtk/gtk.h>

int main (int argc, char *argv[])
{
    GtkWidget *window;
    
    gtk_init (&argc, &argv);
    
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_show (window);
    
    gtk_main ();
    
    return 0;
}

All programs will of course include the gtk/gtk.h which declares the variables, functions, structures etc. that will be used in your GTK application.

The next line

gtk_init (&argc, &argv);

calls the function gtk_init(gint *argc, gchar ***argv) which will be called in all GTK applications. This sets up a few things for us such as the default visual and color map and then proceeds to call gdk_init(gint *argc, gchar ***argv). This function initializes the library for use, sets up default signal handlers, and checks the arguments passed to your application on the command line, looking for one of the following:

It removes these from the argument list, leaving anything it does not recognize for your application to parse or ignore. This creates a set of standard arguments excepted by all GTK applications.

The next two lines of code create and display a window.

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_widget_show (window);

The GTK_WINDOW_TOPLEVEL argument specifies that we want the window to undergo window manager decoration and placement. Rather than create a window of 0x0 size, a window without children is set to 200x200 by default so you can still manipulate it.

The gtk_widget_show() function, lets GTK know that we are done setting the attributes of this widget, and it can display it.

The last line enters the GTK main processing loop.

gtk_main ();

gtk_main() is another call you will see in every GTK application. When control reaches this point, GTK will sleep waiting for X events (such as button or key presses) to occur. In our simple example however, events are ignored.

2.1 Hello World in GTK

OK, now for a program with a widget (a button). It's the classic hello world ala GTK. This would make a good base for a new GTK application.


#include <gtk/gtk.h>

/* this is a callback function. the data arguments are ignored in this example..
 * More on callbacks below. */
void hello (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello World\n");
}

/* another callback */
void destroy (GtkWidget *widget, gpointer *data)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    /* GtkWidget is the storage type for widgets */
    GtkWidget *window;
    GtkWidget *button;
    
    /* this is called in all GTK applications.  arguments are parsed from
     * the command line and are returned to the application. */
    gtk_init (&argc, &argv);
    
    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* when the window is given the "destroy" signal (this can be given
    * by the application, or the window manager, the function destroy
    * will be called as defined above.  The data passed to the callback
    * function is NULL and is ignored in the callback. */
    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (destroy), NULL);
    
    /* sets the border width of the window. */
    gtk_container_border_width (GTK_CONTAINER (window), 10);
    
    /* creates a new button with the label "Hello World". */
    button = gtk_button_new_with_label ("Hello World");
    
    /* When the button receives the "clicked" signal, it will call the
     * function hello() passing it NULL as it's argument.  The hello() function is
     * defined above. */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (hello), NULL);
    
    /* This will cause the window to be destroyed by calling
     * gtk_widget_destroy(window) when "clicked.  Again, the destroy
     * signal could come from here, or the window manager. */
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));
    
    /* this packs the button into the window (a gtk container). */
    gtk_container_add (GTK_CONTAINER (window), button);
    
    /* the final step is to display this newly created widget... */
    gtk_widget_show (button);
    
    /* and the window */
    gtk_widget_show (window);
    
    /* all GTK applications must have a gtk_main().     Control ends here
     * and waits for an event to occur (like a key press or mouse event). */
    gtk_main ();
    
    return 0;
}

2.2 Compiling Hello World

To compile use:

gcc -Wall -g helloworld.c -o hello_world -L/usr/X11R6/lib \
    -lglib -lgdk -lgtk -lX11 -lXext -lm

The libraries above must all be in your default search paths, if not, add -L<library directory> and gcc will look in these directories for the needed libraries. For instance, on my Debian Linux system, I have to add -L/usr/X11R6/lib for it to find the X11 libraries.

The order of the libraries are significant. The linker has to know what functions it needs from a library before it processes it.

The libraries we are linking in are:

2.3 Theory of Signals and Callbacks

Before we look in detail at hello world, we'll discuss events and callbacks. GTK is an event driven toolkit, which means it will sleep in gtk_main until an event occurs and control is passed to the appropriate function.

This passing of control is done using the idea of "signals". When an event occurs, such as the press of a mouse button, the appropriate signal will be "emitted" by the widget that was pressed. This is how GTK does most of its useful work. To make a button perform an action, we set up a signal handler to catch these signals and call the appropriate function. This is done by using a function such as:

gint gtk_signal_connect (GtkObject *object,
                         gchar *name,
                         GtkSignalFunc func,
                         gpointer func_data);

Where the first argument is the widget which will be emitting the signal, and the second, the name of the signal you wish to catch. The third is the function you wish to be called when it is caught, and the fourth, the data you wish to have passed to this function.

The function specified in the third argument is called a "callback function", and should be of the form:

void callback_func(GtkWidget *widget, gpointer *callback_data);

Where the first argument will be a pointer to the widget that emitted the signal, and the second, a pointer to the data given as the last argument to the gtk_signal_connect() function as shown above.

Another call used in the hello world example, is:

gint gtk_signal_connect_object (GtkObject *object,
                                gchar  *name,
                                GtkSignalFunc func,
                                GtkObject *slot_object);

gtk_signal_connect_object() is the same as gtk_signal_connect() except that the callback function only uses one argument, a pointer to a GTK object. So when using this function to connect signals, the callback should be of the form:

    void callback_func (GtkObject *object);

Where the object is usually a widget. We usually don't setup callbacks for gtk_signal_connect_object however. They are usually used to call a GTK function that accepts a single widget or object as an argument, as is the case in our hello world example.

The purpose of having two functions to connect signals is simply to allow the callbacks to have a different number of arguments. Many functions in the GTK library accept only a single GtkWidget pointer as an argument, so you want to use the gtk_signal_connect_object() for these, whereas for your functions, you may need to have additional data supplied to the callbacks.

2.4 Stepping Through Hello World

Now that we know the theory behind this, lets clarify by walking through the example hello world program.

Here is the callback function that will be called when the button is "clicked". We ignore both the widget and the data in this example, but it is not hard to do things with them. The next example will use the data argument to tell us which button was pressed.

void hello (GtkWidget *widget, gpointer *data)
{
    g_print ("Hello World\n");
}

Here is another callback function which just quits by calling gtk_main_quit(). Not really much to say about this, it is pretty self explanatory.

void destroy (GtkWidget *widget, gpointer *data)
{
    gtk_main_quit ();
}

I assume you know about the main() function... yes, as with other applications, all GTK applications will also have one of these.

int main (int argc, char *argv[])
{

This next part, declares a pointer to a structure of type GtkWidget. These are used below to create a window and a button.

    GtkWidget *window;
    GtkWidget *button;

Here is our gtk_init again. As before, this initializes the toolkit, and parses the arguments found on the command line. Any argument it recognizes from the command line, it removes from the list, and modifies argc and argv to make it look like they never existed, allowing your application to parse the remaining arguments.

    gtk_init (&argc, &argv);

Create a new window. This is fairly straight forward. Memory is allocated for the GtkWidget *window structure so it now points to a valid structure. It sets up a new window, but it is not displayed until below where we call gtk_widget_show(window) near the end of our program.

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

Here is an example of connecting a signal handler to an object, in this case, the window. Here, the "destroy" signal is caught. This is emitted when we use the window manager to kill the window, or when we send the gtk_widget_destroy() call passing in the window widget as the object to destroy. By setting this up, we handle both cases with a single call. Here, it just calls the destroy() function defined above with a NULL argument, which quits GTK for us. This will allow us to use the window manager to quit the program.

The GTK_OBJECT and GTK_SIGNAL_FUNC are macros that perform type casting for us, as well as aid the readability of the code.

    gtk_signal_connect (GTK_OBJECT (window), "destroy",
                        GTK_SIGNAL_FUNC (destroy), NULL);

This next function is used to set an attribute of a container object. This just sets the window so it has a blank area along the inside of it 10 pixels wide where no widgets will go. There are other similar functions which we will look at in the section on Setting Widget Attributes

And again, GTK_CONTAINER is a macro to perform type casting.

    gtk_container_border_width (GTK_CONTAINER (window), 10);

This call creates a new button. It allocates space for a new GtkWidget structure in memory, initializes it, and makes the button pointer point to it. It will have the label "Hello World" on it when displayed.

    button = gtk_button_new_with_label ("Hello World");

Here, we take this button, and make it do something useful. We attach a signal handler to it so when it emits the "clicked" signal, our hello() function is called. The data is ignored, so we simply pass in NULL to the hello() callback function. Obviously, the "clicked" signal is emitted when we click the button with our mouse pointer.

    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (hello), NULL);

We are also going to use this button to exit our program. This will illustrate how the "destroy" signal may come from either the window manager, or our program. When the button is "clicked", same as above, it calls the first hello() callback function, and then this one in the order they are set up. You may have as many callback function as you need, and all will be executed in the order you connected them. Because the gtk_widget_destroy() function accepts only a GtkWidget *widget as an argument, we use the gtk_signal_connect_object() function here instead of straight gtk_signal_connect().

    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));

This is a packing call, which will be explained in depth later on. But it is fairly easy to understand. It simply tells GTK that the button is to be placed in the window where it will be displayed.

    gtk_container_add (GTK_CONTAINER (window), button);

Now that we have everything setup the way we want it to be. With all the signal handlers in place, and the button placed in the window where it should be, we ask GTK to "show" the widgets on the screen. The window widget is shown last so the whole window will pop up at once rather than seeing the window pop up, and then the button form inside of it. Although with such simple example, you'd never notice.

    gtk_widget_show (button);

    gtk_widget_show (window);

And of course, we call gtk_main() which waits for events to come from the X server and will call on the widgets to emit signals when these events come.

    gtk_main ();
And the final return. Control returns here after gtk_quit() is called.
    return 0;

Now, when we click the mouse button on a GTK button, the widget emits a "clicked" signal. In order for us to use this information, our program sets up a signal handler to catch that signal, which dispatches the function of our choice. In our example, when the button we created is "clicked", the hello() function is called with a NULL argument, and then the next handler for this signal is called. This calls the gtk_widget_destroy() function, passing it the window widget as it's argument, destroying the window widget. This causes the window to emit the "destroy" signal, which is caught, and calls our destroy() callback function, which simply exits GTK.

Another course of events, is to use the window manager to kill the window. This will also cause the window to emit the "destroy" signal, and again the destroy() callback is called which exits GTK.

Note that these signals are not the same as the Unix system signals, and are not implemented using them, although the terminology is almost identical.


Previous Next Contents