Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Programming in GNOME with C++


May 2002/Programming in GNOME with C++

Programming in GNOME with C++

Paulo Pinto

A C++ binding for GNOME makes Linux GUIs easy work.


Introduction

When choosing a programming language in which to develop an application for GNOME (GNU Network Object Model Environment), C is the natural choice. This is because C is used to develop GNOME. So what options are available to C++ programmers? One of them is to create your own framework, wrapping the C API that you need for your application. However, it would be better if such a framework already existed. In fact, it does, and it is accepted as the official C++ binding for GNOME. It is known as Gnomemm and will be the focus of this article.

Overview

As with any other framework, Gnomemm is composed of several parts (see Figure 1). The main parts are Gtkmm, which wraps Gtk+ functionality; Gnomemm for the GUI part of GNOME; and libsigc++ for signal/slot handling. Table 1 shows all the major components and their functionality.

To build Gnomemm applications, you need to have at least three components:

  • libsigc++
  • Gtkmm
  • Gnomemm

Gtkmm/Gnomemm [1] make heavy use on the abstraction features provided by C++, such as namespaces, templates, and the STL. This means that you need to have a C++ compiler that supports ANSI C++ as closely as possible. For g++, this means at least version 2.95.2.

Advantages

Experienced C++ developers will be able to anticipate the advantages of wrapping a C API like Gtk+ with an object-oriented framework. But just in case, the advantages are:

  • a clearer API:
    • better cohesion between the functions and data structures
    • type-specific parameters (e.g., no mysterious void* struct members, which suppress errors caused by bad parameters)
    • no need to use several arguments just to pass state around (like handles)
  • less code:
    • no need for repetitive cast macros, like GTK_WIDGET
    • the C++ type system will handle conversions between widgets
  • increased type-safety:
    • more code problems found at compile time
  • incredibly simple derivation of new widgets, using C++ inheritance:
    • derivation of new widgets is complex and error-prone in normal Gtk+, because it forces the user to create several structures with a specific layout and with complex initializations
  • type-safe callbacks:
    • the compiler will tell you if your callback (signal handler) does not match the signal
    • in Gtk+/GNOME it is possible to associate a callback to a signal with the wrong signature
  • more organized code:
    • the simpler code and ease of derivation remove obstacles to code refactoring
  • simpler memory management:
    • no worrying about when a char* or GList* should be deallocated; the constructors and destructors of the widgets handle the allocation/deallocation of data

Creating a Window

It’s time to show a Gtkmm application. Start by creating a window with “Hello World” as the caption, using the Gtk+ part of the framework.

Start by including common headers:

#include <gtk—/main.h>
#include <gtk—/window.h>

Initialize the framework and create the window:

int main (int argc, char *argv [])
{
  Gtk::Main kit (argc, argv);
  Gtk::Window win;

Set the window caption to “Hello World”:

  win.set_title ("Hello World");

The destroy signal must be handled or the user won’t be able to close the window by clicking on the “X” button:

  win.destroy.connect
    (Gtk::Main::quit.slot ());

Show the window and begin the event-processing loop:

  win.show ();

  kit.run ();
}

Listing 1 shows the complete source code for the program.

To compile the application, you will need to use the following command:

g++ -o hello hello.cpp 'gtkmm-config —cflags —libs' -lgnomemm

For those who are curious about gtkmm-config, it is a shell script that gives the compiler the correct flags for the platform where it is run.

When running the application, a window (see Figure 2) will appear, which you can close by clicking on the “X” part of the window.

What Are Signals and Slots?

Many readers may not know what signals and slots are if they haven’t looked at Gtk+, or even Qt, before. In GUI toolkits like Gtk+, the events that are sent to the application are known as signals. The callbacks associated with those signals are called slots.

Every signal can have multiple slots, and a slot can respond to several different signals. In Gtk+ slots are functions. In Gtkmm/Gnomemm, they can be simple functions or member functions. In the case of member functions, they can be virtual or static.

A Better wc

The hello world program is fine for a first example, but now it’s time to do something more substantial. Let’s next create a GNOME version of the popular Unix command-line program, wc [2]. This program will allow the user to select a file and then display the line, word, and byte count of the file, just like wc. Figure 3 shows gwc after it is launched. In Figure 4, gwc displays the results of processing a file.

In Gnomemm, all GNOME applications inherit from Gnome::App, and so will gwc. The gwc window will be composed of a menu, a toolbar, and a text label for displaying results. These items will be created in the constructor of the application window (shown below).

First, the label is created and used as the contents for the application window:

m_text = new Gtk::Label ("No file counted");
set_contents (*m_text);

The created label will have to be explicitly deleted. Compare this with the use of the manage call (described below).

Then a file selection dialog is created so that whenever gwc needs to ask the user for a file, it is already there. Note that this is just an application design choice. You could alternatively choose to create the dialog every time the user tries to open a file.

// Allocate the dialog and let the
// library handle the memory used by it
m_filesel =
  manage (new Gtk::FileSelection ("Open File"));

// Connect the ok_button to file_ok_sel function
m_filesel->get_ok_button()->clicked.connect
  (slot(this, &GWCApp::process_file));

// Connect the cancel_button to hide the window
m_filesel->get_cancel_button()->clicked.connect
  (m_filesel->hide.slot());

Some things need to be emphasized here. First, a slot must be connected to the signal that is triggered when the user presses the OK button. This is the best way to process the information from the dialog. Another solution would be to set a flag so that the code that calls the dialog knows the user has selected a file.

