Initiation à la programmation KDE

Le but de cet article est de vous montrer pas à pas comment programmer votre première application KDE. Après une initiation à la programmation Qt, nous verrons, entre autres, comment créer une fenêtre, un menu, une barre d'outils, puis comment internationaliser le programme et enfin comment gérer les sessions.

 

AVANT DE COMMENCER

Ce tutoriel part du principe que vous avez déjà un système Linux avec un compilateur C++ (gcc ou egcs par exemple), Qt (1.40 ou ultérieur) installé, ainsi que KDE (1.1 ou ultérieur, au moins les paquetages kdesupport et kdelibs).

Pour compiler une application utilisant Qt, il vous faut les fichiers d'en-tête de Qt. Ils font partie de la distribution Qt standard, mais font parfois l'objet d'un paquetage séparé (sur une RedHat, il s'agit du rpm appelé qt-devel). Pour vérifier la présence de ces fichiers d'en-tête, regardez dans $QTDIR/include, vous devez avoir de nombreux fichiers ".h"

Si vous n'avez pas installé Qt, vous pouvez le trouver sur ftp://ftp.troll.no/pub/qt/source en version source (tar.gz), ou, pour les utilisateurs RedHat, sur ftp://ftp.lip6.fr/pub/linux/distributions/redhat-contrib/libc6/i386/

Quant à KDE 1.1, rien de plus simple : si vous ne l'avez pas déjà installé, allez faire un tour sur ftp://sunsite.org.uk/Mirrors/ftp.kde.org/pub/kde/stable/1.1/distribution/, où vous trouverez KDE préparé pour toutes les distributions Linux. Pour ce tutoriel, kdesupport et kdelibs suffisent, mais si vous voulez réellement utiliser KDE, prenez au moins aussi kdebase.

Enfin, assurez-vous que $QTDIR pointe vers votre installation de Qt, et que $KDEDIR pointe vers votre installation KDE, cela sera nécessaire pour la suite.

LA LIBRAIRIE QT

Avant d'écrire notre premier programme KDE, il est important de comprendre les mécanismes de base de la librairie Qt, le 'toolkit' graphique sur lequel s'appuie KDE. Qt contient un ensemble de composants graphiques (widgets), comme des boutons, des menus, des champs d'édition, ... ainsi que de nombreuses classes pour faciliter la programmation, comme des conteneurs (tableaux, listes, dictionnaires, ...), et beaucoup d'autres choses encore. Pour en savoir plus sur Qt, voir http://www.troll.no, en particulier le tutoriel Qt sur http://www.troll.no/qt/tutorial.html est très intéressant, mais rassurez-vous ce n'est pas nécessaire pour comprendre cet article.

LES SIGNAUX ET LES SLOTS

Dans cet article, nous allons surtout utiliser les composants graphiques de Qt. La particularité de Qt par rapport à tout autre librairie de composants graphiques, est qu'elle se base sur un mécanisme appelé "signal / slot". Ce mécanisme permet d'exécuter une méthode C++ au moment où l'utilisateur effectue une action particulière : clic sur un bouton, sur élément de menu, en fait toute action sur un composant, graphique ou non.

Pour cela, l'action de l'utilisateur est "connectée" à la méthode C++ en question. Par exemple, si 'quitter' est un élément de menu, ou un bouton,

connect (quitter, SIGNAL (clicked ()), this,

SLOT (quit ()));

permet de connecter l'action "clic sur quitter" à la méthode "quit()" de la classe courante (this). La méthode connect est une méthode statique de la classe QObject ; il ne faut pas la confondre avec la fonction système du même nom (qui concerne les sockets). Dans certains cas, il faudra donc écrire QObject::connect pour lever l'ambiguïté.

UN PROGRAMME KDE MINIMAL

Nous allons enfin pouvoir écrire notre première application KDE. Pour commencer, elle affichera simplement une fenêtre vide, qui proposera un menu "Fichier" et l'élément "Quitter" dans ce menu. La classe KDE permettant de créer une fenêtre est KTMainWindow. Nous voulons créer une fenêtre avec des fonctionnalités supplémentaires, donc nous allons créer une classe, disons MyWin, qui hérite de cette classe. (J'ai l'habitude de programmer en anglais, car c'est préférable si on veut que d'autres personnes sur Internet puissent y contribuer par la suite !).

Déclarons donc MyWin de la manière suivante, disons dans myprog.h :

#ifndef myprog_h

#define myprog_h

#include <ktmainwindow.h> // inclut la définition de KTMainWindow

class MyWin : public KTMainWindow

{

Q_OBJECT

public:

MyWin(); // constructeur, défini dans myprog.cpp

~MyWin(){}; // destructeur, rien de spécial à faire

};

#endif

