GTK+ and Glade3 GUI Programming Tutorial - Part 1

GTK+ Text Editor using Glade3

Designing a User Interface using Glade3

In part 1 of the GTK+ and Glade3 GUI Programming Tutorial series, we will be designing the graphical user interface (GUI) for a GTK+ text editor application (shown left) which will be used throughout these tutorials. This GUI design will be created using the Glade Interface Designer and is completely independent of the programming language used to implement the design, which will come in subsequent tutorials.

GTK+ and Glade3 GUI Programming Tutorial Contents

  1. Part 1 - Designing a User Interface Using Glade 3
    1. Quick Overview of GTK+ Concepts
    2. Introduction to Glade3
    3. Getting Familiar with the Glade Interface
    4. Manipulating Widget Properties
    5. Specifying Callback Functions for Signals
    6. Adding Widgets to the GtkWindow
    7. How Packing Effects the Layout
    8. Editing the Menu (or Toolbar)
    9. Final Touches to the Main Window
    10. Getting Additional Help Using Glade
    11. What Next?
  2. Part 2 - Choosing a Programming Language for GTK+ Development
    1. Which is the BEST Language?
    2. Language Choice Considerations
    3. A Look at Python vs. C
    4. What Next?
  3. Part 3 - Writing a Basic Program to Implement the Glade File
    1. Setting Up Your Development Environment
    2. GtkBuilder and LibGlade
    3. The Minimal Application
    4. Compiling and Running the Application
    5. Stepping Through the Code
    6. What's Next?

Quick Overview of GTK+ Concepts

If you have no experience with GTK+, you may struggle with some of the concepts I am going to cover. Although I am going to attempt to teach some of these concepts on the fly, it would serve you well to read up on these ideas further, perhaps after working through part 1 of this tutorial. Understanding the fundamental concepts of GTK+ will be instrumental in your ability to effectively use Glade.

First of all, GTK+ is not a programming language. GTK+ is a toolkit, or a collection of libraries, which developers can use to develop GUI applications for Linux, OSX, Windows, and any other platform on which GTK+ is available. It can be thought of in the same terms as MFC or the Win32 API on Windows, Swing and SWT in Java, or Qt (the "other" Linux GUI toolkit used by KDE).

Although GTK+ itself is written in C, there are a multitude of language "bindings" allowing programmers to develop GTK+ applications in the language of their choice including C++, Python, Perl, PHP, Ruby, and many others.

GTK+ is based on 3 main libraries: Glib, Pango, and ATK, however, we primarily work with GTK+ and let GTK+ do it's magic with those 3 libraries. GLib wraps most of the standard C library functions for portability (allowing your code to run on Windows and Linux if desired). We use GLib a lot when working in C or C++, which I will explain more thoroughly when implementing our design using C. Higher-level languages such as Python and Ruby won't have to worry about GLib as they have their own standard libraries which provide similar functionality.

GTK+ and associated libraries implement an object oriented approach through GObject. How this works isn't important just yet, and different programming languages will reveal this to you differently, however, it's important to understand that GTK+ uses object orientation (yes, even in C).

Every piece of a GTK+ GUI is comprised of one or more "widgets" which are objects. All widgets will be derived from a base widget called GtkWidget. For example, an application's window is a widget called GtkWindow. The toolbar within that window is a widget called GtkToolbar. Although a GtkWindow is also a GtkWidget, a GtkWidget is not neccesarily a GtkWindow. Child widgets are derived from their parent objects to extend the functionality of that object. These are standard OOP (object oriented programming) concepts (hint: Google search "object oriented programming" if this is a new concept).

We can look at any widget in the GTK+ reference documentation to see which objects it is derived from. In the case of GtkWindow, it looks something like this:

  GObject
   +----GInitiallyUnowned
         +----GtkObject
               +----GtkWidget
                     +----GtkContainer
                           +----GtkBin
                                 +----GtkWindow

