Szilard@BalaBit

How to Add Custom gtkmm Widget to Glade

Wednesday, June 13, 2012 @ 11:06 AM Author: Pfeiffer Szilárd

The inspiration of this post is the fact that as much as it is easy to create a custom widget class in gtkmm, it is just as complicated to add it to Glade, the interface designer tool of GNOME. As gtkmm is C++ we only have to derive from the desired parent class and customize its behaviour to implement a brand new widget. It would be nice to add the instances of this new class just like stock GTK+ widgets in Glade.

Let’s list what is required to add a custom gtkmm widget to Glade

  1. at least one pure custom widget implementation
  2. some Glade-related extra functions to the custom widgets
  3. a catalog file which describes the custom widgets to Glade
  4. a library contains the custom widgets and some Glade-related functions

Pure widget implementation

The simplest custom widget implementation could be something like that. As you can see, it derives simply from the entry widget. The entry was selected because it is a well-known and easily recognizable widget which can be created without any constructor parameter. It is not so useful, but it is still compilable and works as the entry widget does.

#ifndef CUSTOM_WIDGET_H_INCLUDED
#define CUSTOM_WIDGET_H_INCLUDED
 
#include <gtkmm.h>
 
class CustomWidget : public Gtk::Entry
{
};
 
#endif

Glade-capable widget implementation

Let’s see the header file of our custom widget after some Glade-related extension. As you can see almost everything in the class of the custom widget serves the aim of Glade integration.

#ifndef CUSTOM_WIDGET_H_INCLUDED
#define CUSTOM_WIDGET_H_INCLUDED
 
#include <gtkmm.h>
 
class CustomWidget : public Gtk::Entry
{
private:
  static GType gtype;
 
  CustomWidget (GtkEntry *gobj);
  CustomWidget ();
 
  static Glib::ObjectBase * wrap_new (GObject* o);
 
public:
  static void register_type ();
};
 
#endif

Everything is about conversion between C and C++ objects. First of all we have to be able to wrap a plan C widget to a C++ one. The wrap_new function and the constructor with GtkEntry * parameter of the CustomWidget class perform that.

We have to register this wrap function to the GType related to the custom widget. This is performed by the register_type function. To avoid multiple registrations the GType is stored to the gtype static member of the CustomWidget class and it is checked before registration.

#include "custom_widget.h"
 
GType CustomWidget::gtype = 0;
 
CustomWidget::CustomWidget (GtkEntry *gobj) :
  Gtk::Entry (gobj)
{
}
 
CustomWidget::CustomWidget () :
  Glib::ObjectBase ("customwidget")
{
}
 
Glib::ObjectBase *
CustomWidget::wrap_new (GObject *o)
{
  if (gtk_widget_is_toplevel (GTK_WIDGET (o)))
    {
      return new CustomWidget (GTK_ENTRY (o));
    }
  else
    {
      return Gtk::manage(new CustomWidget (GTK_ENTRY (o)));
    }
}
 
void
CustomWidget::register_type ()
{
  if (gtype)
    return;
 
  CustomWidget dummy;
 
  GtkWidget *widget = GTK_WIDGET (dummy.gobj ());
 
  gtype = G_OBJECT_TYPE (widget);
 
  Glib::wrap_register (gtype, CustomWidget::wrap_new);
}

Glade catalog file

We have implemented almost everything that is necessary to use our custom widget in Glade, so we only have to inform Glade about it. It can be done by a catalog, which is an XML file containing the following information about custom widgets.

  1. library containing the implementation of the custom widgets
  2. the name of the function performing the initialization
  3. widget-related information
  4. grouping of widgets

Colon-separated list of additional load path(s) of catalog files can be specified in the GLADE_CATALOG_SEARCH_PATH environment variable.

Library name

The library attribute of the glade-catalog element is the name of the shared object file containing the implementation of the custom widgets and the function which performs the initialization. In this case libcustomwidgetsglade.so must be available in one of the usual library paths or in the directory set by the GLADE_MODULE_SEARCH_PATH environment variable.

Initialization

As it has already been mentioned, initialization means registration of the custom widgets, and it is performed by the function that is named in init-function.

Custom widgets

Widgets are listed between the glade-widget-classes elements described by the attributes of the glade-widget-class element. The most important one is the name attribute, which is prefixed by the magic gtkmm__CustomObject_ string and followed by the name passed to the Glib::ObjectBase as parameter in the constructor of CustomWidget class.

Widget groups

Custom widgets can be grouped by referencing them between the glade-widget-group elements as you can see in the example catalog. It means that the icon of the widget is displayed in the necessary group when we run Glade. Note that icons must be placed in the standard pixmap directory. There is now a way to set user directory for icons for example in a GLADE_ICON_SEARCH_PATH environment variable. That is the main reason why our custom entry has the same icon as the stock entry.

