Skip to main content

skip to main content

developerWorks  >  Linux  >

Making application programming easy with GNOME libraries

Everything you need to get started

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Intermediate

George Lebl (jirka@5z.com), developerWorks columnist, Eazel, Inc.

01 Sep 1999

Developer and GNOME project member George Lebl gives both an overview of the power of the GNOME libraries and an introduction to the techniques of application building. Not only are the libraries used to build GUIs, they also serve as an instrumental part of many other projects. George illustrates GNOME development techniques with a simple Hello World application, taking you through it one step at a time.

Let's start by answering a couple of questions you probably have:

What is GNOME?
GNOME is a number of things: It's a graphical environment for UNIX; a collection of useful, small applications and utilities; and a set of application-programming libraries. For now, we'll concentrate on the GNOME libraries, which can help developers build GNOME-compliant, consistent, and powerful GUI applications quickly and with minimal effort.

Why use GNOME libraries?
Why not just program directly with a GUI toolkit such as GTK+, Motif, or Qt, without any other libraries? These tools have their advantages, but I believe the advantages of using the GNOME libraries far outweigh them. By using the GNOME libraries, you can save a lot time, because a lot of code is already written, and a lot of widgets are already created. These widgets will also be consistent in both look and feel across all applications using the GNOME libraries. And code sharing among applications reduces your memory requirements, because the code will be part of the shared library rather than loaded into memory for each application. But GNOME's greatest advantage is that the library lets you focus on the real functionality of your application; you don't have to spend your time writing and debugging the user interface.

Writing GNOME applications

So you're convinced you want to use GNOME? OK, let's start with the basics: A GNOME application uses a hierarchy of several libraries, with the GNOME libraries making up the highest level. The GNOME libraries contain helper routines, classes, and specialized widgets (such as GnomeCanvas, a fast, flicker-free, high-level drawing widget), and provide a framework for the application.

The second highest level in the hierarchy is GTK , part of the GTK+ library. This library provides the basic toolkit and widgets (such as buttons, labels, and entry boxes) for building GUI applications. Most of a GUI is written directly with GTK, which also provides a sophisticated object system for use in the GNOME libraries.


Basic structure of a GNOME application

The next level in the library hierarchy is GDK , a thin wrapper around X libraries that is used only when you have a special drawing need or need to set special properties on windows.

At the lowest level, GLib , a utility library for C, contains portability and utility functions along with several container classes -- linked lists, resizable arrays, variable-length strings, hashes, caches, an event loop, and other useful things.

Don't get the idea from all the C talk that C is the only language you can use for GNOME application development. In fact, because GTK+ and GNOME were written in C, it was easy to write bindings to other languages, such as Objective C, C++, Perl, Guile, Python, and Eiffel.



Back to top


The GNOME and GTK+ layers

The GNOME libraries are divided into three basic parts: libgnome, libgnomeui, and libgnorba. The libgnome is a utility library, similar in spirit to GLib, which provides services such as configuration loading and saving, application launching, mime-type identification, and metadata storage. The libgnomeui portion, the user interface part, contains the application framework (GnomeApp), the canvas (GnomeCanvas), and many other useful specialized widgets. The last part, libgnorba, is used to integrate GTK+/GNOME applications with CORBA technology.

When programming a GNOME application, it's important to know how to use GTK+. All the libgnomeui classes are based on GTK+, so using them is similar to using the standard GTK+ widgets.

GTK+ is a container-based toolkit, which means most widgets serve as "containers" that hold other widgets. For example, a button is a container that will most likely contain a label widget. But other containers may provide nothing more than a means to put more widgets together (among the most useful of these are the horizontal and vertical boxes that put several widgets in a row, or tables that can be used for formatting and layout).

This hierarchy allows all geometry to be calculated on the fly, which means your application will work with different visual settings (such as font sizes) and react properly when the window is resized (or even when the program is internationalized). It also makes it easier to develop auto-generated GUIs during runtime as well.


An example hierarchy of widgets in a GTK+ application

When you need to connect an action to a certain event (such as a button click), you must use " signals," special GTK+ class methods that allow you to bind actions to custom functions. For example, a button will have a "clicked" signal, to which you can bind an action you'd like to occur when the user clicks on the button. Each signal is different, so make sure you use the correct function prototype when defining your handler function.



Back to top


Sample GNOME application


This application has a docking menu bar with tear-off menus and a status bar with menu hints. It autosaves user-defined accelerators on menus (go over a menu with your mouse, then click the desired key combination and voila, it's there), has a standard "about" box, and features automatic translation of all the standard menu items. (Try running "LANG=es_ES ./hello_world", and the application's menus will appear in Spanish!)