As you can see, a GtkWindow is derived from GtkBin which is derived from GtkContainer, and so on. For your first application, you don't need to worry about anything above the GtkWidget object. The reason this heirarchy is so important is because when you're looking for functions, properties, and signals for any particular widget, you need to realize that the functions, properties, and signals of it's parent objects apply to it as well. In part 2, this will become even more apparent when writing code for this example application.

We also begin to see a naming convention emerge. This is pretty handy. We can easily tell what library an object or function is from. All objects beginning with Gtk are from GTK+. Later, we'll see things like GladeXML which is part of Libglade or GError which is part of GLib. All objects (and thus Widgets) are in camel case. The functions which manipulate these objects are in lower-case with underscores for spaces. For example, gtk_window_set_title() is a function to set the title property of a GtkWindow object.

All the reference documentation you will need is available online from library.gnome.org/devel/references, however, it is much easier to use Devhelp which is likely available as a package for your distribution. Devhelp allows you to browse and search the API documentation for the libraries you have installed on your system (assuming you install that libraries documentation as well).

More information on GTK+ and Glib:

Introduction to Glade3

Glade is a RAD (Rapid Application Development) tool for designing GTK+ applications. Glade is a GTK+ application itself. It is simply a piece of software developers use to simplify the process of laying out an application's interface. Glade creates what will hereforth be refered to a s a "glade file". A glade file is actually an XML file which describes the heirachy of the widgets comprising the interface.

