Documente Academic
Documente Profesional
Documente Cultură
From: http://zetcode.com/gui/gtk2/
This is a GTK+ programming tutorial. In this tutorial, we will learn the basics of GUI programming
in GTK+ and C language. The GTK+ tutorial is suitable for beginners and intermediate
programmers. This tutorial covers GTK+ 2.
Sumário
GTK+ tutorial.......................................................................................................................................1
GTK+...............................................................................................................................................2
Introduction to GTK+...........................................................................................................................2
GTK+...............................................................................................................................................2
Compiling GTK+ applications.........................................................................................................3
Version.............................................................................................................................................4
Sources.............................................................................................................................................4
First programs in GTK+.......................................................................................................................4
Simple example................................................................................................................................4
Centering the window......................................................................................................................6
The application icon.........................................................................................................................7
Tooltip..............................................................................................................................................8
Mnemonic......................................................................................................................................10
Menus and toolbars in GTK+.............................................................................................................11
Simple menu example....................................................................................................................12
Submenu........................................................................................................................................14
Image menus, mnemonics & accelerators.....................................................................................16
CheckMenuItem.............................................................................................................................18
Popup menu...................................................................................................................................20
A toolbar........................................................................................................................................22
Undo redo......................................................................................................................................24
GTK+ layout management.................................................................................................................26
GtkFixed........................................................................................................................................26
GtkAlignment................................................................................................................................28
GtkVBox........................................................................................................................................29
GtkTable.........................................................................................................................................31
Corner buttons................................................................................................................................32
Windows........................................................................................................................................34
GTK+ events and signals....................................................................................................................37
Button click....................................................................................................................................38
Moving a window..........................................................................................................................39
The enter signal..............................................................................................................................40
Disconnecting a callback...............................................................................................................42
Drag and drop example..................................................................................................................43
A timer example.............................................................................................................................45
GTK+ dialogs.....................................................................................................................................47
Message dialogs.............................................................................................................................47
GtkAboutDialog.............................................................................................................................50
GtkFontSelectionDialog................................................................................................................52
GtkColorSelectionDialog...............................................................................................................54
GTK+ Widgets....................................................................................................................................56
GtkButton.......................................................................................................................................56
GtkCheckButton............................................................................................................................57
GtkFrame.......................................................................................................................................59
GtkHScale......................................................................................................................................60
GtkLabel........................................................................................................................................62
Label with markup.........................................................................................................................63
GTK+ Widgets II................................................................................................................................64
GktComboBoxText........................................................................................................................64
GtkHSeparator...............................................................................................................................66
GtkEntry.........................................................................................................................................68
GtkImage.......................................................................................................................................69
GtkStatusbar...................................................................................................................................70
GtkIconView..................................................................................................................................72
GtkTreeView widget...........................................................................................................................75
List view........................................................................................................................................75
Dynamic List view.........................................................................................................................78
Tree view.......................................................................................................................................82
GtkTextView widget...........................................................................................................................85
Simple example..............................................................................................................................85
Lines and columns.........................................................................................................................87
Search & highlight.........................................................................................................................89
Custom GTK+ widget........................................................................................................................92
CPU widget....................................................................................................................................92
GTK+
GTK+ is a library for creating graphical user interfaces. The library is created in C programming
language. The GTK+ is also called the GIMP Toolkit. Originally, the library was created while
developing the GIMP image manipulation program.
Introduction to GTK+
This is an introductory GTK+ programming tutorial. The tutorial is written in the C programming
language. It has been created and tested on Linux. The GTK+ programming tutorial is suited for
novice and intermediate programmers. This tutorials covers GTK+ 2.
GTK+
GTK+ is a library for creating graphical user interfaces. The library is created in C programming
language. The GTK+ library is also called the GIMP toolkit. Originally, the library was created
while developing the GIMP image manipulation program. Since then, the GTK+ became one of the
most popular toolkits under Linux and BSD Unix. Today, most of the GUI software in the open
source world is created in Qt or in GTK+. The GTK+ is an object-oriented application
programming interface. The object-oriented system is created with the Glib Object system, which is
a base for the GTK+ library. GObject also enables to create language bindings for various other
programming languages. Language bindings exist for C++, Python, Perl, Java, C#, and other
programming languages.
The GTK+ itself depends on the following libraries:
• Glib
• Pango
• ATK
• GDK
• GdkPixbuf
• Cairo
The Glib is a general purpose utility library. It provides various data types, string utilities, enables
error reporting, message logging, working with threads, and other useful programming features.
Pango is a library which enables internationalisation. ATK is the accessibility toolkit; it provides
tools which help physically challenged people work with computers. GDK is a wrapper around the
low-level drawing and windowing functions provided by the underlying graphics system. On Linux,
GDK lies between the X Server and the GTK+ library. It handles basic rendering such as drawing
primitives, raster graphics, cursors, fonts, as well as window events, and drag-and-drop
functionality. The GdkPixbuf library is a toolkit for image loading and pixel buffer manipulation.
Cairo is a library for creating 2D vector graphics. It has been included in GTK+ since version 2.8.
Gnome and XFce desktop environments have been created using the GTK+ library. SWT and
wxWidgets are well known programming frameworks that use GTK+. Prominent software
applications that use GTK+ include Firefox or Inkscape.
This line compiles a basic program. The source code consists of one file—simple.c.
$ pkg-config --cflags gtk+-2.0 | xargs -n3
-pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include
-I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0
-I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/freetype2
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
-I/usr/include/pixman-1
-I/usr/include/libpng12 -I/usr/include/harfbuzz
The --cflags parameter prints pre-processor and compile flags required to compile GTK+
programs, including flags for all their dependencies.
$ pkg-config --libs gtk+-2.0 | xargs -n5
-lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0
-lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lpango-1.0 -lfontconfig
-lgobject-2.0 -lglib-2.0 -lfreetype
gtk_init(&argc, &argv);
return 0;
}
Sources
• gtk.org
• gtkforums.com
• Gtk+ 2 reference
This was an introduction to the GTK+ library.
Simple example
Our first example shows a basic window.
simple.c
#include <gtk/gtk.h>
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
GtkWidget is the base class that all widgets in GTK+ derive from. It manages the widget
lifecycle, states, and style.
gtk_init(&argc, &argv);
The gtk_init() function initializes GTK+ and parses some standard command line options.
This function must be called before using any other GTK+ functions.
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
The gtk_window_new() function creates a new GtkWindow, which is a toplevel window that
can contain other widgets. The window type is GTK_WINDOW_TOPLEVEL; toplevel windows have
a titlebar and a border. They are managed by the window manager.
gtk_widget_show(window);
The get_widget_show() flags a widget to be displayed. Any widget that is not shown will not
appear on the screen.
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
This code enters the GTK+ main loop. From this point, the application sits and waits for events to
happen.
$ gcc -o simple simple.c `pkg-config --libs --cflags gtk+-2.0`
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Center");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
In the example, we center the window, set a title, and size the window.
gtk_window_set_title(GTK_WINDOW(window), "Center");
The gtk_window_set_title() function sets a window title. If we do not set a title ourselves,
the GTK+ will use a name of a source file as a title.
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
This gtk_window_set_default_size() sets the size of the window to 230x150. Note that
we are talking about the client area, excluding the decorations provided by the window manager.
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
Passing the GTK_WIN_POS_CENTER constant to the gtk_window_set_position()
function centers the program on the screen.
GdkPixbuf *pixbuf;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_file(filename, &error);
if (!pixbuf) {
return pixbuf;
}
GtkWidget *window;
GdkPixbuf *icon;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Icon");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
icon = create_pixbuf("web.png");
gtk_window_set_icon(GTK_WINDOW(window), icon);
gtk_widget_show(window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_object_unref(icon);
gtk_main();
return 0;
}
The gtk_window_set_icon() displays the icon for the window. The create_pixbuf()
creates a GdkPixbuf from a PNG file.
g_object_unref(icon);
The g_object_unref() decreases the reference count of the pixbuf object. When its reference
count drops to 0, the object is finalized (i.e. its memory is freed).
Figure: Icon
Tooltip
A tooltip is a small rectangular window, which gives a brief information about an object. It is
usually a GUI component; it is part of the help system of the application.
tooltip.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *button;
GtkWidget *halign;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Tooltip");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
button = gtk_button_new_with_label("Button");
gtk_widget_set_tooltip_text(button, "Button widget");
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
The gtk_container_set_border_width() sets some border space around the edges of the
window.
gtk_widget_set_tooltip_text(button, "Button widget");
The GtkAlignment is a basic container which can be used to align its child to the sides of the
window. In our case, the button is placed to the upper-left corner of the window. The first
parameters of the function are the xalign and yalign. A value of 0 for xalign indicates left alignment;
a value of 0 for yalign indicates top alignment. The third and fourth parameters are scaling values.
Passing 0 to both parameters indicates that the widget does not expand in both directions.
gtk_container_add(GTK_CONTAINER(window), halign);
Figure: Tooltip
Mnemonic
Mnemonics are shortcut keys that activate a widget that supports mnemonics. They can be used with
labels, buttons, or menu items. The mnemonic is created by adding the _ character to the widget's
label. It causes the next character to be the mnemonic. The character is combined with the
mouseless modifier, usually Alt. The chosen character is underlined, but it may be emphasized in a
platform specific manner. On some platforms, the character is only underlined after pressing the
mouseless modifier.
mnemonic.c
#include <gtk/gtk.h>
g_printf("Button clicked\n");
}
GtkWidget *window;
GtkWidget *button;
GtkWidget *halign;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Mnemonic");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
button = gtk_button_new_with_mnemonic("_Button");
g_signal_connect(button, "clicked",
G_CALLBACK(print_msg), NULL);
gtk_widget_show_all(window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
We set a mnemonic for a button widget. It can be activated with the Alt+B keyboard shortcut.
button = gtk_button_new_with_mnemonic("_Button");
At this moment, there are three ways to activate the button: a left mouse button click, the Alt+B
shortcut, and the Space key (provided the button has the focus).
Figure: Mnemonic
In this chapter we have created some simple GTK+ programs.
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *quitMi;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Simple menu");
menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();
fileMi = gtk_menu_item_new_with_label("File");
quitMi = gtk_menu_item_new_with_label("Quit");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The menu in the examle has one menu item. By selecting the item, the application quits.
menubar = gtk_menu_bar_new();
The fileMenu is set to the File menu item with the gtk_menu_item_set_submenu()
function. Menus are containers which hold menu items. They are themselves plugged to a particular
menu item.
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
The quitMi is added to the File menu with the gtk_menu_shell_append() function.
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
The File menu item is added to the menubar with the gtk_menu_shell_append() function.
Both GtkMenu and GtkMenuBar are derived from the GtkMenuShell.
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);
Submenu
The next example demonstrates how to create a submenu. A submenu is a menu inside another
menu.
submenu.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *imprMenu;
GtkWidget *sep;
GtkWidget *fileMi;
GtkWidget *imprMi;
GtkWidget *feedMi;
GtkWidget *bookMi;
GtkWidget *mailMi;
GtkWidget *quitMi;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Submenu");
menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();
fileMi = gtk_menu_item_new_with_label("File");
imprMenu = gtk_menu_new();
imprMi = gtk_menu_item_new_with_label("Import");
feedMi = gtk_menu_item_new_with_label("Import news feed...");
bookMi = gtk_menu_item_new_with_label("Import bookmarks...");
mailMi = gtk_menu_item_new_with_label("Import mail...");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(imprMi), imprMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), feedMi);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), bookMi);
gtk_menu_shell_append(GTK_MENU_SHELL(imprMenu), mailMi);
sep = gtk_separator_menu_item_new();
quitMi = gtk_menu_item_new_with_label("Quit");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), imprMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), sep);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example creates a menu inside another menu. The submenu has three menu items. We also add
a horizontal separator.
imprMenu = gtk_menu_new();
imprMi = gtk_menu_item_new_with_label("Import");
feedMi = gtk_menu_item_new_with_label("Import news feed...");
bookMi = gtk_menu_item_new_with_label("Import bookmarks...");
mailMi = gtk_menu_item_new_with_label("Import mail...");
The three menu items are added to the submenu with the gtk_menu_shell_append()
function.
sep = gtk_separator_menu_item_new();
The imprMi and the separator are added to the File menu with the
gtk_menu_shell_append() function.
Figure: Submenu
We might also have our environment configured not to show menu images. To turn the menu
images on, we launch the gconf-editor and go to
/desktop/gnome/interface/menus_have_icons and check the option.
imagemenu.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *newMi;
GtkWidget *openMi;
GtkWidget *quitMi;
GtkWidget *sep;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Images");
menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();
accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
fileMi = gtk_menu_item_new_with_mnemonic("_File");
newMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
openMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
sep = gtk_separator_menu_item_new();
quitMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), newMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), openMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), sep);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example has three menu items with icons. The menu items can be selected with mnemonics.
The Quit menu item has a keyboard accelerator.
accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
...
quitMi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
CheckMenuItem
A GtkCheckMenuItem is a menu item with a check box.
checkmenuitem.c
#include <gtk/gtk.h>
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
gtk_widget_show(statusbar);
} else {
gtk_widget_hide(statusbar);
}
}
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *viewmenu;
GtkWidget *view;
GtkWidget *tog_stat;
GtkWidget *statusbar;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "GtkCheckMenuItem");
menubar = gtk_menu_bar_new();
viewmenu = gtk_menu_new();
view = gtk_menu_item_new_with_label("View");
tog_stat = gtk_check_menu_item_new_with_label("View statusbar");
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(tog_stat), TRUE);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu);
gtk_menu_shell_append(GTK_MENU_SHELL(viewmenu), tog_stat);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
statusbar = gtk_statusbar_new();
gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 0);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(tog_stat), "activate",
G_CALLBACK(toggle_statusbar), statusbar);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example contains a GtkCheckMenuItem in a View menu. If the check box is activated, the
statusbar widget is shown.
tog_stat = gtk_check_menu_item_new_with_label("View statusbar");
gtk_widget_show(statusbar);
} else {
gtk_widget_hide(statusbar);
}
If the check box in the menu item is activated, we show the statusbar widget; otherwise the
statusbar is hidden.
Figure: GtkCheckMenuItem
Popup menu
In the next example, we create a popup menu. A popup menu is also called a context menu. This
type of menu is usually shown when we right click on a GUI object.
popupmenu.c
#include <gtk/gtk.h>
if (event->type == GDK_BUTTON_PRESS) {
if (bevent->button == RIGHT_CLICK) {
return TRUE;
}
return FALSE;
}
GtkWidget *window;
GtkWidget *ebox;
GtkWidget *pmenu;
GtkWidget *hideMi;
GtkWidget *quitMi;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Popup menu");
ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(window), ebox);
pmenu = gtk_menu_new();
hideMi = gtk_menu_item_new_with_label("Minimize");
gtk_widget_show(hideMi);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), hideMi);
quitMi = gtk_menu_item_new_with_label("Quit");
gtk_widget_show(quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(pmenu), quitMi);
g_signal_connect_swapped(G_OBJECT(hideMi), "activate",
G_CALLBACK(gtk_window_iconify), GTK_WINDOW(window));
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect_swapped(G_OBJECT(ebox), "button-press-event",
G_CALLBACK(show_popup), pmenu);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the code example, we create a popup menu with two menu items. The first minimizes the
window, and the second terminates the application.
ebox = gtk_event_box_new();
gtk_container_add(GTK_CONTAINER(window), ebox);
Selecting the first menu item minimizes the window. We connect the activate signal of the Hide
menu item to the gtk_window_iconify() function. The term iconify is a synonym for
minimize.
g_signal_connect_swapped(G_OBJECT(ebox), "button_press_event",
G_CALLBACK(show_popup), pmenu);
Inside the event handler, we check for the button press event type.
if (bevent->button == RIGHT_CLICK) {
When the button triggering the signal is a right mouse button, we show the popup menu with the
gtk_menu_popup() function.
A toolbar
Menus group commands that we can use in application. Toolbars provide a quick access to the most
frequently used commands. GtkToolbar is a toolbar widget in GTK+. A toolbar can contain
instances of a subclass of a GtkToolItem, e.g. a GtkToolButton or a
GtkSeparatorToolItem.
toolbar.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *toolbar;
GtkToolItem *newTb;
GtkToolItem *openTb;
GtkToolItem *saveTb;
GtkToolItem *sep;
GtkToolItem *exitTb;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "toolbar");
toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
newTb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), newTb, -1);
openTb = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), openTb, -1);
saveTb = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), saveTb, -1);
sep = gtk_separator_tool_item_new();
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);
exitTb = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exitTb, -1);
g_signal_connect(G_OBJECT(exitTb), "clicked",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The gtk_toolbar_set_style() function alters the view of toolbar to display either icons
only, text only, or both. Passing the GTK_TOOLBAR_ICONS constant makes the toolbar show only
icons.
newTb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
Figure: Toolbar
Undo redo
The following example demonstrates how to inactivate toolbar buttons on the toolbar. It is a
common practice in GUI programming. For example the Save button; if we save all changes of our
document to the disk, the Save button is inactivated in most text editors. This way the application
indicates to the user that all changes are already saved.
undoredo.c
#include <gtk/gtk.h>
if (g_strcmp0(name, "undo") ) {
count++;
} else {
count--;
}
if (count < 0) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}
if (count > 5) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}
}
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *toolbar;
GtkToolItem *undo;
GtkToolItem *redo;
GtkToolItem *sep;
GtkToolItem *exit;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Undo redo");
toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);
undo = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
gtk_widget_set_name(GTK_WIDGET(undo), "undo");
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);
redo = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), redo, -1);
sep = gtk_separator_tool_item_new();
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);
exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);
g_signal_connect(G_OBJECT(undo), "clicked",
G_CALLBACK(undo_redo), redo);
g_signal_connect(G_OBJECT(redo), "clicked",
G_CALLBACK(undo_redo), undo);
g_signal_connect(G_OBJECT(exit), "clicked",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Our example creates Undo and Redo buttons from the GTK+ stock resources. After several clicks
each of the buttons is inactivated. The buttons are grayed out.
if (count < 0) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}
if (count > 5) {
gtk_widget_set_sensitive(widget, FALSE);
gtk_widget_set_sensitive(item, TRUE);
}
GtkFixed
The GtkFixed container places child widgets at fixed positions and with fixed sizes. This
container performs no automatic layout management. Therefore, it does not work with translations,
font changes, or themes. In most applications, we do not use the GtkFixed container. There might
be some specialised areas where the container can be used (for instance, positioning charts or
images).
fixed.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *btn1;
GtkWidget *btn2;
GtkWidget *btn3;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkFixed");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
btn1 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn1, 150, 50);
gtk_widget_set_size_request(btn1, 80, 30);
btn2 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn2, 15, 15);
gtk_widget_set_size_request(btn2, 80, 30);
btn3 = gtk_button_new_with_label("Button");
gtk_fixed_put(GTK_FIXED(fixed), btn3, 100, 100);
gtk_widget_set_size_request(btn3, 80, 30);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In our example, we create three buttons and place them at fixed coordinates. When we resize the
window of the application, the buttons keep their size and positions.
fixed = gtk_fixed_new();
The first button is placed using the gtk_fixed_put() function at coordinates x=150 and y=50.
gtk_widget_set_size_request(btn1, 80, 30);
GtkAlignment
GtkAlignment controls the alignment of a widget. In addition, it can manage its scaling.
bottomleft.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *align;
GtkWidget *lbl;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkAlignment");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
gtk_container_add(GTK_CONTAINER(align), lbl);
gtk_container_add(GTK_CONTAINER(window), align);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Figure: GtkAlignment
GtkVBox
GtkVBox is a vertical box container. It places its child widgets into a single column. GtkHBox is a
very similar container; it places its child widgets into a single row.
vbox.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *settings;
GtkWidget *accounts;
GtkWidget *loans;
GtkWidget *cash;
GtkWidget *debts;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 250);
gtk_window_set_title(GTK_WINDOW(window), "GtkVBox");
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
vbox = gtk_vbox_new(TRUE, 1);
gtk_container_add(GTK_CONTAINER(window), vbox);
settings = gtk_button_new_with_label("Settings");
accounts = gtk_button_new_with_label("Accounts");
loans = gtk_button_new_with_label("Loans");
cash = gtk_button_new_with_label("Cash");
debts = gtk_button_new_with_label("Debts");
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
This example packs five buttons into one column. If we resize the window of the application, the
child widgets are resized as well.
vbox = gtk_vbox_new(TRUE, 1);
The gtk_box_pack_start() function adds a widget to the box. The first two parameters are
the box container and the child widget. The next three parameters are expand, fill, and
padding. Note that the fill parameter has no effect if the expand parameter is set to FALSE.
Similarly, the expand parameter has no effect if we have created the container with homogeneous
parameter set to TRUE. In our case, the Settings button is given extra space when the window is
enlarged and the widget fills the additional area.
Figure: GtkVBox container
GtkTable
The GtkTable widget arranges widgets in rows and columns.
table.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *table;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 180);
gtk_window_set_title(GTK_WINDOW(window), "GtkTable");
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
int i = 0;
int j = 0;
int pos = 0;
gtk_container_add(GTK_CONTAINER(window), table);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
We create a new GtkTable widget with 4 rows and 4 columns. When we pass TRUE to the third
parameter, all table cells are resized to the size of the cell containing the largest widget.
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);
This code creates 16 buttons and places them into the container. The
gtk_table_attach_defaults() adds children to a table container with identical padding
and expansion options.
Figure: GtkTable
Corner buttons
The next example places two buttons in the bottom-right corner of the window.
cornerbuttons.c
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *okBtn;
GtkWidget *clsBtn;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *halign;
GtkWidget *valign;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 200);
gtk_window_set_title(GTK_WINDOW(window), "Corner buttons");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_container_add(GTK_CONTAINER(hbox), okBtn);
clsBtn = gtk_button_new_with_label("Close");
gtk_container_add(GTK_CONTAINER(hbox), clsBtn);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the example, we use one horizontal box, one vertical box, and two alignment containers.
valign = gtk_alignment_new(0, 1, 0, 0);
okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_container_add(GTK_CONTAINER(hbox), okBtn);
clsBtn = gtk_button_new_with_label("Close");
gtk_container_add(GTK_CONTAINER(hbox), clsBtn);
We create a horizontal box and put two buttons inside it. The
gtk_widget_set_size_request() sets the minimum size of a widget. Since we have set
the homogeneous parameter of a GtkHBox to TRUE, the other button is adjusted to the new size
as well.
halign = gtk_alignment_new(1, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), hbox);
This creates an alignment container that places its child widget to the right. We add the horizontal
box into the alignment container and pack the alignment container into the vertical box. The
alignment container can take only one child widget; therefore, we must also use boxes.
Windows
Next we will create a more advanced example. We show a window that can be found in JDeveloper.
GtkWidget *halign;
GtkWidget *halign2;
GtkWidget *valign;
GtkWidget *actBtn;
GtkWidget *clsBtn;
GtkWidget *hlpBtn;
GtkWidget *okBtn;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_set_size_request (window, 300, 250);
gtk_window_set_title(GTK_WINDOW(window), "Windows");
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
title = gtk_label_new("Windows");
halign = gtk_alignment_new(0, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), title);
gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);
wins = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE);
gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3,
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);
actBtn = gtk_button_new_with_label("Activate");
gtk_widget_set_size_request(actBtn, 50, 30);
gtk_table_attach(GTK_TABLE(table), actBtn, 3, 4, 1, 2,
GTK_FILL, GTK_SHRINK, 1, 1);
okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_table_attach(GTK_TABLE(table), okBtn, 3, 4, 4, 5,
GTK_FILL, GTK_FILL, 0, 0);
gtk_container_add(GTK_CONTAINER(window), table);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The gtk_table_set_col_spacings() sets the space between every column in the table to
3.
gtk_table_set_row_spacing(GTK_TABLE(table), 0, 3);
The gtk_table_row_spacing() sets space between the first and the second row.
title = gtk_label_new("Windows");
halign = gtk_alignment_new(0, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), title);
gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);
This code creates a left-aligned label. The label is placed in the first row and the first column of the
GtkTable container.
wins = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE);
gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3,
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);
The GtkText widget spans two rows and two columns. We make the widget non-editable using
the gtk_text_view_set_editable() method and hide its cursor using the
gtk_text_view_set_cursor_visible() method.
valign = gtk_alignment_new(0, 0, 0, 0);
clsBtn = gtk_button_new_with_label("Close");
The Help button is left-aligned. It is placed below the text widget. We put some space between the
text widget and the button.
okBtn = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(okBtn, 70, 30);
gtk_table_attach(GTK_TABLE(table), okBtn, 3, 4, 4, 5,
GTK_FILL, GTK_FILL, 0, 0);
The OK button goes to the second column, below the Activate and Close buttons.
Figure: Windows
This chapter was dedicated to layout management.
buttonclick.c
#include <gtk/gtk.h>
g_print("clicked\n");
}
GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_add(GTK_CONTAINER(halign), btn);
gtk_container_add(GTK_CONTAINER(window), halign);
g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(button_clicked), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the application, we have two signals: the clicked signal and the destroy signal.
g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(button_clicked), NULL);
g_print("clicked\n");
}
The callback prints the "clicked" string to the console. The first parameter of the callback function
is the object which emitted the signal. In our case it is the Click button. The second parameter is
optional. We may send some data to the callback. In our case, we did not send any data; we
provided a NULL value to the fourth parameter of the g_signal_connect() function.
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
If we press on the x button located in the upper right corner of the titlebar, or we press Atl+F4, a
destroy signal is emitted. The gtk_main_quit() function is called, which terminates the
application.
Moving a window
The next example shows how we react to window move events.
moveevent.c
#include <gtk/gtk.h>
int x, y;
GString *buf;
x = event->configure.x;
y = event->configure.y;
buf = g_string_new(NULL);
g_string_printf(buf, "%d, %d", x, y);
gtk_window_set_title(window, buf->str);
g_string_free(buf, TRUE);
}
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(configure_callback), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
In the example, we show the current position of the upper-left corner of our window in the titlebar.
gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);
The event mask of the widget determines what kind of events will a particular widget receive. Some
event are preconfigured, other events have to be added to the event mask. The
gtk_widget_add_events() adds a GDK_CONFIGURE event type to the mask. The
GDK_CONFIGURE event type accounts for all size, position, and the stacking order of the window
changes.
g_signal_connect(G_OBJECT(window), "configure-event",
G_CALLBACK(configure_callback), NULL);
The configure-event is emitted when the size, position, or stacking of the widget's window
has changed.
void configure_callback(GtkWindow *window,
GdkEvent *event, gpointer data) {
int x, y;
GString *buf;
x = event->configure.x;
y = event->configure.y;
buf = g_string_new(NULL);
g_string_printf(buf, "%d, %d", x, y);
gtk_window_set_title(window, buf->str);
g_string_free(buf, TRUE);
}
The callback function has three parameters: the object that emitted the signal, the GdkEvent, and
the optional data. We determine the x, y coordinates, build a string, and set it to the window title.
GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_title(GTK_WINDOW(window), "Enter signal");
btn = gtk_button_new_with_label("Button");
gtk_widget_set_size_request(btn, 70, 30);
gtk_container_add(GTK_CONTAINER(halign), btn);
gtk_container_add(GTK_CONTAINER(window), halign);
g_signal_connect(G_OBJECT(btn), "enter",
G_CALLBACK(enter_button), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the example, the background colour of the button widget is changes when we hover a mouse
pointer over it.
g_signal_connect(G_OBJECT(btn), "enter",
G_CALLBACK(enter_button), NULL);
We call the enter_button() user function when the enter signal occurs.
void enter_button(GtkWidget *widget, gpointer data) {
Inside the callback, we change the background of the button by calling the
gtk_widget_modify_bg() function.
Disconnecting a callback
We can disconnect a callback from the signal. The next code example demonstrates such a case.
disconnect.c
#include <gtk/gtk.h>
gint handler_id;
g_print("clicked\n");
}
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
handler_id = g_signal_connect(G_OBJECT(window), "clicked",
G_CALLBACK(button_clicked), NULL);
} else {
g_signal_handler_disconnect(window, handler_id);
}
}
GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *btn;
GtkWidget *cb;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_title(GTK_WINDOW(window), "Disconnect");
btn = gtk_button_new_with_label("Click");
gtk_widget_set_size_request(btn, 70, 30);
gtk_box_pack_start(GTK_BOX(hbox), btn, FALSE, FALSE, 0);
cb = gtk_check_button_new_with_label("Connect");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(cb), "clicked",
G_CALLBACK(toogle_signal), (gpointer) btn);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the code example, we have a button and a check box. The check box connects or disconnects a
callback from the clicked signal of the button.
handler_id = g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(button_clicked), NULL);
The g_signal_connect() returns the handler id which uniquely identifies the callback.
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
handler_id = g_signal_connect(G_OBJECT(window), "clicked",
G_CALLBACK(button_clicked), NULL);
} else {
g_signal_handler_disconnect(window, handler_id);
}
This code determines the state of the check box. Depending on the state, it connects the callback
with the g_signal_connect() function or disconnects with the
g_signal_handler_disconnect() function.
Figure: Disconnect
if (event->type == GDK_BUTTON_PRESS) {
if (event->button == 1) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
event->button,
event->x_root,
event->y_root,
event->time);
}
}
return TRUE;
}
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "Drag & drop");
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(window), "button-press-event",
G_CALLBACK(on_button_press), NULL);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show(window);
gtk_main();
return 0;
}
if (event->type == GDK_BUTTON_PRESS) {
if (event->button == 1) {
gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
event->button,
event->x_root,
event->y_root,
event->time);
}
}
return TRUE;
}
Inside the on_button_press() function, we perform the drag and drop operation. We check if
the left mouse button was pressed. Then we call the gtk_window_begin_move_drag()
function, which starts moving the window.
A timer example
The following example demonstrates a timer example. Timers are used when we have some
repeating tasks. It could be a clock, a count down, visual effects, or animations.
timer.c
#include <cairo.h>
#include <gtk/gtk.h>
gchar buf[256];
cairo_t *cr;
cr = gdk_cairo_create(widget->window);
cairo_destroy(cr);
return FALSE;
}
g_free(my_time);
g_date_time_unref(now);
gtk_widget_queue_draw(widget);
return TRUE;
}
GtkWidget *window;
GtkWidget *darea;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
g_signal_connect(darea, "expose-event",
G_CALLBACK(on_expose_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Timer");
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
gtk_widget_show_all(window);
time_handler(window);
gtk_main();
return 0;
}
The example displays the current local time on the window. The Cairo 2D library is also used.
g_signal_connect(darea, "expose-event",
G_CALLBACK(on_expose_event), NULL);
We draw the time inside the on_expose_event() callback. The callback is connected to the
expose-event signal, which is is emitted when the window is going to be redrawn.
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) window);
This function registers the timer. The time_handler() function is called repeatedly at regular
intervals; in our case in every second. The timer function is called until it returns FALSE.
time_handler(window);
This calls the timer function immediately. Otherwise, there would be one sec delay.
cairo_t *cr;
cr = gdk_cairo_create(widget->window);
cairo_destroy(cr);
This code draws the current time on the window. For more information about the Cairo 2D library,
see the ZetCode's Cairo graphics tutorial.
if (widget->window == NULL) return FALSE;
When the window is destroyed, it may happen that the timer function is called. This line prevents
working on an already destroyed widget.
GDateTime *now = g_date_time_new_now_local();
gchar *my_time = g_date_time_format(now, "%H:%M:%S");
The gtk_widget_queue_draw() function invalidates the window area, which then emits the
expose-event signal.
GTK+ dialogs
In this part of the GTK+ programming tutorial, we work with dialogs.
Dialog windows or dialogs are an indispensable part of most modern GUI applications. A dialog is
defined as a conversation between two or more persons. In a computer application a dialog is a
window which is used to "talk" to the application. A dialog is used to input data, modify data,
change the application settings etc.
Message dialogs
Message dialogs are convenient dialogs that provide messages to the user of the application. The
message consists of textual and image data.
messagedialogs.c
#include <gtk/gtk.h>
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"Download Completed");
gtk_window_set_title(GTK_WINDOW(dialog), "Information");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"Error loading file");
gtk_window_set_title(GTK_WINDOW(dialog), "Error");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK,
"Unallowed operation");
gtk_window_set_title(GTK_WINDOW(dialog), "Warning");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
GtkWidget *window;
GtkWidget *table;
GtkWidget *info;
GtkWidget *warn;
GtkWidget *que;
GtkWidget *err;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 220, 150);
gtk_window_set_title(GTK_WINDOW(window), "Message dialogs");
info = gtk_button_new_with_label("Info");
warn = gtk_button_new_with_label("Warning");
que = gtk_button_new_with_label("Question");
err = gtk_button_new_with_label("Error");
gtk_table_attach(GTK_TABLE(table), info, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), warn, 1, 2, 0, 1,
GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), que, 0, 1, 1, 2,
GTK_FILL, GTK_FILL, 3, 3);
gtk_table_attach(GTK_TABLE(table), err, 1, 2, 1, 2,
GTK_FILL, GTK_FILL, 3, 3);
gtk_container_add(GTK_CONTAINER(window), table);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
g_signal_connect(G_OBJECT(info), "clicked",
G_CALLBACK(show_info), (gpointer) window);
g_signal_connect(G_OBJECT(warn), "clicked",
G_CALLBACK(show_warning), (gpointer) window);
g_signal_connect(G_OBJECT(que), "clicked",
G_CALLBACK(show_question), (gpointer) window);
g_signal_connect(G_OBJECT(err), "clicked",
G_CALLBACK(show_error), (gpointer) window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In our example, we show four kinds of message dialogs: Information, Warning, Question, and Error
message dialogs.
void show_question(GtkWidget *widget, gpointer window) {
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
In the show_question() function, we pop up the message dialog. The message dialog is created
using the gtk_message_dialog_new() call. The parameters of the function specify what kind
of message dialog we create. The GTK_MESSAGE_QUESTION constant creates a question type
dialog. The GTK_BUTTONS_YES_NO constant will add Yes and No buttons in the dialog. The last
parameter is the text that we display in the dialog. The gtk_dialog_run() function shows the
dialog and blocks the main loop until the dialog responds or is destroyed. The dialog must be
destroyed with the gtk_widget_destroy() function.
Figure: Message dialogs
GtkAboutDialog
GtkAboutDialog is a dialog whose purpose is to display information about the application. It
can display the logo, the name of the application, the version, the copyright, the website, and the
licence information. It is also possible to give credits to the authors, documenters, translators, and
artists.
aboutdialog.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *about;
GdkPixbuf *battery;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 220, 150);
gtk_window_set_title(GTK_WINDOW(window), "Battery");
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(window), "button-press-event",
G_CALLBACK(show_about), (gpointer) window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The code example uses a GtkAboutDialog with some of its features. A click on the client area
of the window pops up the dialog.
GtkWidget *dialog = gtk_about_dialog_new();
These function calls set the name, the version, and the copyright of the application.
gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
"Battery is a simple tool for battery checking.");
gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
"http://www.batteryhq.net");
These lines set descriptive comments and the website of the application.
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("battery.png", NULL);
...
gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
g_object_unref(pixbuf), pixbuf = NULL;
GtkFontSelectionDialog
GtkFontSelectionDialog is a dialog for selecting fonts. It is typically used in applications
that do some text editing or formatting.
fontdialog.c
#include <gtk/gtk.h>
GtkResponseType result;
result = gtk_dialog_run(GTK_DIALOG(dialog));
PangoFontDescription *font_desc;
gchar *fontname = gtk_font_selection_dialog_get_font_name(
GTK_FONT_SELECTION_DIALOG(dialog));
font_desc = pango_font_description_from_string(fontname);
gtk_widget_modify_font(GTK_WIDGET(label), font_desc);
g_free(fontname);
}
gtk_widget_destroy(dialog);
}
GtkWidget *window;
GtkWidget *label;
GtkWidget *vbox;
GtkWidget *toolbar;
GtkToolItem *font;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 280, 200);
gtk_window_set_title(GTK_WINDOW(window), "Font Selection Dialog");
toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);
font = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_FONT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), font, -1);
label = gtk_label_new("ZetCode");
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 5);
g_signal_connect(G_OBJECT(font), "clicked",
G_CALLBACK(select_font), label);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the code example, we put a simple label into the center of the window. We show a font selecting
dialog by clicking on the toolbar button.
GtkWidget *dialog = gtk_font_selection_dialog_new("Select Font");
result = gtk_dialog_run(GTK_DIALOG(dialog));
PangoFontDescription *font_desc;
gchar *fontname = gtk_font_selection_dialog_get_font_name(
GTK_FONT_SELECTION_DIALOG(dialog));
font_desc = pango_font_description_from_string(fontname);
gtk_widget_modify_font(GTK_WIDGET(label), font_desc);
g_free(fontname);
}
If the user clicks on the OK or Apply button, we proceed. We get the selected font name using the
gtk_font_selection_dialog_get_font_name() function. Then we change the label's
font to the selected font name.
Figure:
GtkFontSelectionDialog
GtkColorSelectionDialog
GtkColorSelectionDialog is a dialog for selecting a colour.
colordialog.c
#include <gtk/gtk.h>
GtkResponseType result;
GtkColorSelection *colorsel;
result = gtk_dialog_run(GTK_DIALOG(dialog));
if (result == GTK_RESPONSE_OK) {
GdkColor color;
colorsel = GTK_COLOR_SELECTION(
GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel);
gtk_color_selection_get_current_color(colorsel,
&color);
gtk_widget_modify_fg(GTK_WIDGET(label),
GTK_STATE_NORMAL,
&color);
}
gtk_widget_destroy(dialog);
}
GtkWidget *toolbar;
GtkToolItem *font;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 280, 200);
gtk_window_set_title(GTK_WINDOW(window), "Color Selection Dialog");
toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);
font = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), font, -1);
label = gtk_label_new("ZetCode");
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 5);
g_signal_connect(G_OBJECT(font), "clicked",
G_CALLBACK(select_font), label);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example is very similar to the previous one. This time we change the colour of the label.
GtkWidget *dialog = gtk_color_selection_dialog_new("Font Color");
result = gtk_dialog_run(GTK_DIALOG(dialog));
GdkColor color;
colorsel = GTK_COLOR_SELECTION(
GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel);
gtk_color_selection_get_current_color(colorsel,
&color);
gtk_widget_modify_fg(GTK_WIDGET(label),
GTK_STATE_NORMAL,
&color);
}
If we press the OK button, we get the colour and modify the label's colour. The colour value is
returned with the gtk_color_selection_get_current_color() function.
Figure:
GtkColorSelectionDialog
This chapter was about dialogs in GTK+.
GTK+ Widgets
In this part of the GTK+ programming tutorial, we will introduce some GTK+ widgets.
Widgets are basic building blocks of a GUI application. Over the years, several widgets became a
standard in programming toolkits; for example a button, a check box, or a scroll bar. The GTK+
toolkit's philosophy is to keep the number of widgets at a minimum level. More specialised widgets
are created as custom GTK+ widgets.
GtkButton
GtkButton is a simple widget that is used to trigger an action.
button.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *halign;
GtkWidget *btn;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
btn = gtk_button_new_with_label("Quit");
gtk_widget_set_size_request(btn, 70, 30);
gtk_container_add(GTK_CONTAINER(halign), btn);
g_signal_connect(G_OBJECT(btn), "clicked",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example shows a button that is positioned in the upper-left corner of the window. The
application quits when we click on the button.
btn = gtk_button_new_with_label("Quit");
The button's clicked signal is connected to the gtk_main_quit() function, which terminates
the application.
Figure: GtkButton
GtkCheckButton
GtkCheckButton is a widget that has two states: on and off. The on state is visualized by a
check mark.
checkbutton.c
#include <gtk/gtk.h>
void toggle_title(GtkWidget *widget, gpointer window) {
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
gtk_window_set_title(window, "GtkCheckButton");
} else {
gtk_window_set_title(window, "");
}
}
GtkWidget *window;
GtkWidget *halign;
GtkWidget *cb;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
gtk_window_set_title(GTK_WINDOW(window), "GtkCheckButton");
cb = gtk_check_button_new_with_label("Show title");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
GTK_WIDGET_UNSET_FLAGS(cb, GTK_CAN_FOCUS);
gtk_container_add(GTK_CONTAINER(halign), cb);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(cb, "clicked",
G_CALLBACK(toggle_title), (gpointer) window);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example displays a window title depending on the state of the GtkCheckButton.
cb = gtk_check_button_new_with_label("Show title");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
The GtkCheckButton is created and is marked by default; the title is initially shown.
GTK_WIDGET_UNSET_FLAGS(cb, GTK_CAN_FOCUS);
Figure: GtkCheckButton
GtkFrame
GtkFrame is a bin with a decorative frame and optional label.
frames.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *table;
GtkWidget *frame1;
GtkWidget *frame2;
GtkWidget *frame3;
GtkWidget *frame4;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 250);
gtk_window_set_title(GTK_WINDOW(window), "GtkFrame");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example shows four different frame types. The frames are attached into a table container.
frame1 = gtk_frame_new("Shadow In");
Figure: GtkFrame
GtkHScale
GtkHScale is a horizontal slider widget for selecting a value from a range of values.
hscale.c
void value_changed(GtkRange *range, gpointer win) {
g_free(str);
}
GtkWidget *window;
GtkWidget *halign;
GtkWidget *hbox;
GtkWidget *hscale;
GtkWidget *label;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_title(GTK_WINDOW(window), "GtkHScale");
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(hscale, "value-changed",
G_CALLBACK(value_changed), label);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the example, we have a horizontal scale widget and a label widget. The currently chosen value is
displayed in the label.
gdouble val = gtk_range_get_value(range);
The gtk_range_get_value() function retrieves the currently selected value from the scale
widget.
gchar *str = g_strdup_printf("%.f", val);
gtk_label_set_text(GTK_LABEL(win), str);
We build a string value with the g_strdup_printf() function and set it to the label with the
gtk_label_set_text() function.
hscale = gtk_hscale_new_with_range(0, 100, 1);
Figure: GtkHScale
GtkLabel
The GtkLabel widget displays text.
label.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *label;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "No sleep");
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_container_add(GTK_CONTAINER(window), label);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
We create a GtkLabel widget. We can create multiline text label by using a new line character.
Note the escape character. We use a rather long string and we do not want to put all the text into one
line. In such cases, we can use an escape character.
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
The gtk_label_set_justify() function aligns the text in the label. With the
GTK_JUSTIFY_CENTER type, the text is centered.
Figure: GtkLabel
markup.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *label;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 100);
gtk_window_set_title(GTK_WINDOW(window), "Markup label");
gtk_container_add(GTK_CONTAINER(window), label);
gtk_widget_show(label);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show(window);
gtk_main();
return 0;
}
The gtk_label_set_markup() parses the string which is marked up and applies its attributes
to the label.
GTK+ Widgets II
In this part of the GTK+ programming tutorial, we continue covering various GTK+ widgets.
GktComboBoxText
GktComboBoxText is a widget that allows the user to choose from a list of options. The options
are strings.
combobox.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *combo;
GtkWidget *label;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkComboBox");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
combo = gtk_combo_box_new_text();
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Ubuntu");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Arch");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Fedora");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Mint");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Gentoo");
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Debian");
label = gtk_label_new("...");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(combo), "changed",
G_CALLBACK(combo_selected), (gpointer) label);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example shows a combo box and a label. The combo box has a list of six options. These are the
names of Linux distros. The label widget shows the selected option from the combo box.
combo = gtk_combo_box_text_new();
We get the selected text and set the label text to it. The
gtk_combo_box_get_active_text() function returns the currently active string in the
combo box. We set the string to the label with the gtk_label_set_text() function.
Figure: GktComboBoxText
GtkHSeparator
The GtkHSeparator is a horizontal separator. It is a kind of an ornament widget. There is also a
sister GtkVSeparator widget.
separator.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *label1;
GtkWidget *label2;
GtkWidget *hseparator;
GtkWidget *vbox;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "GtkHSeparator");
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_label_set_line_wrap(GTK_LABEL(label1), TRUE);
gtk_label_set_line_wrap(GTK_LABEL(label2), TRUE);
hseparator = gtk_hseparator_new();
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The code example shows definitions of two chemical elements; they are separated by a horizontal
separator. This makes the example more visually appealing.
label1 = gtk_label_new("Zinc is a moderately reactive, blue gray metal \
that tarnishes in moist air and burns in air with a bright bluish-green flame,\
giving off fumes of zinc oxide. It reacts with acids, alkalis and other non-
metals.\
If not completely pure, zinc reacts with dilute acids to release hydrogen.");
The gtk_label_set_line_wrap() function break lines if text exceeds the widget's size.
hseparator = gtk_hseparator_new();
GtkEntry
GtkEntry is a single line text entry field. This widget is used to enter textual data.
entry.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *table;
GtkWidget *label1;
GtkWidget *label2;
GtkWidget *label3;
GtkWidget *entry1;
GtkWidget *entry2;
GtkWidget *entry3;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "GtkEntry");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
label1 = gtk_label_new("Name");
label2 = gtk_label_new("Age");
label3 = gtk_label_new("Occupation");
gtk_table_attach(GTK_TABLE(table), label1, 0, 1, 0, 1,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), label2, 0, 1, 1, 2,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), label3, 0, 1, 2, 3,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
entry1 = gtk_entry_new();
entry2 = gtk_entry_new();
entry3 = gtk_entry_new();
gtk_table_attach(GTK_TABLE(table), entry1, 1, 2, 0, 1,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), entry2, 1, 2, 1, 2,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_table_attach(GTK_TABLE(table), entry3, 1, 2, 2, 3,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 5, 5);
gtk_widget_show_all(window);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
Figure: GtkEntry
GtkImage
GtkImage is a widget used to display an image.
image.c
#include <gtk/gtk.h>
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "Red Rock");
image = gtk_image_new_from_file("redrock.jpg");
gtk_container_add(GTK_CONTAINER(window), image);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
GtkStatusbar
GtkStatusbar displays status information. It is placed at the bottom of the application window.
statusbar.c
#include <gtk/gtk.h>
gchar *str;
str = g_strdup_printf("%s button clicked",
gtk_button_get_label(GTK_BUTTON(widget)));
gtk_statusbar_push(GTK_STATUSBAR(window),
gtk_statusbar_get_context_id(GTK_STATUSBAR(window), str), str);
g_free(str);
}
GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *halign;
GtkWidget *balign;
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *statusbar;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "GtkStatusbar");
button1 = gtk_button_new_with_label("OK");
gtk_widget_set_size_request(button1, 70, 30 );
button2 = gtk_button_new_with_label("Apply");
gtk_widget_set_size_request(button2, 70, 30 );
g_signal_connect(G_OBJECT(button1), "clicked",
G_CALLBACK(button_pressed), G_OBJECT(statusbar));
g_signal_connect(G_OBJECT(button2), "clicked",
G_CALLBACK(button_pressed), G_OBJECT(statusbar));
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the code example, there are two buttons and a statusbar. If we click on the button, a message is
displayed in the statusbar. It says which button was pressed.
gchar *str;
str = g_strdup_printf("Button %s clicked",
gtk_button_get_label(GTK_BUTTON(widget)));
The message is built with the g_strdup_printf() function. We get the label of the button with
the gtk_button_get_label() function.
gtk_statusbar_push(GTK_STATUSBAR(window),
gtk_statusbar_get_context_id(GTK_STATUSBAR(window), str), str);
We show the message in the statusbar. The gtk_statusbar_push() function pushes a new
message onto a statusbar’s stack. The function requires a context id, which is returned by the
gtk_statusbar_get_context_id() function.
statusbar = gtk_statusbar_new();
Figure: GtkStatusbar
GtkIconView
GtkIconView is a widget which displays a list of icons in a grid. It uses a GtkListStore to
store its data.
iconview.c
#include <gtk/gtk.h>
#include <assert.h>
enum {
COL_DISPLAY_NAME,
COL_PIXBUF,
NUM_COLS
};
GtkTreeModel *init_model(void) {
GtkListStore *list_store;
GdkPixbuf *p1, *p2, *p3, *p4;
GtkTreeIter iter;
GError *err = NULL;
p1 = gdk_pixbuf_new_from_file("ubuntu.png", &err);
p2 = gdk_pixbuf_new_from_file("gnumeric.png", &err);
p3 = gdk_pixbuf_new_from_file("blender.png", &err);
p4 = gdk_pixbuf_new_from_file("inkscape.png", &err);
assert(err==NULL);
list_store = gtk_list_store_new(NUM_COLS,
G_TYPE_STRING, GDK_TYPE_PIXBUF);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Ubuntu", COL_PIXBUF, p1, -1);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Gnumeric", COL_PIXBUF, p2, -1);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Blender", COL_PIXBUF, p3, -1);
gtk_list_store_append(list_store, &iter);
gtk_list_store_set(list_store, &iter, COL_DISPLAY_NAME,
"Inkscape", COL_PIXBUF, p4, -1);
g_object_unref(p1);
g_object_unref(p2);
g_object_unref(p3);
g_object_unref(p4);
return GTK_TREE_MODEL(list_store);
}
GtkWidget *window;
GtkWidget *icon_view;
GtkWidget *sw;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "IconView");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 300);
sw = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(window), sw);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
GTK_SHADOW_IN);
icon_view = gtk_icon_view_new_with_model(init_model());
gtk_container_add(GTK_CONTAINER(sw), icon_view);
gtk_icon_view_set_text_column(GTK_ICON_VIEW(icon_view),
COL_DISPLAY_NAME);
gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(icon_view), COL_PIXBUF);
gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(icon_view),
GTK_SELECTION_MULTIPLE);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
The example displays 4 icons. The icons represent four prominent open source projects.
p1 = gdk_pixbuf_new_from_file("ubuntu.png", &err);
p2 = gdk_pixbuf_new_from_file("gnumeric.png", &err);
p3 = gdk_pixbuf_new_from_file("blender.png", &err);
p4 = gdk_pixbuf_new_from_file("inkscape.png", &err);
We load four images from the disk using the gdk_pixbuf_new_from_file() function.
list_store = gtk_list_store_new(NUM_COLS,
G_TYPE_STRING, GDK_TYPE_PIXBUF);
Figure: IconView
In this part of the GTK+ tutorial, we have continued covering GTK+ widgets.
GtkTreeView widget
In this part of the GTK+ programming tutorial, we work with the GtkTreeView widget.
GtkTreeView widget is a complex widget which can be used to display lists and trees. The
widget can have one or multiple columns. The GtkTreeView widget has a MVC (Model View
Controller) design architecture. This means that the data is separated from the view.
There are several other objects that are used with the GtkTreeView widget. The
GtkCellRenderer determines how the data is going to be displayed in the
GtkTreeViewColumn. The GtkListStore and the GtkTreeStore represent the model.
They handle data that are displayed in the GtkTreeView widget. GtkTreeIter is a structure
used to refer to a row in the GtkTreeView. GtkTreeSelection is an object that handles
selections.
List view
The first example will show a simple list view. We will display textual data.
listview.c
#include <gtk/gtk.h>
enum {
LIST_ITEM = 0,
N_COLUMNS
};
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkListStore *store;
gtk_tree_view_set_model(GTK_TREE_VIEW(list),
GTK_TREE_MODEL(store));
g_object_unref(store);
}
GtkListStore *store;
GtkTreeIter iter;
store = GTK_LIST_STORE(gtk_tree_view_get_model
(GTK_TREE_VIEW(list)));
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
}
GtkTreeIter iter;
GtkTreeModel *model;
gchar *value;
if (gtk_tree_selection_get_selected(
GTK_TREE_SELECTION(widget), &model, &iter)) {
GtkWidget *window;
GtkWidget *list;
GtkWidget *vbox;
GtkWidget *label;
GtkTreeSelection *selection;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
list = gtk_tree_view_new();
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
label = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
gtk_container_add(GTK_CONTAINER(window), vbox);
init_list(list);
add_to_list(list, "Aliens");
add_to_list(list, "Leon");
add_to_list(list, "The Verdict");
add_to_list(list, "North Face");
add_to_list(list, "Der Untergang");
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
g_signal_connect(selection, "changed",
G_CALLBACK(on_changed), label);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In our code example, we show five items in the GtkTreeView. We have only one column and the
header of the column is hidden. We place a GtkVBox into the window. This box has two widgets: a
GtkTreeView and a GtkLabel.
list = gtk_tree_view_new();
gtk_tree_view_set_model(GTK_TREE_VIEW(list),
GTK_TREE_MODEL(store));
The TreeView increases the reference of the store object. We decrease the reference from 2 to 1
with the g_object_unref() function. The model is then destroyed automatically with the view.
add_to_list(list, "Aliens");
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
The GtkTreeSelection does not need to be created explicitly; it is automatically created with
the GtkTreeView widget. The reference to the widget is obtained using the
gtk_tree_view_get_selection() function call.
g_signal_connect(selection, "changed",
G_CALLBACK(on_changed), label);
Inside the handler function, we get the value of the cell in the row referenced by the iter object.
gtk_label_set_text(GTK_LABEL(label), value);
The retrieved value is set to the label with the gtk_label_set_text() function.
enum {
LIST_ITEM = 0,
N_COLUMNS
};
GtkWidget *list;
GtkListStore *store;
GtkTreeIter iter;
store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
gtk_entry_set_text(entry, "");
}
GtkListStore *store;
GtkTreeModel *model;
GtkTreeIter iter;
store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection),
&model, &iter)) {
gtk_list_store_remove(store, &iter);
}
}
GtkListStore *store;
GtkTreeModel *model;
GtkTreeIter iter;
store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
gtk_list_store_clear(store);
}
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkListStore *store;
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("List Item",
renderer, "text", LIST_ITEM, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
g_object_unref(store);
}
GtkWidget *window;
GtkWidget *sw;
GtkWidget *remove;
GtkWidget *add;
GtkWidget *removeAll;
GtkWidget *entry;
GtkWidget *vbox;
GtkWidget *hbox;
GtkTreeSelection *selection;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
sw = gtk_scrolled_window_new(NULL, NULL);
list = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER(sw), list);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
GTK_SHADOW_ETCHED_IN);
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
add = gtk_button_new_with_label("Add");
remove = gtk_button_new_with_label("Remove");
removeAll = gtk_button_new_with_label("Remove All");
entry = gtk_entry_new();
gtk_widget_set_size_request(entry, 120, -1);
gtk_container_add(GTK_CONTAINER(window), vbox);
init_list(list);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
g_signal_connect(G_OBJECT(add), "clicked",
G_CALLBACK(append_item), entry);
g_signal_connect(G_OBJECT(remove), "clicked",
G_CALLBACK(remove_item), selection);
g_signal_connect(G_OBJECT(removeAll), "clicked",
G_CALLBACK(remove_all), selection);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the example, we have three buttons and one text entry. The buttons add a new item, remove the
selected item, and remove all items.
sw = gtk_scrolled_window_new(NULL, NULL);
list = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER(sw), list);
This code checks if there is some item left in the list. Obviously, we can remove items only if there
is at least one left in the list.
Figure: Dynamic List view
Tree view
The following example uses the GtkTreeView widget to display hierarchical data. In the previous
two examples, we used the list view; now we are going to use tree view.
treeview.c
#include <gtk/gtk.h>
enum {
COLUMN = 0,
NUM_COLS
};
GtkTreeIter iter;
GtkTreeModel *model;
gchar *value;
if (gtk_tree_selection_get_selected(
GTK_TREE_SELECTION(widget), &model, &iter)) {
GtkTreeModel *create_and_fill_model(void) {
GtkTreeStore *treestore;
GtkTreeIter toplevel, child;
treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING);
return GTK_TREE_MODEL(treestore);
}
GtkWidget *create_view_and_model(void) {
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
GtkTreeModel *model;
view = gtk_tree_view_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "Programming languages");
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer,
"text", COLUMN);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model);
return view;
}
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *view;
GtkTreeSelection *selection;
GtkWidget *vbox;
GtkWidget *statusbar;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "Tree view");
gtk_widget_set_size_request(window, 350, 300);
view = create_view_and_model();
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
statusbar = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);
g_signal_connect(selection, "changed",
G_CALLBACK(on_changed), statusbar);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In the example, we divide programming languages into two groups: scripting languages and
compiled languages. The language categories serve as toplevel nodes for their list of items. The
currently selected item is shown in the statusbar. The steps to create a tree view are very similar to
creating a list view.
treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING);
GtkTextView widget
In this part of the GTK+ programming tutorial, we work with the GtkTextView widget.
GtkTextView widget is used for displaying and editing multiline text. GtkTextView widget
has also the MVC design. The GtkTextView represents the view component and the
GtkTextBuffer represents the model component. GtkTextBuffer is used to manipulate
textual data. GtkTextTag is an attribute that can be applied to the text. GtkTextIter
represents a position between two characters in the text. All manipulation with the text is done using
text iterators.
Simple example
In our first example, we show some of the GtkTextView's functionality. We show how to apply
various text tags to the text data.
simpletextview.c
#include <gtk/gtk.h>
GtkWidget *window;
GtkWidget *view;
GtkWidget *vbox;
GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkTextIter iter;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "GtkTextView");
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_create_tag(buffer, "gap",
"pixels_above_lines", 30, NULL);
gtk_text_buffer_create_tag(buffer, "lmarg",
"left_margin", 5, NULL);
gtk_text_buffer_create_tag(buffer, "blue_fg",
"foreground", "blue", NULL);
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "gray", NULL);
gtk_text_buffer_create_tag(buffer, "italic",
"style", PANGO_STYLE_ITALIC, NULL);
gtk_text_buffer_create_tag(buffer, "bold",
"weight", PANGO_WEIGHT_BOLD, NULL);
gtk_container_add(GTK_CONTAINER(window), vbox);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Figure: GtkTextView
update_statusbar(GtkTextBuffer *buffer,
GtkStatusbar *statusbar) {
gchar *msg;
gint row, col;
GtkTextIter iter;
gtk_statusbar_pop(statusbar, 0);
gtk_text_buffer_get_iter_at_mark(buffer,
&iter, gtk_text_buffer_get_insert(buffer));
row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);
gtk_statusbar_push(statusbar, 0, msg);
g_free(msg);
}
update_statusbar(buffer, GTK_STATUSBAR(data));
}
GtkWidget *toolbar;
GtkWidget *view;
GtkWidget *statusbar;
GtkToolItem *exit;
GtkTextBuffer *buffer;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines & columns");
toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);
view = gtk_text_view_new();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_widget_grab_focus(view);
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
statusbar = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(exit), "clicked",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(buffer, "changed",
G_CALLBACK(update_statusbar), statusbar);
g_signal_connect_object(buffer, "mark_set",
G_CALLBACK(mark_set_callback), statusbar, 0);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
update_statusbar(buffer, GTK_STATUSBAR(statusbar));
gtk_main();
return 0;
}
In this code example, we show the current position of the text cursor in the statusbar.
g_signal_connect(buffer, "changed",
G_CALLBACK(update_statusbar), statusbar);
When we change the text, we call the update_statusbar() handler.
g_signal_connect_object(buffer, "mark_set",
G_CALLBACK(mark_set_callback), statusbar, 0);
This code line clears the message with context ID 0 from the statusbar.
gtk_text_buffer_get_iter_at_mark(buffer,
&iter, gtk_text_buffer_get_insert(buffer));
row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);
switch (event->keyval) {
case GDK_m :
selected = gtk_text_buffer_get_selection_bounds(buffer,
&start_sel, &end_sel);
if (selected) {
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);
gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
text = (gchar *) gtk_text_buffer_get_text(buffer, &start_sel,
&end_sel, FALSE);
gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg",
&start_match, &end_match);
gint offset = gtk_text_iter_get_offset(&end_match);
gtk_text_buffer_get_iter_at_offset(buffer,
&start_find, offset);
}
g_free(text);
}
break;
case GDK_r:
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);
gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
break;
}
}
return FALSE;
}
GtkWidget *window;
GtkWidget *view;
GtkWidget *vbox;
GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkTextIter iter;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 350, 300);
gtk_window_set_title(GTK_WINDOW(window), "Search & highlight");
GTK_WINDOW(window)->allow_shrink = TRUE;
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "lightgray", NULL);
gtk_container_add(GTK_CONTAINER(window), vbox);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(window), "key-press-event",
G_CALLBACK(key_pressed), buffer);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
In our code example we use keyboard shortcuts. The Ctrl+M shortcut highlights the all
occurrences of the currently selected text. The Ctrl+R removes the highlights from the text.
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "lightgray", NULL);
This is the GtkTextTag that we use in our example. The tag makes the background of the text
gray.
selected = gtk_text_buffer_get_selection_bounds(buffer,
&start_sel, &end_sel);
We get the first and the last position in the text buffer.
gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg",
&start_match, &end_match);
gint offset = gtk_text_iter_get_offset(&end_match);
gtk_text_buffer_get_iter_at_offset(buffer,
&start_find, offset);
}
This code searches for all occurences of the selected text. If we find any match, we apply the text
tag. After the match, the ending point of the word becomes the starting point for the next search.
CPU widget
In the next example we create a custom CPU widget.
mycpu.h
#ifndef __MY_CPU_H__
#define __MY_CPU_H__
#include <gtk/gtk.h>
#include <cairo.h>
G_BEGIN_DECLS
/* Standart GObject macros */
#define MY_TYPE_CPU (my_cpu_get_type())
#define MY_CPU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MY_TYPE_CPU, MyCpu))
#define MY_CPU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), MY_TYPE_CPU,
MyCpuClass))
#define MY_IS_CPU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), MY_TYPE_CPU))
#define MY_IS_CPU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), MY_TYPE_CPU))
#define MY_CPU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), MY_TYPE_CPU,
MyCpuClass))
/* Type definition */
typedef struct _MyCpu MyCpu;
typedef struct _MyCpuClass MyCpuClass;
typedef struct _MyCpuPrivate MyCpuPrivate;
struct _MyCpu {
GtkWidget parent;
struct _MyCpuClass {
GtkWidgetClass parent_class;
};
/* Public API */
GType my_cpu_get_type(void) G_GNUC_CONST;
GtkWidget *my_cpu_new(void);
G_END_DECLS
#endif /* __MY_CPU_H__ */
In the mycpu.h file, we define types, macros, and functions for the custom widget.
mycpu.c
/* mycpu.c */
#include "mycpu.h"
/* Properties enum */
enum {
P_0, /* Padding */
P_PERCENT
};
gdouble percent;
GdkWindow *window;
};
/* Internal API */
static void my_cpu_set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec);
static void my_cpu_get_property(GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static void my_cpu_size_request(GtkWidget *widget,
GtkRequisition *requisition);
static void my_cpu_size_allocate(GtkWidget *widget,
GtkAllocation *allocation);
static void my_cpu_realize(GtkWidget *widget);
static gboolean my_cpu_expose(GtkWidget *widget,
GdkEventExpose *event);
/* Define type */
G_DEFINE_TYPE(MyCpu, my_cpu, GTK_TYPE_WIDGET)
/* Initialization */
static void my_cpu_class_init(MyCpuClass *klass) {
GObjectClass *g_class;
GtkWidgetClass *w_class;
GParamSpec *pspec;
g_class = G_OBJECT_CLASS(klass);
w_class = GTK_WIDGET_CLASS(klass);
w_class->realize = my_cpu_realize;
w_class->size_request = my_cpu_size_request;
w_class->size_allocate = my_cpu_size_allocate;
w_class->expose_event = my_cpu_expose;
/* Install property */
pspec = g_param_spec_double("percent", "Percent",
"What CPU load should be displayed", 0, 1, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
MyCpuPrivate *priv;
gtk_widget_set_has_window(GTK_WIDGET(cpu), TRUE);
switch(prop_id) {
case P_PERCENT:
my_cpu_set_percent(cpu, g_value_get_double(value));
break;
default:
switch(prop_id) {
case P_PERCENT:
g_value_set_double(value, cpu->priv->percent);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
gtk_widget_set_realized(widget, TRUE);
gtk_widget_get_allocation(widget, &alloc);
attrs.x = alloc.x;
attrs.y = alloc.y;
attrs.width = alloc.width;
attrs.height = alloc.height;
attrs.window_type = GDK_WINDOW_CHILD;
attrs.wclass = GDK_INPUT_OUTPUT;
attrs.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
priv->window = gdk_window_new(gtk_widget_get_parent_window(widget),
&attrs, attrs_mask);
gdk_window_set_user_data(priv->window, widget);
gtk_widget_set_window(widget, priv->window);
requisition->width = WIDTH;
requisition->height = HEIGHT;
}
MyCpuPrivate *priv;
priv = MY_CPU(widget)->priv;
gtk_widget_set_allocation(widget, allocation);
if (gtk_widget_get_realized(widget)) {
cr = gdk_cairo_create(event->window);
cairo_translate(cr, 0, 7);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_paint(cr);
limit = 20 - priv->percent / 5;
if (i > limit) {
cairo_set_source_rgb(cr, 0.6, 1.0, 0);
} else {
cairo_set_source_rgb(cr, 0.2, 0.4, 0);
}
cairo_destroy(cr);
return TRUE;
}
/* Public API */
GtkWidget *my_cpu_new(void) {
return(g_object_new(MY_TYPE_CPU, NULL));
}
g_return_val_if_fail(MY_IS_CPU(cpu), 0);
return(cpu->priv->percent);
}
g_return_if_fail(MY_IS_CPU(cpu));
cpu->priv->percent = sel;
gtk_widget_queue_draw(GTK_WIDGET(cpu));
}
The mycpu.c is the implementation of the CPU widget. The CPU widget is a GtkWidget on
which we draw with Cairo API. We draw a black background and 40 small rectangles. The
rectangles are drawn in two colours: dark green and bright green colour. The GtkVScale widget
controls the number of the bright green rectangles drawn on the widget.
main.c
#include "mycpu.h"
my_cpu_set_percent(MY_CPU(cpu), gtk_range_get_value(range));
}
GtkWidget *window;
GtkWidget *hbox;
GtkWidget *vbox;
GtkWidget *cpu;
GtkWidget *scale;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(window), 15);
gtk_window_set_title(GTK_WINDOW(window), "CPU widget");
cpu = my_cpu_new();
gtk_box_pack_start(GTK_BOX(hbox), cpu, FALSE, FALSE, 0);
gtk_main();
return 0;
}
This is the main file. We create the custom CPU widget and make it work with the GtkVScale
widget.
gcc -Wall $(pkg-config --cflags gtk+-2.0) -o mycpu.o -c mycpu.c
gcc -Wall $(pkg-config --cflags gtk+-2.0) -o main.o -c main.c
gcc -o test_cpu main.o mycpu.o $(pkg-config --libs gtk+-2.0)