ProgrammingFoodLanguagePersonalLinuxHealthPythonLaw

Samuel's Portrait

Hi! My name is Samuel. This website exists to help us become more open, clear, and curious people, especially for the programmers among us.


Home
Sitemap
References
Schedule
Resume

Links

GTK Menus

June 13th, 2009

GTK, the Gimp Tool Kit named for it's origin in the Gimp image editor, is one of the two major user interface toolkits available on Linux. In fact, it's available on many other platforms as well, including Windows and Mac OS X. Rooted in C programming and visually attractive in programs such as Firefox, GTK is simple and beautiful from the ground up.

The following program demonstrates the creation of a File menu with a Quit item. As this article explores menu creation, why not compile the program using the command "cc menu.c -o menu `pkg-config --cflags --libs gtk+-2.0`" and tinker with it while reading this article?

#include <stdlib.h> // includes EXIT_SUCCESS
#include <gdk/gdkkeysyms.h> // includes GDK_q
#include <gtk/gtk.h>

int main (int argc, char *argv[]) {
  gtk_init (&argc, &argv);
  
  // Create the window.
  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Menu Demo");
  gtk_window_set_default_size (GTK_WINDOW (window), 250, 0);
  g_signal_connect (G_OBJECT (window), // source object
                    "destroy", // source signal
                    G_CALLBACK (gtk_main_quit), // destination callback
                    NULL); // extra data to pass to callback

  // Create an accelerator group.
  GtkAccelGroup *accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

  // Create the menu bar.
  GtkWidget *menubar = gtk_menu_bar_new();
  gtk_container_add (GTK_CONTAINER (window), menubar);

  // Create the File menu.
  GtkWidget *file = gtk_menu_item_new_with_mnemonic ("_File");
  gtk_menu_shell_append (GTK_MENU_SHELL (menubar), file);
  GtkWidget *file_menu = gtk_menu_new();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (file), file_menu);

  // Create the Quit menu item.
  GtkWidget *quit =
      gtk_image_menu_item_new_from_stock
      (GTK_STOCK_QUIT, accel_group);
  gtk_widget_add_accelerator (quit, // widget
                              "activate", // signal to emit
                              accel_group, // accelerator group
                              GDK_q, // key
                              GDK_CONTROL_MASK, // modifier keys
                              GTK_ACCEL_VISIBLE); // flags
  gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), quit);
  g_signal_connect (G_OBJECT (quit), // source object
                    "activate", // source signal
                    G_CALLBACK (gtk_main_quit), // destination callback
                    NULL); // extra data to pass to callback
  
  gtk_widget_show_all (window);

  gtk_main();
  
  return EXIT_SUCCESS;
}

Let's dig into some code.

#include <stdlib.h> // includes EXIT_SUCCESS
#include <gdk/gdkkeysyms.h> // includes GDK_q
#include <gtk/gtk.h>

Among other headers, we need to include the GDK and GTK libraries. GDK, the Gimp Drawing Kit, wraps up low level system features in an easily portable API. GTK uses this, along with GLib to maintain it's own ease of portability.

gtk_init (&argc, &argv);

We start using GTK by initializing it. Passing in pointers to the application's arguments allows GTK to use those it recognizes and remove them from the list. After that, an application typically parses whatever is returned.

// Create the window.
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Menu Demo");
gtk_window_set_default_size (GTK_WINDOW (window), 250, 0);
g_signal_connect (G_OBJECT (window), // source object
                  "destroy", // source signal
                  G_CALLBACK (gtk_main_quit), // destination callback
                  NULL); // extra data to pass to callback

Like any other widget, a window is created with a function similar to gtk_*_new. No matter what type of widget creation function is used, a GtkWidget* is returned. As C has no language-level concept of pointer hierarchies, any time we want to use a pointer, such as a GtkWidget*, as another type of pointer, we have to explicitly convert it to avoid compilation errors.

With the window created, we'll probably want to connect the destroy signal that it sends, when clicking the close button, to an appropriate termination function. In this case, it would be the function gtk_main_quit. After we finish setting up the user interface, we'll enter GTK's main event loop, which the quit function will eventually terminate.

GtkAccelGroup *accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

Programs typically have two types of keyboard shortcuts. The first are mnemonics. An example would be the underlined Q of a Quit menu entry. This tells the user that, while that menu is visible, Alt+Q can be pressed to quickly activate that menu item. The second type of shortcuts are accelerators. Continuing with our Quit example, a typical accelerator would be Ctrl+Q. Unlike mnemonics, these shortcuts can be used nearly anywhere in the program to achieve the desired behavior. Above, we create an accelerator group to contain all such shortcuts.

// Create the menu bar.
  GtkWidget *menubar = gtk_menu_bar_new();
  gtk_container_add (GTK_CONTAINER (window), menubar);

Before we can start adding menus, we have to setup a menu bar to contain them.

// Create the File menu.
GtkWidget *file = gtk_menu_item_new_with_mnemonic ("_File");
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), file);
GtkWidget *file_menu = gtk_menu_new();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (file), file_menu);

Onward to adding the File menu. Here we give the File menu an underline beneath the F. By creating this mnemonic, we allow the user to press Alt+F, while the window with this menu is active, to quickly access the menu. Then we add the actual File menu to the menubar's file item.

// Create the Quit menu item.
GtkWidget *quit = gtk_image_menu_item_new_from_stock (GTK_STOCK_QUIT, accel_group);
gtk_widget_add_accelerator (quit, // widget
                            "activate", // signal to emit
                            accel_group, // accelerator group
                            GDK_q, // key
                            GDK_CONTROL_MASK, // modifier keys
                            GTK_ACCEL_VISIBLE); // flags
gtk_menu_shell_append (GTK_MENU_SHELL (file_menu), quit);
g_signal_connect (G_OBJECT (quit), // source object
                  "activate", // source signal
                  G_CALLBACK (gtk_main_quit), // destination callback
                  NULL); // extra data to pass to callback

The only item we add to this demo File menu is Quit. Like other common items, we can obtain the menu item's label, icon, and mnemonic from a stock item. We attach the accelerator group to it and add a shortcut for Ctrl+Q.

gtk_widget_show_all (window);

Every widget starts off hidden. We could call gtk_widget_show several times to make each widget visible. However, since they are grouped in the window, we can use the above code to recursively make them all visible at once.

gtk_main();

return EXIT_SUCCESS;

With the user interface finally setup, we call gtk_main to start the main event loop. It receives its name from the fact that it handles events generated by the user interacting with the user interface. The loop ends, and the program will continue on to the following return statement, once gtk_main_exit is signaled.

This completes our GTK menu tutorial. If you'd like to learn more, I recommend the official GTK tutorial and the GTK reference manual

.

Tagged as: GTKC

Comments

January 27th, 2010

Have a look at my project rapidshare SE . Will be glad if it is of any use to you ;)
Comment

Copyright 2009 by Samuel Pauls. Graphic design by Peter McAlpine.

Permission is granted to share verbatim copies of this site or substantially adapt its contents for other uses.

Anonymous usage statistics are collected. Accuracy is attempted but no warranties are provided.