" Dont judge those who try and fail, judge those who fail to try "

Beginning KDevelop Programming Version 3.x

Chapter One :- Introducing KDevelop Chapter Two :- The KDE Application Chapter Three :- Common Widgets Chapter Four :- Containers And Views Chapter Five :- Database Programming With MySQL Chapter Six :- Input And Display Chapter Seven :- KDE Display Widgets Chapter Eight :- KDE Buttons And Input Chapter Nine :- KDE Containers And Views Chapter Ten :- Custom Widgets Chapter Eleven :- Events Chapter Twelve :- Drawing Chapter Thirteen :- Global Information and Configuration Files Chapter Fourteen :- A Simple Editor Application

Appendices

Appendix A :- Upgrading KDevelop

Downloads

PDF

Beginning KDevelop Programming ( 13.4 mb )

ZIP

Chapter One Source ( 1.2mb ) Chapter Two Source ( 3.8 mb ) Chapter Three Source ( 6.9 mb ) Chapter Four Source ( 6.4 mb ) Chapter Five Source ( 6.6 mb ) Chapter Six Source ( 3.6 mb ) Chapter Seven Source ( 2.2 mb ) Chapter Eight Source ( 2.2 mb ) Chapter Nine Source ( 1.1 mb ) Chapter Ten Source ( 5.3 mb ) Chapter Eleven Source ( 1.6 mb ) Chapter Twelve Source ( 5.2 mb ) Chapter Thirteen Source ( 3.8 mb ) Chapter Fourteen Source ( 616 kb )

Contributions

Contributors Page

Contacts

Author

[email protected]

Page Designer

[email protected]

Chapter 13 :- Global Information and Configuration Files

In the previous chapter we used a function called BarIconSet which loads the standard icons from a program based on what KDE already knows about the system and where to find things on that system. In this chapter we will look deeper into just what KDE knows about the system and how we can use it as well as looking at some files types in more detail.

To start with though if we're going to be looking in more detail about KDE specifc things that it's only right that we use a proper KDE application with which to do it.

KDE Information

The first project in this chapter is going to look at what information is available to us about the KDE environment in which we are working. So first of all we set up a Simple Designer Based KDE application as before and then completely ignore the widget.

Then main reason for this is because we want to use a splitter to seperate the two parts of the application. Another reason for this is that we want to use the KMainWindow directly, this gives us access to toolbars and status bars and the menus if we need them. Although for this project we will basically just be using the status bar.

Splitter Windows


A Splitter Window will divide two or sections of the widget from each other as you can see above. It will also maintain any sizes for widgets that are placed within it which is the main reason why we are ignoring the widget for this application and writing all the code in the generated KMainWindow file.

To add a splitter you hold down the left mouse and drag it over the widgets and then right click to get,


If you add a Splitter to the widget as shown above you get the following code in the generated ChapterThirteenInformationWidgetBase.cpp file

splitter1 = new QSplitter( this, "splitter1" );
splitter1->setGeometry( QRect( 10, 30, 430, 370 ) );
splitter1->setOrientation( QSplitter::Horizontal );

This is fine up to a point and the point is that in the original ChapterThirteenInformation.cpp file you have the line.

setCentralWidget( new ChapterThirteenInformationWidget( this ) );

What is happening here is that the Widget is set as the central widget and the splitter is a child of the widget. This means that when the widget is resized we have to mess around with layouts to get the splitter window to do its work properly as it is dependant on it's parent for it's sizing instructions. If instead we use the code.

splitter = new QSplitter( this, "splitter1" );
splitter->setGeometry( QRect( 0, 15, 580, 410 ) );
splitter->setOrientation( QSplitter::Horizontal );
setCentralWidget( splitter );

In the ChapterThirteenInformation.cpp file what we are doing is making the splitter the central widget which means the splitter will then automatically take care of any resizing issues leaving us to concentrate on making the code work.

This means that the widget will look exactly the same as you would expect the above to look,


Because a splitter window is used as the main widget the application doesn't really require a menu bar and a toolbar as all the available options are made available through the KListBox on the left hand side and the options selected are displayed through the use of a QWidgetStack on the right hand side. Essentially the application is nothing more than a displaying of the various paths and options that are readily availbe to KDE. Naturally the application is very selective and should only be taken as a starting point for system information rather than as the definitive guide.

The application essentially uses KGlobal's static functions to get it's information.

