Settings module (KCM) development

This tutorial will help you create a Plasma configuration module.

Introduction

Settings in Plasma are provided by Configuration Modules (KCM). These can be loaded by multiple wrapper applications such as systemsettings5 on the desktop, plasma-settings on mobile or kcmshell5 for standalone config windows.

You can query the available KCMs using kcmshell5 --list. To load an individual module in a standalone window pass its name to kcmshell5, e.g. kcmshell5 kcm_kaccounts. To open it in plasma-settings use the -m parameter, e.g. plasma-settings -m kcm_kaccounts.

Basic KCM

KCMs consist of a KPackage holding the QML UI and a C++ library holding the logic. Some legacy KCMs are based on QtWidgets, however this is not recommended for new KCMs and it’s not possible to load these in plasma-settings. In Plasma, new KCMs should be built using QML and Kirigami.

As an example, we are going to create a time settings module that allows us to configure the time in our system. The basic structure of this hypothetical time settings module is the following:

├── CMakeLists.txt
├── timesettings.{cpp,h}
└── package
    ├── metadata.desktop
    └── contents/ui
        └── main.qml

CMakeLists.txt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# SPDX-FileCopyrightText: Year Author <email@company.com>
#
# SPDX-License-Identifier: BSD-2-Clause

cmake_minimum_required(VERSION 3.0)

project(timekcm)

set(QT_MIN_VERSION "5.14.0")
set(KF5_MIN_VERSION "5.73.0")

find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)

include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)

find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
    Quick
    Svg
)

find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
    I18n
    KCMUtils
    Declarative
    Config
)

set(timesettings_SRCS
    timesettings.cpp
)

add_library(kcm_time MODULE ${timesettings_SRCS})

target_link_libraries(kcm_time
    Qt5::Core
    KF5::CoreAddons
    KF5::I18n
    KF5::QuickAddons
)

kcoreaddons_desktop_to_json(kcm_time "package/metadata.desktop")

install(TARGETS kcm_time DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms)
install(FILES package/metadata.desktop RENAME kcm_time.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) # Install the desktop file


kpackage_install_package(package kcm_time kcms) # Install our QML kpackage.

This CMake file contains a few packages of note: KCMUtils provides various classes that allow us to work with KCModules, and Config includes the KConfig classes. You are likely to have seen most of the other packages elsewhere in this documentation; if not, you can read this page which goes through a similar CMakeLists file line by line.

What’s different here is that we are using C++ code as a plugin for our QML code. This is why we don’t have a main.cpp: we only need the class that will provide the backend functionality for our KCM.

For our KCM to work properly we must install it in our system, so at the end of our CMakeLists.txt we instruct CMake to install our KCM as a KPackage (similarly to a plasmoid, for example).

timesettings.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * SPDX-FileCopyrightText: Year Author <author@domanin.com>
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#pragma once

#include <KQuickAddons/ManagedConfigModule>

class TimeSettings : public KQuickAddons::ManagedConfigModule
{
    Q_OBJECT
public:
    TimeSettings(QObject *parent, const QVariantList &args);
    virtual ~TimeSettings() override = default;
};

Here we are defining the class we will be using for our KCM. KQuickAddons::ConfigModule serves as the base class for all QML-based KCMs. The KQuickAddons::ManagedConfigModule inherits ConfigModule and adds the KConfigXT integration. You can read the linked API documentation to get a full description, and the previous page in this section goes into more detail about how KConfigXT works.

timesettings.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * SPDX-FileCopyrightText: Year Author <author@domain.com>
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "timesettings.h"

#include <KPluginFactory>
#include <KLocalizedString>
#include <KAboutData>

K_PLUGIN_CLASS_WITH_JSON(TimeSettings, "metadata.json")

TimeSettings::TimeSettings(QObject *parent, const QVariantList &args)
    : KQuickAddons::ManagedConfigModule(parent, args)
{
    KAboutData *aboutData = new KAboutData(QStringLiteral("kcm_time"),
        i18nc("@title", "Time"),
        QStringLiteral("0.1"),
        QStringLiteral(""),
        KAboutLicense::LicenseKey::GPL_V2,
        i18nc("@info:credit", "Copyright Year Author"));

    aboutData->addAuthor(i18nc("@info:credit", "Author"),
                        i18nc("@info:credit", "Author"),
                        QStringLiteral("author@domain.com"));

    setAboutData(aboutData);
    setButtons(Help | Apply | Default);
}

#include "timesettings.moc"

Here is where we would put any backend code that changes things behind the scenes. We can expose functions in here to our QML so that elements of our UI can be used to trigger backend functionality. This C++ code is then installed in our system on compilation in a KCM plugin directory, where upon execution the KCM can access and use it.

