Development/Tutorials/KConfig: Difference between revisions

From KDE TechBase
mNo edit summary
(Fix redirect link)
 
(18 intermediate revisions by 11 users not shown)
Line 1: Line 1:
{{Template:I18n/Language Navigation Bar|Development/Tutorials/KConfig}}
This tutorial was updated and moved to https://develop.kde.org/docs/features/configuration/introduction/  
{{TutorialBrowser|


series=KConfig|
[[Category:MovedDevelop]]
 
name=Introduction to KConfig|
 
next=[[../Using_KConfig_XT|Using KConfig XT]]|
 
reading=None
 
}}
 
== Abstract ==
 
This tutorial looks at the KDE configuration data system, starting with an overview of the design fundamentals from an application developer's point of view. It then looks at the classes relevant to application development one by one before moving on to kiosk (user and group profiles) integration.
 
== Design Essentials ==
 
KConfig is designed to abstract away the concept of actual storage and retrieval of configuration settings behind an API that allows easy fetching and setting of information. Where and in what format the data is stored is not relevant to an application using KConfig. This keeps all KDE applications consistent in their handling of configurations while alleviating each and every application author to build such a system on their own from scratch, which can be a highly error prone exercise.
 
A KConfig object represents a single configuration object. Each configuration object is referenced by its unique name and may be actually read from multiple local or remote files or services. Each application has a default configuration object associated with it and there is also a global configuration object.
 
These configuration objects are broken down into a two level hierarchy: groups and keys. A configuration object can have any number of groups and each group can have one or more keys with associated values.
 
Values stored may be of any number of data types. They are stored and retreived as the objects themselves. For example, a {{qt|QObject}} object is passed to a config object directly when storing a color value and when retreived a {{qt|QObject}} object is returned. Applications themselves therefore generally do not have to perform serialization and deserialization of objects themselves.
 
== The KConfig Class ==
 
The {{class|KConfig}} object is used to access a given configuration object. There are a number of ways to create a config object:
 
<code cppqt n>
// a plain old read/write config object
KConfig config("myapprc");
 
// a specific file in the filesystem
// currently must be an INI style file
KConfig fullPath("/etc/kderc");
 
// not merged with global values
KConfig globalFree( "localsrc", KConfig::NoGlobals );
 
// not merged with globals or the $KDEDIRS hierarchy
KConfig simpleConfig( "simplerc", KConfig::SimpleConfig );
 
// config specific to a component, in this case a plugin
KConfig pluginConfig( componentData(), "pluginrc" );
 
// outside the standard config resource
KConfig dataResource( "data", "myapp/somefile" );
</code>
 
The KConfig object create on line 2 is a regular config object. We can read values from it, write new entries and ask for various properties of the object.  This object will be loaded from the config resource as determined by {{class|KStandardDirs}}, meaning that every instance of the <tt>myapprc</tt> object in each of the directories in the config resource hierarchy will be merged to create the values seen in this object. This is how system wide and per-user/group profiles are generated and supported and it all happens transparently to the application itself.
 
{{tip|For more information on how the merging works, see the [[KDE System Administration/KDE_Filesystem_Hierarchy|KDE Filesystem Hierarchy]] article.}}
 
On line 6 we open a specific local file, this case {{path|/etc/kderc}}. This performs no merging of values and expects an INI style file.
 
Line 9 sees the creation of a configuration object that is not merged with the global <tt>kdeglobals</tt> configuration object, while the configuration file on line 12 is additionally not merged with any files in the <tt>$KDEDIRS</tt> hierarchy. This can noticeably improve performance in the case where one is simply reading values out of a simple configuration for which global values are not meaningful.
 
Line 15 creates a configuration object using the {{class|KComponentData}} object that belongs to a plugin or other component. The plugin may have a different set of directories defined in its {{class|KStandardDirs}} and this is a way of ensuring that {{class|KConfig}} respects this.
 
Finally on line 18 we see the creation of a configuration object that does not exist in the <tt>config</tt> resource but rather in the application <tt>data</tt> resource. You may use any resource that {{class|KStandardDirs}} is aware of, including ones that are added at runtime.
 