Screenshot of the hello_world.c program

Source code of hello_world.c




hello_world.c
1       /* Hello World (Gnome Edition)                  
2        * Listing 1                    
3        * This code is public domain, so use it as you please.                 
4        */                     
5                               
6       /* this include will include everything needed for GNOME, including all the                     
7        * libraries that gnome needs, such as gtk, imlib, etc ...*/                    
8       #include <gnome.h>                      
9                               
10      /* this is usually defined by autoconf but we're just using simple makefiles */                 
11      #define VERSION "1.0"                   
12                              
13      /* "callback" function (signal handler) which will quit the application*/                       
14      static void                     
15      exit_hello(GtkWidget *widget, gpointer data)                    
16      {                       
17              gtk_main_quit ();               
18      }                       
19                              
20      /* callback function for when the window closes */                      
21      static int                      
22      delete_event(GtkWidget *widget, gpointer data)                  
23      {                       
24              gtk_main_quit ();               
25              return FALSE; /* false means continue with closing the window */                
26      }                       
27                              
28      /* a callback for the about menu item, it will display a simple "About"                 
29       * dialog box standard to all gnome applications                        
30       */                     
31      static void                     
32      about_hello(GtkWidget *widget, gpointer data)                   
33      {                       
34              GtkWidget *box;         
35              const char *authors[] = {               
36                      James Bond,     
37                      NULL    
38              };              
39                              
40              box = gnome_about_new(/*title: */ "Hello World (Gnome Edition)",                
41                                    /*version: */VERSION,
42                                    /*copyright: */ "(C) 1999 Secret Agents Inc.",
43                                    /*authors: */authors,
44                                    /*other comments: */
45                                    "An extremely complicated application which "
46                                    "does absolutely nothing useful",
47                                    NULL);
48              gtk_widget_show(box);           
49      }                       
50                              
51      /* define the menus here */                     
52                              
53      static GnomeUIInfo file_menu [] = {                     
54              /* some item which is not one of the standard ones, the null            
55               * would be the callback, however we don't want to really do anything */         
56              GNOMEUIINFO_ITEM_NONE("Something","Just an item which does nothing",NULL),              
57              /* standard exit item */                
58              GNOMEUIINFO_MENU_EXIT_ITEM(exit_hello,NULL),            
59              GNOMEUIINFO_END         
60      };                      
61                              
62      static GnomeUIInfo help_menu [] = {                     
63              /* load the helpfiles for this application if available */              
64              GNOMEUIINFO_HELP("hello_world"),                
65              /* the standard about item */           
66              GNOMEUIINFO_MENU_ABOUT_ITEM(about_hello,NULL),          
67              GNOMEUIINFO_END         
68      };                      
69                              
70      /* define the main menubar */                   
71      static GnomeUIInfo main_menu [] = {                     
72              GNOMEUIINFO_MENU_FILE_TREE(file_menu),          
73              GNOMEUIINFO_MENU_HELP_TREE(help_menu),          
74              GNOMEUIINFO_END         
75      };                      
76                              
77      /* Our main function */                 
78      int                     
79      main(int argc, char *argv[])                    
80      {                       
81              GtkWidget *app; /* pointer to our main window */                
82              GtkWidget *w; /* pointer to some widget */              
83                              
84              /* initialize gnome */          
85              gnome_init("hello_world", VERSION, argc, argv);         
86                              
87              /* create main window */                
88              app = gnome_app_new("hello_world", "Hello World (Gnome Edition)");              
89              /* connect "delete_event" (happens when the window is closed */         
90              gtk_signal_connect(GTK_OBJECT(app), "delete_event",             
91                                 GTK_SIGNAL_FUNC(delete_event),
92                                 NULL);
93                              
94              /* add the menus to the main window */          
95              gnome_app_create_menus(GNOME_APP(app), main_menu);              
96                              
97              /* setup appbar (bottom of window bar for status, menu hints and                
98               * progress display) */         
99              w = gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_USER);              
100             gnome_app_set_statusbar(GNOME_APP(app), w);             
101                             
102             /* make menu hints display on the appbar */             
103             gnome_app_install_menu_hints(GNOME_APP(app), main_menu);                
104                             
105             /* set up some bogus window contents */         
106             w = gtk_label_new("Some really really really random\ntext\ngoes\nhere!");               
107                             
108             /* add those contents to the window */          
109             gnome_app_set_contents(GNOME_APP(app), w);              
110                             
111             /* show all the widgets on the main window and the window itself */             
112             gtk_widget_show_all(app);               
113                             
114             /* run the main loop */         
115             gtk_main();             
116                             
117             return 0;               
118     }                       