In this C++ file we define the constructor for the class in the previous file. We include some basic metadata about this KCM and we provide the buttons that we will want included in our window.

package/metadata.desktop

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
[Desktop Entry]
Name=Time
Name[ca]=Data i hora
Name[cs]=Čas
Name[de]=Zeit
Name[es]=Hora
Name[fi]=Aika
Name[it]=Ora
Name[nl]=Tijd
Name[pl]=Czas
Name[pt]=Hora
Name[pt_BR]=Hora
Name[sk]=Čas
Name[sl]=Čas
Name[sv]=Tid
Name[uk]=Час
Name[x-test]=xxTimexx
Name[zh_CN]=时间
Comment=Configure Time
Comment[ca]=Configura la data i l'hora
Comment[cs]=Nastavit čas
Comment[de]=Zeit einrichten
Comment[es]=Configurar la hora
Comment[fi]=Aika-asetukset
Comment[it]=Configura ora
Comment[nl]=Tijd configureren
Comment[pl]=Ustawienia czasu
Comment[pt]=Configurar a Hora
Comment[pt_BR]=Configurar hora
Comment[sk]=Nastaviť čas
Comment[sl]=Nastavi čas
Comment[sv]=Anpassa tid
Comment[uk]=Налаштувати час
Comment[x-test]=xxConfigure Timexx
Comment[zh_CN]=配置时间
Encoding=UTF-8
Type=Service
Icon=preferences-system-time
X-KDE-Library=kcm_time
X-KDE-ServiceTypes=KCModule
X-KDE-FormFactors=desktop,handset,tablet,mediacenter
X-Plasma-MainScript=ui/main.qml
X-KDE-System-Settings-Parent-Category=personalization
X-KDE-Keywords=Time,Date,Clock
X-KDE-Keywords[ca]=hora,data,rellotge
X-KDE-Keywords[cs]=Čas,Datum,Hodiny
X-KDE-Keywords[de]=Time,Date,Clock,Zeit,Datum,Uhr
X-KDE-Keywords[es]=Hora,Fecha,Reloj
X-KDE-Keywords[fi]=Aika,Kellonaika,Päiväys,Päivämäärä,Kello
X-KDE-Keywords[it]=Ora,data,orologio
X-KDE-Keywords[nl]=Tijd,datum,klok
X-KDE-Keywords[pl]=Czas,Data,Zegar
X-KDE-Keywords[pt]=Hora,Data,Relógio
X-KDE-Keywords[pt_BR]=Hora,Data,Relógio
X-KDE-Keywords[sk]=Čas,dátum,hodiny
X-KDE-Keywords[sl]=Čas,Datum,Ura
X-KDE-Keywords[sv]=Tid,Datum,Klocka
X-KDE-Keywords[uk]=час,дата,годинник
X-KDE-Keywords[x-test]=xxTimexx,xxDatexx,xxClockxx
X-KDE-Keywords[zh_CN]=Time,Date,Clock,时间,日期,时钟
X-KDE-ParentApp=kcontrol
X-KDE-PluginInfo-Name=kcm_time

This .desktop file provides further metadata about our KCM. These entries specify the following:

  • Name defines the name of the KCM which is shown in the settings app.
  • Description is a short, one sentence description of the module.
  • X-KDE-Library must match the library name defined in CMakeLists.txt.
  • X-KDE-FormFactors defines on which kinds of devices this KCM should be shown.
  • X-Plasma-MainScript points to the main QML file in the KPackage.
  • X-KDE-System-Settings-Parent-Category defines the category systemsettings5 is showing the module in.
  • X-KDE-Keywords defines Keywords used for searching modules.

This file will allow our KCM to appear in desktop launchers and KRunner, providing quick access to our KCM from outside the system settings application.

package/contents/ui/main.qml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * SPDX-FileCopyrightText: Year Author <author@domanin.com>
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

import QtQuick 2.12
import QtQuick.Controls 2.12 as Controls

import org.kde.kirigami 2.7 as Kirigami
import org.kde.kcm 1.2

SimpleKCM {
    Controls.Label {
        text: i18n("Configure Time")
    }
}

As you can see, this is a very basic KCM QML file. We have used a SimpleKCM component as the root component, and we have just included a label inside here.

More complex layouts will require using a different root component. Each has its own use:

Run it!

All we need to do now is compile and run our KCM. In this case, we are installing our KCM to ~/.local/kde/, a non-standard location, so that we don’t risk messing up anything on our local environment (if you’re feeling confident, you can omit -DCMAKE_INSTALL_PREFIX).

mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=~/.local/kde
make -j8 install
source prefix.sh
kcmshell5 kcm_time

Now that our KCM is installed, we can run it (that is, so long as we have executed source prefix.sh, which includes our non-standard ~/.local/kde/ location in our current environment).

the time kcm running