KDE Development - Part 9

From LXF Wiki

Table of contents

USING KDEVELOP


KDE Development PART 9 Pushing on with his KDE development series, Jono Bacon explores how you can manage configuration files with LXFGallery...

So far in our KDE development series, we've managed to create a compelling little tool that loads some photos and provides some of the common graphical sparkle that's found in most modern tools. As you can see from our progress, our humble little LXFGallery that started life as an empty program has grown to become something that's already becoming useful.

Although LXFGallery is simple, within any software the configuration files are critical to the success of a program. The vast majority of software is complex enough that it can be used by a variety of different users for different purposes, and with this huge array of options, the user needs to be able to tweak and configure the program in a way that's simple and intuitive.

For the majority of programs, this kind of functionality requires the following two components:

A configuration backend. The backend will store the configuration settings somewhere useful. This backend will typically store the settings in a format that's unseen by the user. This may be a special text file, a configuration database or some other medium.

A configuration frontend. The frontend is the interface within your program that the user uses to configure settings. This interface can be a dialog box, a graphical widget or some other kind of simple control that the user can understand.

In this issue, we're going to push forward and fill in this important gap in LXFGallery, as well as exploring how to create a method of presenting the user with a simple-to-use configuration frontend, along with a reliable backend to store our settings. Luckily, KDE provides us with a strong toolset with which we can achieve these goals.

KDE and configuration files


Over the years, the KDE project has experienced a few different attempts at managing configuration files. These various incarnations have been changed and rewritten to reflect the new demands of how software should be configured, and the latest version is the snappily titled KConfig XT.

In its simplest form, the KDE environment stores configuration files in $KDEHOME/share/config, and as $KDEHOME typically refers to .kde in your home directory, this is ~/.kde/share/config. Inside this directory are a number of text files that contain settings in Key-Value pairs. These pairs consist of the setting on the left side of an equals sign and the value on the right side. As an example of this, here are some settings from kdesktoprc:

[Desktop Icons]
Preview=imagethumbnail
[Desktop0]
BackgroundMode=VerticalGradient
Color1=30,1 16014,
Color2=192,192,192

As you can see, there are different categories indicated by square brackets and the settings are shown below in the Key-Value pairs. These configuration files are easy to edit by hand if required, but are hooked to the frontend so that the user can adjust the settings easily.

KConfig XT provides a means for you to interact with these configuration files in a way that's consistent with the rest of KDE. When you use KConfig XT, you'll create some extremely simple configuration management files that will be converted by a special KDE compiler into code, and this code will then be used within your main application. Although the process can seem a little archaic at first, the KConfig XT system is a fairly streamlined method of dealing with configuration because only the non-default settings are ever stored in configuration files. This makes the files smaller and easier to manage.


Start your engines


The very first step in using KConfig XT is to create a special XML file that indicates what kind of settings you want in your program. To demonstrate these settings, we'll create a simple configuration dialog box that will enable you to adjust the size of the picture shown in the window. For this we'll need to have two settings ­ the width of the photo and its height.

Create a new file in the src directory and give it the name lxfgallery.kcfg. In this file, add the following code:

 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd">
 <kcfg>
 <kcfgfile name="lxfgalleryrc"/>
 <group name="general">
 <entry name="PhotoWidth" type="Int">
 <label>The width of the photo in pixels</label>
 <default>640</default>
 </entry>
 <entry name="PhotoHeight" type="Int">
 <label>The height of the photo in pixels</label>
 <default>480</default>
 </entry>
 </group>
 </kcfg>

This XML file contains key information about the settings in our program, and the settings contained here will be understood by the familiar-looking dialog box when we compile the code.

The first tag of interest is <kcfgfile name="lxfgalleryrc"/> in which we specify in the name attribute that our configuration file is called lxfgalleryrc. This is the name of the file in .kde/share/config where our settings are stored.

Next, we use the <group> tag to create a category in the dialog box called `general' and we create two entries with the <entry> tag. The first entry is PhotoWidth and is given a type Int' to indicate that this setting stores a number. We also use the <label> tag to describe what the setting means. We then use a similar set of tags for the PhotoHeight setting.

An important feature to note here is the use of the <default> tags to indicate what each default setting should be. These tags will be checked by KDE so any settings that are different to the defaults will be stored in the configuration file.

The next step is to create a file in which we'll describe how we want our configuration code to be generated. This generated code is used later to hook into the configuration engine, and this file generally stays the same for many projects. Create this file and call it settings.kcfgc with the following lines:

File=lxfgallery.kcfg
ClassName=Settings
Singleton=true
Mutators=true

The reason we called this file settings.kcfgc is that the generated class will be called Settings, as is indicated in the second line. The first line indicates our XML configuration file and the final two lines indicate if a Singleton (a special design pattern) and Mutators (functions for accessing code with a writer' or `setter') can be used in our class. We won't be using these features much at the moment, but it's worth Googling for information on them as you use KDE more.