Now let's look at "Hello World" in detail. Because some parts are self-explanatory, and others are explained by programmer's comments, we'll focus on the fundamental parts of the program. At the top of the listing, you can see we include gnome.h, which in turn includes all the other necessary libraries for full GNOME functionality.

In the main function, you see the code:



84              /* initialize gnome */          
85              gnome_init("hello_world", VERSION, argc, argv); 

The above line initializes all of GNOME's libraries, GTK+, and all the other necessary libraries; and it parses default command-line arguments. This initialization must appear in every GNOME application without exception.

A little lower in the listing, you see this code:



87              /* create main window */                
88              app = gnome_app_new("hello_world", "Hello World (Gnome Edition)");              
89              /* connect "delete_event" (happens when the window is closed */         
90              gtk_signal_connect(GTK_OBJECT(app), "delete_event",             
91                                 GTK_SIGNAL_FUNC(delete_event),
92                                 NULL);

The above segment does two things: The first line creates a new application (main) window for "hello_world" with the title "Hello World (Gnome Edition)," and sets "app" to point to this newly created object. The second part, (gtk_signal_connect), connects the application to a function to be called when a certain signal ("delete_event") is emitted to the object "app." This signal is emitted when the user requests to close the window from the window manager. Here's the function we call:



20      /* callback function for when the window closes */                      
21      static int                      
22      delete_event(GtkWidget *widget, gpointer data)                  
23      {                       
24              gtk_main_quit ();               
25              return FALSE; /* false means continue with closing the window */                
26      }                       

The above function tells GTK+ that it should quit the main loop (gtk_main_quit), then returns FALSE, meaning GTK+ should proceed with closing the window. Note that each signal should have its own prototype (any return values can have different meanings, and argument lists can be different). For example, a callback from a menu item should look like "void function(GtkWidget *, gpointer data)." For help with this, take a look at the GTK+ and GNOME reference documentation on available signals and their correct prototypes (see Resources).

Now let's concentrate on how we define the menus. With the GNOME application framework (the GnomeApp class), you define the structures -- the framework builds the menus and binds the necessary signals for you. The menus are static structure arrays that you preset with the correct information to build the menus. To make this simpler, helper macros (defined in libgnomeui/gnome-app-helper.h) exist to define a single entry in the menu definition. Here's the menu definition for the "hello_world" example (the comments have been deleted here for clarity):



53      static GnomeUIInfo file_menu [] = {                     
54              /* some item which is not one of the standard ones, the null            
55               * would be the callback, however we don't want to really do anything */         
56              GNOMEUIINFO_ITEM_NONE("Something","Just an item which does nothing",NULL),              
57              /* standard exit item */                
58              GNOMEUIINFO_MENU_EXIT_ITEM(exit_hello,NULL),            
59              GNOMEUIINFO_END         
60      };                      
61                              
62      static GnomeUIInfo help_menu [] = {                     
63              /* load the helpfiles for this application if available */              
64              GNOMEUIINFO_HELP("hello_world"),                
65              /* the standard about item */           
66              GNOMEUIINFO_MENU_ABOUT_ITEM(about_hello,NULL),          
67              GNOMEUIINFO_END         
68      };                      
69                              
70      /* define the main menubar */                   
71      static GnomeUIInfo main_menu [] = {                     
72              GNOMEUIINFO_MENU_FILE_TREE(file_menu),          
73              GNOMEUIINFO_MENU_HELP_TREE(help_menu),          
74              GNOMEUIINFO_END         
75      };   

The first array defines the "File" menu (which is required by the style guide and should always have an "Exit" item). This menu has one item -- our own custom item -- with the title "Something" and a hint of "Just an item which does nothing." However, the item has a NULL callback and, thus, does absolutely nothing when clicked. The "File" menu has, of course, the standard exit item, which calls the exit_hello function when clicked (with the data argument being NULL). The help menu is similarly defined, except the first entry can expand to the standard help topics if you actually make and install HTML help files. The main_menu then defines the menu bar, which has two (standard) trees. After you've thus defined the menus, you need to call gnome_app_create_menus(GNOME_APP(app), main_menu). This will create the menu bar and stick it on the window.