<?xml version="1.0" encoding="UTF-8" ?>
<glade-catalog name="customwidgets" library="customwidgetsglade" depends="gtk+">
 
  <init-function>custom_widgets_glade_init</init-function>
 
  <glade-widget-classes>
    <glade-widget-class name="gtkmm__CustomObject_customwidget" generic-name="customwidget" icon-name="widget-gtk-entry" title="Custom Widget">
    </glade-widget-class>
  </glade-widget-classes>
 
  <glade-widget-group name="customwidgets" title="Custom Widgets" >
    <glade-widget-class-ref name="gtkmm__CustomObject_customwidget" />
  </glade-widget-group>
 
</glade-catalog>

 

Glade related functions

There is nothing to do, but to implement the function mentioned in the catalog file, which performs the initialization. It practically registers the custom widgets. Initializing gtkmm internals is a must, because  custom_widgets_glade_init is called from Glade, which is written in C not in C++ so it initializes only GTK+ internals.

#ifndef CUSTOM_WIDGETS_GLADE_H_INCLUDED
#define CUSTOM_WIDGETS_GLADE_H_INCLUDED
 
void custom_widgets_register ();
 
#endif
#include "custom_widgets_glade.h"
 
#include "custom_widget.h"
 
#include <gtkmm/main.h>
 
void
custom_widgets_register ()
{
  CustomWidget::register_type ();
}
 
extern "C" void
custom_widgets_glade_init ()
{
  Gtk::Main::init_gtkmm_internals ();
  custom_widgets_register ();
}

 

The function custom_widgets_register is used to register our custom widgets. It must be done in the application where we want to load a .glade file with a custom widget. Initialization of gtkmm is unnecessary as it is done in a normal way.

Compilation and run

Simple application

Here is a simple .glade file that contains a custom widget. As you can see there is an XML comment that shows that the 0.0 version of customwidgets interface is required. The required GTK+ version is also mentioned in XML comment, so if you use the last major version do not forget to replace the 3.0 version number to the necessary 2.x one.

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <!-- interface-requires customwidgets 0.0 -->
  <!-- interface-requires gtk+ 3.0 -->
  <object id="window">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Custom Widget</property>
    <child>
      <object id="custom_widget">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="invisible_char"></property>
      </object>
    </child>
  </object>
</interface>

The following code loads the .glade file which contains only a window with a custom widget in it and displays it.

#include <gtkmm.h>
 
#include "custom_widget.h"
#include "custom_widgets_glade.h"
 
int
main(int argc, char *argv[])
{
  Gtk::Main kit(argc, argv);
  custom_widgets_register();
 
  Gtk::Window *window;
  Glib::RefPtr<Gtk::Builder> builder;
 
  builder = Gtk::Builder::create_from_file("custom_widget.glade");
  builder->get_widget("window", window);
 
  Gtk::Main::run(*window);
 
  return 0;
}

The example code can be compiled and run with the following commands.

g++ custom_widget_example.cc custom_widget.cc custom_widgets_glade.cc \
-o custom_widget_example -fPIC `pkg-config --cflags --libs gtkmm-3.0`
./custom_widget_example

The example also works with gtkmm 2.x versions, but in that case you should compile the code with the following command.

g++ custom_widget_example.cc custom_widget.cc custom_widgets_glade.cc \
-o custom_widget_example -fPIC `pkg-config --cflags --libs gtkmm-2.4`

Glade interface designer

Shared object to Glade can be created with one of the the following commands depending on the version of gtkmm. The name of the file comes from the name that was given in the catalog file. As it was customwidgetsglade the name of the file must be libcustomwidgetsglade.so.

g++ custom_widget.cc custom_widgets_glade.cc -o libcustomwidgetsglade.so -fPIC -shared `pkg-config --cflags --libs gtkmm-3.0`

 

g++ custom_widget.cc custom_widgets_glade.cc -o libcustomwidgetsglade.so -fPIC -shared `pkg-config --cflags --libs gtkmm-2.4`

If each mentioned files are in our working directory Glade 3.x can be run with the following command. Both  GLADE_CATALOG_SEARCH_PATH and GLADE_MODULE_SEARCH_PATH environment variables are set the working directory, so the catalog and the library files will be searched there.

GLADE_CATALOG_SEARCH_PATH=$PWD GLADE_MODULE_SEARCH_PATH=$PWD glade custom_widget.glade

The environment variables are a little bit different in case of Glade 2.x, but the working method is the same.

GLADE_CATALOG_PATH=$PWD GLADE_MODULE_PATH=$PWD glade-gtk2 custom_widget.glade

Now we can find our widget class in the Custom Widgets block and an instance of it in window of custom_widget.glade file.

Be Sociable, Share!

Creative Commons License
The How to Add Custom gtkmm Widget to Glade by Szilard@BalaBit, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.