GConf



I've attached an updated version of the article on GConf.

Natan
The GConf Configuration System is a system used by the GNOME desktop to handle user preferences. This article will explain the fundamental concepts of how GConf works and will demonstrate how developers can use GConf in their applications. The examples in this article are all written in C.
 
Before we begin, there are a few details about GConf that are worth noting:
• GConf should not be used for crucial application settings (e.g. an address book's contacts). It should only be used for user preferences ( e.g. the address book's font).
• The GConf database is not guaranteed to be constantly running. As we'll see later on, values fetched from GConf should be checked to make sure that they exist.
• GConf values are (by definition) user writable. Applications should verify data stored in GConf when necessary.
• GConf auto-applies settings. In other words, when a setting changes, the change applies immediately without waiting for the user to press an "apply" button.
• GConfClient wraps the GConfEngine functions in a friendlier object that descends from GObject. For the most part, you won't want to use GConfEngine directly.

GConf settings are stored in a key-value format. The database is stored in set of xml files located under ~/.gconf. You should never read or write to these files directly. If you'd like to manually edit the database without using the GConf API from a program, you can do so graphically using the gconf-editor application. You can also get and set keys from the command line with the gconftool-2 command line tool.

Lets begin by looking at some some code to create a new GConfClient:
GConfClient* gconf_client_get_default (void);
The returned engine has a reference count of 1. It should be unreferenced when you're done using it with g_object_unref().

We can set a key with the following functions:
gboolean gconf_client_set_float (GConfClient *client,
const gchar *key,
gdouble val,
GError **err);

*client is the GConfClient that we created earlier.
*key is the name of the key to set.
*val is the value to set for key.
*err is the return location for an error or NULL to ignore errors.
 
Similar functions exist for manipulating integers, booleans, and strings.
gboolean gconf_client_set_int (GConfClient *client,
const gchar *key,
gint val,
GError **err);

gboolean gconf_client_set_bool (GConfClient *client,
const gchar *key,
gboolean val,
GError **err);

gboolean gconf_client_set_string (GConfClient *client,
const gchar *key,
const gchar *val,
GError **err);

There are three additional functions for setting schemas, lists, and pairs. They're a bit more complicated and much less common, so we'll leave them for another time.

To get a key's value use gchar * gconf_client_get_string (GConfClient *client, const gchar *key, GError **err) or the corresponding function for any of the other data types.

The following example is heavily commented, and will explain the bare basics of gconf.
 
#include <gconf/gconf-client.h>
#include <gtk/gtk.h>
 
// this callback is called whenever the button is pressed
void pressed_cb (GtkWidget *widget, GConfClient *config)
{
   // gconf_client_get_int () returns 0 if the key does not exist. In this case, we don't need to bother checking for errors.
   gint clicks = gconf_client_get_int (config, "/apps/gconf-test/clicks", NULL);
   clicks++;
  
   // set the value so that it will exist next time this function is called
   gconf_client_set_int (config, "/apps/gconf-test/clicks", clicks, NULL);
 
   g_print ("%d\n", clicks);
}
 
int main (int argc, char **argv)
{
   GtkWidget *window;
   GtkWidget *button;
   GConfClient *config;
   // common gtk init
   gtk_init (&argc, &argv);
   // create the GConfClient
   config = gconf_client_get_default ();
   // create the window and make it quit when we want it to
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
   // and the button...
   button = gtk_button_new_with_label ("Press me");
   // when it is pressed call our callback
   g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (pressed_cb), config);
   gtk_container_add (GTK_CONTAINER (window), button);
   // some more common stuff
   gtk_widget_show_all (window);
   gtk_main ();
   return 0;
}
 
You can compile the file with:
gcc -Wall -g gconf-test.c -o gconf-test `pkg-config --cflags gtk+-2.0 gconf-2.0` \
`pkg-config --libs gtk+-2.0 gconf-2.0`
 
Test it out and play around with it. You'll notice that the program will "remember" the number of times that it was clicked the last time that it was run.
 
When you want to associate a particular GObject property (e.g. a window's opacity) with a gconf key, then things begin to get complicated.  The usual method of doing so is as follows:
1) The application starts up and sets the default value from GConf.
2) The application tells gconf that it wants to be notified when the key changes.
3) The user opens the app 's preferences window and changes a key.
4) A callback gets called to set the GConf value.
5) Setting the GConf value triggers another callback that reads in the key and applies the setting.

For example:
   /* At application startup */
   gconf_client_notify_add(client, key, update_application_to_reflect_setting, data);
   set_initial_app_state(client);

   /* From preferences dialog */
   gconf_client_set(client, key, value);

However, you should never do the following:
  gconf_client_set(client, key, value);
  application_update_to_reflect_setting();
That will break if the user changes a GConf key from out outside of your app.

 
There are two important functions for notifications. The first is:
void gconf_client_add_dir (GConfClient *client,
const gchar *dir,
GConfClientPreloadType preload,
GError **err);