We also create an "appbar" (a glorified status bar on the bottom of the window), and install hints (the long descriptions) for the menu items on it. We do this with the following code:



98               * progress display) */         
99              w = gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_USER);              
100             gnome_app_set_statusbar(GNOME_APP(app), w);             
101                             
102             /* make menu hints display on the appbar */             
103             gnome_app_install_menu_hints(GNOME_APP(app), main_menu);  

The last major thing we do in the example is add some content to the window. This is just an example, so we'll simply create a GtkLabel with random text and add that with the following code:



105             /* set up some bogus window contents */         
106             w = gtk_label_new("Some really really really random\ntext\ngoes\nhere!");               
107                             
108             /* add those contents to the window */          
109             gnome_app_set_contents(GNOME_APP(app), w);    

We then show the application window (and all the widgets it contains) and jump into the main loop function (gtk_main), where we wait for something to happen. The following code does exactly that:



111             /* show all the widgets on the main window and the window itself */             
112             gtk_widget_show_all(app);               
113                             
114             /* run the main loop */         
115             gtk_main();

So now we've covered the absolute basics of a GNOME application. Of course you can do a lot more with the libraries, and we'll cover some of this a little later.



Back to top


Compiling your GNOME application

Now that you know how to write a simple GNOME application, you need to know how to compile it. A special script, gnome-config, has been designed to set up the correct command lines for compiling an application. It requires a switch (either "--cflags," for obtaining compiler flags, or "--libs," which supplies the linker arguments). And you must specify which libraries you want to use (for our example, "gnome" and "gnomeui"). So, say you have a simple application like hello_world.c. The following is a simple Makefile that will compile your application correctly:



  CFLAGS=-g -Wall `gnome-config --cflags gnome gnomeui`
  LDFLAGS=`gnome-config --libs gnome gnomeui`
  all: hello_world
  clean:
    rm -f hello_world core

The space before the "rm" on the last line consists of a single TAB, not spaces. And the quotes are single backquotes.

Leave the rest of the magic to make. Of course you'll need a more sophisticated setup for complicated applications, but that's not really a GNOME issue. In fact, a project called gIDE is already under way to actually shield you from all this complexity.



Back to top


Other benefits of GNOME programming

The GNOME libraries also provide developers with other benefits. For instance, the canvas widget can provide a high-level means of displaying graphics, and its use is not limited to graphics applications. For example, the Gnumeric spreadsheet uses the canvas for its grid. GNOME applications should also work with a session manager, and there's a considerable amount of support for this in the libraries. A framework for building a multiple-document interface, or MDI, application, which is useful for most applications with data files, is a little more complicated than that of a single-document application, but the MDI framework in GNOME handles as much as it can to make this simple. If you keep data and user interfaces separate, you should be able to convert a single-document application to MDI without much trouble.

Another aspect of GNOME programming is its use of CORBA. GNOME uses ORBit as its orb (a module that takes care of communication between applications). In some places, CORBA is already used extensively, but its use is not as common as it should be. The latest document model, Bonobo, is still under development, but applications, especially the ones in the GNOME Workshop office project, are starting to use it. Bonobo will allow applications to be much more modular than in the past, and it will allow much wider interapplication cooperation than is currently possible.



Back to top


Development tools

Several tools are being developed to simplify the life of a GNOME programmer. The ones I mention here are in development, but are already usable (although I wouldn't recommend using them in mission-critical projects until these tools hit the 1.0 version).

One tool is Glade, an interface builder. It not only creates source code, but also creates the entire build environment for you. In the future, this should be integrated with an IDE to become a completely self-sufficient development tool.


The Glade interface builder in action

Another "tool," libglade, is not actually a program; it's a library that takes a description file from Glade and builds the interface at the start of the application. This is an attractive alternative to the code-generation scheme of Glade, and it allows for greater flexibility in the definition and modification of interfaces.

A third tool, gIDE , provides the services of an IDE for those who don't already have a preferred source editor. This tool will also be integrated with Glade to provide a development environment rivaling commercial offerings.



Resources



About the author

George (Jiri or Jirka in Czech) Lebl was born in Prague, Czechoslovakia (now Czech Republic), and lives in San Diego, California where he is trying to finish his degree. After several years using non-UNIX operating systems, he started using UNIX. He became a UNIX bigot about four years ago, and a Free Software bigot about two years ago. He joined the GNOME project in the fall of 1997, and finally became a C bigot as well. Nowadays he works at Eazel, Inc. on the next generation of GNOME. Most importantly, he's a VI user. You can contact George Lebl at jirka@5z.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top