gtk2-perl

About GNOME · Users · Developers · Bindings

Introduction to GUI Programming with Gtk2-Perl

Background

Graphical User Interface (GUI) programming perhaps is not as straightforward as one might hope. While there is a very steep learning curve, the good news is that it tops off quickly. There is, however, some initial mind bending involved to be able to understand the structure of a GUI application that is not required for their simpler command line/background cousins.

As a toolkit for developing GUI applications Gtk is no exception. It is, however, more straightforward than most.[1] Gtk is a graphical user interface toolkit written in C by a loose knit team of developers spread around the worl. The choice of C is obvious due to the universal availability of high quality C compilers and the relative ease with which other languages can access the libraries created by them. Even though C is not an object-oriented programming language things can still be implemented in an object-oriented fashion. Doing so requires some extra effort by both the library developers and the application programmers, to keep up with the type and lifespan of variables and a plethora of other issues that often can be ignored in languages designed specifically for object-oriented programming.

This is where Gtk2-Perl comes in providing bindings for the C libraries of Gtk to the Perl programming language. Due to a substantial amount of effort put in to these wrappers by the team that developed them, much of the overhead and book keeping of Gtk is handled automatically. There is not need to ref (increase the reference count of) or type-cast a variable in Gtk2-Perl; it is all auto-magical. All that is required from the programmer is to create the objects they need and the wrappers deal with all of the dirty work.

Prerequisites

This document should be read in concert with the documentation provided by the Gtk team. The tutorials and the API reference will prove invaluable for programming in Gtk2-Perl even though they are written for developers using Gtk. In 95% of the cases the C information will apply to Gtk2-Perl with only slight modification. The information about necessary to translate between the two will be covered in this document.

Event Driven Programming

Perhaps the most significant concept in for GUI programming is the event driven model. (definition) Whereas a typical batch mode tool runs all of its operations in a linear fashion, an interactive user interface must be prepared to run its operations in fairly random order. A user may click on any button she wishes, meander through the menus, fill in some text boxes, switch to another application for a while, only to return and finish what she earlier started. The program responds to user events, it is "event-driven." behavior is expected by users and is nearly impossible to implement without being event driven. Reduced to its basic elements, an event driven program has a central dispatcher, an queue on which events are placed as they occur, and subscribers that request to be notified when certain events make their way to the front of the queue. When the user clicks a button an event is placed on to the queue. When the dispatcher makes its way to the button clicked event it sends out notifications to everyone who expressed an interest in knowing the button has been clicked. [3] All of the application logic is implemented in callback functions who are called from within the library, by the dispatcher.

At a given point in time it is possible there are several events pending processing in the event queue in even the simplest of Gtk applications. As a general rule though, the event loop will spend 95% of its time idle, with no events to process. This is the case even if the application code itself has not connected to any signals. The most aesthetically beautiful and well designed application will do nothing more than sit there and look pretty if there are no signals are connected. The details of connecting signals in Gtk will be left for a little further through this document, but event driven programming is an important concept and a general understanding of it is required to build applications in Gtk.

Widgets; objects, parents, children, and inheritance

The root/parent of the Gtk library is GtkObject. It is abstract and of no use by itself, but other widgets are derived from it, and each other, to build up the entire Gtk widget library. [4] As part of the Gtk library's documentation includes a a page detailing the object hierarchy which is very useful when developing Gtk applications. Each node in the hierarchy links to the page containing the details of that widget, including all of the functions that operate on them, the signals they may emit, along with other useful information. This leads us to the most important skill required of a Gtk2-Perl programmer; translating the call signatures of the Gtk C functions in to their Gtk2-Perl counterparts. Since Gtk2-Perl is meant to be a one-for-one mapping of the C API to Perl, it would be an enormous undertaking and duplication of work to document the API information for Gtk2-Perl. Translation is accomplished by following some simple rules that will work in 95% of the cases encountered. The other 5% make use of features the Perl language affords programmers, such as multiple return values and strings/arrays that know their own length. Places where the Perl implementation differs significantly from C should be documented in the Gtk2::api man page. Also where the functionality of Glib/Gtk overlap with Perl, Perl's verrsion takes precedence. The Gtk2::api man page covers much of the information discussed here, some of it verbatim.