*dir should be the directory to watch (e.g. "/apps/gconf-test").
GConf can preload the values before you try to read them in order to save time. preload should be either GCONF_CLIENT_PRELOAD_NONE, GCONF_CLIENT_PRELOAD_ONELEVEL, or GCONF_CLIENT_PRELOAD_RECURSIVE.
 
Once we have added a directory, we can tell GConf which specific keys we want to monitor with:
guint gconf_client_notify_add (GConfClient *client,
const gchar *namespace_section,
GConfClientNotifyFunc func,
gpointer user_data,
GFreeFunc destroy_notify,
GError **err);
 
*client is a GConfClient.
*namespace_section is a directory or key to listen to for changes.
*func is the function to call when changes occur.
*user_data is the data to pass to func.
*destroy_notify is the function to call on user_data when the notify is removed or the GConfClient is destroyed, or NULL for none.
*err is the return location for an allocated GError, or NULL to ignore errors.

The function return a connection id that can be used to remove the connection.
 
Finally, lets look at the GConfClientNotifyFunc function signature:
void (*GConfClientNotifyFunc) (GConfClient *client,
guint cnxn_id,
GConfEntry *entry,
gpointer user_data);
 
*client is the GConfClient notifying us.
*cnxn_id is connection ID from gconf_client_notify_add().
*entry is a GConfEntry.
*user_data is the user data that we specified with gconf_client_notify_add().
 
This can get pretty lengthy when you have a few keys to read on startup and then monitor for changes. If you're going to use GConf for more than one or two prefereces, it's recommended that you use a wrapper for GConf to reduce your app's boilerplate code. libgconf-bridge is the most common library that is used to do so. (Note that libgconf-bridge may not be installed on all distributions.)
 
Lets take a look at an example with libgconf-bridge. As you'll notice, by using the gconf_bridge_bind_property function, we can make the code a lot more compact:
 
#include <gconf/gconf-client.h>
#include <gtk/gtk.h>
#include <libgconf-bridge/gconf-bridge.h>
 
int main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *scale;
  gtk_init (&argc, &argv);
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
  gtk_window_set_default_size (GTK_WINDOW (window), 100, 30);
  scale = gtk_hscale_new_with_range (0, 1, 0.1);
  gtk_container_add (GTK_CONTAINER (window), scale);
  gconf_bridge_bind_property (gconf_bridge_get (),
    "/apps/gconf-test/scale",
    G_OBJECT (gtk_range_get_adjustment (GTK_RANGE (scale))), "value");
  gtk_widget_show_all (window);
  gtk_main ();
  return 0;
}
Compile the example (you need to add libgconf-bridge to pkq-config) and watch the value in gconf-editor as you move the slider around. As you'll notice, the gconf_bridge_bind_property function "binds" a GConf key to a GObject property and automatically updates the key when the property changes.

The last topic that we'll cover is schemas. Schemas are xml files that are installed for all users and contain the default key:value properties for an app. When the app is run for the first time, gconf will set the default values from the schema. Here's an example schema file:

<?xml version="1.0" ?>
<gconfschemafile>
  <schemalist>
    <schema>
      <key>/schemas/apps/avant-window-navigator/applets/awn-terminal/opacity</key>
      <applyto>/apps/avant-window-navigator/applets/awn-terminal/opacity</applyto>
      <owner>avant-window-navigator</owner>
      <type>float</type>
      < default>0.5</default>
      <locale name="C">
        <short>opacity</short>
        <long>The applet 's opacity level.</long>
      </locale>
    </schema>
    <schema>
      <key>/schemas/apps/avant-window-navigator/applets/awn-terminal/bg_img</key>
      <applyto>/apps/avant-window-navigator/applets/awn-terminal/bg_img</applyto>
      <owner>avant-window-navigator</owner>
      <type>string</type>
      <default>""</default>
      <locale name= "C">
        <short>Background</short>
        <long>The applet's background image.</long>
      </locale>
    </schema>
  </schemalist>
</gconfschemafile>

All of the keys and their values are contained inside <gconfschemafile> and <schemalist>. Each individual key is defined by <schema>. Under <schema>, <key> is the location of the actual schema and <applyto> is the location of the key that the schema will set. <owner> is the app's name, <type> is the key's type, and <default> is the key's default value. <locale> is used to provide a <short> and <long> description of the key's purpose.

To install a schema with autotools, you need to add something like the following to your Makefile.am:
schemasdir = @GCONF_SCHEMA_FILE_DIR@
schemas_FILE = awn-terminal.schemas
schemas_in_files = $(schemas_FILE).in
schemas_DATA = $(schemas_in_files:.schemas.in=.schemas)
 
@INTLTOOL_SCHEMAS_RULE@
 
if GCONF_SCHEMAS_INSTALL
install-data-local: $(schemas_DATA)
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(schemas_DATA)
endif
 
That's really all that there is to it. Now go out and add some preferences to your app!


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]