The GSettings configuration storage system
| Anke (encarsia) | Also available in: Deutsch
Contents
Manage application settings with GNOME's GSettings
GSettings is the central application configuration system of the GNOME desktop. Settings are stored in binary form so you will need to use a low-level configuration tool which provides a backend for GSettings. This can be either have a graphical (dconf-editor) or commandline interface (gsettings).
In this article's example background images will be read, set and bookmarked.
Schemas
An initial configuration for an application is defined in a schema file. This XML formatted text file then will be transformed into its machine readable equivalent.
This is an example for a schema file with one property (key):
<schemalist> <schema id="org.gtk.Test" path="/org/gtk/Test/"> <key name="string-key" type="s"> <default>""</default> <summary>A string</summary> <description> Configuration key defined for a string. Default value is set to an empty string. </description> </key> </schema> </schemalist>
The nomenclature for the file is "schema.id.gschema.xml
".
The standard installation directory for schema files is /usr/share/glib-2.0/schemas
. Schema files can also be stored outside of this folder (p.e. local, for testing purposes) but these will not be shown by dconf-editor.
Now the schema files must be compiled
glib-compile-schemas /path/to/schema/files/ #default directory glib-compile-schemas /usr/share/glib-2.0/schemas/
The compiled file which is now used by GSettings is named gschemas.compiled
.
Glade
For displaying image files the GtkImage widget is required. All controls of the example app are placed in the headerbar:
"Open File" button: opens FileChooserDialog
switch: turns desktop icons on or off
"Fav" togglebutton: bookmarks file drawn in the image widget, shows whether image file is bookmarked as favourite
"Set as wallpaper" button: use file as background image
MenuButton: list of bookmarked files
Python
Load global schema
Load an existing configuration:
setting = Gio.Settings.new("full.schema.path") #load desktop background configuration setting = Gio.Settings.new("org.gnome.desktop.background")
Load local schema
If the schema file is not stored in the standard directory the location of the schemas.compiled
file must be given first:
schema_source = Gio.SettingsSchemaSource.new_from_directory(os.getcwd(), Gio.SettingsSchemaSource.get_default(), False) schema = Gio.SettingsSchemaSource.lookup(schema_source,"org.example.wallpaper-changer",False) setting = Gio.Settings.new_full(schema, None, None)
Bind widget
It is possible to directly bind GSettings properties to a widget. Property statuses then can be displayed or manipulated:
setting.bind("setting-key", widget, property, Gio.SettingsBindFlags...)
In the example app this is done with the switch widget:
self.bg_setting.bind("show-desktop-icons", self.obj("switch"), "active", Gio.SettingsBindFlags.DEFAULT)
The switch shows the current configuration status on application startup. Changes on the switch control button are applied instantly.
Get and set values
Property settings can be retrieved and defined by get_"type"
and set_"type"
. The relevant function to use is dependent on the key type, use get_string
and set_string
for strings, get_int
and set_int
for interger and so on (see PyGObject API Reference).
A property value request via get_value(key)
returnes a value of type GLib.Variant.
Therefore the set_value(key)
function also requires this datatype.
Value contents can be converted into simple datatypes:
#return string setting.get_value(key).get_string() #return anything (list, string, bool etc.) setting.get_value(key).unpack()
and vice versa:
setting.set_value(key, GLib.Variant(string_type, value)
The GNOME developer documentation provides a list of avaliable string types.
In the example app this is used to update the bookmark list:
app_setting.set_value("favourites", GLib.Variant('as',fav_list))
Listings
Schema
org.example.wallpaper-changer.gschema.xml (Source)
<?xml version="1.0" encoding="utf-8"?> <schemalist> <schema path="/org/example/wallpaper-changer/" id="org.example.wallpaper-changer"> <key name="favourites" type="as"> <default>[]</default> <summary>List of favourite wallpapers</summary> <description> Add or remove entry by pressing the 'fav' toggle button. </description> </key> </schema> </schemalist>
Glade
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkFileFilter" id="filefilter"> <mime-types> <mime-type>image/*</mime-type> </mime-types> </object> <object class="GtkImage" id="image1"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="icon_name">emblem-favorite</property> </object> <object class="GtkMenu" id="menu"> <property name="visible">True</property> <property name="can_focus">False</property> </object> <object class="GtkApplicationWindow" id="window"> <property name="can_focus">False</property> <signal name="destroy" handler="on_window_destroy" swapped="no"/> <signal name="size-allocate" handler="on_window_size_allocate" swapped="no"/> <child> <object class="GtkImage" id="image_area"> <property name="width_request">400</property> <property name="height_request">300</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="stock">gtk-missing-image</property> </object> </child> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">Wallpaper changer</property> <property name="has_subtitle">False</property> <property name="show_close_button">True</property> <child> <object class="GtkButton" id="open_button"> <property name="label" translatable="yes">Open file...</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_open_button_clicked" swapped="no"/> </object> </child> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkLabel"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label" translatable="yes">Desktop icons</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkSwitch" id="switch"> <property name="visible">True</property> <property name="can_focus">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> </object> <packing> <property name="position">2</property> </packing> </child> <child> <object class="GtkMenuButton" id="fav_menu"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="popup">menu</property> <child> <placeholder/> </child> </object> <packing> <property name="pack_type">end</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkButton" id="setwp_button"> <property name="label" translatable="yes">Set as wallpaper</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_setwp_button_clicked" swapped="no"/> </object> <packing> <property name="pack_type">end</property> <property name="position">3</property> </packing> </child> <child> <object class="GtkToggleButton" id="fav_button"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="image">image1</property> <property name="always_show_image">True</property> <signal name="toggled" handler="on_fav_button_toggled" swapped="no"/> </object> <packing> <property name="pack_type">end</property> <property name="position">3</property> </packing> </child> </object> </child> </object> <object class="GtkImage" id="preview"> <property name="width_request">200</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="margin_right">5</property> </object> <object class="GtkFileChooserDialog" id="filechooser_dialog"> <property name="width_request">800</property> <property name="height_request">600</property> <property name="can_focus">False</property> <property name="type_hint">dialog</property> <property name="transient_for">window</property> <property name="attached_to">window</property> <property name="filter">filefilter</property> <property name="preview_widget">preview</property> <property name="use_preview_label">False</property> <signal name="delete-event" handler="on_dialog_close" swapped="no"/> <signal name="file-activated" handler="on_filechooser_dialog_file_activated" swapped="no"/> <signal name="response" handler="on_filechooser_dialog_response" swapped="no"/> <signal name="update-preview" handler="on_filechooser_dialog_update_preview" swapped="no"/> <child internal-child="vbox"> <object class="GtkBox" id="fcbox"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child internal-child="action_area"> <object class="GtkButtonBox"> <property name="can_focus">False</property> <child> <placeholder/> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> <property name="position">0</property> </packing> </child> </object> </child> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">Choose image file</property> <property name="show_close_button">True</property> </object> </child> </object> </interface>
Python
#!/usr/bin/python # -*- coding: utf-8 -*- import os import sys import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio, GLib, GdkPixbuf class Handler: def on_window_destroy(self, window): window.close() def on_dialog_close(self,widget, *event): widget.hide_on_delete() return True def on_filechooser_dialog_response(self, widget, response): if response == 1: self.on_dialog_close(widget) elif response == 0: app.uri = widget.get_filename() app.draw_pixbuf(app.uri) app.handle_fav(app.uri) self.on_dialog_close(widget) def on_filechooser_dialog_file_activated(self, widget): self.on_filechooser_dialog_response(widget, 0) def on_open_button_clicked(self, widget): app.obj("filechooser_dialog").show_all() def on_setwp_button_clicked(self, widget): app.bg_setting.set_string("picture-uri", "file://{}".format(app.uri)) def on_window_size_allocate(self, widget, size): app.draw_pixbuf(app.uri) def on_filechooser_dialog_update_preview(self, widget): if widget.get_filename() != None and os.path.isfile(widget.get_filename()): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(widget.get_filename(),200, 200, True) app.obj("preview").set_from_pixbuf(pixbuf) def on_fav_button_toggled(self,widget): if widget.get_active(): #add file to fav_list if not in list if app.uri not in app.fav_list: app.fav_list.append(app.uri) else: #remove file from fav_list if in list if app.uri in app.fav_list: app.fav_list.remove(app.uri) #update GSettings entry for favourites app.app_setting.set_value("favourites", GLib.Variant("as", app.fav_list)) #update fav list in popup menu popup = app.obj("menu") #remove all items for i in popup.get_children(): popup.remove(i) #reload all items from fav_list for fav in app.fav_list: #only label menuitem with filename instead of path item = Gtk.MenuItem(os.path.split(fav)[1]) item.connect("activate", self.on_choose_fav_from_menu, fav) popup.append(item) popup.show_all() def on_choose_fav_from_menu(self, widget, filename): app.uri = filename app.draw_pixbuf(filename) app.handle_fav(filename) class ExampleApp: def __init__(self): self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0)) self.app.connect("activate", self.on_app_activate) self.app.connect("shutdown", self.on_app_shutdown) def on_app_activate(self, app): builder = Gtk.Builder() builder.add_from_file("17_gsettings.glade") builder.connect_signals(Handler()) self.obj = builder.get_object #load existing GSettings application config self.bg_setting = Gio.Settings.new("org.gnome.desktop.background") #get_value returns Gio formatted file path file = self.bg_setting.get_value("picture-uri") #convert path into string self.uri = file.get_string()[7:] #bind GSettings key to GTK+ object self.bg_setting.bind("show-desktop-icons", self.obj("switch"), "active", Gio.SettingsBindFlags.DEFAULT) #add GSettings schema from compiled XML file located in current directory (only recommended for test use, standard location: /usr/share/glib-2.0/schemas/) schema_source = Gio.SettingsSchemaSource.new_from_directory(os.getcwd(), Gio.SettingsSchemaSource.get_default(), False) schema = Gio.SettingsSchemaSource.lookup(schema_source,"org.example.wallpaper-changer", False) self.app_setting = Gio.Settings.new_full(schema, None, None) #convert value (GLib.Variant) into native list self.fav_list = self.app_setting.get_value("favourites").unpack() self.obj("window").set_application(app) self.obj("window").show_all() self.draw_pixbuf(self.uri) self.handle_fav(self.uri) def draw_pixbuf(self,file): size=self.obj("image_area").get_allocation() pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(file, size.width, size.height, True) self.obj("image_area").set_from_pixbuf(pixbuf) def handle_fav(self,uri): #set toggle button to correct state if uri in self.fav_list: self.obj("fav_button").set_active(True) else: self.obj("fav_button").set_active(False) def on_app_shutdown(self, app): self.app.quit() def run(self, argv): self.app.run(argv) app = ExampleApp() app.run(sys.argv)
Comments
Comments powered by Disqus