Create the dialogue box


With our configuration files complete, the next step is to create a graphical configuration dialog box in which the user can interact with the settings and change them graphically. To create this box, we can use Qt Designer to create the controls. They will be added to the KDE general configuration dialog type, in which the multiple categories are listed down the side. Load up Qt Designer and click on File > New to create a new form. In the dialog box where you select a form type, select Widget. We're selecting a widget so we can create our controls as a special widget and then embed our interface into the general KDE configuration dialog box. This will make a lot more sense later on when we write the code to hook our interface in.

Designing the configuration box in Qt Designer is simple
Designing the configuration box in Qt Designer is simple
The completed configuration dialog
The completed configuration dialog

Click on TextLabel in the Displays category in the left panel of Qt Designer and add a new label to the form. Inside this label, add the text "Set the size of the displayed picture". Now resize the dialog a little bit and add two more labels, one with "Width" written on it, and one with "Height".

Inside the Input (KDE) category, select the KIntNumInput widget and drag two of them onto the form. With one of the boxes, enter the Name of the widget as PhotoWidth in the Properties panel and set the Value option to 640. For the second KIntNumInput widget, set the Name to PhotoHeight and set the Value option to 480. You should also set the minValue option for both KIntNumInput boxes as 0 This will ensure that your users won't be able to select any values lower than 0 (such as -1, -2 and so on) for either of the boxes.

The next step is to configure the Qt Designer layout manager so that the widgets are managed and resized when the dialog box itself is resized. First, Shift-click on the Width label and the 640 KIntNumInput box so that they're both selected. Now right-click the selected widgets and select Lay Out Horizontally from the context menu. You'll see a red line drawn around both widgets to indicate that they've been grouped.

Repeat the process for the Height label and the 480 KIntNumInput widget. You'll now have two separate groupings.

The final step is to select all of the widgets we've just grouped and then also select the main label in the dialog box. Now right-click and select Lay Out Vertically from the context menu. All of the widgets will now be lined up and you'll see that they're grouped together too.

A nice touch after doing this is to right-click the form and select `Layout in a grid'. This will then ensure that the widgets are hooked into the main form and that any size changes made to the dialog window will be reflected in the widgets.


Write the code


Our next step is to write the code that will actually create our configuration dialog box. To do this, you need to create a new function in the LXFGallery class and call it optionsPreferences(). First of all, you'll need to add the declaration to lxfgallery.h:

void optionsPreferences();

Now create the new function:

void LXFGallery::optionsPreferences()
{
    

This function will create the dialog box that will provide the user with a means to edit the settings. The first thing we should check is whether the dialog box is already open and, if it is, return control to the dialog box. We can check this by seeing if KConfigDialog::showDialog("settings") exists:

if ( KConfigDialog::showDialog("settings") ) return;
    If KConfigDialog::showDialog("settings") doesn't exist, we'll

create a dialog with that same name:

KConfigDialog *dialog = new KConfigDialog(this, "settings",
Settings::self() );

The purpose of the KConfigDialog class is to create a standardised KDE configuration dialog box that applies the same look and feel fundamentals across KDE applications. The next step is to create an instance of our General class (from our Qt Designer file):

General *mGeneral = new General( 0, "Normal Settings" );

With an object instantiated for our configuration widget, we can now add it to the KConfigDialog dialog box as a page. Use the addPage() method to add the object (mGeneral) with the title of the page "General" ( i18n("General") ):

dialog->addPage(mGeneral, i18n("General"), "wizard");

The next step is to create a connection that will tie together a change in the dialog configuration and update the settings. To do this, we'll use the settingsChanged() signal within KConfigDialog and connect it to a new slot that we'll create next, called updateSettings():

connect(dialog, SIGNAL(settingsChanged()), this,
SLOT(updateSettings()));

As with every other dialog box, we need to call show() to actually display the dialog to the user:

dialog->show();
}

One final thing to check is that you haven't forgotten to #include "general.h" at the top of the file.


Create the slot


The final step in coding our configuration is to write the slot that will actually update our configuration backend with any necessary changes that the user made in the dialog box. To do this we'll implement the updateSettings() slot that was mentioned as part of the connection in the previous function. First, add a declaration for the new slot to lxfgallery.h:

void updateSettings();

Next, create the start of the slot in lxfgallery.h:

void LXFGallery::updateSettings()
{

We now use the resize() function to take the settings of photoWidth() and photoHeight() from the class that was generated for us to resize the picture:

pictureView->pictureDisplay->resize(Settings::photoWidth(),
Settings::photoHeight());

Finally, call the update() function to update the position of the widgets in the program:

update();
}

Because we're using Settings:photoWidth and Settings::photoHeight, you need to make sure you #include "settings.h" at the top of the file.


Reshuffling the menus


With the configuration code complete, we need to make a few adjustments to our menus to reflect our additions. When we added the menus to LXFGallery a few months ago, they served their purpose as simple menus, but now we're going to rewrite them so they're a little more dynamic. This will ensure that our menus fit in with the general look and feel of KDE, use the right development framework and stay consistent.

The first step is to create a file called lxfgalleryui.rc in the src directory. This file will contain some XML that will indicate how our menu bar should be laid out. You need to add the following code to the file: <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> <kpartgui name="lxfgallery" version="1"> <MenuBar> </MenuBar> </kpartgui>

The code above makes use of the KPart XMLGUI system to indicate how menu items should be laid out in the program. It's possible to put all of your menu items in this XML file, but many of our menu options are standard KDE menu features that can be specified in code instead, and which can be merged in with our XML file later on.

To create our menu code, we've removed our createMenus() function and created a new function called setupActions(). In KDE and Qt programs, there are a number of so-called Actions that refer to interactive objects such as menus and toolbar buttons. These actions can be grouped together so that a toolbar button reflects a menu option, and there are a number of standard actions that can be used to implement commonly required menu items.

To get started, remove the createMenus() function and create a new function called setupActions(). You should first add a declaration to lxfgallery.h:

void setupActions();
   Next, create the main function in lxfgallery.cpp:
void LXFGallery::setupActions()
{

To create our first menu action, we use the KAction class to create an object called openAct:

KAction *openAct = new KAction(i18n("&Open..."), "file_open",
CTRL+Key_O , selectionBar, SLOT(addAlbum()),
actionCollection(), "file_open");

In this line of code, we're naming the action entry `Open' (with an underscore under the `O', indicated by the &). In this line of code we're also indicating the keyboard shortcut for the action as Ctrl+O, the icon name (file_open), and we also indicate the slot that will be triggered by this action (addAlbum()). Finally, we're specifying a generic name for the icon (file_open).

