Yolinux.com Tutorial

GTK+: Gnome cross platform GUI API programming Tips and Tricks

This YoLinux.com GTK+ 2 tutorial is aimed at C/C++ application developers who wish to write software with a single GUI API for compilation for a multitude of target OS platforms. The Gnome home page (GTK.org) is a valuable resource for basic use of the GTK+ 2 API.

GTK+ Tutorial Contents:

Related YoLinux Tutorials:

°GNOME basics

°XML parsing with GNOME libXML2

°GDK Threads

°Software development tools

°Advanced VI

°Emacs and C/C++

°C++ Info, links

°MS/Visual C++ Practices

°C++ Memory corruption and leaks

°C++ String Class

°C++ STL vector, list

°Clearcase Commands

°YoLinux Tutorials Index




Free Information Technology Magazines and Document Downloads
TradePub link image


Free Information Technology Software and Development Magazine Subscriptions and Document Downloads


   

    Bookmark and Share


Advertisements




Introduction to GTK+:

This tutorial will cover tips on using Gnome GTK+ (Gimp ToolKit) to build a GUI application for cross platform deployment. Suggestions, tips and pitfalls are noted about the use of various GTK+ components.

GTK+ is a multi-platform GUI API library which includes the standard interface widgets. It is a component of the GNOME desktop and applications development environment which also includes multi-platform API's for graphics, video, window management, XML parsing, a thread library, internationalization, database access and general desktop application development. There is a GUI interface builder, Glade which generates a GTK+ source code skeleton for rapid application development. The API's support the "C" computer language. I usually encapsulate these calls within C++ objects where convenient. There is also a C++ framework available known as "gtkmm".

GTK is published under the GPL license and thus is free to use and redistribute.


Download/Install/Configure:

Linux:

  • RPM:
    Systems which are Linux based will have GTK and Gnome as part of a regular install. The Gnome desktop and support services are NOT necessary to write a basic GTK+ GUI application.

    RedHat Linux RPMS's to install:

    • gtk2
    • gtk2-devel-2.XX
    • gtk2-engines
    • gtkhtml2-2.XX
    • gtkam
    • gtkam-gimp
    • gtkglarea
    • pango (Internationalized text handling)
    • atk (Accessibility Toolkit)
    • gdk-pixbuf-devel
    • gdk-pixbuf-gnome
    • gdk-pixbuf
    • glib
    • glade
    • freetype
    • fontconfig
    • fontconfig-devel
    Also requires tiff, png and jpeg libraries.

  • Building GTK+ from source:
    The following must be done before configuring and building:
    • If installing to /opt, your PATH must include /opt/bin before /bin and /usr/bin if conflicting versions of pkgconfig are pressent.
      (export PATH=/opt/bin:$PATH)
    • Set the environment variable LD_LIBRARY_PATH to include /opt/lib.
      (export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH)
    • Set environment variable PKG_CONFIG_PATH.
      (export PKG_CONFIG_PATH=/opt/lib/pkgconfig:/usr/lib/pkgconfig)

    Download the latest stable release from ftp://ftp.gtk.org/pub/gtk/. This requires downloading glib, atk, pango and gtk+ conpressed tar files. Also download from the "dependencies/" directory pkgconfig.

    Uncompress: tar -xzf pkgconfig-0.15.0.tar.gz glib-2.4.7.tar.gz pango-1.4.1.tar.gz atk-1.6.1.tar.gz gtk+-2.4.13.tar.gz

    For each subdirectory created configure and make:

    config-0.15.0
    ./configure --prefix=/opt
    make
    make install
    
    cd ../glib-2.4.7
    ./configure --prefix=/opt
    make
    make install
    
    cd ../pango-1.4.1
    ./configure --prefix=/opt
    make
    make install
    
    cd ../atk-1.6.1
    ./configure --prefix=/opt
    make
    make install
    
    cd ../gtk+-2.4.13
    ./configure --prefix=/opt
    make
    make install
    

    Also see GTK+ building reference.

    [Potential Pitfall]: Gtk+ configure error: "cofigure: error: Pango 1.2.0 and Xft backend is required for x11 target"
    Test for problem with: pkg-config --modversion pangoxft
    This command must return a version number or else this is an indication of a configuration problem.

    Actual problem is in configure of pango which gives error:

    ...
    checking for fontconfig >= 1.0.1... Package fontconfig was not found in the pkg-config search path.
    Perhaps you should add the directory containing `fontconfig.pc'
    to the PKG_CONFIG_PATH environment variable
    No package 'fontconfig' found
    configure: WARNING: No fontconfig found, skipping tests for FreeType and Xft
    ...
    
    Fix: Set environment variable PKG_CONFIG_PATH. (export PKG_CONFIG_PATH=/opt/lib/pkgconfig:/usr/lib/pkgconfig)
    This is so that the file fontconfig.pc can be located. This file is installed with the RPM fontconfig-devel. (required)


Microsoft Windows:

GTK can be compiled under Cygwin or Microsoft Visual C++.

MS/Windows install: I strongly recommend the GTK+ installer available from the Glade website:
MS/Windows GTK+ installer: http://gladewin32.sourceforge.net/

Two installers are available:

  • gtk-win32-aio-2.4-rc16.exe: Installs the complete GTK development environment (headers, libraries, Glade GUI builder, GTK, GDK, Pango, etc.) Developers should use this installer to get the latest version of Glade.
  • gtk-win32-2.4.6-rc2.exe: Installs GTK runtime environment.

GladWin32:

See: GladWin32: GTK+ GUI builder.

VC++ 6.0 Compiler/IDE:

If creating a new Visual C++ project:
  • File + New and select "Win 32 Console Application". Enter a project name.
  • The next dialog box allows one to define the type of application: Select "An empty project" + "Finish".
To add default include directories which can be referenced with <file.h> instead of "file.h": Tools + Options + select tab "Directories" + "include files".

Note: If using VC++ 6.0, update VC++ 6.0 to SP5. Download and run to expand to subdir. Then run setupsp5.exe

Visual C++ 6.0 configuration:

  • Add sockets library to support htonl(): Select "Project" + "Settings..." + "Link". Add "Object/library module": ws2_32.lib
  • GTK:
    VC++ compiler settings:
    • Project + Settings + C++ tab:
      Category: C++ Language Check "Enable exception handling".
      Category: Code Generation
      • Calling convention: _cdecl*
      • Struct member alignment: 8 byte.
      • Processor: Blend

      Category: Precompiled Headers Check not using
      Category: Preprocessor
      Additional Include directories: C:\GTK\Win32\include,C:\libxml2\Win32\include
    • Project + Settings + Link tab:
      • Category: General
        Object/library modules: glib-2.0.lib gobject-2.0.lib gthread-2.0.lib gdk-win32-2.0.lib gdk_pixbuf-2.0.lib gtk-win32-2.0.lib atk-1.0.lib pango-1.0.lib libxml2.lib
        Check: "Link incrementally"
      • Category: Input
        Additional library path: C:\GTK\Win32\lib,C:\libxml2\Win32\lib

System configuration:

  • System Properties:
    • Select the "Advanced" tab.
    • Select the "Environment Variables ..." button.
    • In the "User Variables" frame, edit the environment variable "PATH" and add:
      C:\GTK\bin
    • Also set the following environment variables:
      Environment Variable
      LIB c:\GTK\lib
      GTK_BASEPATH c:\GTK
      INCLUDE c:\GTK\INCLUDE;c:\GTK\GTK-2.0;c:\GTK\GLIB-2.0; c:\GTK\PANGO-1.0; c:\GTK\ATK-1.0; c:\GTK\GTKGLEXT-1.0; c:\GTK\GTK-2.0\INCLUDE; c:\GTK\GLIB-2.0\INCLUDE; c:\GTK\GTKGLEXT\INCLUDE; c:\GTK\INCLUDE\LIBGLADE-2.0; c:\GTK\INCLUDE\LIBXML2;

Also see the YoLinux tutorial on MS/Visual C++ best practices so that the mess that this IDE can create is kept to a minimum.


Program Framework:

The basic framework of a GTK+ program requires:

  • the include file: gtk/gtk.h
  • callbacks used to close the principal window.
  • main program.
  • a call to gtk_init (&argc, &argv);
  • instantiation of main window
  • attach main window callbacks to main window. i.e. Callbacks to close window.
  • a container widget to attach other widgets to the main window and begin the "widget tree".
  • Attach widgets to parent components.
  • render the widgets including the container widgets and principal window.
  • a call to the main event loop: gtk_main ();

#include <gtk/gtk.h>
    ..
    ...
static gboolean delete_event( GtkWidget *widget,
                              GdkEvent  *event,
                              gpointer   data )
{
   return FALSE;
}

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

int main( int   argc, char *argv[] )
{
    GtkWidget *window_Widget;
    ...
    ..

    gtk_init (&argc, &argv);

    //instantiate principal parent window

    window_Widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window_Widget), "Example GTK Skeleton");

    // Connect callbacks to main window

    g_signal_connect (G_OBJECT (window_Widget), "delete_event",
                      G_CALLBACK (delete_event), NULL);

    g_signal_connect (G_OBJECT (window_Widget), "destroy",
                      G_CALLBACK (destroy), NULL);

    gtk_container_set_border_width (GTK_CONTAINER (window_Widget), 10);

    // Call to container widget

    vBox_Widget = gtk_vbox_new (.......);  

    ...
    ..

    // Attach widgets to container widget.

    gtk_box_pack_start (GTK_BOX (vBox_Widget), new_component_1_Widget, TRUE, TRUE, 0);
    ...
    ..

    // Attach container to main window.

    gtk_container_add (GTK_CONTAINER (window_Widget), vBox_Widget);  

    ...
    ..

    gtk_widget_show (vBox_Widget);
    gtk_widget_show (window_Widget);

    gtk_main ();

    return 0;
}
                

GTK Objects and Widgets:

GTK is designed around a hierarchical class mechanism of widgets and objects of which GtkObject is the root. GtkObject provides the minimal set of information to support the GTK signal mechanism of callbacks which provide the functionality behind the GUI application. GTK is written in C and thus implements this mechanism with structures, composition and casting.

struct Child
{
   GtkObject  Parent    // Appears first in structure definition
   
   ...
}
                
Casting allows one to view just the object info held in all objects without knowing the details of the widget or derived object. This principal is built-in to the GTK object and widget api.

GTK Widgets form a hierarchy of widgets joined together in a tree. Thus all widgets must be "linked" together with container or packing widgets to create this tree. It is this tree which is traversed for rendering of the GUI and for signal/callback handling. Thus an object which is "destroyed" will destroy the entire branch unless it is re-attached at some point in the hierarchy.


Widgets:

There are numerous widgets defined and available for use. One can also generate thier own widgets and compose widgets from combinations of other widgets.

Examples:

  • Push Buttons
  • Menu Widget
  • Radio Button
  • Check Button
  • Scroll Bar
  • Drawing Area
  • Tree View
  • Text View
  • Text Entry Box
  • Combo Box
  • Scroll Bars (horizontal and vertical)

Signals can then be associated with a widget so that an action (i.e. button release) on a Button widget will invoke a callback function to be executed and perform the desired task (i.e. process or display something or both).


Container/Packing Widgets:

Container, Alignment and Packing widgets are used to place display widgets and other container widgets within a window frame. They do not take any form when rendered. (invisible)
Types:

  • gtk_container_add()
    This will hold a single widget.


  • gtk_alignment_new()
    Position a widget within it's window.
    Use the function gtk_alignment_set() to control layout and spacing.

  • gtk_fixed_new()
    Fix widget position in window relative to upper left hand corner.
    Use the functions gtk_fixed_put(), gtk_fixed_move(), gtk_fixed_set_has_window() and gtk_fixed_get_has_window() to control layout.

  • gtk_layout_new()
    Layout in infinite scrolling area.
    Use the functions gtk_layout_put(), gtk_layout_move(), gtk_layout_set_size(), gtk_layout_get_hadjustment(), gtk_layout_get_vadjustment(), gtk_layout_set_hadjustment() and gtk_layout_set_vadjustment() to control layout and spacing.

  • gtk_hbox_new()
    Horizontal boxes









    Use the functions gtk_box_pack_start() and gtk_box_pack() to control layout and spacing.
    Also see gtk_hbutton_box_new().

  • gtk_vbox_new()
    Vertical Boxes





    Use the functions gtk_box_pack_start() and gtk_box_pack() to control layout and spacing.
    Also see gtk_vbutton_box_new().

  • gtk_table_new()
    Table













    Use the functions gtk_table_attach(), gtk_table_attach_defaults(), gtk_table_set_row_spacing(), gtk_table_set_col_spacing(), to control layout and spacing.


Signals:

Signals allow for a routine to be called when a user interaction occurs. Use the routine g_signal_connect() to attach a "callback" function to a user interaction ("signal").

The signals have pre-defined names. Each widget has a defined list of signals which can act on the widget. For example, a "window" widget may have the signals:

  • activate_focus
  • frame_event
  • move_focus
  • ...
The widget also inherits the ability to receive a set of signals which all widgets can receive:
  • delete_event
  • button_press_event
  • button_release_event
  • expose_event
  • hide_event
  • show
  • ...

Define the callback functions:

..
...

// Calback function definitions

gboolean
delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    gtk_main_quit();
    return FALSE;
}

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

...
..

Associate the callback function to a user action performed on a specified widget:

    ..
    ...

    // Callback is invoked when signal "delete_event" is applied by the user to 
    // the widget "mainWindow". This occurs when the user selects the X in the 
    // window corner to close/terminate the window.

    g_signal_connect((gpointer) mainWindow, "delete_event", G_CALLBACK(delete_event), NULL);

    // Attach the callback routine destroy to the action "destroy" when applied to the widget mainWindow

    g_signal_connect((gpointer) mainWindow, "destroy", G_CALLBACK(destroy), NULL);

    ...
    ..

The list of signals available are dependent on the widget. It is for this reason that the GUI builder Glade is very useful.

Passing C++ object to a Signal:

static void
on_applyButton_clicked(GtkWidget *button, gpointer data)
{
    CclassDef *obj = static_cast<CclassDef *>(data);   
    assert(obj != NULL);
    gtk_widget_destroy((GTK_WIDGET(obj->getWindowPtr()));
}
 
   ..
   ...

   // Within class function

   g_signal_connect((gpointer) applyButton, "clicked",
                     G_CALLBACK(on_applyButton_clicked), this);
   ...
   ..

Signal inheritance:

The object oriented nature of GTK allows a widget to inherit the ability to receive signal types of the parents in its inheritance tree. Thus signals inherit pre-defined behaviors already configured in the GTK GUI and components. A signal daisy-chain is processed which executes the user defined signals and then processes the inherited signals. One may break this chain of events so that only your signal handlers are processed, To prevent inheritance of predefined signal behaviors, the signal function must return a boolean. Return "true" to break inheritance. Return "false" to maintain the behavior of predefined parent signals such that the chain of inherited signals are maintained and processed after your signal is processed.

Example:
GTK predefines the behavior of the up/down arrow keys for GUI focus navigation traversal of the interactive GUI control widgets. If using the up/down arrow within a text entry box we would need to deactivate the predefined navigation of the GUI by returning "true" to break the chain.

This example handles text entry and up/down arrows with two callbacks attached to the same widget.

// Callback to handle text entry

void
on_entry_activate(GtkWidget *widget, gpointer data)
{
   char *string_entered = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
   if(strlen(string_entered) > 0)
   {
      ..
      ...
   }

   g_free(string_entered);
}

// Callback to handle up/down arrows

gboolean
on_entryBox1_key_press_event  (GtkWidget   *widget,
                               GdkEventKey *event,
                               gpointer     user_data)
{
   gboolean breakInheritance = true;

   ..
   ...

   if(event->keyval == 65362)        // Up arrow
   {
      gtk_entry_set_text(GTK_ENTRY(widget), text_string_prev);
      gtk_editable_set_position(GTK_EDITABLE(widget), strlen(text_string_prev);
   }
   else if(event->keyval == 65364)   // down arrow
   {
      if(isEndList)
      {
         gtk_entry_set_text(GTK_ENTRY(widget), "");
         gtk_editable_set_position(GTK_EDITABLE(widget), "");
      }
      else
      {
         gtk_entry_set_text(GTK_ENTRY(widget), text_string_next);
         gtk_editable_set_position(GTK_EDITABLE(widget), strlen(text_string_next);
      }
   }
   else
   {
      breakInheritance = false;
   }
}

..
...

   // In main code tie callbacks to widget.

   g_signal_connect((gpointer) textEntryWidget, "activate",
                     G_CALLBACK(on_entry_activate), NULL);

   gtk_signal_connect (GTK_OBJECT (textEntryWidget), "key_press_event",
                      GTK_SIGNAL_FUNC (on_entryBox1_key_press_event),
                      NULL);
...
..

Also see: GtkKeySnoopFunc()


Tips:

  • If trying to fix a crash, don't "gtk_widget_show()" a widget until it has been attached to a parent widget first.
    The widget structure is a tree hierarchy starting from the parent window and branching to each and every widget.


Colors:

Change/assign button color:

(code snippet)
    GdkColor   colorButton;
    GtkWidget *button_Widget;

    colorButton.red=65535;
    colorButton.green=0;
    colorButton.blue=0;

    gtk_color_button_set_color(button_Widget,colorButton);

    gtk_widget_show (button_Widget);
    ...
    ..
OR:
GdkColor   colorRed;
GtkWidget *button_Widget;

gdk_color_parse ("red", &colorRed);
    ...
    ...
button_Widget = gtk_button_new_with_label("Button");

gtk_widget_modify_base (button_Widget, GTK_STATE_NORMAL, &colorRed);
gtk_widget_modify_fg (button_Widget, GTK_STATE_NORMAL, &colorRed);
gtk_widget_modify_bg (button_Widget, GTK_STATE_NORMAL, &colorRed);
gtk_widget_modify_bg (button_Widget, GTK_STATE_PRELIGHT, &colorRed);

gtk_widget_show (button_Widget);
    ...
    ..


Color declaration and assignment:

(code snippet)
...
static GdkColor colorGreen;
static GdkColor colorYellow;
static GdkColor colorRed;
static GdkColor colorBlack;

    ...

    gdk_color_parse("green", &colorGreen);
    gdk_color_parse("yellow", &colorYellow);
    gdk_color_parse("red", &colorRed);
    gdk_color_parse("black", &colorBlack);
    ...


Text Labels:

Text labels can be placed anywhere on the GUI display. Placement is usually with a container such as "vbox" or "hbox". Note that the text can be modified using the html/xml tag "small".

    ...

    GtkWidget *textLabel_Widget;
    char       displayLabel[64];
    char      *textLabel="Display Label";

    sprintf(displayLabel, "<small>%s</small>", textLabel);

    textLabel_Widget = gtk_label_new (NULL);
    gtk_label_set_markup(GTK_LABEL (textLabel_Widget), displayLabel);
    gtk_label_set_justify(GTK_LABEL (textLabel_Widget),GTK_JUSTIFY_LEFT);
    gtk_misc_set_alignment (GTK_MISC (textLabel_Widget), 0, 0.5);

    gtk_widget_show (textLabel_Widget);

    ...


Pango:

Pango is the framework for layout and rendereing of internationalized text in GTK+. On Linux systems the final rendering is performed by X-Windows. Pango uses Unicode characters internally. Examples here use UTF-8 which is compatable with existing ASCII 8-bit software. Offsets in Pango are counted in bytes and not characters. While Pango was created to support non-Roman character languages like Japanese, Greek and Arabic, only English UTF-8 examples are shown.

Pango also supports a simple subset of HTML/XML for text attributes:

HTML Tag Description
<b> Bold
<big>
or
<span size="larger">
Increase font size
<small>
or
<span size="smaller">
Decrease font size
<i> Italic
<s> Strikethrough
<subs> Subscript
<sup> superscript
<tt> Monospace Font
<u> Underline
<span> Use with attributes to specify rendering:
  • font_desc: Shorthand label for font family, style, size, ...
  • font_family: Name of font family.
  • face: A font family attribute.
  • size: # of thousandths of a point.
  • style: normal, oblique, italic
  • weight: ultralight, normal, heavy
  • variant: normal, smallcaps
  • stretch: ultracondensed, normal, ultraexpanded
  • foreground: RGB color specification or color name. (i.e. #ff0000 or "red")
  • background: RGB color specification or color name. (i.e. #0000ff or "blue")
  • underline: single, double, low, none
  • rise: position change in ten thousandths em. Subscript (-)ve, superscript (+)ve.
  • strikethrough: true, false
  • lang: Language code.
Note: This also describes the set of Pango text attributes.
The XML tree root element tags are <markup> and </markup> but it is not necessary to reference or include them.

Example:

Pango converts HTML markup string into a text string and list of attributes and enables layout and rendering.

#include <gtk/gtk.h>

    ..
    ...
    gchar *stringMarkupText = "<span foreground=\"blue\"><b>Bold</b> <u>is</u> <i>beautiful</i></span>";
    gchar *stringPlainText;
    PangoAttrList  *attrList;
    GtkWidget      *displayLabelWidget;

    ...
    pango_parse_markup(stringMarkupText, -1, 0, &attrList, &stringPlainText, NULL, NULL);
    displayLabelWidget = gtk_label_new(stringPlainText);
    gtk_label_set_attributes(GTK_LABEL(displayLabelWidget), attrList);
    ...
    gtk_widget_show(displayLabelWidget);
    ...
This converts the HTML representation of the text in "stringMarkupText" into text ("stringPlainText") and applied attributes "attrList".

Note: One may also use the equivalent GTK+ calls to gtk_label_set_markup() and the function above in the Text Labels example.

Result:

Bold is beautiful

Links:


Text Boxes/Fonts:

The first series of examples are for the GTK text_entry and the second set briefly cover the GTK text_view widget. The text entry widget is preffered as it supports more features and fully supports view only functionality.


GTK Text Entry Widget:

Creates a new text entry/display widget.

    ..
    ...
    GtkWidget *text_entry_Widget = gtk_entry_new();
    ...
    ..

Set entry text box background color:

    ..
    ...
    gtk_widget_modify_base(text_entry_Widget, GTK_STATE_NORMAL, &colorBlack);
    ...
    ..
See discussion of color definitions above.

Display entry text in green.

    ..
    ...
    gtk_widget_modify_text(text_entry_Widget, GTK_STATE_NORMAL, &colorGreen);
    ...
    ..
See discussion of color definitions above.

Modify entry text box style so text is BOLD:

    ..
    ...
    GtkStyle *style = gtk_widget_get_style(text_entry_Widget);
    pango_font_description_set_weight(style->font_desc, PANGO_WEIGHT_BOLD);
    gtk_widget_modify_font(text_entry_Widget, style->font_desc);
    ...
    ..

Set entry text box size:

    ..
    ...
    int        text_width = 20;           // Width of field in characters

    gtk_entry_set_width_chars(GTK_ENTRY(text_entry_Widget), text_width);
    ...
    ..

Set entry text box editable(default)/not editable:

    ..
    ...
    bool       text_is_editable = false;  // Flag indicating if text field is editable or display only

    if (!text_is_editable)
    {
        gtk_editable_set_editable(GTK_EDITABLE(text_entry_Widget), FALSE);
        GTK_WIDGET_UNSET_FLAGS(text_entry_Widget, GTK_CAN_FOCUS);
    }

    ...
    ..

Display text in the GTK text entry box widget.

    ..
    ...

    char    _txtBuffer = "Display Text";

    gtk_entry_set_text(GTK_ENTRY(text_entry_Widget), _txtBuffer);

    ...
    ..

Set display box as "insensitive". Display background as "grayed out" and disable user interaction.

    ..
    ...
    gboolean _isSensitive = false;

    gtk_widget_set_sensitive(text_entry_Widget, _isSensitive);
    ...
    ..
Sensitivity:
  • Sensitive: boolean set to true
    Interaction with widget (text entry widget) is allowed.
  • Insensitive: boolean set to false
    Interaction with widget (text entry widget) is not allowed. Background is "grayed out".
One can also set the state to sensitive/insensitive using the routine gtk_widget_set_state().

Display tooltip when hovering text box:

    ..
    ...

    char        *toolTipsText = "Tool Tips Text Displayed When Mouse Hovers Text Entry Box";
    GtkTooltips *tooltipsA;

    ...

    tooltipsA = gtk_tooltips_new(); 

    ...
   
    gtk_tooltips_set_tip(tooltipsA, text_entry_Widget, toolTipsText, NULL);

    ...
    ..

GTK.org: Tooltips tutorial

Text display box with modified background and text:

gtk Text Entry Box Display
Source code to this example: gtkTextEntryBox.c


Widgets and g_Threads:

Note: If updating the text with new data from a spawned GTK thread which listens to a socket or performs digital acquisition, one must protect the code segment with a GTK mutex. This must be applied to the calls in the spawned thread and not in the GUI main thread. This will avoid GUI update conflicts between gtk_main() and the GTK calls invoked in the thread. Thread support and stability is significantly improved in GTK+ 2.6.2.

Widget in a thread:
    ..
    ...
    gdk_threads_enter();  // Protect from gtk main loop which also performs a gtk_entry_set_text.
    gtk_entry_set_text(GTK_ENTRY(text_entry_Widget), _txt);
    gdk_flush();
    gdk_threads_leave();
    ...
    ..
The widget pointer text_entry_Widget is global to both the main thread and the spawned thread.

The required basic gdk threads framework:

    ..
    ...
    if( !g_thread_supported() )
    {
       g_thread_init(NULL);
       gdk_threads_init();                   // Called to initialize internal mutex "gdk_threads_mutex".
       printf("g_thread supported\n");
    }
    else
    {
       printf("g_thread NOT supported\n");
    }
    ...
    ..


    ..
    ...
    gdk_threads_enter();
    gtk_main();
    gdk_threads_leave();
    ...
    ..
gdk_threads_enter() is simply a wrapper function that locks the gdk_threads_mutex. Similarly, gdk_threads_leave() is a wrapper that unlocks the mutex. A look at the source of gtkmain.c shows that the lock is released while the main loop is running.

[Potential Pitfall]: You will create a deadlock condition if you try to update a widget before it has been realized.


GTK Text View Widget:

This is an alternate method of displaying text in a box. I prefer using a text entry widget as it has more available options.

    ..
    ...
    GtkWidget *textView_Widget;
    int        text_width = 20;           // Width of field
    char      *txtBuffer  = "Display Text";

    textView_Widget = gtk_text_view_new ();
    gtk_widget_set_usize (textView_Widget, text_width, -2);
    gtk_text_view_set_editable( GTK_TEXT_VIEW (textView_Widget), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (textView_Widget), FALSE);
    gtk_text_view_set_pixels_above_lines( GTK_TEXT_VIEW (textView_Widget), 5);
    gtk_text_view_set_left_margin( GTK_TEXT_VIEW (textView_Widget), 3);

    gtk_text_buffer_set_text(gtk_text_view_get_buffer (GTK_TEXT_VIEW (textView_Widget)),
                             txtBuffer, -1);
    gtk_widget_modify_text (textView_Widget, GTK_STATE_NORMAL, &colorGreen);
    gtk_widget_modify_base (textView_Widget, GTK_STATE_NORMAL, &colorBlack);

    gtk_widget_show (textView_Widget);
    ...
    ..
See discussion of color definitions above.


Bitmaps:

The following code snippet shows how to embed and display an image within software. Any bitmap image (gif, jpeg, etc) can be converted to an "xpm" file using image manipulation software such as XV or Gimp and performing a "Save as ...".

[Potential Pitfall]: If your application core dumps when trying to employ pixmaps, try one of these clearly opposite solutions:

  • Place "gtk_widget_show(window)" near the end just before gtk_main()
  • Call "gtk_widget_show(window)" after declaration and attributes are set. (Fairly standard)
I have employed both solutions to fix a core dump. If one does not work try the other.

Transparent xpm include file: green_DOT.xpm

static char *green_DOT_xpm[] = {
/* width height num_colors chars_per_pixel */
"    20    20       22            1",
/* colors */
". c #000000",
"# c #010301",
...
"t c None",   /* Transparent background. */
...
/* pixels */
"tttttttttttttttttttt",
"ttttttt.....tttttttt",
"ttttt#hklmlkd.tttttt",
...
};
                

Embedding the pixmap in your display:

#include <gtk/gtk.h>
#include "green_DOT.xpm"
...
    GtkWidget *window;
    GtkWidget *hbox;
    GtkWidget *image;
    GdkPixmap *icon;
    GdkBitmap *icon_mask;
    GtkStyle   *style;
...
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
...
    if(!window)printf("No window. Bad!\n");
    style = gtk_widget_get_style( window );
    icon = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(window)->window,
                                        &icon_mask,
                                        &style->bg[GTK_STATE_NORMAL],
                                        green_DOT_xpm);
    image = gtk_pixmap_new(icon, icon_mask);

    gtk_widget_show(image);
    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
...
    gtk_widget_show (window);     /* Do NOT place after "gtk_window_new". */
    g_object_unref(icon);
    g_object_unref(icon_mask);

    gtk_main ();
...
                
One may also read the XPM file using the function gtk_image_new_from_file(file-name) to generate a widget.


Using images to display status: It may be necessary to use pixmaps to display status thus changing the display. In this example we generate and display three color boxes (grey, red and green) but only display one at a time depending on which is relevant.

Generate the pixmap widgets: (See above discussion to see full source code.)

// Global variables
GtkWidget *redBox;
GtkWidget *greenBox;
GtkWidget *greyBox;
...
greyBox = gtk_pixmap_new(_icon_grey, _icon_mask_grey);
...
greenBox = gtk_pixmap_new(_icon_green, _icon_mask_green);
...
redBox = gtk_pixmap_new(_icon_red, _icon_mask_red);
...
gtk_widget_show(greyBox);  // Start by displaying the Grey box
...

Attach all three pixmap widgets to the same location:

...
GtkWidget *window;
GtkWidget *hbox1;
...
hbox1 = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), hbox1);
...
// Attach both images to the same place!!
gtk_box_pack_start(GTK_BOX(hbox1), greyBox,  TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), greenBox, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), redBox,   TRUE, TRUE, 0);
...
// Position both images in the same location
gtk_misc_set_alignment(GTK_MISC(greyBox),  1, 0.5);
gtk_misc_set_alignment(GTK_MISC(greenBox), 1, 0.5);
gtk_misc_set_alignment(GTK_MISC(redBox),   1, 0.5);
...
gtk_widget_show(hbox1);
...

Callbacks to switch displays:

Display green box:

...
gtk_widget_hide(greyBox);
gtk_widget_hide(redBox);
gtk_widget_show(greenBox);
...

Display red box:

...
gtk_widget_hide(greyBox);
gtk_widget_hide(greenBox);
gtk_widget_show(redBox);
...

The three images are all located in the same place but only one is displayed at a time.


Source code to this example: gtkSwapPixmaps.c

Compile:

  • gcc gtkSwapPixmaps.c -o gtkSwapPixmaps `pkg-config --cflags --libs gtk+-2.0`
    OR
  • gcc gtkSwapPixmaps.c -o gtkSwapPixmaps -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/pango-1.0 -I/usr/X11R6/include -I/usr/include/freetype2 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -Wl,--export-dynamic -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangoxft-1.0 -lpangox-1.0 -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0


Check Box:

Creates a check box button. This button is not "grayed out" when insensitive but they are unselectable until the state is changed to GTK_STATE_SENSITIVE (default).

    ...

    GtkWidget *button_Widget = gtk_check_button_new();

    GtkStyle *style = gtk_widget_get_style(button_Widget);
    gtk_widget_modify_base(button_Widget, GTK_STATE_INSENSITIVE,
                           &style->base[GTK_STATE_NORMAL]);
    gtk_widget_modify_text(button_Widget, GTK_STATE_INSENSITIVE,
                           &style->text[GTK_STATE_NORMAL]);

    return button_Widget;  OR gtk_widget_show(button_Widget);
    ...
                


Using Glade Support Routines:

The GNOME GTK+ GUI builder Glade will generate a software skeleton for an application which is compilable and executable. Glade will also include many support routines which are useful whether you use Glade or not in the construction of your GTK+ application. These support routines are found in the Glade generated file src/support.c.

Glade lookup routine lookup_widget() (See Glade generated file src/support.c): Start at tree root "widget" and return "GtkWidget" corresponding to the "widget_name" (index to lookup table). If the widget tree root is given, then this routine can traverse the widget tree and return the GtkWidget pointer to the widget identified by name. This eliminates the need to pass all possible pointers used in processing but instead just pass around one, the root (i.e. main window) widget, and use lookup_widget() to locate all the others. Actually any widget can be passed as the routine will traverse the tree to first find the root ans then scan the tree. One must assign widget names using the Glade macro GLADE_HOOKUP_OBJECT. Glade will generate these associations in the generated file src/interface.c.

..
...
// Macro definition:

#define GLADE_HOOKUP_OBJECT(component,widget,name) \
        g_object_set_data_full (G_OBJECT (component), name, \
                                gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)

#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
        g_object_set_data (G_OBJECT (component), name, widget)
...
..
...
    /* Store pointers to all widgets, for use by lookup_widget(). */
    GLADE_HOOKUP_OBJECT_NO_REF(mainWindow, mainWindow, "mainWindow");
    GLADE_HOOKUP_OBJECT(mainWindow, vbox1, "vbox1");
    GLADE_HOOKUP_OBJECT(mainWindow, mainMenuBar, "mainMenuBar");
    GLADE_HOOKUP_OBJECT(mainWindow, fileMenuitem, "fileMenuitem");
    GLADE_HOOKUP_OBJECT(mainWindow, fileMenuitem_menu, "fileMenuitem_menu");
    GLADE_HOOKUP_OBJECT(mainWindow, quitMenuItem, "quitMenuItem");
...
..

At the heart of the GLADE_HOOKUP_OBJECT macro is the GObject call g_object_set_data_full. This enables the association of text labels to widget pointers. One may add an association directly by using this routine.

The widget pointer can then be found using the widget identifier. i.e.:

..
...
    GtkWidget *widgetPtrToBefound = lookup_widget(window, "name_of_widget_ie_vbox1");
    assert(widgetPtrToBefound != NULL);
...
..

The routine lookup_widget as generated by Glade in the file src/support.c.

..
...
widget.
GtkWidget*
lookup_widget(GtkWidget *widget, const gchar *widget_name)
{
    GtkWidget *parent, *found_widget;

    for (;;)
    {
        if (GTK_IS_MENU(widget))
            parent = gtk_menu_get_attach_widget(GTK_MENU(widget));
        else
            parent = widget->parent;

        if (!parent)
           parent = (GtkWidget*) g_object_get_data(G_OBJECT(widget), "GladeParentKey");

        if (parent == NULL)
           break;

        widget = parent;
    }

    found_widget = (GtkWidget*) g_object_get_data(G_OBJECT(widget), widget_name);
    if (!found_widget)
        g_warning ("Widget not found: %s", widget_name);
    return found_widget;
}
...
..


Links:


Books:

"Developing Linux Applications with GTK+ and GDK"
by Eric Harlow
Pearson Education; 1st edition, ISBN# 0735700214

GTK+ 1.X

Amazon.com
"GTK+/Gnome Application Development"
by Havoc Pennington
Pearson Education; 1st edition, ISBN# 0735700788

GTK+ 1.X

Amazon.com
"Gtk+ Programming in C"
by Syd Logan
Prentice Hall PTR; 1st edition

GTK+ 1.X

Amazon.com

Copyright © 2004 by Greg Ippolito