GSettings : utilisation en « local »
Depuis Gnome 3, le stockage de la configuration d'une application se fait par l'intermédiaire de GSettings, une API « agnostique » dont l'utilisation dans un plugin Gedit ou une extension Gnome-shell pose de petits problèmes car elle nécessite les droits administrateurs. Voici quelques pistes pour l'utiliser « en local ».
§Présentation
L'enregistrement de la configuration d'une application est un principe simple qui consiste à stocker une série de couples clé/valeur, soit le nom d'une propriété de configuration et sa valeur. L'utilisateur retrouve ensuite ces valeurs à chaque fois qu'il lance l'application.
Dans ce qui suit, nous allons essayer de doter un plugin Gedit d'une configuration ; celle-ci sera uniquement constituée d'une chaîne de caractère « foo ».
- myplugin
- __init__.py
- myplugin.plugin
§Configuration sous Gnome 2 : GConf
Sous Gnome 2 (et donc Gedit < 3), le stockage des informations de configuration se faisait avec GConf. Son utilisation était dés plus simple :
# Importer gconf
import gconf
# Chemin de stockage de la configuration du plugin Gedit
GCONF_DIR = '/apps/gedit-2/plugins/myplugin/'
# Récupérer la valeur de 'foo' => 'bar'
value = gconf.client_get_default().get_string(GCONF_DIR + 'foo')
# Assigner une nouvelle valeur à 'foo' => 'baz'
gconf.client_get_default().set_string(GCONF_DIR + 'foo', 'baz')
§Configuration sous Gnome 3 : GSettings
Avec Gnome 3, les choses se complexifient un (tout) petit peu. D'abord parce que GConf est remplacé par Dconf ; ensuite parce que les applications n'interagissent plus directement avec l'outil de stockage mais avec GSettings, une API générique qui est chargée d'aller dialoguer avec DConf.
L'avantage de cette technique est évident : en utilisant GSettings, on reste indépendant de la manière dont les données vont être stockées. Si le projet Gnome décide un jour d'abandonner DConf pour utiliser un autre système de stockage, le changement sera transparent.
Pour pouvoir utiliser GSettings, il nous faut d'abord déclarer les propriétés de configuration de notre application dans un fichier xml. Voici à quoi ressemblerait celui de notre plugin Gedit :
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema id="org.gnome.gedit.plugins.myplugin" path="/org/gnome/gedit/plugins/myplugin/">
<key type="s" name="foo">
<default>"bar"</default>
<summary>Foo value</summary>
<description>This is the Foo value</description>
</key>
</schema>
</schemalist>
org.gnome.gedit.plugins.myplugin.gschema.xml.Ces fichiers de « schémas » doivent avoir un nom qui se termine par « gschema.xml ». Ils ne sont utilisés que sous forme compilée, compilation qui s'effectue grâce au programme glib-compile-schemas. Nous pouvons ensuite créer un objet de configuration correspondant à notre plugin :
# Importer Gio
from gi.repository import Gio
# Nom du schéma
SETTINGS_SCHEMA = "org.gnome.gedit.plugins.myplugin"
# Liste des schémas disponibles
schemas = Gio.Settings.list_schemas()
# Notre schéma est dans la liste globale des schémas
if SETTINGS_SCHEMA in schemas:
# Récupérer la configuration
settings = Gio.Settings.new(SETTINGS_SCHEMA)
# Récupérer la valeur de 'foo' => 'bar'
value = settings.get_string('foo')
# Assigner une nouvelle valeur à 'foo' => 'baz'
settings.set_string('foo', 'baz')
Bien. Il n'y a là rien de vraiment compliqué ni de fondamentalement différent par rapport à ce qui se faisait sous Gnome 2. Nous sommes cependant passé un peu trop rapidement sur la notion de compilation des schémas, car c'est-là que nous allons avoir des problèmes. Pas dans la compilation elle-même mais plutôt sur le lieu où elle doit avoir lieu.
§Un problème de compilation
En effet, GSettings ne lit que les schémas compilés présents dans les répertoires système de données ($XDG_DATA_DIRS dans les spécifications des répertoires de base), soit /usr/local/share/glib-2.0/schemas/ et /usr/share/glib-2.0/schemas/ par défaut, et pas dans le répertoire local de données propre à l'utilisateur ($XDG_DATA_HOME), soit ~/.local/share/glib-2.0/schemas/.
On devine dés lors le soucis : pour utiliser un schéma de configuration GSettings dans un plugin Gedit qu'un utilisateur installe, il va falloir copier le fichier xml et le compiler dans un répertoire système avec les droits administrateurs :
su -c "cp path/to/org.gnome.gedit.plugins.myplugin.gschema.xml /usr/share/glib-2.0/schemas/"
su -c "glib-compile-schemas /usr/share/glib-2.0/schemas/"
/usr/share/glib-2.0/schemas/.Un peu rude comme manipulation pour permettre la configuration d'un plugin Gedit, alors même que celui-ci peut être installer par l'utilisateur dans son répertoire local de données (~/.local/share/gedit/plugins/). C'est une véritable régression par rapport à GConf.
§Compilation embarquée dans le plugin
Heureusement, nous pouvons nous passer de cette étape et utiliser un schéma compilé localement, au sein même du plugin. Pour ce faire, nous organisons d'abord le plugin pour y placer le fichier xml de schémas dans un dossier dédié :
- myplugin
- schemas
- org.gnome.gedit.plugins.myplugin.gschema.xml
- __init__.py
- schemas
- myplugin.plugin
Nous compilons ensuite le schéma dans ce répertoire :
glib-compile-schemas path/to/myplugin/schemas/
La commande glib-compile-schemas va créer un fichier de compilation nommé « gschemas.compiled ».
- myplugin
- schemas
- gschemas.compiled
- org.gnome.gedit.plugins.myplugin.gschema.xml
- __init__.py
- schemas
- myplugin.plugin
Nous allons maintenant utiliser cette source de schémas compilés :
# Importer Gio
from gi.repository import Gio
# Répertoire des schémas
SCHEMAS_DIR = "path/to/myplugin/schemas/"
# Nom du schéma
SETTINGS_SCHEMA = "org.gnome.gedit.plugins.myplugin"
# Liste des schémas disponibles
schemas = Gio.Settings.list_schemas()
# Notre schéma est dans la liste globale des schémas
if SETTINGS_SCHEMA in schemas:
# Récupérer la configuration
settings = Gio.Settings.new(SETTINGS_SCHEMA)
# Sinon, charger le schéma "embarqué"
else:
# Créer une nouvelle source de schémas
schema_source = Gio.SettingsSchemaSource.new_from_directory(
SCHEMAS_DIR,
Gio.SettingsSchemaSource.get_default(),
False
)
# Chercher le schéma du plugin
schema = schema_source.lookup(SETTINGS_SCHEMA, False)
# Récupérer la configuration
settings = Gio.Settings.new_full(schema, None, None)
# Récupérer la valeur de 'foo' => 'bar'
value = settings.get_string('foo')
# Assigner une nouvelle valeur à 'foo' => 'baz'
settings.set_string('foo', 'baz')
§Compilation en local
Comme expliqué plus haut, GSettings n'est pas capable de lire des schémas de configuration déployés en local, dans ~/.local/share/glib-2.0/schemas/. En fait, cela n'est pas tout à fait vrai, cela grâce à la variable d'environnement GSETTINGS_SCHEMA_DIR :
GSETTINGS_SCHEMA_DIR. This variable can be set to the name of a directory that is considered in addition to the glib-2.0/schemas subdirectories of the XDG system data dirs when looking for compiled schemas for GSettings.
Cependant, pour pouvoir utiliser GSETTINGS_SCHEMA_DIR, il faut que l'utilisateur la définisse lui-même car elle ne l'est pas par défaut - du moins sous Fedora 17, définition à effectuer dans son fichier .profile par exemple :
# GSettings local dir
GSETTINGS_SCHEMA_DIR=$HOME/.local/share/glib-2.0/schemas/
export GSETTINGS_SCHEMA_DIR
~/.profile.Il faudra ensuite copier le schéma de configuration dans ce répertoire et lancer sa compilation, avec un script d'installation du plugin par exemple :
import os
# Variable d'environnement
local_schema_dir = os.environ.get('GSETTINGS_SCHEMA_DIR')
# Si la variable d'environnement est définie:
if local_schema_dir is not None:
cmd_copy = 'cp path/to/myplugin/schemas/org.gnome.gedit.plugins.myplugin.gschema.xml ' + local_schema_dir
cmd_compil = 'glib-compile-schemas ' + local_schema_dir
os.system(cmd_copy)
os.system(cmd_compil)
else:
print 'GSETTINGS_SCHEMA_DIR is not defined'
if [[ $GSETTINGS_SCHEMA_DIR ]]; then
cp path/to/myplugin/schemas/org.gnome.gedit.plugins.myplugin.gschema.xml $GSETTINGS_SCHEMA_DIR
glib-compile-schemas $GSETTINGS_SCHEMA_DIR
else
echo "GSETTINGS_SCHEMA_DIR is not defined"
fi
Obliger l'utilisateur à définir une variable d'environnement pour avoir à disposition la configuration d'un plugin est tout de même contraignant, moins que de devoir installer des fichiers avec des droits administrateurs mais contraignant tout de même. La compilation dans le plugin reste donc la solution la plus acceptable, en attendant que GSETTINGS_SCHEMA_DIR soit définie par les systèmes d'exploitation.
§Extensions Gnome-shell
A noter enfin qu'on peut aussi utiliser une compilation embarquée de schémas dans une extension Gnome-shell, le javascript remplaçant le python comme langage de programmation. L'API de l'objet Gio est sensiblement identique dans les 2 cas :
// Importer Gio
const Gio = imports.gi.Gio;
// Répertoire des schémas
let SCHEMAS_DIR = "path/to/myextension/schemas/";
// Nom du schéma
let SETTINGS_SCHEMA = "org.gnome.shell.extensions.myextension";
// Créer une nouvelle source de schémas
let schema_source = Gio.SettingsSchemaSource.new_from_directory(
SCHEMAS_DIR,
Gio.SettingsSchemaSource.get_default(),
false
);
// Chercher le schéma de l'extension
let schema = schema_source.lookup(SETTINGS_SCHEMA, false);
// Récupérer la configuration
let settings = new Gio.Settings({ settings_schema: schema });
§Ressources et références
GIO Reference Manual. Gnome Dev Center. GSettings
GIO Reference Manual. Gnome Dev Center. Running GIO applications
BASTIAN, Waldo ; LORTIE, Ryan ; POETTERING, Lennart. XDG Base Directory Specification. freedesktop.org
GConf Reference Manual. Gnome Dev Center
CARRICK, Micah. Using GSettings with Python/PyGObject. micahcarrick.com,