Name Spaces and Function Name Translation

Functions that have the following prefixes will be found under the name spaces provided to their right. These are meant to serve as examples for all of the Gtk2-Perl libraries.

  g_            Glib
  gtk_          Gtk2
  gdk_          Gtk2::Gdk
  gdk_pixbuf_   Gtk2::Gdk::Pixbuf
  pango_        Gtk2::Pango

Each widget has its own name space, which can be determined by the above rules when applied to the C function names.

  gtk_window_                   Gtk2::Window
  gtk_button_                   Gtk2::Button
  pango_font_description_       Gtk2::Pango::FontDescription

The above examples provide the tools needed to translate most of the C functions to their Perl object counterparts. The prefix is omitted as it is not needed since the Gtk2-Perl widget knows its type. As with Gtk C programming and any object oriented environment any descendant widget can be passed to a function which asks for for one of its ancestors. In C this would require a type cast, but with Perl's objects no such effort is required.

The following are two simple examples of how to determine the Perl function call from the C name.

  gtk_window_new                Gtk2::Window->new 
  gdk_pixbuf_new_from_file      Gtk2::Gdk::Pixbuf->new_from_file

Function Parameters

The C member functions always take an object as their first parameter; in Perl that parameter is implicitly passed by the -> operator. Therefore the first parameter of all member functions that operate on objects is omitted. After leaving out the first parameter, most functions take the exact same parameters as their C brethren. So gtk_window_set_title(GtkWindow * window, gchar * title) becomes $window->set_title($title) where $window is a Gtk2::Window and $title is a scalar (Perl variable) containing a string or the string itself. So to generalize this to a rule: omit the first parameter and pass the rest as normal.

  gtk_window_set_default_size (GtkWindow * window, gint width, gint height)
  $window->set_default_size ($width, $height);          

  gtk_calendar_select_month (GtkCalendar * calendar, guint month, guint year)
  $calendar->select_month ($month, $year);

Before moving on to some specific deviations from the C API it is worth noting that the Gtk2::api man page section titled "DIFFERENT CALL SIGNATURES OR SEMANTICS" contains a list of functions that differ from the C API in significant ways.

One type of exception to the above rule is functions that take arrays as parameters, there are several such cases. Some take a GList (linked list,) others take an array of objects as a single parameter and a number stating the size of that array, and finally some take a NULL terminated array of objects. Since Perl functions (and XS wrappers) can easily handle variable number of arguments (via the argument stack) these are all replaced by functions that do so. In order to implement this the variable length parameter is always the last in the function call. Some examples.

GList replaced by variable number of arguments:
  gtk_window_set_icon_list (GtkWindow * window, GList * list)
  $window->set_icon_list (@icons)

Same with the array moved to the end of the parameter list:
  gtk_list_insert_items (GtkList *list, GList *items, gint position) 
  $list->insert_items ($position, @items)
  
Array parameter and integer with the size of that array, replaced by variable number of arguments:
  gtk_curve_set_vector (GtkCurve *curve, int veclen, gfloat vector[])
  $curve->set_vector (@vector)

Same with the array moved to the end of parameter list:
  gtk_item_factory_create_items (GtkItemFactory * ifactory, guint n_entries, 
                                 GtkItemFactoryEntry * entries, gpoitner callback_data)
  $itemfactory->create_items ($callback_data, @entries)

Multiple New Functions

There are numerous places throughout the library where widgets have several "new" functions. This is typically a response to the lack default argument values in C, or an interface to common setups. In such cases the wrappers provide some added functionality via Perl's variable arguments. As a general rule, widgets that have text labels often have multiple new functions. In most cases when a text string is passed in to a new function it is assumed to be a new_with_mnemonic if one exists and new_with_label otherwise. The specific new functions are accessible through their full names. As always check the Gtk2::api man page for more information.