=== Special Configuration Objects ===
 
Each application has its own configuration object that uses the name provided to {{class|KAboutData}} appended with "rc" as its name. So an app named "myapp" would have the default configuration object of "myapprc". This configuration file can be retrieved in this way:
 
<code cppqt n>
#include <KComponentData>
#include <KConfig>
#include <KGlobal>
 
MyClass::MyClass()
{
    // note that this is actually a KSharedConfig
    // more on that class in a bit!
    KConfig* config = KGlobal::config();
}
</code>
 
The default configuration object for the application is accessed when no name is specified when creating a {{class|KConfig}} object. So we could also do this instead:
 
<code cppqt n>
#include <KConfig>
 
MyClass::MyClass()
{
    KConfig config;
}
</code>
 
Additionally, each component may have its own configuration object. This is generally accessed via the plugin's <tt>componentData()</tt> rather than <tt>{{class|KGlobal}}::mainComponent()</tt>.
 
Finally there is a global configuration object, <tt>kdeglobals</tt>, that is shared by every application. It holds such information as the default application shortcuts for various actions. It is "blended" into the configuration object if the <tt>KConfig::IncludeGlobals</tt> flag is passed to the {{class|KConfig}} constructor, which is the default.
 
=== Commonly Useful Methods ===
 
To save the current state of the configuration object we call the <tt>sync()</tt> method. This method is also called when the object is destroyed. If no changes have been made or the resource reports itself as non-writable (such as in the case of the user not having write permissions to the file) then no disk activity occurs. <tt>sync()</tt> merges changes performed concurrently by other processes - local changes have priority, though.
 
If we want to make sure that we have the latest values from disk we can call <tt>reparseConfiguration()</tt> which calls <tt>sync()</tt> and then reloads the data from disk.
 
If we need to prevent the config object from saving already made modifications to disk we need to call <tt>markAsClean()</tt>. A particular use case for this is rolling back the configuration to the on-disk state by calling <tt>markAsClean()</tt> followed by <tt>reparseConfiguration()</tt>.
 
Listing all groups in a configuration object is as simple as calling <tt>groupList()</tt> as in this code snippet:
 
<code cppqt n>
KConfig* config = KGlobal::mainComponent().config();
 
foreach ( const QString& group, config.groupList() ) {
    kDebug() << "next group:" << group;
}
</code>
 
== KSharedConfig ==
 
The {{class|KSharedConfig}} class is a reference counted version of {{class|KConfig}}. It thus provides a way to reference the same configuration object from multiple places in your application without the extra overhead of separate objects or concerns about syncronizing writes to disk even if the configuration object is updated from multiple code paths.
 
Accessing a {{class|KSharedConfig}} object is as easy as this:
 
<code cppqt n>
KSharedConfigPtr config = KSharedConfig::openConfig();
</code>
 
<tt>openConfig()</tt> take the same parameters as {{class|KConfig}}'s constructors do, allowing one to define which configuration file to open, flags to control merging and non-<tt>config</tt> resources.
 
{{class|KSharedConfig}} is generally recommended over using {{class|KConfig}} itself, and the object returned from {{class|KComponentData}} is indeed a {{class|KSharedConfig}} object.
 
== KConfigGroup ==
 
Now that we have a configuration object, the next step is to actually use it. The first thing we must do is to define which group of key/value pairs we wish to access in the object. We do this by creating a KConfigGroup object:
 
<code cppqt n>
KConfig config;
KConfigGroup generalGroup( &config, "General" );
KConfigGroup colorsGroup = config.group( "Colors" ); // ... or a bit differently ...
</code>
 
You can pass {{class|KConfig}} or {{class|KSharedConfig}} objects to {{class|KConfigGroup}}.
 
Config groups can be nested as well:
 
<code cppqt n>
KConfigGroup subGroup1( &generalGroup, "LessGeneral" );
KConfigGroup subGroup2 = colorsGroup.group( "Dialogs" );
</code>
 