Glade originally generated C code to build the GUI (and you'll still find examples and tutorials doing this). This was later discouraged in favor of using a library, Libglade, to build the interface at run time. And finally, as of Glade3, the old method has become deprecated. That means the ONLY thing glade does is allow you to generate a glade file which describes how the GUI is going to be built. This allows more flexibility with the developer, prevents having to re-compile applications when a minor interface change is needed, and allows more programming languages to be used with Glade.

Glade3 has had significant changes since previous versions such as Glade2. Glade3 has been available for some time and you shouldn't have any problems obtaining it. The package manager for your distribution (yum, aptitude, etc.) should have Glade3 available. You should note however, that the package will have 3 in it. Where 'glade' may be the name for the old package, 'glade-3' or 'glade3' will be the package name for the new version on which this tutorial is based. Glade is also available from source at glade.gnome.org.

Getting Familiar with the Glade Interface

Start up Glade and let's get familiar with it's interface. I will be referring to various aspects of Glade by the names described here. On the left hand side is the "Palette". The Palette is like that of a graphics editing application. It is a palette of GtkWidgets which you can use to design your application. In the middle area (which is empty when you first start Glade) is the "Editor". This is where you see your design in progress. On the right hand side is the "Inspector" on top and the widget "Properties" below that. The Inspector shows your design as a tree allowing you to access and view the heirarchy of the widgets making up your design. We manipulate various properties of widgets in the Properties tabs, including specifying callback functions for signals (explained later).

So, the verfy first thing we're going to do, is create a Toplevel widget and save our file. To do this, Click on the GtkWindow icon  GtkWindow in the Palette under the 'Toplevels' section. You should notice a gray box show up inside the Editor area of Glade. This is the workable area of a GtkWindow. The titlebar, close button, etc. will be added to the widget by the window manager (ie: GNOME) so we don't see it while editing. We will always start with a toplevel widget in Glade, typically a GtkWindow.

Before going further, save the file as "tutorial.glade".

Glade3

Now the file you just saved, "tutorial.glade", is an XML file. If you were to open it up in a text editor, it would look something like this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.0 on Tue Nov 20 14:05:37 2007 -->
<glade-interface>
  <widget class="GtkWindow" id="window1">
    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    <child>
      <placeholder/>
    </child>
  </widget>
</glade-interface>

As you can see, it's just a simple XML file. In part2 we will be using C with Libglade to parse this XML file and generate the UI at run-time. Being XML, this could just as easily be parsed by a Python program or any other language. As we continue to work in Glade, this file will be updated to describe our interface in this XML format any time we save. Exit out of your text editor and return to Glade.

Manipulating Widget Properties

The Editor of Glade now shows an empty GtkWindow widget. We are going to manipulate some of the widget's properties. If you look in the Properties pane of Glade, you will see 4 tabs: 'General', 'Packing', 'Common', and 'Signals'. Let's talk about the first 2 tabs. GtkWidgets typically have various properties which manipulate how they function and/or how they are displayed on the screen.

If you look at the reference documentation for a GtkWidget and scroll down to the "Properties" section, you'll see a list of the properties for a GtkWindow. These are typically the properties which appear in the 'General' tab of the Glade properties pane and will vary from widget to widget. The name property exists for every widget in Glade and is what we will use to reference the widget when it comes time to write code for the application. Change the 'name' property of this GtkWindow from "window1" to "window". Then, add the text "GTK+ Text Editor" to the 'Window Title' property. 

GtkWindow Properties in Glade

We'll discuss the 'Packing' tab in a bit, but first, let's look at the 'Common' tab. This tab also contains properties which belong to our GtkWindow, however, we don't see them in the reference documentation for GtkWindow. This is because this is where we set properties which areinherited from parent objects. Looking at the reference documentation for a GtkWidget in the section called "Object Hierarchy", you'll see the objects from which GtkWindow is derived. Click on the GtkContainer link to jump to the reference documentation for a GtkContainer. You'll notice that GtkContainer has a property called "border-width" and we have a property in Glade for "Border width" at the bottom of the 'Common' tab. We'll learn more about what a container widget is later, however, this demonstrates how important that object heirarchy is. Since many widgets are derived from GtkContainer, Glade puts it's properties into the 'Common' tab. 

In the "Object Hierarchy" section of the  reference documentation for a GtkContainer you'll see that it is derived from a GtkWidget. Now click the GtkWidget link in to jump to the reference documentation for a GtkWidget. The GtkWidget has a bunch of properties, many of which are also shown in the 'Common' tab of Glade's Properties pane. These are properties which are common to all GTK+ widgets since all GTK+ widgets are derivitives of GtkWidget.

Common properties of GtkWindow in Glade

Specifying Callback Functions for Signals

Objects emit a "signal" when something that might be useful to the programmer happens. These are similiar to "events" from Visual Basic. If a user does anything within your GUI, chances are they are emitting signals. As a programmer, you choose which signals you want to capture and perform a task, and connect a callback function to that signal.

The first signal we'll learn, and the one which you'll use in just about every GTK+ application you write, is the "destroy" signal emitted by GtkObject. This signal is emitted whenever a GtkObject is destroyed. This is important, because when the user closes the window through the little 'x' up in the title bar or any other means, the widget is destroyed. We want to capture this signal and exit our application properly. This is better illustrated when we write code for this GUI, however, for now, let's just specify the function we want to call when the "destroy" signal is emitted for our GtkWindow.

Look at the 'Signals' tab in the Glade Properties pane. You see a tree view where GtkWindow and each of the objects from which it is derived are listed. If you expand the GtkObject, you'll see all the signals emitted by GtkObject. These correspond to the reference documentation for a GtkObject in the "Signals" section.

Under the 'Handler' column, click the gray text "<Type here>" to begin editing. Select 'on_window_destroy' from the drop down and then hit ENTER. We can type anything we want here, however, glade provides a drop-down list of some of the mroe common callback function naming conventions. How this value is used depends on how the programmer connects signals in the code, however, for this tutorial, we want the GtkWindow's "destroy" signal to be associated with the handler string "on_window_destroy". We'll look at this closer in Part 2.

GtkWindow Signals in Glade

At this point, we actually have a working GUI. We could write a few lines of code in C, Python, Ruby, or any number of programming languages which would show our empty window and then properly terminate when we clicked the "x" in the titlebar. For this tutorial however, I will be showing you how to build the entire GUI in Glade3 before writing any code.  However, to satisfy any possible curiosity, if you would like to see what a simple program looks like that would implement this Glade interface so far...

In C

/*
First run tutorial.glade through gtk-builder-convert with this command:
  gtk-builder-convert tutorial.glade tutorial.xml
  
Then save this file as main.c and compile it using this command
(those are backticks, not single quotes):
  gcc -Wall -g -o tutorial main.c `pkg-config --cflags --libs gtk+-2.0` -export-dynamic
  
Then execute it using:
  ./tutorial
*/
#include <gtk/gtk.h>

void 
on_window_destroy (GtkObject *object, gpointer user_data)
{
    gtk_main_quit ();
}

int
main (int argc, char *argv[])
{
    GtkBuilder      *builder; 
    GtkWidget       *window;

    gtk_init (&argc, &argv);

    builder = gtk_builder_new ();
    gtk_builder_add_from_file (builder, "tutorial.xml", NULL);
    window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
    gtk_builder_connect_signals (builder, NULL);

    g_object_unref (G_OBJECT (builder));
        
    gtk_widget_show (window);                
    gtk_main ();

    return 0;
}

In Python (note: you must set the 'visible' property of  'window' to "Yes" in the 'Common' properties tab in Glade)

#!/usr/bin/env python

# First run tutorial.glade through gtk-builder-convert with this command:
# gtk-builder-convert tutorial.glade tutorial.xml
# Then save this file as tutorial.py and make it executable using this command:
# chmod a+x tutorial.py
# And execute it:
# ./tutorial.py

import pygtk
pygtk.require("2.0")
import gtk

class TutorialApp(object):       
	def __init__(self):
	    builder = gtk.Builder()
	    builder.add_from_file("tutorial.xml")
	    builder.connect_signals({ "on_window_destroy" : gtk.main_quit })
	    self.window = builder.get_object("window")
	    self.window.show()

if __name__ == "__main__":
	app = TutorialApp()
	gtk.main()

Again, I'm not going to go over the details of the code used to implement this GUI in this part of the tutorial, but instead focus on using Glade3. However, you can see that implementing an interface designed in Glade is just a few lines of code in the language of your choosing!

Adding Widgets to the GtkWindow

If you recall from the reference documentation for a GtkWindow, the GtkWindow is a derivative of GtkContainer. Widgets derived from GtkContainer are container widgets, meaning they can contain other widgets. This is another fundamental concept of GTK+ programming. If you come from a Windows programming background, you may be expecting to just drop a bunch of widgets onto the window and drag them around into the position you want them. But this is not how GTK+ works--and for good reason.

GTK+ widgets "packed" into various containers. Containers can be packed into containers into containers and so forth. There are various packing properties which effect how space is allocated for widgets packed into containers. Through these packing properties and nesting containers, we can have complex GUI designs without having to write code to handler the resizing and re-positioning of our widgets. 

This is probably one of the more difficult concepts for a new GTK+ developer, so let's just see it in action!

The GtkWindow is a derivative of the container GtkBin which is a container that contains only one child widget. But this text editor is going to have 3 main sections; a menu bar, a text editing area, and a status bar. Therefore, we use a fundamental GTK+ widget, the GtkVBox. The GtkVBox (vertical box) is a container widget which can contain any number of child widgets stacked up vertically like rows (GtkHBox is the horizontal equivelant).

Click on the GtkVBox icon GtkVBox in Glade in the Glade Palette under the 'Containers' section. You'll notice that the 'Select' toolbar button on the top of the Glade window is no longer depressed and your mouse cursor is the GtkVBox icon with a plus (+) sign when hovering over the Glade Editor. This means you are ready to drop the GtkVBox somewhere. Click in gray area of the Editor which is the empty space of the GtkWindow. 

A dialog box pops up asking you for the 'Number of items'. In this case we want 3 rows, so leave it as 3 and click 'OK'.

GtkVBox in Glade

You should see the GtkWindow in the Editor divided into 3 rows now. These are the 3 empty child widgets of the GtkVBox we just added. You should also notice that the 'Select' toolbar at the top of Glade is once again depressed--allowing you to select widgets within the Editor.

Adding a GtkVBox in Glade

Next, click on the GtkMenuBar icon GtkMenuBar in Glade in the Glade Palette under 'Containers'. Drop this one in the top row of the GtkVBox you just added.

GtkMenuBar in GtkVBox

Now click on the GtkScrolledWindow icon GtkScrolledWindow in Glade in the Glade Palette under 'Containers'. Drop this one in to the middle row of the GtkVBox. When you do that, it may not seem like anything has happened. However, you should notice that that middle row looks selected. It's not--the GtkScrolledWindow is.The reason you don't see anything, is because a GtkScrolledWindow doesn't have any initial visible components. It's a container which will provide scroll bars when it's child widget gets too large. We'll need this for our text editing widget. 

GtkToolbar in GtkVBox

Click the GtkTextView icon GtkTextView in Glade in the Glade Palette under 'Control and Display'. Drop this one right on top of that GtkScrolledWindow (the middle row). We have now just added the GtkTextView to the GtkScrolledWindow which was added to the GtkVBox.

GtkScrolledWindow in GtkVBox

Finally, click on the GtkStatusbar icon GtkStatusbar in Glade in the Glade Palette under 'Control and Display' and drop it into the bottom row.

GtkStatusbar in GtkVBox

And there you have it; the basic layout of our GTK+ text editor.  If you look at the Glade Inspector you will see the parent-child relationship of our design. 

Glade Inspector

The Inspector will come in handy. You cannot always click on a widget in the Editor as it might not be visible. For example, you cannot click on the GtkScrolledWindow we added, because you can only see it's child, the GtkTextView. Therefore, if you need to change the properties of "scrolledwindow1", you will have to select it in the Inspector.

I mentioned earlier how "packing" is an often frustrating concept to new GTK+ developers. Therefore, I'm going to show you first hand how various packing effects the layout of your design.

How Packing Effects the Layout

When you look at the interface we've designed so far, you may take for granted how "smart" Glade was. How did it know we didn't want to make the status bar taller? Moreover, if you resize the window, how does it know that the text editing area grows to fill the new vertical space? Well, Glade guessed. It applied default packing options which are often what we want--but not always.

The best way to learn about packing is to play around with packing properties in Glade as you can see the effects in real time. First, a quick description of the applicable properties. Once you get a feel for GTK+, you may want to read more on packing and space allocation.

  • homogeneous: A property of the container widget which when set, tells GTK+ to allocate the same amount of space for each child.
  • expand: A property of the child being packed specifying if it should recieve extra space when the parent grows.
  • fill: A property of the child being packed specifying whether any extra space should be given to the child or used as padding around the child.

Let's look at the default packing for our design. The GtkScrolledWindow has "expand"=TRUE which means it recieves extra space when the parent grows, and it has "fill"=TRUE which means it uses that extra space it recieves. This is how we want it to work.

Widget Property Value
GtkVBox "vbox1" homogeneous FALSE
GtkMenuBar "menubar1" expand FALSE
fill TRUE
GtkScrolledWindow "scrolledwindow1" expand TRUE
fill TRUE
GtkStatusbar "statusbar1" expand FALSE
fill TRUE

Now, let's see what homogeneous does. Select the GtkVBox in the Glade Inspector and change it's "Homogeneous" property under 'General' properties tab to "Yes". Now the parent, "vbox1", allocates the same amount of space to each of it's children. Since all 3 child widgets have "fill"=TRUE, they fill up this extra space allocated to them.

GtkVBox with Homogeneous TRUE

Set the "Homogeneous" property back to "No".

Click on the GtkScrolledWindow "scrolledwindow1" in the Glade Inspector and set the "Expand" property in the 'Packing' properties tab to "No". Now none of the child widgets will recieve the extra space when the GtkVBox grows. I've highlighted the 3 children in the image below to illustrate this. Each of the child widgets is it's initially requested size. The extra space allocated to the GtkVBox is simply unused since none of it's children want it (but still belongs to the GtkVBox).

expand = FALSE

Now set the "Expand" property of the GtkScrolledWindow back to "Yes" and change the "Fill" property to "No" instead. This time, the extra space is allocated to the GtkScrolledWindow since "expand"=TRUE, however, the GtkScrolledWindow doesn't use the space it was allocated since "fill"=FALSE. 

fill = FALSE

Set the "Fill" property back to "Yes" to restore our original packing properties.

I know it seems odd at first, but as you continue to work in Glade, you'll start to pick up on how these packing properties work and once you've conquored that part of the learning curve, you'll be amazed at how little work you have to do related to the position and size of your GUI's elements.

Editing the Menu (or Toolbar)

Glade3 comes with a new Menu and Toolbar editor. Although we aren't using a GtkToolbar in this tutorial, the process is very similar to that of the GtkMenubar. We will use the Glade3 Menu Editor to remove many of the items we won't be using and to specify signal handlers for the menu items we will be using.

Although you can manipulate the properties and signals of GtkMenuItems from the standard Glade properties pane and can remove items from the Glade Inspector, the Glade3 menu editor provides a simpler way to edit your application's menu.

Select the GtkMenuBar by clicking it in the Glade Editor or in the Glade Inspector. Then right-click and select 'Edit...' from the popup menu. This will launch the menu editor.

Menu Editor in Glade3

The menu editor contains properties just like Glade's main Properties pane and signals at the bottom just like the 'Signals' tab of Glade's main Properties pane. The main difference in the editor is the tree view on the left. It allows you easily add and remove menu items and drag them around. Go ahead and remove the one labeled "_View". This is the only menu item which Glade generates that we won't be using in this tutorial.

For the remaining menu items we will need to do a few things. We need to rename them so that we have a clean, legible way to reference them in our source code. Then, we'll make some changes as a work around to a bug that might effect some readers using GTK+ 2.12.9 or below (Bug #523932), and finally we'll specify signal handlers. The steps for each of the menu items will be the same, so I'll just walk you through the 'New' menu item. Remember, this is all done in the menu editor but can also be done using the Inspector and Properties pane in glade.

  1. Click on the new menu item 'gtk-new'
  2. Change the 'Name' property under 'Menu Item' to "new_menu_item"
  3. Change the 'Label' property under 'Properties' to "_New" (note the underscore, that's an accellerator)
  4. Change the 'Stock Item' property under 'Properties' to "None"
  5. Click on another menu item such as 'gtk-open' and then click back to 'gtk-new' to refresh the properties (Bug #533503)
  6. Change the 'Stock Image' under 'Internal Image Properties' to the 'New' image
  7. Specify a handler for the "activate" signal callled "on_new_menu_item_activate" (This is done just like before, where we click the tree view to get a drop down list of possible signal names)
Now repeat those steps for each of the menu items: 'gtk-open', 'gtk-save', etc. Below are screen shots of the before and after on the 'New' menu item:

Glade 3 Menu Editor
Glade Menu Editor before changes

Glade3 Menu Editor
Glade Menu Editor after changing 'New' menu item

Final Touches to the Main Window

It's never very easy to understand references to things like "textview1", "textview2", etc. when you're coding. So now that you know how to set properties, change the names of the following widgets (remember, the name is in the 'General' tab of the Properties pane):

  1. Rename "textview1" to "text_view"
  2. Rename "statusbar1" to "statusbar"

And just to make it look a little nicer when the scroll bars are visible, let's add a a border and shadow to the GtkScrolledWindow

  1. Change the "Shadow Type" to "Etched In' in the 'General' properties tab for "scrolledwindow1"
  2. Change the "Border Width" to 1 in the 'Common' properties tab for "scrolledwindow1"
  3. Change the "Left Margin" to 2 in the 'General' properties for "text_view"
  4. Change the "Right Margin" to 2 in the 'General' properties for "text_view"

The finished glade file can be downloaded here: tutorial.glade

Getting Additional Help Using Glade

If you have additional questions about using Glade, you can always ask on the glade-users mailing list or post your question in the GTK+ Forums.

What Next?

In GTK+ and Glade3 GUI Programming Tutorial - Part 2 I will talk a little bit about choosing a programming language to implement the GUI we just created.

If you don't want to read along and would rather just see the final implementations:

Did you enjoy GTK+ and Glade3 GUI Programming Tutorial - Part 1? If you would like to help support my work, A donation of a buck or two would be very much appreciated.
blog comments powered by Disqus
Linux Servers on the Cloud IN MINUTES