GtkButton's News:
  GtkWidget*  gtk_button_new                  (void);
  GtkWidget*  gtk_button_new_with_label       (const gchar *label);
  GtkWidget*  gtk_button_new_with_mnemonic    (const gchar *label);
  GtkWidget*  gtk_button_new_from_stock       (const gchar *stock_id);

In Gtk2-Perl
  $button = Gtk2::Button->new                        gtk_button_new
  $button = Gtk2::Button->new($str);                 gtk_button_new_with_mnemonic
  $button = Gtk2::Button->new_with_label($str);      gtk_button_new_with_label
  $button = Gtk2::Button->new_with_mnemonic($str);   gtk_button_new_with_mnemonic
  $button = Gtk2::Button->new_from_stock($stkid);    gtk_button_new_from_stock

Output variables

C functions can only return one value. In situations where more than one value is provided by a function variables are passed by reference, as write-through parameters. Perl is capable of returning more than one value from functions and Gtk2-Perl utilizes this ability to "perlify" the API. Any time the C API for a function specifies that a parameter is passed by reference and used for output those parameters will be returned in the order that they occur in the C function call.

  gtk_window_get_size (GtkWindow *window, gint *width, gint *height)
  ($width, $height) = $window->get_size
  
  gtk_calendar_get_date (GtkCalendar * calendar, guint year, guint month, guint day)
  ($year, $month, $day) = $calendar->get_date

Hello World! (First Application)

#!/usr/bin/perl -w

use strict;
use Gtk2 '-init';

my $window = Gtk2::Window->new;
my $label = Gtk2::Label->new ('Hello World!');
$window->add ($label);
$window->show_all;
Gtk2->main;
Hello World Screen Shot
(src)

The example above is very simplistic, but deserves dissection nonetheless. The first relevant line use Gtk2 '-init'; loads the Gtk2-perl module and causes its initialization procedure to be called, which in turn invokes the C library's initialization. There are very few circumstances when init should not be called in this manner. In those few cases or if preferred the same initialization may accomplished by calling Gtk2->init at any point after the Gtk module is loaded and before starting to use any of the Gtk2-Perl widgets and functions.

Every user interface object in Gtk is referred to as a widget. [5] The next line, my $window = Gtk2::Window->new;, creates a widget. This line of code creates a new Gtk2::Window, the primary object of almost all Gtk applications. Normally, the window is what gets wrapped by the title-bar, borders, resize targets, and maximize/minimize/close buttons. The next line, my $label = Gtk2::Label->new ('Hello World'); creates a new Gtk2::Label widget. Label widgets are one of the simplest and provide the programmer with a mechanism to place text strings in their applications.

The next line introduces a new concept, a container. Containers are widgets that hold within them one or more widgets. Containers are a fundamental concept of Gtk, they provide the programmer the ability to group buttons, labels, images, etc. together as they desire. A Gtk2::Window is a container that can hold a single widget, in this case the label just created. $window->add ($label); A container widget can hold within it other container widgets.

The next to last line of code, $window->show_all; causes the window widget and all of its children (contained) widgets to be made visible. The widgets will not immediately appear on the user's screen. In actuality the show_all method does not itself show the widgets, it places items on the event list requesting the realization of the widgets.

The finial statement in the example application invokes the Gtk event loop, Gtk2->main;. After executing this line of code the program will continually loop handling events, as mentioned in the section on Event Driven Programming. This call to the main loop will go on forever until the quit event is placed on the queue. This is done by calling the function Gtk2->main_quit. Upon handling of the quit event the line after Gtk2->main will be executed. A call to Gtk2>main can be thought of as a blocking function waiting for a Gtk2->main_quit; and yes calls to main can be nested, but that can get rather confusing, but at the same time very useful useful.


