KDE Frameworks – Part 1 KConfig, for Storing and Fetching Configuration Data
The KDE Community has been developing a variety of Free Software products using Qt for 25 years now. Among them, the Plasma Desktop Environment, creativity tools like Krita and Kdenlive, educational applications like GCompris, groupware suites like Kontact and countless other applications, utilities, and widgets.
Qt is famous for its rich set of high-quality, cross-platform APIs. However, it does not cover every single use case. Indeed, that would be impossible. So, to fill in the gaps, over time, KDE has created code that has been incorporated into many KDE projects. To foster reusing these battle-tested solutions outside KDE projects, we share this code in the form of modular libraries.
We call these libraries KDE Frameworks.
Currently, there are 83 KDE Frameworks available that offer a wide range of features. For example, KNotifications allows you to create popup notifications on Windows, macOS, Linux, and Android without having to write platform-specific code. Other Frameworks provide wrappers for specialized libraries or interfaces, making them easier to use by Qt programmers. The bluez-qt framework, for example, provides a Qt-style interface to the bluez D-Bus API. Some Frameworks are a collection of useful classes like KWidgetsAddons, which contains a number of useful widgets that are not part of QtWidgets.
As a Qt developer, you likely have used software built using KDE Frameworks without even knowing it. The syntax-highlighting framework that powers KDE applications like Kate and KDevelop is also used in Qt Creator.
There are many advantages to leveraging KDE Frameworks. In this series, we will examine some of them, providing practical and real-world examples that will help you learn how to incorporate KDE Frameworks into your own products.
In this first blog in the series, I’d like to introduce you to KConfig.
KConfig is one of the most used frameworks. It allows developers to store and fetch configuration data in the filesystem. Its basic functionality is similar to Qt’s own QSettings, however it provides several additional features.
Before we can use KConfig in our application, we need to add it to our build system. For CMake, this is done as follows:
find_package(KF5Config) ... target_link_libraries(myapp PRIVATE KF5::ConfigCore)
If your application uses QMake, all you need is:
QT += KConfigCore
The following code shows the basic usage of KConfig:
#include <KConfig> #include <KConfigGroup> #include <QDebug> int main() { KConfig config("myappsettings"); KConfigGroup general = config.group("General"); qDebug() << general.readEntry("someSetting", "A default value"); general.writeEntry("someSetting", "A new value"); qDebug() << general.readEntry("someSetting", "A default value"); }
First, a KConfig object is created. By default, the config is saved to a file with the specified name in QStandardPaths::GenericConfigLocation, however the exact location can be tweaked.
The config entries are organized in groups. Each KConfig object can contain a number of groups and each group holds a number of key-value pairs containing the config data.
To read a config entry, first create a KConfigGroup from the KConfig object and then use readEntry to query a specific key. readEntry takes an optional default value that is used when no data for that key is stored.
To write a setting, writeEntry is used. The data is not immediatley written to the disk. When the KConfigGroup object is destructed, all pending write operations are executed. It is possible to force writing to the disk by using the sync() method.
So far, all of this is possible to do with QSettings as well. So, what’s the benefit of using KConfig?
Both QSettings and KConfig allow for config cascading. Here, config values are read from two locations: a system-wide one and a per-user one. This allows the defining of system-wide defaults and gives users the ability to override the value for them. However, in an enterprise setup, this may be undesirable. KConfig allows system administrators to mark settings as immutable to prevent users from overriding the provided default. This does not require any code changes in the application. The application can query whether a certain key is marked as immutable to disable the relevant UI pieces.
Sometimes two processes access the same config file. Here, it’s important that a process is notified when the other process changes the config so it can react accordingly. KConfigWatcher allows notifying another process about a config change. It does this via D-Bus. So, it works only on systems where D-Bus is available (i.e. Linux).
This plain usage of KConfig (and QSettings) has a number of drawbacks. The library/compiler has no information about the structure of the configuration data. Most of the access is done using string identifiers. These are prone to typing errors and the compiler cannot verify those at build time. There is also no information about the data type of a configuration entry, e.g., whether an entry is a single string, a list of strings, or an integer. Another problem is that KConfig cannot directly be used in a QML context.
KConfig offers the KConfigXT mechanism that solves both of these problems. It is based on a XML description of the configuration data structure. At compile time this information is used to generate a C++ class that is used to access the config. The class can also have the entries exposed as properties so it can be consumed by QML directly.
The above example expressed as XML description looks like this:
<?xml version="1.0" encoding="UTF-8"?> <kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > <kcfgfile name="myappsettings" /> <group name="General"> <entry name="someSetting" type="String"> <label>A setting</label> <default>A default value</default> </entry> </group> </kcfg>
This is stored in the myappsettings.kcfg file.
The behavior of KConfigXT is controlled by a separate config file, myappsettings.kcfgc:
File=myappsettings.kcfg ClassName=MyAppSettings Mutators=true DefaultValueGetters=true GenerateProperties=true
Then the above code example becomes:
#include "myappsettings.h" #include <QDebug> int main() { MyAppSettings settings; qDebug() << settings.someSetting(); settings.setSomeSetting("A new value"); qDebug() << settings.someSetting(); }
You can also expose the settings to QML by doing:
qmlRegisterSingletonInstance<MyAppSettings>("myapp", 1, 0, "Settings", &settings);
and use them via properties:
import myapp 1.0 console.log(Settings.someSetting)
The settings code is generated by the kconfig_compiler_kf5 executable. As a developer you usually don’t interact with it directly though. Instead there is a CMake macro that takes care of the details.
kconfig_add_kcfg_files(myapp myappsettings.kcfgc)
For a full introduction to KConfigXT, please consult its documentation.
Stay tuned for more posts introducing interesting KDE Frameworks!
If you like this article and want to read similar material, consider subscribing via our RSS feed.
Subscribe to KDAB TV for similar informative short video content.
KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.
What was bothering me in the last time regarding some applications (e.g., konversation, kid3, ….), is that they mix runtime state (such as the size of the window or recently opened documents) with real application settings. This causes the configurations to change frequently and make them hard to compare, wich is an issue when synchronizing settings, e.g. in a dotfile repository.
I was wondering if there is a good example or best practice to separate these two kinds of configs and if KConfig does help the developers to do so.
KConfig offers a way to get a separate KConfig object that is suitable for that kind of data (https://api.kde.org/frameworks/kconfig/html/classKSharedConfig.html#a5dcfbe60478f169753342d37212c1b58)
However it is still up to application developers to make use of it correctly
Thx for the fast answer. I will open application specific bug reports in this case.
The sad part is to access them with python as python-kde is not maintained anymore.