metz·log

Embedding GtkBuilder files in GTK+ 2.x

·

As part of modernizing GKrellWeather I changed the hand-coded configuration UI to a file that can be edited visually using Glade.

GTK+ 2.24 already supported generating UI from XML files directly via the GtkBuilder API and editing is possible as long as you have Glade 3.8.x around (Glade dropped support for GTK+ 2.x after 3.8.x, unfortunately Debian/jessie only contains Glade 3.18.x).

Because most GKrellM plugins are pretty self-contained, loading the UI from an external file was not what I had in mind though, so I started looking for ways to embed the UI file directly into the plugin binary. After looking a bit into the gritty details of using ld or objcopy I discovered that GLib already covers this scenario, enter GResource.

Using GResource

The documentation for GResource already shows the steps of how to get files embedded. It’s actually very similar to the Qt Resource System, just the tool names and file syntax differ.

  • Create an XML file (by hand, eek) that explains which files to embed and what resource path they should have.
  • Call glib-compile-resources on the XML file and tell it to generate C sources (--generate-source)
  • Add the name of the generated file to your project source-list as usual

In the end I had my UI file inside the plugin already, but it still had to be loaded somehow.

Tieing GtkBuilder to GResource

The next question was how to use GtkBuilder and GResource together and searching for that combo only lead me to GTK+ 3.x examples. Since GTK+ 3.10 doing this is as easy as calling gtk_builder_new_from_resource() or creating your own GtkWidget subclass and call gtk_widget_class_set_template_from_resource().

Because GKrellM is currently still bound to GTK+ 2.x and the mentioned functions were not present back then I began digging a bit on how other applications do this on GTK+ 2.x, quite a lot of Read-The-Fine-Source was caused by that.

In the end it turned out to be fairly easy if you know how to get the data out of a global GResource and push data (including length) into a GtkBuilder object. The following code snippet does exactly that.

/* Simple GTK+2 variant of gtk_builder_add_from_resource() */
static guint builder_add_from_resource(GtkBuilder *builder
    , const gchar *resource_path
    , GError **error)
{
    GBytes *data;
    const gchar *buffer;
    gsize buffer_length;
    guint ret;

    g_assert(error && *error == NULL);

    data = g_resources_lookup_data(resource_path, 0, error);
    if (data == NULL) {
        return 0;
    }

    buffer_length = 0;
    buffer = g_bytes_get_data(data, &buffer_length);
    g_assert(buffer != NULL);

    ret = gtk_builder_add_from_string(builder, buffer, buffer_length, error);

    g_bytes_unref(data);

    return ret;
}

void gkrellweather_config_ui_create(GtkWidget *parent)
{
    GtkBuilder *builder;
    GError *error;
    GtkWidget *config_widget;

    builder = gtk_builder_new();
    error = NULL;
    if (!builder_add_from_resource(builder, "/org/gkrellweather/config.ui", &error)) {
        g_warning("Could not build config ui: %s", error->message);
        g_error_free(error);
        g_object_unref(G_OBJECT(builder));
        return;
    }

    config_widget = GTK_WIDGET(gtk_builder_get_object(builder, "config_widget"));

    /* And off you go with that shiny config_widget… */

    g_object_unref(G_OBJECT(builder));
}

The actual code to get GResource and GtkBuilder working together can be summed up as:

User Interface changes for GKrellWeather

After all this fiddling now for the actual results. Here is a screenshot showing the main GKrellWeather configuration tab with the old layout that had been created in C years ago

GkrellWeather Configuration UI - Before

The layouting clearly is a bit odd, especially the radiobuttons and their labels are quite uncommon since it is difficult to tell which group they belong to.

And here is the current state based on a Glade file targetting Gtk+ 2.24.

GkrellWeather Configuration UI - After

The dialog layout now starts following GNOME Human Interface Guidelines a bit more, at least in terms of alignment and spacing of elements.

Right now the UI also lost a few features temporarily, i.e. the link to find your station id or the unit names for switch and update intervals. With the UI now being editable via Glade, fixing the last remaining bits should be a lot faster than it was with hand-crafted C code before. The next step for GKrellWeather however is improving functionality itself and hopefully see an official release soon.