Q_OBJECT est une macro Qt, nécessaire pour utiliser les signaux et les slots dans cette classe. En effet, Qt génèrera un "métaobjet" contenant certaines informations sur notre classe, dont son nom, son arbre d'héritage et le nom de ses méthodes, sous forme textuelle. C'est ce qui permet au mécanisme signal/slot de fonctionner. Pour générer le métaobjet, il suffit d'invoquer le programme "moc" (metaobject compiler) de la manière suivante :

$QTDIR/bin/moc myprog.h -o myprog.moc

Pour éviter la migraine, il est déconseillé de lire le fichier résultant ! Il nous suffira de l'inclure dans myprog.cpp pour que le métaobjet soit inclu dans le programme.

Maintenant, il nous faut définir le constructeur MyWin::MyWin() dans myprog.cpp :

#include "myprog.h"

#include <kapp.h>

#include <qpopupmenu.h>

#include <qkeycode.h>

MyWin::MyWin()

{

QPopupMenu* p = new QPopupMenu;

p->insertItem(i18n("&Quit"), kapp, SLOT(quit()), CTRL+Key_Q);

menuBar()->insertItem(i18n("&File"), p);

}

Dans le constructeur de la fenêtre, nous créons un menu déroulant : il s'agit d'un "popup" menu, créé ligne 1 du constructeur, que nous insérons dans la barre de menus en lui donnant le titre de "&File", en ligne 3. Les titres sont en anglais, mais l'appel à i18n() permet de demander au programme de transformer ce titre en sa traduction si elle est disponible (i18n est le nom de code signifiant internationalisation). Nous verrons plus loin comment fournir les traductions. Le symbole "&" indique que la lettre F doit être soulignée dans le titre, et définit un raccourci Alt+F pour dérouler le menu.

Etudions maintenant la ligne 2 :

p->insertItem(i18n("&Quit"), kapp, SLOT(quit()), CTRL+Key_Q);

Ceci crée un élément de menu, intitulé "Quit" (ou sa traduction), directement connecté au slot "quit()" de l'application (kapp est une macro qui pointe vers l'application courante). La méthode quit() de la classe KApplication existe, nous n'avons donc pas à la définir. Le 4e argument permet de définir un raccourci pour quitter le programme, Control Q, à partir des constantes définies dans <qkeycode.h>. On peut constater que tout ceci est fait par un seul appel, il n'est même pas nécessaire d'appeler la méthode connect nous-même.

Ensuite, toujours dans myprog.cpp, définir le point d'entrée (main) du programme :

int main(int argc, char* argv[])

{

KApplication a(argc,argv); // crée l'application

MyWin* mywin = new MyWin; // crée la fenêtre

mywin->show(); // affiche la fenêtre

a.exec(); // lance l'application

}

Comme prévu, nous incluons le métaobjet généré pour MyWin en ajoutant

#include "myprog.moc"

Pour compiler ce programme, il nous reste à définir un Makefile. En effet, ceci est préférable plutot que de taper les commandes de compilation à la main à chaque changement de notre source. Le Makefile proposé est le suivant :

LDADD = -lkfile -lkfm -lkdeui -lkdecore -lqt -lXext -lX11

LDFLAGS = -L${QTDIR}/lib -L${KDEDIR}/lib -L/usr/X11R6/lib

CXXFLAGS = -I${QTDIR}/include -I${KDEDIR}/include

myprog : myprog.cpp myprog.moc

g++ myprog.cpp $(CXXFLAGS) $(LDFLAGS) $(LDADD) -o myprog

myprog.moc : myprog.h

${QTDIR}/bin/moc myprog.h -o myprog.moc

Il est hors de propos d'expliquer le fonctionnement des Makefiles, mais si vous n'en avez jamais utilisé, sachez que les commandes (les deux lignes qui ne commencent pas sur la première colonne) doivent commencer par le caractère tabulation. De plus, il ne faut pas confondre l'écriture ${...} utilisée pour les variables d'environnement, ici QTDIR et KDEDIR, avec l'écriture $(...), utilisée pour les définitions faites dans le Makefile.

Voilà, tout est prêt, il n'y plus qu'à lancer "make" pour compiler notre premier programme KDE. Si tout se passe bien, l'exécutable myprog est créé.

Selon la configuration de ldconfig, une erreur de librairie peut apparaitre au lancement. Dans ce cas, il faut taper :

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$KDEDIR/lib:$QTDIR/lib

puis réessayer de lancer le programme ("./myprog").

UN VISUALISEUR DE TEXTE SIMPLIFIE

Maintenant que les mécanismes de base sont acquis, nous pouvons passer à la vitesse supérieure. Nous allons transformer notre programme de départ en un visualiseur de texte simplifié, mais néanmoins utilisable. Précisément, nous allons mettre en place le choix d'un fichier et son chargement, ajouter une barre d'outils et même permettre d'ouvrir plusieurs fenêtres à l'intérieur de l'application.