You'll also see actionCollection() included in the above line. This shows that our action has been added to the action collection that will be merged with our XML code later.

Next, we'll create two standard actions that are common to most applications: Quit and Preferences. To do this we use the KStdAction class to create the entries and connect them to the relevant slots. For the Quit action, we use the quit() slot and for the Preferences action we use the optionsPreferences() slot that we created earlier:

KStdAction::quit(kapp, SLOT(quit()), actionCollection());
KStdAction::preferences(this, SLOT(optionsPreferences()),
actionCollection());

The final job in creating our new menus is to say which file is our XML file with the menu boilerplate code in and then issue the createGUI() command to actually build the menus. We can do this with the following code:

setXMLFile("lxfgalleryui.rc");
createGUI();
}

With this function complete, you should now check the LXFGallery() constructor and ensure that you call setupActions() instead of createMenus(). This will call the function and set up the menus.


Compile the code


With the files created, you now need to adjust the build system so that kconfig_compiler is run on the .kcfg and .kcfgc files in order to generate the Settings class source code. This can be done automatically, simply by adding the files to the Makefile.am file inside the src directory.

You'll need to add settings.kcfgc and general.ui to the end of the lxfgallery_SOURCES line, and then comment out the shellrcdir and shellrc_DATA lines with # symbols:

#shellrcdir = $(kde_datadir)/lxfgallery
#shellrc_DATA = lxfgalleryui.rc

Finally, add the following two lines:

xdg_apps_DATA = lxfgallery.desktop
kde_kcfg_DATA = lxfgallery.kcfg

All configured up

At this point in your programming journey, you've covered many of the basic concepts, and in this issue we've focused on some of the more complex issues that you're likely to encounter. Any programming series will take a series of baby steps until it reaches the more in-depth subjects, and we've tried to keep the pace of this series fairly consistent. However, in this issue we turned up the heat a little and covered some of the more complex topics. This may mean that you're a little confused by some of the subjects we've covered, but you'll find that with a little bit of practice and some playing with the code, you'll soon be able to get the hang of how it all works.

One of the main things to remember when you're learning any kind of programming system for the first time is that you should never assume anything. Earlier in this issue we discussed how KActions provide a means for you to create collections of interactions that can be mapped with menus and toolbar buttons. To the untrained hacker, this concept of action collections should make sense with the wide variety of other widgets in KDE, but this isn't the case. The moral of this story is that although programming is a game of logic, it's not necessarily always consistent. Keep this in mind when you're programming, and we'll see you next month! LXF