static KInstance *   instance ()
static KStandardDirs *   dirs ()
static KConfig *   config ()
static KSharedConfig *   sharedConfig ()
static KIconLoader *   iconLoader ()
static KLocale *   locale ()
static KCharsets *   charsets ()
static const QString &   staticQString (const char *str)
static const QString &   staticQString (const QString &str)
static void   registerStaticDeleter (KStaticDeleterBase *d)
static void   unregisterStaticDeleter (KStaticDeleterBase *d)
static void   deleteStaticDeleters ()
static void   setActiveInstance (KInstance *d)
static KInstance *   activeInstance ()

We mostly use the instance which gives us the current KInstance, the dirs function which gives us the standard directories the iconLoader function which gives us the information about the current icon theme and the KLocale which gives us time and date and curreny information. So the application is just a matter of setting up the gui and then displaying the information for the seperate parts. A standard piece of the setup code would be,

stackPageFour = new QWidget( globalWidgetStack, "stackPageFour" );

typesPathList = new KListBox( stackPageFour, "typesPathList" );
typesPathList->setGeometry( QRect( 10, 15, 300, 420 ) );
QHBoxLayout *pageFourLayout = new QHBoxLayout( stackPageFour );
pageFourLayout->addWidget( typesPathList );
globalWidgetStack->addWidget( stackPageFour, 3 );

This sets up the stack page and KListBox to display the data. The list box is then placed in a layout so that the layout will take care of any resizing of the application and the stack page is added to the QWidgetStack. That's it the icon theme gets a little more complicated with adding labels but there's nothing that should be hard by now.

When an item is selected in the list on the left the correct page is shown and the information is filled in from scratch every time. So a sample would be,

globalWidgetStack->raiseWidget( 5 );
iconThemesList->clear();
                
QStringList list = KIconTheme::list();
                
for( QStringList::iterator it = list.begin(); it != list.end(); it++ )
{
   iconThemesList->insertItem( *it );
}

In this section we raise the widget to display the current icon themes on the system and then use KIconTheme to get the list, for the rest we just iterate through the list adding the items to the list box.

I've also included styles in the list as when looking at it even I was thinking it was needed because the application looked half done without them, so even though we are technically within the Application domain they are included here.

When you click on Styles in the left list box you will get a list just like some of the other options on the difference here is that when you click on the style in the right list box that style will be set as the application style. Don't expect miracles though there are a number of items that affect the look of the gui and this just sets how the individual widgets items are drawn, so for most of them there will only be a slight change to the drawing of the splitter bar in the center ( or whereever you have move it ) of the application. One of the more noticable changes is if you select the SGI style, as shown below.


Tip Of The Day

When using layouts programatically rather than through the gui you may be tempted to try something like this,

QVBoxLayout *pageFiveBasicLayout = new QVBoxLayout( stackPageFive );
QHBoxLayout *pageFiveChildLayout = new QHBoxLayout( stackPageFive );

Basically what you are trying to say is that you want to use the two layouts on the current widget in this case stackPageFive. This is completely and utterly wrong. The general effect of this when you run the program is to create two layout areas, both of the current size of the parent widget in this case stackPageFive, which expands your widget to twice it's size.

The correct way is,

QVBoxLayout *pageFiveBasicLayout = new QVBoxLayout( stackPageFive );
QHBoxLayout *pageFiveChildLayout = new QHBoxLayout( pageFiveBasicLayout );

Create one layout which is the parent for any other layout's on the widget, you can have as many child layout's as you wish you can even use the pageFiveChildLayout as a parent for another layout. But if you stick to the one main layout for the widget and the rest as children of the layout you should save yourself some headaches.

Application Settings With KDE

Configuration files are usually stored as matched pair settings, by matched pair I mean something like,

MenuColour Red

The traditional way to save an applications settings in KDE was to simply add an rc to the name of the application and save the settings in that file. So A project that was called ChapterThirteenTest would save it's config file as chapterthirteentestrc. This of course isn't entirely adhered to and you will find that some newer applications use the extension .rc and even some files don't use the rc identifier at all. kdeglobals being one example. As most applications use the rc extnesion we'll take that as the default. On a KDE 3.x system these are stored in opt/kde3/share/config a selection from the kdeveloprc file reads,

MinConditional=-1
PadOperators=false
PadParentheses=false
Style=UserDefined

[Editor]
EmbeddedKTextEditor=Embedded KDE Advanced Text Editor Component

[General Options]
Read Last Project On Startup=true

[Mainwindow]
Height 768=548
Width 1024=735

So to get us going with configuration files we'll write a little program that can edit and save these files. First of all though we need to create a project in the usual way called ChapterThirteenConfiguration and add a main window to it


