Creating a C/C++ GUI with GTK+

Many programming languages support GUI development as one of the core parts of its language features. C/C++ has no such library attached to it; something like the string library,IO library, and so forth, that we frequently use. This shortcoming opened the horizon for developers to pick from a wide variety of GUI library toolkit available in C/C++. GTK+ is one of them. It stands for GIMP (GNU Image Manipulation Program) Toolkit and can be used to program modern GUI interfaces. If you find yourself struggling as you work along, consider visiting the TechRepublic Academy!

Overview

The good thing about GTK+ is that it is stable, mature, and its origin can be traced back to the old days of X Windows that form the core GUI system of Linux today. GTK is entirely written in C and the GTK+ software that we commonly use in Linux are also written in C. The desktop managers, such as GENOME and XFCE, also are built using GTK. There are several language bindings available for GTK, such as the following:

A GTK+ application is not restricted to the Linux platform only; it can be ported to non-UNIX/Linux platforms as well.

Getting Started

Here, we shall adhere to the basic form of GTK+, which is its C avatar on the Linux platform. The official site to download GTK+ is https://www.gtk.org. The site contains API documentation, tutorials, and other Gnome libraries that are often used along with GTK. In fact, GTK is built on top of libraries, such as:

  • Glib: It is a general purpose utility library that provides support for threads, dynamic loading, event loops, low-level data structures, and so on.
  • GObject: This library provides full featured object-oriented support in C, without using C++. This library facilitates the language binding created for other languages to give you easy access to C APIs.
  • Pango: This library supports text and layout rendering.
  • ATK: This library provides support to create accessibility tools such as sticky keys, screen readers, and the like.
  • GDK (GIMP Drawing Toolkit): This is the graphics library that provides low-level drawing functions on top of Xlib.
  • GdkPixBuf: This library provides image manipulation functions.
  • Xlib: This library provides low-level graphics support for Linux systems.

When writing code with GTK, we often find that many of the primitive data types are prefixed with ‘g,’ as in gint, gchar, gshort, gpointer, and so forth. These data types ensure that the code can be recompiled on any platform without making any changes. These data types are defined in these libraries to aid cross-platform development.

The main problem of GUI programming is that it is inherently object-oriented. Therefore, a procedural paradigm does not fit perfectly in the scheme. For this reason, in spite of GTK being written in C, it provides object-oriented support through GObject. Note that this object-oriented support has nothing to do with C++. C++ has its own GTK library, called gtkmm. GObject facilitates some of the object-oriented principles, like polymorphism and inheritance with the help of macros. The following hierarchical relation will clarify the idea further.

The GObject hierarchical relation structure
Figure 1: The GObject hierarchical relation structure

GtkWindow is a child of or inherits GtkBin, which itself is a child of GtkContainer; therefore, an object of GtkWindow can call the function defined in GtkBin or GtkContainer. This is an example of object-oriented behavior enforced in C by GTK.

GUI with GTK

Let us understand a few things from our first GTK code in C. First, we include the header file <gtk/gtk.h>. This includes all the files one needs to create a GUI, including the Glib library.

#include <gtk/gtk.h>

Now, we declare a pointer to GtkWidget, which is nothing but a window in our case. And, another GtkWidget pointer will be the button. Recall that GtkWidget is a top-level storage type for widgets in the hierarchy.

GtkWidget *window;
GtkWidget *button;

Next, we invoke gtk_init function to initialize the GTK+ libraries by passing the command line parameters of the main function.

gtk_init(&argc, &argv);

All GTK+ applications are initialized in this manner; it is a “must” statement. It parses the command line arguments and returns back to the application. As a result, these parameters may be used to modify the run time behavior of the application.

Now, we create the window and the button.

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label
   ("Click Me!");

The window type value GTK_WINDOW_TOPLEVEL means that the window created will be a standard framed window. Other type values may be GTK_WINDOW_POPUP, which means a frame-less dialog window will be created.

Once we create a window, it must be closable so that the user must at least able to close the application in response to the user hitting the top-right close button. This means the window must be able to respond to an event (close event). Like all windowing systems, GTK+ also implements events and event handlers. Because the code that emits the signal is internal to a particular object, we need to write a connecting callback function. The format of a typical callback function is:

void my_callback_function(GtkWidget *widget,
   gpointer data);

The first parameter represents the widget that emits the signal and the second parameter is a void pointer that may be used for any purpose. Therefore, the callback function to handle the close event of our window will be as follows:

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

The function gtk_main_quit() closes the application. Now, we must connect the window object with the callback function.

g_signal_connect (window, "destroy",
   G_CALLBACK (destroy), NULL);

Similarly, we create the callback function to handle the button event and connect it with the button widget.

void greet( GtkWidget *widget, gpointer data )
{
   g_print ("Hi there! Welcome to GTK\n");
   g_print ("%s clicked %d times\n",
      (char*)data, ++counter);
}
g_signal_connect (GTK_OBJECT(button), "clicked",
   G_CALLBACK (greet), "button");

Because the button widget is contained within the window, we must specifically add it to the container.

gtk_container_add (GTK_CONTAINER (window), button);

And, finally, we display the widgets created in memory with the gtk_widget_show_all() function that takes a reference to the window we have created. Lastly, the gtk_main() function is invoked to start the interactive process.

gtk_widget_show_all(window);
gtk_main();

This is a key function because normally a C program terminates after executing the last statement. Here, it passes the control of the program to GTK+ and never returns until the gtk_main_quit event is triggered by the user clicking the close button in our case.

Now, let’s put it together.

#include <gtk/gtk.h>

static int counter = 0;

void greet( GtkWidget *widget, gpointer   data )
{
   // printf equivalent in GTK+
   g_print ("Hi there! Welcome to GTK\n");
   g_print ("%s clicked %d times\n",
      (char*)data, ++counter);
}

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

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

   GtkWidget *window;
   GtkWidget *button;
   gtk_init (&argc, &argv);

   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

   g_signal_connect (window, "destroy",
   G_CALLBACK (destroy), NULL);
   /* Let's set the border width of the window to 20.
    * You may play with the value and see the
    * difference. */
   gtk_container_set_border_width
      (GTK_CONTAINER (window), 20);

   button = gtk_button_new_with_label ("Click Me!");

   g_signal_connect (GTK_OBJECT(button),
      "clicked",G_CALLBACK (greet),
      "button");

   gtk_container_add (GTK_CONTAINER (window), button);

   gtk_widget_show_all(window); 

   gtk_main ();

   return 0;
}

Compile it as follows (with gcc in Linux)

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

Now run it,

./p1

The program in action
Figure 2: The program in action

Conclusion

GTK+ has all the GUI components one needs to create a professional looking interface. The basic idea of GUI event-driven programming with GTK+ is not much different from the one shown in the example. Add a few more components, use different types of containers, play with layouts, and of course never forget to consult the GTK+ documentation. Professional programmers often used RAD tools such as Glade to design the GUI interface quickly. But, to begin with, try writing the code from scratch to get a feel of what goes where and how it is actually done. It will reward you later.

Manoj Debnath
Manoj Debnath
A teacher(Professor), actively involved in publishing, research and programming for almost two decades. Authored several articles for reputed sites like CodeGuru, Developer, DevX, Database Journal etc. Some of his research interest lies in the area of programming languages, database, compiler, web/enterprise development etc.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read