Dans la déclaration de MyWin (myprog.h), ajoutons

public slots:

void slotOpenFile();

void slotNewWindow();

void slotToolbarClicked(int);

private:

void loadFile();

QMultiLineEdit* edit;

QString fileName;

enum {TOOLBAR_EXIT, TOOLBAR_OPEN};

Les "slots" seront respectivement déclenchés lors de l'appel de "Fichier/Ouvrir", "Fichier/Nouvelle fenêtre", et du clic sur un bouton de la barre d'outils. La syntaxe "public slots", destinée au programme moc, est nécessaire pour définir des slots dans notre classe.

On remarque que les slots peuvent prendre un argument : en effet, le signal "clicked" de la classe KToolBar comporte un argument, l'identifiant du bouton cliqué. Le slot connecté à ce signal doit donc aussi prendre un argument, afin de connaitre le bouton cliqué. En effet, le mécanisme signal/slot inclut une vérification de type des arguments, par opposition au mécanisme de callback standard en C (passage d'adresse de fonction). L'énuméré de la dernière ligne définit les identifiants des boutons de la barre d'outils.

Le composant principal de notre visualiseur est un champ d'édition multi-line, appelé QMultiLineEdit. Pour utiliser ce composant, il est nécessaire d'ajouter dans myprog.h, avec #include <ktmainwindow.h>,

#include <qmultilinedit.h>

Il nous faut maintenant compléter myprog.cpp :

#include "myprog.h"

#include <kapp.h>

#include <kiconloader.h>

#include <kfiledialog.h>

#include <qpopupmenu.h>

#include <qkeycode.h>

MyWin::MyWin()

{

// Le menu "fichier"

QPopupMenu* p = new QPopupMenu;

p->insertItem(i18n("&New Window"),

this, SLOT(slotNewWindow()), CTRL+Key_N);

p->insertItem(i18n("&Open..."),

this, SLOT(slotOpenFile()), CTRL+Key_O);

p->insertSeparator();

p->insertItem(i18n("&Quit"), kapp,

SLOT(quit()), CTRL+Key_Q);

menuBar()->insertItem(i18n("&File"), p);

// Le composant principal de notre programme : QMultiLineEdit

edit = new QMultiLineEdit(this);

setView(edit);

// La barre d'outils, avec deux boutons

toolBar()->insertButton(ICON("exit.xpm"),

TOOLBAR_EXIT, true,i18n("Exit"));

toolBar()->insertButton(ICON("fileopen.xpm"),

TOOLBAR_OPEN, true,i18n("Open File"));

// Connexion de la barre d'outils à

la méthode slotToolbarClicked

connect(toolBar(), SIGNAL(clicked(int)),

this, SLOT(slotToolbarClicked(int)));

}

void MyWin::slotToolbarClicked(int item){

switch (item) {

case TOOLBAR_OPEN: // bouton Ouvrir

slotOpenFile();

break;

case TOOLBAR_EXIT: // bouton Quitter

kapp->quit();

break;

}

}

void MyWin::loadFile(){

// Chargement du fichier dans une chaîne de caractères

QFile f(fileName);

QString s;

if ( f.open(IO_ReadOnly) ) {

QTextStream t( &f );

while ( !t.eof() ) {

s += t.readLine() + "\n";

}

f.close();

}

// Affectation du texte au composant graphique

edit->setText(s);

// Changement du titre de la fenêtre

setCaption(fileName);

}

void MyWin::slotNewWindow(){

// Appelé par "Fichier/Nouvelle fenêtre"

(new MyWin)->show();

}

void MyWin::slotOpenFile(){

// Appelé par "Fichier/Ouvrir"

fileName = KFileDialog::getOpenFileName();

if (!fileName.isEmpty()) loadFile();

}

int main(int argc, char* argv[])

{

KApplication a(argc,argv);

MyWin* mywin = new MyWin;

mywin->show();

a.exec();

}

#include "myprog.moc"

Le menu fichier a été complété par deux nouveaux éléments, connectés aux slots définis dans cette classe. Ensuite le composant multi-ligne est créé, puis défini comme composant principal de la fenêtre.

La création de la barre d'outils est très simple : la méthode toolBar() de la classe KTMainWindow crée la toolbar si nécessaire, et renvoie sa référence.

toolBar()->insertButton(ICON("exit.xpm"),TOOLBAR_EXIT, true,i18n("Exit"));

La macro ICON() utilise le chargeur d'icônes, défini dans kiconloader.h, pour trouver et charger l'icône exit.xpm, fournie en standard avec KDE. Le dernier argument est le texte de la bulle d'aide qui apparait quand on laisse la souris sur le bouton sans cliquer.