There is a simple reason for adding a new main window to the project when we already have on created for us in the setup and that is that by creating a new window we get all the file menu options and the associated images code generated for free. You could always, of course, generate a main window in another temporary project and simply copy the generated code across to the projects main window.

Once we have filled in the name as above we get the add to target dialog.


which we can just click through and then right click on the ChapterThirteenMainWindow.ui file in the Automake manager and choose Subclassing Wizard.


Which will then ask us to name the class,


We will then be asked to confirm our choices,


This will create the class and the header files for the main window which we can treat largely as if it was just any other widget with more functionality provided by the menu's and toolbars than we would get with a standard widget.

Now in a perfect world we would just change the references in main.cpp from using the ChapterThirteenConfiguration class to using the ChapterThirteenMainWindowWidget class, this wont work though because when the generated files for the ChapterThirteenMainWindowWidget class will derive the class from QMainWindow which doesn't have the restore function and not KMainWindow which does, so we leave main.cpp exactly as it is.

ChapterThirteenConfiguration *mainWin = 0;

if (app.isRestored())
{
   RESTORE(ChapterThirteenConfiguration);
}
else
{
   // no session.. just start up normally
   KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

   /// @todo do something with the command line args here

   mainWin = new ChapterThirteenConfiguration();
   app.setMainWidget( mainWin );
   mainWin->show();

   args->clear();
}

Now the problem is that there is stuff in the ChapterThirteenMainWindowWidget class that we want to use in the ChapterThirteenConfiguration class and there is nothing else for it but to flex the old cut and paste muscles.

Next open up the ChapterThirteenConfigurationMainWindow.h in the debug/src directory off the main project folder. This is the generated class file and so we basically want to cut and paste everything declared in the class to the ChapterThirteenConfiguration.h class. Don't forget to include the headers and class definitions.

#include <qvariant.h>
#include <qpixmap.h>
#include <qmainwindow.h>

class QVBoxLayout;
class QHBoxLayout;
class QGridLayout;
class QSpacerItem;
class QAction;
class QActionGroup;
class QToolBar;
class QPopupMenu;

Then complete the header by copying all the variables and function definitions declared in the class. We can add or remove things as required later for now just do a complete copy.

Once this is done open up the ChapterThirteenConfigurationMainWindow.cpp file, which is once again in the Debug/src folder, basically cut and paste from the header files to the constructor definition this includes the definitions for the icons that are defined as,

static const char* const img0_ChapterThirteenConfigurationMainWindow[] = { 
"22 22 7 1",
". c None",
"# c #000000",
"b c #2e2e2e",
"c c #5c5c5c",
"d c #878787",
"e c #c2c2c2",
"a c #ffffff",
"......................",
"....##########........",
"....#aaaaaaa#b#.......",
"....#aaaaaaa#cb#......",
"....#aaaaaaa#dcb#.....",
"....#aaaaaaa#edcb#....",
"....#aaaaaaa#aedcb#...",
"....#aaaaaaa#######...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....#aaaaaaaaaaaaa#...",
"....###############...",
"......................",
"......................"};

Now add the QPixmap constructors to the constructor, so it looks like,

ChapterThirteenConfiguration::ChapterThirteenConfiguration()
    : KMainWindow( 0, "ChapterThirteenConfiguration" ),
      image0( (const char **) img0_ChapterThirteenConfigurationMainWindow ),
      image1( (const char **) img1_ChapterThirteenConfigurationMainWindow ),
      image2( (const char **) img2_ChapterThirteenConfigurationMainWindow ),
      image3( (const char **) img3_ChapterThirteenConfigurationMainWindow ),
      image4( (const char **) img4_ChapterThirteenConfigurationMainWindow ),
      image5( (const char **) img5_ChapterThirteenConfigurationMainWindow ),
      image6( (const char **) img6_ChapterThirteenConfigurationMainWindow ),
      image7( (const char **) img7_ChapterThirteenConfigurationMainWindow ),
      image8( (const char **) img8_ChapterThirteenConfigurationMainWindow ),
      image9( (const char **) img9_ChapterThirteenConfigurationMainWindow )

This will just create the images that appear on the menus. Next cut and paste everything in ChapterThirteenconfigurationMainWindow.cpp file constructor to the ChapterThirteenConfiguration constructor, you should remove the lines,

if ( !name )
   setName( "Form1" );

You can remove the line,

setCentralWidget( new ChapterThirteenConfigurationWidget( this ) );