== Reading Entries ==
 
With a {{class|KConfigGroup}} object in hand reading entries is now quite straight forward:
 
<code cppqt n>
QString accountName = generalGroup.readEntry( "Account",
                                              QString() );
QColor color = colorsGroup.readEntry( "background",
                                      Qt::white );
QStringList list = generalGroup.readEntry( "List",
                                          QStringList() );
QString path = generalGroup.readPathEntry( "SaveTo",
                                          defaultPath );
</code>
 
As can be seen from the above, you can mix reads from different {{class|KConfigGroup}} objects created on the same {{class|KConfig}} object. The read methods take the key, which is case sensitive, as the first argument and the default value as the second argument. This argument controls what kind of data, e.g. a color in line 3 above, is to be expected as well as the type of object returned. The returned object is wrapped in a {{qt|QVariant}} to make this magic happen.
 
There are a couple of special read methods, including <tt>readPathEntry</tt> which returns a file system path. It is vital that one uses <tt>readPathEntry</tt> if it is a path as this enables such features as roaming profiles to work properly.
 
If no such key currently exists in the configuration object, the default value is returned instead. If there is a localized (e.g. translated into another language) entry for the key that matches the current locale, that is returned.
 
== Writing Entries ==
 
Setting new values is similarly straightforward:
 
<code cppqt n>
generalGroup.writeEntry( "Account", accountName );
generalGroup.writePathEntry( "SaveTo", savePath );
colorGroup.writeEntry( "background", color );
generalGroup.config()->sync();
</code>
 
Note the use of <tt>writePathEntry</tt> and how the type of object we use, such as {{qt|QColor}} on line 3, dictates how the data is serialized. Additionally, once we are done writing entries, <tt>sync()</tt> must be called on the config object for it to be saved to disk. We can also simply wait for the object to be destroyed, which triggers an automatic <tt>sync()</tt> if necessary.
 
== KDesktopFile: A Special Case ==
 
When is a configuration file not a configuration file? When it is a [http://freedesktop.org/wiki/Standards_2fdesktop_2dentry_2dspec desktop file]. These files, which are essentially configuration files at their heart, are used  to describe entries for application menus, mimetypes, plugins and various services.
 
When accessing a .desktop file, one should instead use the {{class|KDesktopFile}} class which, while a {{class|KConfig}} class offering all the capabilities described above, offers a set of methods designed to make accessing standard attributes of these files consistent and reliable.
 
== Kiosk: Lockdown and User/Group Profiles ==
 
KConfig provides a powerful set of lockdown and configuration definition capabilities, collectively known as "Kiosk", that many system administrators and system integrators rely on. While most of this framework is provided transparently to the application, there is occassion when an application will want to check on the read/write status of a configuration object.
 
Entries in configuration objects that are locked down using the kiosk facilities are said to be ''immutable''. An application can check for immutability of entire configuration objects, groups or keys as shown in this example:
 
<code cppqt n>
KConfig* config = KGlobal::config();
 
if ( config->isImmutable() ) {
    kDebug() << "configuration object is immutable";
}
 
KConfigGroup group(config, "General");
if ( group.isImmutable() ) {
    kDebug() << "group General is immutable";
}
 
if ( group.entryIsImmutable("URL") ) {
    kDebug() << "URL entry in group General is immutable";
}
</code>
 
This can be useful in particular situations where an action should be taken when an item is immutable. For instance, the KDE panels will not offer configuration options to the user or allow them to otherwise change the order of applets and icons when the panel's configuration object is marked as immutable.
 
== KConfig XT ==
 
There is a way to make certain use cases of KConfig easier, faster and more reliable: KConfig XT. In particular, for main application or plugin configuration objects and when syncing configuration dialogs and other interfaces with these values, KConfig XT can help immensely. It also  simultaneously documents the configuration options available, which makes every sys admin and system integrator that uses KDE that much more happy.
 
[[../Using_KConfig_XT|The next tutorial in the KConfig series covers what KConfig XT is and how to use it.]]

Latest revision as of 20:09, 16 March 2023