La méthode loadFile utilise les classes QString, QFile et QTextStream, qui font partie des classes non graphiques de Qt. Il serait un peu long de détailler leur utilisation, mais l'aide de Qt est très bien faite et vous permettra de vous renseigner sur toutes ces classes. Petit conseil : pour accéder rapidement à l'aide de Qt, créez un alias ou un script contenant la commande "kfmclient exec file:$QTDIR/html".

La méthode slotNewWindow est très simple : il suffit de créer une nouvelle fenêtre (c'est à dire une instance de MyWin), et de demander son apparition. Les connaisseurs du C++ se demandent sûrement si le code de myprog.cpp n'est pas faux en termes d'allocation mémoire. En effet, de nombreuses classes sont créées avec "new" et ne sont jamais détruites avec "delete". Mais il faut savoir que Qt gère la destruction des composants graphiques automatiquement. Les fenêtres sont détruites lors de leur fermeture, ainsi que tous les composants assignés à la fenêtre. C'est pour cette raison que le QMultiLineEdit est créé en passant "this" (la fenêtre) comme parent.

La méthode slotOpenFile utilise KFileDialog, la boîte de dialogue de sélection de fichier fournie avec KDE.

L'INTERNATIONALISATION

Notre programme est destiné à être international, et donc traduit dans de nombreuses langues. Pour cela, exécutez

xgettext -C -ki18n -x ${KDEDIR}/include/kde.pot myprog.cpp -o myprog.po

pour générer le fichier myprog.po. Ouvrez ce fichier, il se termine par les lignes suivantes :

#: myprog.cpp:23

msgid "Exit"

msgstr ""

#: myprog.cpp:24

msgid "Open File"

msgstr ""

Vous remarquerez que de nombreux messages en anglais dans le source ne sont pas présents. En effet, le fichier $KDEDIR/include/kde.pot contient les messages courants, déjà traduits.

Il nous suffit donc de traduire les deux messages ci-dessus, en remplaçant les deux "" par les traductions correspondantes ("Quitter", et "Ouvrir un fichier", vous l'avez deviné). Ensuite, il faut compiler et installer les traductions :

msgfmt myprog.po -o myprog.mo

cp myprog.mo $KDEDIR/share/locale/fr/LC_MESSAGES/

Voilà ! Relancez le programme, et (si vous avez choisi le français pour tout KDE), les menus et bulles d'aides apparaissent en français. Même la boîte de dialogue d'ouverture de fichiers est passée en français. Vous l'avez compris, xgettext et msgfmt permettent de traduire les applications sans les recompiler.

LA GESTION DES SESSIONS

Les utilisateurs de KDE auront remarqué que les applications laissées ouvertes à la fermeture de la session sont relancées à la réouverture d'une session. Ceci est automatique pour toute application, mais dans notre cas, nous voulons en plus réouvrir le fichier qui était ouvert la fois précédente. Rien de plus simple. Il suffit de surcharger deux méthodes de KTMainWindow, en ajoutant à myprog.h :

protected:

void saveProperties(KConfig*);

void readProperties(KConfig*);

La classe KConfig est celle utilisée de manière générale dans KDE pour gérer la configuration des programmes (en particulier les fichiers présents dans ~/.kde/share/config/). Le gestionnaire de sessions utilise aussi cette classe, pour enregistrer temporairement les données nécessaires (taille et position des fenêtres, mais aussi les données propres au programme).

Le contenu de ces méthodes, dans myprog.cpp, sera donc :

void MyWin::saveProperties(KConfig* config){

config->writeEntry("filename", fileName);

};

void MyWin::readProperties(KConfig* config){

fileName = config->readEntry("filename");

loadFile();

};

La fermeture de la session appelle saveProperties qui enregistre le nom du fichier ouvert. L'ouverture de la session suivante appelle readProperties qui relit ce nom de fichier, et l'ouvre dans la fenêtre. Notez que KDE gère automatiquement les multiples fenêtres de l'application : elles sont toutes réouvertes, chacune avec le fichier qu'elle affichait.

Pour que le gestionnaire de sessions fonctionne, il faut modifier le main() du programme :

int main(int argc, char* argv[])

{

KApplication a(argc,argv);

if (a.isRestored())

RESTORE(MyWin) // macro qui recrée toutes

// les fenêtres de l'application

else {MyWin* mywin = new MyWin; mywin->show();}

a.exec();

}

Pour tester ceci, il va falloir fermer et réouvrir KDE, désolé ! Notez qu'il faut aussi installer le programme (myprog) dans $KDEDIR/bin, afin qu'il puisse être trouvé et relancé.

 

Par David Faure

Etudiant INSA et développeur KDE


© Copyright 2000 Diamond Editions/Linux magazine France. - Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".