that was in the ChapterThirteenConfiguration constructor as we wont be needing it. Then cut and paste the languagechange function from the ChapterThirteenConfigurationMainWindow.cpp file into the ChapterThirteenConfiguration.cpp file remembering to change the class name,

Finally you'll need to add

#include <klocale.h>

Oh and you should probably blank out the line that reads

setCaption( tr2i18n( "Form1" ) );

in the languagechange function. Or not that one's entirely optional.

to the ChapterThirteenConfiguration.cpp file or the languagechange calls to tr2i18n wont compile. Finally copy all the empty function definitions for the menus remembering to change the class names. Hit Build and Run and you should get,


All your going to need to do now is remember that any changes you make to the gui are not going to be reflected here until you cut and paste the code from the generated files.

Now back to the application to edit config files. To start with we are going to have to set up the basic gui for the application. We do this by adding a couple of splitter windows with the code,

mainSplitter = new QSplitter( this, "mainSplitter" );
mainSplitter->setGeometry( QRect( 10, 20, 510, 470 ) );
mainSplitter->setOrientation( QSplitter::Horizontal );

mainGroupList = new KListBox( mainSplitter, "mainGroupList" );

childSplitter = new QSplitter( mainSplitter, "childSplitter" );
childSplitter->setOrientation( QSplitter::Vertical );

The standard addition of the mainSplitter is as you would expect from the last program. The new thing here is that where we would have added a text box or something last time we now add a vertical splitter which divides the right hand side of the splitter into two. As with most gui systems Qt places importance on where and when you add things so if you change the ordering of the additions to the mainSplitter above so that the childSplitter is added before the listBox then the List box will still be in the same place but the splitter would be on the left. This is the reason why layouts are so much easier to implement through code than through the gui because you can add them when you require them and more importantly you can add a layout to the widget at the start and then add the widgets to it rather than adding the layout as an after thought which is the way the gui forces you to do it and then you have to mess with the layout because layout doesn't always leave things where you put them.

Next we set up the right hand side ( as you look at it ) of our widget,

groupContentsList = new KListBox( childSplitter, "groupContentsList" );

editStack = new QWidgetStack( childSplitter, "editStack" );
editStack->setMinimumSize( QSize( 200, 200 ) );

editPageOne = new QWidget( editStack, "editPageOne" );
editStack->addWidget( editPageOne, 0 );
         
nameLabelOne = new QLabel( editPageOne, "nameLabelOne" );
nameLabelOne->setGeometry( QRect( 12, 20, 72, 20 ) );
nameLabelOne->setText( i18n( "Name" ) );

nameEditOne = new KLineEdit( editPageOne, "nameEditOne" );
nameEditOne->setGeometry( QRect( 12, 40, 138, 23 ) );
nameEditOne->setEnabled( false );

valueLabelOne = new QLabel( editPageOne, "valueLabelOne" );
valueLabelOne->setGeometry( QRect( 10, 90, 71, 20 ) );
valueLabelOne->setText( i18n( "Value" ) );

valueEditOne = new KLineEdit( editPageOne, "valueEditOne" );
valueEditOne->setGeometry( QRect( 10, 110, 138, 23 ) );
valueEditOne->setEnabled( false );

editButtonOne = new KPushButton( editPageOne, "editButtonOne" );
editButtonOne->setGeometry( QRect( 120, 160, 124, 31 ) );
editButtonOne->setText( i18n( "Edit" ) );
editButtonOne->setEnabled( false );
        
setCentralWidget( mainSplitter );

We start by adding a KListBox which will be in the top right and a widget stack to the bottom right. The original idea here was that there might be a need for multiple pages at the bottom as a cursory glance at KConfigBase will show that there are multiple read write options. These proved top be more then what is required since as we are dealling with text files we can just edit the strings in the files. So we only have a one page widget stack, with a simple display and edit option. Once this code is added the running gui should look like.


The way the resource files work is that they are divided into sections called groups, each of which deals with a certain area so say one group deals with with one menu and another with another menu and another with the popup menu then all three groups could have a variable called menuColour and because each is in it's own group then there's no confusion about which menuColour is for which menu. So the idea here is that the groups are displayed on the left and the variables contained within each group are displayed on the right when a group is selected. As long as we are not viewing the global variables which is the default, when you select a variable in the right hand list box it will become available for editing in the bottom right panel. Once a variable is edited the file will automatically be saved and then reloaded. Basically the program works on the assumption that you wouldn't have edited it if you didn't mean to. And the very fact that I'm explaining that shows just how crappy using copmputers is getting these days.