Simple Signals (Second Application)

#!/usr/bin/perl -w

use strict;
use Data::Dumper;
use Gtk2 '-init';

my $window = Gtk2::Window->new;
$window->set_title ('Simple Signals');

# signal 1, attached to the main window, when it is destroyed, clicked close 
# button, <alt-f4>, etc., quit the main loop
$window->signal_connect (destroy => sub { Gtk2->main_quit; });

my $button = Gtk2::Button->new ('Click Me To Quit');

# something to be passed by gtk to our signal handler, can be just about 
# anything perl has to offer
my $user_data = 'Hello';

# signal 2, attached to the button, when the button is clicked this function 
# is called.
$button->signal_connect (clicked => \&button_callback, $user_data);
$window->add ($button);

$window->show_all;
Gtk2->main;

sub button_callback 
{
        # print out the parameters we were passed: (button, user_data)
        print Dumper (@_);
        # then quit
        Gtk2->main_quit;
        1;
}
Simple Signals Screen Shot
(src)

The first difference between this script and the Hello World example above is the line $window->set_title ('Simple Signals'); as is probably obvious this line sets the title of the window. Another difference is the introduction of a new type of widget, a Gtk2::Button, my $button = Gtk2::Button->new ('Click Me To Quit');. Notice that an overloaded new function was utilized in this case. Passing a string in to the new call of Gtk2::Button results in a call to the C function gtk_button_new_with_mnemonic and thus the button will be labeled with the provided string.

The next differences, all important ones at that, are examples of connecting a signals. $window->signal_connect (destroy => sub { Gtk2->main_quit; }); and $button->signal_connect (clicked => \&button_callback, $user_data);. The signal_connect function can take either two or three parameters. The first parameter is always the signal name, in these cases 'destroy' and 'clicked'. The second parameter is a code reference. There are two common ways of providing it, in-line code or as a function reference. The destroy signal handler uses the in-line method. The function is defined and passed as the parameter. The clicked handler uses a reference to a function which is defined elsewhere in the script. The final optional parameter is a scalar that will be passed as the user data to the callback function.

The first signal connection in the above example, $window->signal_connect (destroy => sub { Gtk2->main_quit; }); is very common, in fact it should be in almost every Gtk2(-perl) application. It ensures that when the application's window is destroyed the current call to Gtk2->main exits. So when an application's window disappears the program will exit, leave it out and click the 'X' on an application's window and the problem will be obvious. As previously mentioned the callback is defined and passed as the parameter in this case. Since the only purpose of the callback is to exit the main loop there is no need to pass user data in to the callback function.

The signal connected to the button is more interesting, $button->signal_connect (clicked => \&button_callback, $user_data);. The event is clicked, so when the button is clicked the callback will be invoked. The callback function is passed as a reference to a function in this case. The \& creates a reference to the function button_callback. The finial parameter is a perl scalar (variable) containing a simple text string only for illustration. The callback serves no real function other than to print out the parameters it was passed via the Data::Dumper module's Dumper function. It then calls main_quit and the script will then exit.

Signals have many different call signatures, parameters passed to them. The call to Dumper is very useful when working with new callbacks, many times it will provide all of the information needed about the parameters passed. In the case of this callback function its output would be:

  $VAR1 = bless( {}, 'Gtk2::Button' );
  $VAR2 = 'Hello';
So this callback was passed are two parameters. the first is a Gtk2::Button, the one that was clicked, and the second is the user data provided in the signal_connect call. Placing a call to Dumper passing the parameters, @_, as the first thing in a new callback will list the data passed in to it. An alternative and/or supplemental source of information is the Gtk API reference. For a particular widget type there is normally a handful of events for subscription. In addition to those signals any signal that can be connected to one of that widget's parents can also be connect to the widget. This can make searching for the desired event to use as the signal name difficult as the widget's documentation must be consulted as well as the documentation for each of its parents. The documentation for each widget has a section listing and describing the signals that may be connected to it. It also details the parameters that will be provided to that callback function and their type.

