[Development] Settings API for QML

Shawn Rutledge shawn.rutledge at digia.com
Tue Dec 11 14:55:03 CET 2012

On Mon, Dec 10, 2012 at 07:51:32PM -0800, Alan Alpert wrote:
> There was a discussion a while ago about a better settings API for
> QML, http://permalink.gmane.org/gmane.comp.lib.qt.qml/3162 was the
> best link I could find, but no progress has been made since. The main
> concensus I got from that thread was just that no one likes QSettings
> (ancient, file-based) and no one uses local storage (just not simple
> enough). For just simple, persistent settings there was a need for a
> new API. And there still is, since I don't think we agreed on an API
> or location for it.

Hmm well JsonDB has its issues too.  The nice thing about .ini files
is they are easy to edit by hand; and the (only?) nice thing about the 
Windows registry is that it's a system-wide standard for which a fall-back
editor is always available (regedit instead of vi).  So the fact that
QSettings can transparently support both has always been pretty cool
in my book.  And I'm not sure what you mean by "nobody uses local storage";
did you mean that we should try to abstract cloud-based settings too?

API-wise the main thing wrong with QSettings is just that it needs to
remember the branch of the tree that you are currently looking at;
it's a stateful rather than a RESTful API.  But even that mainly
bites you when you try to reuse the QSettings instances.  A better
design would have been to separate the stateful part into an Iterator
class which operates on the stateless Settings class.  But if the
typical usage in QML were something like

Checkbox {
	text: "Run bug-free"
	Setting {
		id: workProperly
		path: "general.workProperly"
		defaultValue: true
	checked: workProperly.boolValue

that is, with Setting objects distributed all over the app, then maybe 
it's actually correct for each instance to remember its own path in the 
overall settings tree, if instances are cheap and as long as changing
settings in many objects at once doesn't result in conflict when they
all try to write.  (They could have an implicitly shared part which does
the writing.)  But this approach doesn't make it easy to organize the 
whole settings tree.  Maybe most applications don't care about that 
though.  Each setting tends to affect one or just a few places in the 
code.  And the setting object's path could specify its position in the
overall hierarchy, like an xpath in XML.

Alternatively the typical approach could be to write a qml file which
just specifies the whole settings tree, and then reference individual 
settings by ID all over the app.  I guess it just depends whether you 
value having an organized tree that much, or would rather just be able
to create new settings ad-hoc without even thinking about it very much.

As for using the settings as a publish/subscribe mechanism, in the past
I've heard some derision about the cases when the registry has been used 
that way; if memory serves, there were aspects of the Windows Bluetooth API 
which could only be accessed via registry keys.  So attempting to use 
JsonDB that way reminded me of that; it's primarily a storage mechanism, 
so trying to use it as an RPC mechanism feels a bit dirty to me, even though
it can be made to work.  (Maybe there's no reason for this bias actually?
The distate is a gut feeling for me, but maybe unifying the two is just 
a matter of good implementation.)  There are distributed processing 
systems which use something like mailboxes to distribute work units, but 
they probably tend to end up more complicated than the idea sounds at first.  
And if you want to support syncing settings across multiple machines,
that could get pretty complicated too.  I doubt that we could get any
of these things right the first time.

Nevertheless QSettings as we know it could maybe be upgraded to use the 
inotify equivalents on all platforms to notice when the settings are changed 
externally, and emit signals.

So in short, maybe we could do better by trying to reinvent settings,
but we could also do a lot worse. And  the existing formats for storing 
settings have such long history that I don't think it's wise to replace them 
unconditionally with something new and incompatible.

> PersistentSettings
> {
>     property bool loadOnStartup: true
>     property bool saveOnExit: true
>     function load()
>     function save()
> }
> When save is called (or the item is destroyed and saveOnExit is true)
> all properties other than the three it started with (the two bools and
> objectName) are saved to disk. When load is called (or item is created
> and loadOnStartup is true) all properties previously saved to disk
> will have those properties set to the values saved on disk. Typical
> usage would not call load/save or set either bool, the settings are
> just 'persistent'. E.g in use it would just look like
> PersistentSettings
> {
>     property string lastUser: "Moi"
>     property bool useMinimalUI: true
>     property int lastDifficultyLevel: 3
> }
> The real question is whether it over-simplifies the settings usecase,

The API looks like a simple starting point for a simple use case, as it
should be.

> even for the convenience API. Note that there's no
> application/organization name or versioning. The name used internally

Well the organizationName and applicationName are typically set on the 
QCoreApplication, so that it affects other things besides settings, right?  
So we probably need a way to do that in QML too.  But it also affects
packaging, so maybe it rather needs to be set in the .pro file (or 
qbs replacement for that).

> would be based on the location of the QML file containing the element,
> so the database would be recreated every time you move the file
> containing Settings{}. 

Hmm, why?  Almost every application has a fixed name (you can't even 
find a place to store the code on your own hard drive, or on a git 
hosting service, until you make up a good unique name), so I think 
we should try to have just one place to specify it (such as the .pro) 
and have it trickle down to all the places where it's needed, 
including settings.

> On top of that, properties are likely going to
> be converted to strings for storage (both inefficient and ruling out
> complete var/variant support). Would this still be worthwhile just for
> convenience when you have to set a couple of boolean user preferences?

If we want to use settings as an RPC or publish/subscribe or mailbox
mechanism then maybe performance is important, in which case we can 
surely think of a way to store objects in their native binary form while
still keeping random access.  But the tradeoff is not being able to edit
settings externally using just a text editor; it becomes necessary to have
a special tool like regedit or gconf.

Another idea I would like to explore some day is making better use of
filesystem metadata.  In modern Linux filesystems, arbitrary name/value 
pairs can be added to any file; this resembles the old MacOS resource fork,
although it's not easy to store icons that way (the data limit is too small).
So maybe the settings API could be extended to support that.  On some future
Linux variant the application settings could be stored as metadata on the application
itself; then the application becomes more portable.  It's not very multi-user
friendly, but most computers aren't multi-user nowadays anyway.  If I drag and
drop my favorite applications onto another system, my usual settings would 
go along for the ride.  But that idea is applicable to other use cases, not
just settings, so maybe we should rather call this a PersistentDictionary 
or something.  (Yeah yeah, here come the naysayers already, but you can't stop
me dreaming)

More information about the Development mailing list