Using KConfig

The KConfig class looks extremely complicated, especially if you start looking at the base class KConfigBase, with all it's different options for reading and writing just about every type imaginable. In fact if your just saving strings to a configuration file, using KConfig is really quite easy. In this project we use two versions of KConfig, the first being the default use of the global configuration options and the second being a local KConfig class object that is used to read and edit files that you either create or open. So the code itself contains lines like,

if( globalContents() == true )
{
    map = KGlobal::config()->entryMap( text );
}
else
{
    if( localConfig == 0l )
    {
        qWarning( "local config == 0, returning" );
        return;
   }
                
   map = localConfig->entryMap( text );
}

which checks to see if we are using the global configuration or a local file. It then uses the entryMap function to return the contents.

QMaps

QMap is a Qt template class that holds a key value pair which in the context of these configuration files is a string for the name and a string for the value so if the definition of QMap is,

template<class Key, class T>

class QMap

in qmap.h then our defintion to use it is,

QMap< QString, QString > map;

For our purposes in this project all we really need to know is that we are dealling with what is essentially a list of objects that have both a key and a value. ( where any sorting and searching etc would be done using the key string ) So our adding a map to the list box is almost identical to our use of QValueLists earlier.

for( QMap< QString, QString >::iterator it = map.begin(); it != map.end(); it++ )
{
   groupContentsList->insertItem( it.key() + "   " + it.data() );
}

with the main difference being that we declare the types in the iterator declaration rather than have a predefined definition in the header of our class.

Opening A Config File

As you can see above when dealling with the KDE global settings we just use the static KGlobal::config() call but when we use the local configuration files for a particular application we have to open the file ourselves,


which is just a case of opening a KFileDialog at the directory provided by,

QStringList list = KGlobal::dirs()->resourceDirs( "config" );
QStringList::iterator it = list.begin();
KFileDialog *dlg = new KFileDialog( *it, "*rc |Old Resource Files\n*.rc |New 	Resource Files", this, "Open Resource File", true );

Here we get the resource directories for the configuration files by using the KGlobal::dirs() call and then we use the first directory in the returned list which as you can see is the default to our local .kde/share/config folder.

When the dialog returns we pass the file name to KConfig and let KConfig take care of all that for us. So creating a new KConfig to open a local file goes something like,

localConfig = new KConfig( file, false, false );
setGlobalContents( false );
                
QStringList listLocal = localConfig->groupList();
    
for( QStringList::iterator it = listLocal.begin(); it != listLocal.end(); it++ )
{
    mainGroupList->insertItem( *it );
}

which as you can see creates the KConfig object and passes it the filename. The groups from the file are then added to the list box on the left.

Saving A Config File

The code for saving a file is a little trickier in that we have to make sure that we save the variable in the right place for example if you just saved the information

Colour red

to the configuration file it would be saved as a global variable in the file that is to say that it wouldn't be saved to any particular group. If you want to save a variable to a specific group then you have to tell the KConfig file object which group you want to save it to.

// If you don't set the group before a write KConfig
// assumes a default variable
localConfig->setGroup( currentGroup() );
localConfig->writeEntry( nameEditOne->text(), valueEditOne->text() );

// call sync straight away as this is an editor it's not unreasonable to
// assume that more than one value will be edited at a time.
localConfig->sync();
// reload the file with the change
openFile( currentFile() );

Here we set the group to currentGroup. currentGroup is merely a QString class variable that I use to keep track of the current group by setting it whenever a new group is created or when one is selected in the left list box. The information from the edit boxes in the bottom right section of the window is then written to the file under the current group heading.

sync is then called to flush the file and write our changes to the disk before the file is reloaded. ( This is required as if we don't the display will continue to show the old, now out of date information. )

Editing A Config File

To edit a config file select File/Open and you'll be shown the dialog above that defaults to .kde/share/config in your home folder. If you've been running the demo programs you'll see that certain of the test programs have saved information in a config file without you knowing about it. If we open up the chaptersevenimagepreviewsrc file we see that what has been saved is the KFileDialog settings.


If we run the program before changing anything we get,


and if we change the Automatic Preview option to false and hit edit and then run the program again we get,


Finishing Up

The rest of the code is a matter of enabling and disabling things at the correct time, adding the help files and an about box which you should be able to work out just by looking at the code in ChapterThirteenConfiguration.cpp.


Summary

In this chapter we have taken a look at the global settings available to us and taken a rather long winded way to create an application that could view edit and create KDE application settings.