The second noteworthy point has to do with the manage call. In Gtkmm/Gnomemm, you can create widgets on the stack, inside class objects, or on the heap. If you create them on the heap, you are expected to delete the widgets unless you pass the ownership of the widget to the Gtkmm/Gnomemm library, by calling manage. In that case, the library will delete the widget when it is safe to do so. There are some subtleties in using manage, so some care must be taken in its use. Don’t use manage like it is the solution for all your memory problems. In fact, use it only for widgets that are added to a containers [3] and not for top-level windows, or dialogs for that matter. Think of it as letting the widget be “managed” by the container that owns it.

Next, how are the menus constructed? You can use the wrapped Gtk+/GNOME APIs, the native Gtk+/GNOME APIs, or the menu helpers. Our sample gwc program will use the last solution because it is simple to understand, and to me it’s also the most natural choice. Helpers are special objects that manage the creation of widgets that must be inserted into Gtk::Container widgets. Their use makes the task of building multi-item widgets, like menus, less error-prone.

gwc’s “File” menu is built like this:

UI::Info file_menu[] =
  { MenuItems::Open (slot (this, &GWCApp::on_menu_open)),
    UI::Separator(),
    MenuItems::Exit(Gtk::Main::quit.slot ())
    };

The first and third array entries create standard menu options and specify which slots will be invoked when the user selects that option. As for the second entry, it is a normal horizontal line that’s commonly used to separate different sets of menu options. But a menu will be useless if it isn’t attached to a menu bar, so you will also need to declare the menu bar:

UI::SubTree menus[] = { Menus::File(file_menu) };

Now that you have a menu bar, the application must generate the menu layout from it and attach the result to the application window.

create_menus(menus);

I must emphasize that although the menu helpers are based on templates, they are quite easy to use.

You may have noted that Listing 2 doesn’t connect a callback to the delete event, which would seem to mean that the users won’t be able to close the gwc window. Well that isn’t quite true, because the user will be able to close it.

How? If you take a closer look at Listing 2, you will see a declaration for delete_event_impl, and that method is responsible for terminating the application. All widgets have virtual member functions for their signals, with the same name as the signal and a _impl suffix. So it is possible to process signals without having to connect the signal to a slot. That was what happened to the delete event. This way it is easier to alter the behavior of some signal handlers.

Finally, no modern GUI application would be complete without a toolbar, so gwc should also have one. The process for creating toolbars is the same as for menus, so a vector is declared with an item for each toolbar button and an icon for that button [4]. After that, the following are added: a name for the button, the slot to call when the user presses the button, and the text for the tooltip. Simple, isn’t it?

UI::Info toolbar[] =
  {UI::Item(UI::Icon
    (GNOME_STOCK_PIXMAP_OPEN),
    "Open", slot(this,
      &GWCApp::on_menu_open),
    "Open a file"),
    UI::Separator (),
    UI::Item(UI::Icon
    (GNOME_STOCK_PIXMAP_EXIT),
      "Exit", Gtk::Main::quit.slot (),
      "Exit the application")
    };

After initializing the toolbar vector, it must be added to the application window with:

create_toolbar(toolbar);

In this case, I just used a built-in array of UI::Info, but create_toolbar accepts other types (i.e., std::vector<> or std::list<> with an UI::Info argument can also be used). It all depends on the needs of your application.

Current State of Gtkmm/Gnomemm

At this time, Gtkmm wraps all of the widgets provided by the stable Gtk+ 1.2.x. Support for the upcomming Gtk+ 2.0, with Pango and GObject, is now available in CVS as of version 1.3.x. Gnomemm wraps all the gnome-libs 1.2.x widgets. Unfortunately, Gnomemm still lacks support for Bonobo components, the XML handling framework [5], the audio handling, and some other things. But don’t be put off by this; after all, Gtkmm/Gnomemm are already very usable, and you can always wrap the functionality that you need. And who knows, you could even give that work back to the community and help Gtkmm in doing so.

Conclusion

This article provides a very quick overview about Gtkmm and shows that C++ programmers are supported and welcome in GNOME. Of course many things are still lacking. If you can help, the Gtkmm team would surely appreciate your help.

I hope that you will give Gtkmm a try in you next project.

Acknowledgements

I wish to thank to Murray Cumming, one of the Gtkmm developers, for his input on this article. I also dedicate this article to the memory to my grandfathers.

Notes

[1] Gtkmm/Gnomemm were previously known as Gtk--/Gnome--.

[2] For those who don’t know what wc is, it is a very common program in Unix that counts the number of words, lines and bytes in a file.

[3] That is, widgets that can own other widgets. For example, a window is a container for any kind of widget that can be placed inside it.

[4] In Gtk+/GNOME, the icons in the toolbars are optional.

[5] You also could use any other C++ XML parser, such as Xerces from <http://xml.apache.org>. Bakery uses Xerces-C++ to provide a Document/View for Gnome--, which uses XML.

References

Gtkmm — <http://gtkmm.sourceforge.net/>

Gtkmm mailing list — <http://gtkmm.sourceforge.net/mailinglist.html>

Gtkmm FAQ — <http://gtkmm.sourceforge.net/docs/gtkmm-faq.html>

libsigc++ — <http://libsigc.sourceforge.net/>

Bakery — <http://bakery.sourceforge.net>

Glade-- — <http://home.wtal.de/petig/Gtk/>

Gladecc — <www.geocities.com/SiliconValley/Bit/8083/gladecc.htm>

GConf-- — <http://gconfmm.sourceforge.net/>

GtkExtra-- — <http://gtkextramm.sourceforge.net/>

GtkGLArea-- — <www.ece.ucdavis.edu/~kenelson/gtkglareamm/>

Gtk+ — <www.gtk.org>

GNOME — <www.gnome.org>

Paulo Pinto holds a degree in CS from New University of Lisbon. He currently works at Altitude Software doing systems programming. His interests include graphics programming and compiler development.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.