[Home] [Credit Search] [Category Browser] [Staff Roll Call] | The LINUX.COM Article Archive |
Originally Published: Wednesday, 25 July 2001 | Author: Subhasish Ghosh |
Published to: develop_articles/Development Articles | Page: 1/1 - [Std View] |
GNOME Programming in Linux using GTK+
Ever wonder how those little garden Gnomes move around and stuff at night? It's because they are programmed, of course! In this article Linux.com asked Subhasish Ghosh to show us how to start programming our own Gnome applications using the GTK+ toolkit.
|
The code samples provided along with the text have been checked on a computer system with the following configurations: Compaq Presario 4010 Series computer system, 15.5 GB Hard Disk Space, 96 MB RAM, 400 MHz Intel Celeron Processor, Red Hat Linux 7.1 Distribution Release underlying Kernel: 2.4.2-2
This article has been divided into the following sections for easy understanding of the subject matter:
Before entering into the exciting world of Gnome programming in Linux, let's try to understand what Gnome actually refers to. GNOME is the acronym for "GNU's Not Unix Network Object Model Environment". Though it sounds a bit complicated, Gnome is a software project with a simple aim: To provide all Linux users with an extremely user-friendly yet powerful and complete programming Desktop environment. GNOME is currently the default Desktop system installed with the latest releases of Red Hat and Debian Distribution releases of Linux.
For more specific info on GNOME and its various wonderful
features, make sure you check out the GNOME Project home page at
www.gnome.org
which provides readers with a
wealth of information on GNOME, including online documentation,
news; and one can also download the binaries and source code of
GNOME compatible with most Linux systems.
Now let's try to look at GNOME from both a "Linux programmer's" as well as a "Linux System Administrator's" point of view. The basic question that comes to mind is: Do they think and feel the same when they talk about GNOME? The answer to this question is not straightforward. Most Linux system administrators currently are, or have been, Linux programmers in the past and that makes this quite a difficult question to answer. For an average Linux system administrator the GNOME environment provides a wealth of tools that makes his or her administrative job so simple, while it is the duty and responsibility of the GNOME programmer to continue providing these facilities by designing even better programs. So, they are in perfect harmony with each other as far as their respective works are concerned.
Now let's take a bit closer look at Gnome's functionality. GNOME is actually a programming layer placed in between the X Window System (or X) and the Window Manager software. Thus, as mentioned earlier, it provides Linux GUI programmers with an enormous functionality they can harness to design Linux based programs. But most significant of all, the reason why GNOME is nearly indispensable for all Linux/Unix developers is because GNOME provides these developers/programmers with an Integrated Framework that was specifically designed for building open-source applications with a consistent graphical user interface.
The GNOME Project started in August, 1997; some of the initial founders included, amongst others, Peter Mattis, Spencer Kimball, Richard Stallman, and Erik Troan and Mark Ewing of Red Hat, Inc.
Let's now take a look into the GNOME Architecture.
Now we come to a very significant portion, the GNOME Architecture. It is this extremely powerful, yet flexible architecture that provides GNOME its terrific functionality. The base toolkit in GNOME is named GTK+ (the GIMP toolkit). It was originally written for using in the GIMP (GNU Image Manipulation Program). The proper understanding of GTK+ is extremely necessary for the understanding of GNOME Programming. GTK+ is an object-oriented, cross-platform language-neutral toolkit that is primarily used for creating applications independently of GNOME. Then the question that comes up is: Then why was GTK+ chosen as the toolkit for GNOME? The answer is simple: It was for its support for many programming languages including C, C++, PERL, Python, ADA etc. But it is helpful to keep in mind always that both GNOME as well as GTK+ are written using C; so we will be dealing here with C only.
Another question that may come up in the reader's mind is: Hey, what do these things called "Toolkits" contain? Toolkits like GTK+, Qt (the KDE Environment is based on Qt) are collections of widgets. Which begs the question: What are "Widgets"?
Widgets are GUI objects like buttons, menus, dialog boxes and other such objects or object-related general functions. This can be compared with Active Template Library (ATL 3.0) on the Microsoft Platform, which provides Component Object Model (COM) developers with a ready-made framework for creating COM Objects and Components (ActiveX EXEs & ActiveX DLLs).
GLIB data type C type gchar Char gshort Short glong Long gint Int gboolean Boolean gpointer void*
A vital requirement for proper understanding of GTK+ is the
concept of "Widget Hierarchy". Widgets in GTK+ belong to a
hierarchy so that functions that are common to a set of widgets
need only be implemented once. For example, the function
gtk_widget_show
: This leads to the removal of
duplicate codes. Thus leading to better and faster program
development. The point to be kept in mind is always: new widgets
are derived from existing higher-level widgets so that only the new
features of the widget need to be written by the programmer. For
example, let's look closely to this particular widget
hierarchy:
GtkObject
--> GtkWidget
--> GtkContainer
--> GtkBin
--> GtkWindow
--> GnomeApp
Thus, if you look carefully, you can see that GnomeApp widget is derived from the higher-level GtkWindow, which itself has been derived from the higher-level GtkBin and so on. If we take into the consideration the essential features of the C++ programming language, well, this reminds us of the concept of "Inheritance". Doesn't it? Well, surely it does. And it is this feature of the "Widget Hierarchy" that incorporates the derived functionality in GTK+.
Let's now take a brief look at the widget creation functions. For these functions to operate correctly one must make sure that all the GNOME and GTK+ libraries are correctly installed. Another important thing to be kept in mind is that the library's path must be correctly set before trying to compile any code.
Let's first consider the widget creation function,
gnome_app_new( )
. This function as shown returns a
GtkWidget pointer, which is the generic widget. This maybe shown
as:
GtkWidget *ghosh;
ghosh = gnome_app_new(.........);
Please note that this also means that if we want to call a
GnomeApp specific function such as gnome_app_set_menus(
)
, then we have to use a macro to perform the cast from a
GtkWidget type to a GnomeApp type; which is only possible because
GnomeApp is derived from GtkWidget (consult figure above).
Boot your system in Linux, and if you are in the CLI (command line interface) mode, switch over to gnome, using the command "switchdesk gnome", and then issue a "startx" command to boot into the X Window System GUI mode. Once into the GNOME environment, open the GNOME Terminal, create a file named myapp.c using vi, and type in the following:
/* A sample GNOME program
Created By: Subhasish Ghosh
Date: 4th July, 2001
*/
#include <gnome.h>
int main(int argc, char *argv[ ])
{
GtkWidget *ghosh;
gnome_init("sample", "0.1", argc, argv);
ghosh = gnome_app_new("sample", "My Window");
gtk_widget_show(ghosh);
gtk_main( );
return 0;
}
Now, to compile the program myapp.c, make sure you type in: (note the back-ticks carefully)
# gcc myapp.c -o myapp `gnome-config --cflags --libs
gnomeui`
Note, GNOME comes with a shell script named gnome-config that supplies the compiler with the correct flags required for compilation. Once compiled, run the program using the command:
# ./myapp &
and press enter.
An empty window will appear on the screen, which you can move,
resize, as well close. Now, let's take a closer look at the code.
At the top we included a few commented lines describing the
program, it's creator and date of creation. Though not necessary
it's a good programming practice to include these in each and every
program. Then, we included the header file, gnome.h
that takes care of all necessary GNOME and GTK+ library functions
and definitions. Then comes "ghosh", which is a GtkWidget pointer.
This would point to our new Window object. The function
gnome_init
is then called. It initializes libraries,
and is used for correct session management. The ID passed to this
gnome_init
function is "sample", the version number
being "0.1", and then the usual command line arguments of main.
These are necessary for the internal workings of GNOME. Then comes
the function gnome_app_new( )
, which when executed,
creates our window. This takes two arguments as shown in the sample
code: "sample" and "My Window", where "sample" is the application
name, and "My Window" being the window title. But please note:
Though the name of this function is gnome_app_new( )
,
it does not create any sort of new application. It
creates a top-level window. That's all. The next function being
called is gtk_widget_show( )
, which makes our window
visible. Next comes gtk_main( )
which is a very
important function, as it makes sure that GNOME functions like
events, button presses and such, are executed, by handing on the
functionality to GNOME.
So, that's the internal workings of our first GNOME program.
gtk_signal_connect
to connect signals to handler
functions.
The gtk_signal_connect function has the following four parameters:
It should be noted that various kinds of widgets emit different signals. The signals from buttons are as follows:
We will see how signals and callbacks play a vital role in the applications that we develop later.
gtk_hbox_new
and gtk_vbox_new
,
respectively. We will see these functions in action soon, in the
applications that we create later. For now, let's take a look into
the parameters of these two functions:
Creating radio buttons is very similar to check boxes, and all that we need to do extra is to specify a group for the radio button to belong to. Radio buttons are derived from check buttons, which are derived from toggle buttons, so this means that we have the same set of functions to read and modify their state and also use the same old events. Please note: For more information of specific functions, consult the GTK+ Reference Documentation available at: http://www.gtk.org
gtk_entry_new(
)
. Entry widgets are mainly used to enter small amounts of
information. Let's take a look at a program that creates a "Login
Window", and outputs the password field, when the activate signal
occurs, when the button is pressed. Type in the following and
execute the program as has been explained above.
/* Creating a Login GNOME-style using GTK+ Toolkit:
Created By: Subhasish Ghosh
Date: Sunday, July 1, 2001
*/
#include <gnome.h>
static void enter_pressed(GtkWidget *button, gpointer data)
{
GtkWidget *text_entry = data;
char *string = gtk_entry_get_text(GTK_ENTRY(text_entry));
g_print(string);
}
int main(int argc, char *argv[])
{
GtkWidget *app;
GtkWidget *text_entry;
GtkWidget *label;
GtkWidget *hbox;
gchar *text;
gnome_init("example", "0.1", argc, argv);
app = gnome_app_new("example", "entry widget");
gtk_container_border_width(GTK_CONTAINER(app), 5);
hbox = gtk_hbox_new(FALSE, 0);
/* we now create a Label: */
label = gtk_label_new("Password: ");
gtk_misc_set_alignment(GTK_MISC(label), 0, 1.0);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
text_entry = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(text_entry), FALSE);
gtk_box_pack_start(GTK_BOX(hbox), text_entry, FALSE, FALSE, 0);
gtk_signal_connect(GTK_OBJECT(app), "delete_event", GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
gtk_signal_connect(GTK_OBJECT(text_entry), "activate", GTK_SIGNAL_FUNC(enter_pressed), text_entry);
gnome_app_set_contents(GNOME_APP(app), hbox);
gtk_widget_show_all(app);
gtk_main();
return 0;
}
When executed this program will cause a login window to appear on the screen. Type in any text (assuming it to be a password), and press enter. Observe what happens.
Just hang on for a second, someone may ask: "Hey, we were doing pretty well with ordinary code and all the stuff that you discussed earlier. What's the use of this so-called "specific GNOME programming libraries"? Are they indeed useful? Or are you just including them here for making your article a bit longer?"
Well, here's the reason for considering specific GNOME programming libraries. With plain GTK+ code, though nearly everything can be done that we would usually do using non-specific GNOME programming libraries, using simple and plain GTK+ code often leads to much code-repetition, in-efficient code-blocks and such other things, finally making the whole program a bloated one. Now, to prevent this from happening, we use specific GNOME programming libraries that provide a great deal of extra functionality with much lower programming overheads.
So, let's talk about "Menus" and "Toolbars" in this section.
GNOME let's us create menus and toolbars for our GnomeApp widgets
that can be docked and undocked from the window. The arrays need to
be filled with necessary information, and then call
gnome_app_create_menus
or
gnome_app_create_toolbar
.
The menus and toolbar items each have properties. The arrays (we earlier talked about) are a list of structures that define these properties. A few such properties include type, string, callback pointer etc. For the majority of the time menu entries are pretty simple, and we can just use one of a set of macros provided by GNOME to create the structure for us. So, let's check out a few most used top-level macros.
Please note: These top-level macros are the ones that create
top-level menus when passed an array that can contain any or all of
the following GnomeUIInfo
structures.
Menu Macro File GNOMEUIINFO_MENU_FILE_TREE(tree) Edit GNOMEUIINFO_MENU_EDIT_TREE(tree) View GNOMEUIINFO_MENU_VIEW_TREE(tree) Settings GNOMEUIINFO_MENU_SETTINGS_TREE(tree) Windows GNOMEUIINFO_MENU_WINDOWS_TREE(tree) Help GNOMEUIINFO_MENU_HELP_TREE(tree) Game GNOMEUIINFO_MENU_GAME_TREE(tree)
Within the top-level menu there exists over thirty macros for
creating common menu items. The macros associate small images
(pixmaps) and accelerator keys with each menu item. A
callback
function is required to be called when the
item is selected and a data pointer is to be passed to that
function.
Let's look at some of these common menu items and their respective macros.
File --> New --> GNOMEUIINFO_MENU_NEW_ITEM (label, hint, cb, data)
Open --> GNOMEUIINFO_MENU_OPEN_ITEM (cb, data)
Save --> GNOMEUIINFO_MENU_SAVE_ITEM (cb, data)
Print --> GNOMEUIINFO_MENU_PRINT_ITEM (cb, data)
Exit --> GNOMEUIINFO_MENU_EXIT_ITEM (cb, data)
Edit --> Cut --> GNOMEUIINFO_MENU_CUT_ITEM (cb, data)
Copy --> GNOMEUIINFO_MENU_COPY_ITEM (cb, data)
Paste --> GNOMEUIINFO_MENU_PASTE_ITEM (cb, data)
Settings --> Preferences --> GNOMEUIINFO_MENU_PREFERENCES_ITEM (cb, data)
Help --> About --> GNOMEUIINFO_MENU_ABOUT_ITEM (cb, data)
Just as with menu bars, toolbars require an array using the
GNOMEUIINFO_ITEM_STOCK
(label, tooltip,
callback, stock_id
) macro. Here, "stock_id
" is
the id of a predefined icon that we want to use for that item.
Let's look at this example and see how the arrays and macros work in reality.
#include <gnome.h>
static void callback (GtkWidget *button, gpointer data)
{
g_print("Item Selected");
}
GnomeUIInfo file_menu[ ] = {
GNOMEUIINFO_ITEM_NONE ("A menu item", "This is the Status bar info", callback),
GNOMEUIINFO_MENU_EXIT_ITEM (gtk_main_quit, NULL),
GNOMEUIINFO_END
};
GnomeUIInfo menubar[ ] = {
GNOMEUIINFO_MENU_FILE_TREE (file_menu),
GNOMEUIINFO_END
};
GnomeUIInfo toolbar[ ] = {
GNOMEUIINFO_ITEM_STOCK ("Print", "This is another tooltip", callback, GNOME_STOCK_PIXMAP_PRINT),
GNOMEUIINFO_ ITEM_STOCK ("Exit", "Exit the application", gtk_main_quit, GNOME_STOCK_PIXMAP_EXIT),
GNOMEUIINFO_END
};
int main (int argc, char *argv[ ])
{
GtkWidget *app;
gnome_init ("example", "0.1", argc, argv);
app = gnome_app_new ("example", "A Sample Toolbar and Menu");
gnome_app_create_menus (GNOME_APP (app), menubar);
gnome_app_create_toolbar (GNOME_APP (app), toolbar);
gtk_widget_showall (app);
gtk_main( )
return 0;
}
When executed this program will create a small window with an embedded menu and toolbar. It can be clicked, docked, undocked and dragged around the screen.
gnome_message_box_new
function and pass it the message
text, also mention the type of dialog box we need, and the buttons
we want on it. All of this is included in a NULL terminated list.
Then we bind the "clicked" signal of the dialog widget that we have
just created to a handling function that is passed the button the
user pressed as an integer. Finally, we call the
gtk_widget_show
function for displaying a non-modal
box.
Let's look at this code extract from a program that creates a simple question dialog box, adds three buttons and responds to the user's code.
static void messagebox_clicked(GnomeDialog *dlg, gint button, gpointer data)
{
switch (button)
{
case 1: /* user pressed apply */
return;
case 0: /* user pressed ok */
case 2: /* user pressed close */
gnome_dialog_close(dlg);
}
}
GtkWidget *dlg;
dlg = gnome_message_box_new("Hi, pal, How are you doing??? I am fine!",
GNOME_MESSAGE_BOX_QUESTION,
GNOME_STOCK_BUTTON_OK,
GNOME_STOCK_BUTTON_APPLY,
GNOME_STOCK_BUTTON_CLOSE,
NULL);
gtk_signal_connect (GTK_OBJECT(dlg), "clicked", GTK_SIGNAL_FUNC(messagebox_clicked), NULL);
gtk_widget_show (dlg);
For more information and detailed coverage of this topic, check out the following links that I am sure would provide rich information on Gnome programming using GTK+ toolkit and more:
About the Author: My name is Subhasish Ghosh. I am 20, currently a computer-systems engineering student in India. I am a Microsoft Certified Professional (MCP), MCP certified on NT 4.0, recently completed Red Hat Linux Certified Engineer (RHCE) Training. I have been working with Linux for a long time now, have had programmed using C, C++, VC++, COM, MFC, DCOM, ATL 3.0, Perl, Python and Linux programming using GTK+. Currently busy learning the Linux Kernel Architecture in detail, writing articles for Linux.com and most importantly practicing non-geek talk with my girlfriend, Hanna. E-mail: auspicious_blessingsindia@hotmail.com