Just as each type of signal handler is passed a specific set of parameters it may be required to return a value obeying certain rules. If unexplainable things are occurring in an application which relies on signal callbacks, timeouts, and/or idlers then the odds are good there is a problem with the return value(s) in these callbacks. Gtk2::Button's clicked callback does not need to return a value and neither does the window's destroy callback (attachable to any GktObject) in the example above. Glib::Idle and Glib::Timeout are examples of callbacks where the return value is vitally important. With the idle and timeout callbacks the return value specifies whether they should be called again or removed. If a callback function for a timeout returns a 1 it will be called again after the next timeout period, if it returns 0 it will no longer be called. Heed this warning, it is very easy to forget to return a value at the end of a callback function when one is required in which case often the return value of the last perl statement will be implicitly return causing unpredictable behavior. The information required to determine the necessary return value behavior can be found in the Gtk API on the pages of each widget under the the "Signal Prototypes" sections.

Widget Layout (Packing)

#!/usr/bin/perl -w

use strict;
use Gtk2 '-init';

use constant TRUE  => 1;
use constant FALSE => 0;

my $window = Gtk2::Window->new;
$window->set_title ('Widget Layout');
$window->signal_connect (destroy => sub { Gtk2->main_quit; });
$window->set_border_width(3);

my $vbox = Gtk2::VBox->new(FALSE, 6);
$window->add($vbox);

my $frame = Gtk2::Frame->new('Buttons');
$vbox->pack_start($frame, TRUE, TRUE, 0);
$frame->set_border_width(3);

my $hbox = Gtk2::HBox->new(FALSE, 6);
$frame->add($hbox);
$hbox->set_border_width(3);

my $inc_button = Gtk2::Button->new('_Click Me');
$hbox->pack_start($inc_button, FALSE, FALSE, 0);
my $count = 1;

my $quit_button = Gtk2::Button->new('_Quit');
$hbox->pack_start($quit_button, FALSE, FALSE, 0);
$quit_button->signal_connect( clicked => sub {
		Gtk2->main_quit;
	});

my $label = Gtk2::Label->new('Clicked 0 times.');
$vbox->pack_start($label, TRUE, TRUE, 0);

# has to be done after we've created the label so we can get to it
$inc_button->signal_connect( clicked => sub {
		$label->set_text("Clicked $count times.");
		$count++;
	});

$window->show_all;
Gtk2->main;
Widget Layout Screen Shot
(src)

Widget Layout Graph
(src)

Laying out the widgets of user interface can be as difficult as any part of programming a graphical application. The complexities of the process are lessened first and foremost by experience, but second to that by the proper approach.

Dynamic Layout

Gkt is a dynamic layout toolkit, meaning the size and location of widgets are not explicitly specified or fixed. Resizing of the window of an application will cause the layout of the widgets within that window to be modified in a reasonable and defined fashion. A static application can be created if such a thing is desired. With the dynamic layout of Gtk widgets are placed in a hierarchy. A top level object, normally a Gtk2::Window, contains another widget that can in turn contain one or more widgets.

Designing the layout

For a simple application that pops up a window with a button or two and a text label, as the example does, the process discussed here is overkill, but when creating a more typical application then it is apt to save both time and trouble. The first step is to get an idea of what the user interface is going to look like. This is best done with a simplistic sketch on a scrap of paper. After the sketch is finalized the next step is to create a diagram showing the hierarchy of widgets required to create the application as drawn. There is some skill involved in creating an application that will look as desired/designed with the Gtk widgets, but it will come with a few trips through the process.

At the root of the tree should be a Gtk2::Window, which can contain a single child. Normally the window will contain either a VBox or HBox. In the example above a VBox is used. That VBox has two children the first of which is a frame and the last of which is a Label. The label has no children and therefore is a leaf node in the widget hierarchy. A Frame is a simple widget with a text label and outline box, it can contain a single widget. The frame in this case has the text 'Button' and contains a HBox. In that HBox are two buttons one with the text 'Click Me' and the other with the text 'Quit'. Both of these buttons are leaf nodes in that they contain no other widgets. A simplistic tree graph of this widget hierarchy is shown above.

