Programming in GNOME with C++

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


May 01, 2002
URL:http://www.drdobbs.com/programming-in-gnome-with-c/184401522

May 2002/Programming in GNOME with C++/Figure 1

Figure 1: Gnomemm framework

May 2002/Programming in GNOME with C++/Figure 2

Figure 2: Window that appears when application runs

May 2002/Programming in GNOME with C++/Figure 3

Figure 3: gwc after launching

May 2002/Programming in GNOME with C++/Figure 4

Figure 4: gwc shows results of processing a file

May 2002/Programming in GNOME with C++/Listing 1

Listing 1: Gtkmm source code

/*
 * Paulo Pinto ([email protected]) (c) 2001
 * A very simple hello world to
 * show basic Gtk-- usage. 
 */
#include <gtk--/main.h>
#include <gtk--/window.h>

// Please note that in ANSI C++ the
// return is optional
int main (int argc, char *argv [])
{
  // Initialize the framework
  Gtk::Main kit (argc, argv);
  Gtk::Window win;

  // Set the main window title
  win.set_title ("Hello World");

  // Connect the signal that is generated when the
  // user tries to close the window, to the
  // quit slot. This way the user will be able to
  // close the window by clicking on the 'X' mark.
  win.destroy.connect (Gtk::Main::quit.slot ());

  // Make the window appear
  win.show ();

  // Enter into the event loop
  kit.run ();
}
— End of Listing —
May 2002/Programming in GNOME with C++/Listing 2

Listing 2: A GUI version of the wc program

/*
 * Paulo Pinto ([email protected]) (c) 2001
 * An GUI version of the wc program
 */
#include <gtk--/label.h>
#include <gtk--/fileselection.h>
#include <gnome--/main.h>
#include <gnome--/app.h>
#include <gnome--/stock.h>

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cctype>

//Bring the slot binding command to global scope
using SigC::slot;

// Application
// A minimalist graphical view of wc
class GWCApp: public Gnome::App {
public:
  GWCApp ();
  virtual ~GWCApp ();

protected:
  gint delete_event_impl (GdkEventAny*);

  //Signal handlers:
  virtual void on_menu_open ();

  void process_file ();

private:
  bool count_words (const std::string &filename, int &words, 
    int &lines, int &bytes);
  void init_menus ();
  void init_toolbar ();

  Gtk::Label *m_text;
  Gtk::FileSelection *m_filesel;
};

// The program entry point.
// Just create a GWCApp instance and
// launch it.
// Please note that in ANSI C++ the
// return is optional
int main (int argc, char *argv [])
{
  // Initialize the framework
  Gnome::Main kit ("gwc", "0.0.1", argc, argv);
  GWCApp app;

  // Make the window appear
  app.show ();

  // Enter into the event loop
  kit.run ();
}



GWCApp::GWCApp ():
  Gnome::App ("gwc", "GWC")
{
  // Create the text label for showing the word count
  m_text = new Gtk::Label ("No file counted");

  // Set the window contents. Please note that
  // m_text must be deleted explicitly.
  // It is deleted in GWCApp::~GWCApp ()
  set_contents (*m_text);

  // Create the dialog box for opening files
  // We don't need to create it here but this
  // is just for ilustrive purposes.
  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());
  
  init_menus ();
  init_toolbar ();
}


GWCApp::~GWCApp ()
{
  // Delete the label. We don't need to delete
  // the file selection because it is handled
  // by manage.
  delete m_text;
}

// Creates the application menus
void GWCApp::init_menus ()
{
  // Create a file menu using helper functios.
  // There are other ways of building menus but this
  // is the easiest.
  using namespace Gnome;
  using namespace Gtk::Menu_Helpers;
  
  // Entries for the file submenu
  UI::Info file_menu[] = { MenuItems::Open (slot (this, 
    &GWCApp::on_menu_open)),
               UI::Separator(),
               MenuItems::Exit(Gtk::Main::quit.slot ())
  };

  // This is the for the menu toolbar
  UI::SubTree menus[] = { Menus::File(file_menu) };
  
  //  Create and attach the menus to the
  // application window
  create_menus(menus);
}



// Creates the application toolbar
void GWCApp::init_toolbar ()
{
  // Create a toolbar using helper functios.
  using namespace Gnome;

  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")
                       };
  
  //  Create and attach the toolbar to the
  // application window
  create_toolbar(toolbar);
}



//  Called when the user presses the 'X' mark
// on the window.
//  Redefining this method is another way of
// receiving the 'delete' signal.
gint GWCApp::delete_event_impl(GdkEventAny*)
{ 
  Gtk::Main::quit();
  return FALSE; 
}


//  Called when the user selects the
// File->Open option
void GWCApp::on_menu_open ()
{
  m_filesel->set_modal (true);
  m_filesel->show ();
}

//  Called when the user presses the
// Ok button on the FileSelection dialog
void GWCApp::process_file ()
{
  m_filesel->hide ();
  std::string filename = m_filesel->get_filename ();
  int words, lines, bytes;
  std::ostringstream msg;

  // Clear and force a redraw, or else
  // the older string will appear
  m_text->set_text ("");
  draw (0);

  if (!count_words (filename, words, lines, bytes)) {
    msg << "Couldn't open " << filename;
    Gnome::Dialogs::error (msg.str ());
  }
  else {
    msg << "The file " << filename;
    msg << ", has " << lines << " lines, " << words << " words and ";        
    msg << bytes << " bytes." ;

    m_text->set_text (msg.str ());
  }
}

// Counts all the words, lines and bytes.
// This isn't the most eficient way to do it, but
// it servers the purpose of the example.
bool GWCApp::count_words (const std::string &filename, int &words, 
  int &lines, int &bytes)
{
  std::ifstream in (filename.c_str ());
  if (!in)
     return false;

  words = 0;
  lines = 0;
  bytes = 0;
  char ch;
  while (!in.eof ()) {
    // Advance the file pointer to the next
    // word
    do {
      in.get (ch);
      if (!in.eof ()){
        bytes++;

        // Ok, now changing for the next line
        if (ch == '\n')
          lines++;
      }
    } while (!in.eof () && isspace (ch));

    // Advance the file pointer to the next
    // white space
    if (!in.eof ()) {
      words++;
      do {
        in.get (ch);
        if (!in.eof ())
          bytes++;
      } while (!in.eof () && !isspace (ch));
    }
  }

  return true;
}
— End of Listing —
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:

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:

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.

May 2002/Programming in GNOME with C++/Table 1

Table 1: Gnomemm components and functionality

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.