Translating From Design to Code

Once the widget tree has been built up translating it to equivalent Perl code is pretty straightforward. Start at the root of the tree and create that object, in this case $window = Gtk2::Window->new('Widget Layout');. Then create that widget's child, $vbox = Gtk2::VBox->new(FALSE, 6);, and place it in the parent, $window->add($vbox);. The method to place a widget within another that accepts only one is normally called add, Gtk2::Window is such a widget. The next step is to create the frame which is done with the code $frame = Gtk2::Frame->new('Buttons');. This widget goes in to $vbox and since $vbox can contain multiple widgets the add add member function is not used, instead pack_start is utilized, $vbox->pack_start($frame, TRUE, TRUE, 0);. The meaning of the parameters passed to pack_start will be left for a few paragraphs down the road. In to $frame goes $hbox, $hbox = Gtk2::HBox->new(FALSE, 6); $frame->add($hbox); Then the first button would be created, $inc_button = Gtk2::Button->new('Click Me'); and then placed in to $hbox, $hbox->pack_start($inc_button, FALSE, FALSE, 0);. From here the process continues completing the remainder of the widget tree.

Fine-Tuning the Layout

After building up the widget hierarchy some time and effort is required to get the exact look desired. There are several tools at the disposal of the programmer to manipulate the details of the layout. The member function set_border_width is available on all Gtk2::Container derivative widgets, including windows, frames, vertical and horizontal boxes. The set_border_width member function, $window->set_border_width(3);, controls the amount of space left around the outside of the container object.

In addition to set_border_width vertical and horizontal boxes have several parameters that control how widgets are packed. The new call of a Gtk2::Box derivative takes two parameters the first of which is whether or not the widgets contained by the box should be allocated equal amounts of space, the second is spacing with the default number of pixels to place between widgets. In addition to the new function there are several important options controlled when the pack_start or pack_end functions are called. The first parameter expand controls whether the child should be given extra space allocated to the box. The second controls whether the widget should expand to fill that extra space or if the space should be consumed as extra padding. The finial parameter is a number of pixels of padding to be used around this widget. This number is in addition to the default padding, the number passed to the new call. The vertical and horizontal box widgets have a function pack_end that is similar to pack_start in all ways except that widgets added with it will be placed near the bottom. This will not be noticeable if the widgets are packed with an expand value of true.

Where To Go From Here

First and foremost the most important thing to do is practice. Many questions will be answered in the Gtk2-Perl FAQ. If the answer can't be found there try the mailing list.

Further reading on the subject is avaiable both through on-line reasources as mentioned throughout this document, as well as printed books. There are no Gtk2-Perl books, but just as the C documentation can be applied to Gtk2-Perl the topics covered in such books will apply.

Corrections and suggestions are welcome. They should be directed to rwmcfa1 at neece dot com.

Footnotes

  1. Obviously this is the author's (un-)biased opinion. Curious about some of the many GUI toolkits available? Take a look here.
  2. Perl Resources:
  3. Event driven models have many uses outside of GUI applications. Any environment where events can happen in indeterminate order and at arbitrary times can benefit from event driven models. Operating system software often makes extensive use of similar techniques.
  4. That is only half of the story though, GtkObject is not the root in reality, above it lies GObject, which provides some usefully functionality that is independent of GUI applications, GObjects can be useful in any programming context where objects capable of emitting signals are desired.
  5. In addition to the user interface widgets there are widgets who have no graphical components and exist for other specialized purposes.

Credits & Copyright

Author:         Ross McFarland

COPYRIGHT AND LICENCE

Copyright (C) 2003 

This document is copyrighted by its author and may be redistributed so long as
this notice remains attached and unmodified.