English Deutsch Chinois

Les composants KDE

par Philippe Fremy

Traduction de Sebastien Biot

Introduction

KDE 2.0, lancé en Septembre 2000, présente des améliorations considérables par rapport à KDE 1 : le bureau a été presque entièrement réécrit et tire maintenant avantage de nouvelles technologies versatiles: composants, RPC/IPC, intégration naturelle au service en réseau, démon son, etc..

On pourrait croire que toutes ces technologies nouvelles rendent l´écriture de logiciel pénible, mais ça n´est pas le cas; elles sont en fait très agréable et très facile à utiliser comme j´entends le montrer dans cet article.

Tous les exemples qui suivent ont été écrits et testés sous KDE 2.1. Le code lui-même vient de différents endroits: CVS, livres sur KDE, listes de discussion, documentation, tutoriels, exemples, applications. Vous pouvez trouver presque tout sur le site des developpeurs de KDE: developer.kde.org

Il est intéressant de signaler que ces technologies sont déjà passées par trois versions stables (KDE 2.0 à KDE 2.2) presque sans modifications et qu´elles peuvent donc être considérées dores et déjà comme mûres. KDE en fait un usage constant; leur stabilité n´est plus à mettre en doute.

KDE et ses composants graphiques

Les composants participent d´un ensemble de technologies évoluées. Un composant permet à une application d´exposer ses fonctionnalites et son interface non seulement en tant qu´application mais aussi à l´intérieur d´un cadre incorporé dans une autre application du bureau. Chaque composant peut être amélioré séparément et l´on peut assigner certaines tâches à certains composants privilégiés. Par exemple: un client e-mail peut invoquer un composant éditeur pour composer un nouveau message; un environnement de développement peut aussi invoquer un composant éditeur, pas nécessairement le même que dans l´exemple précédent, pour permettre d´éditer du code; un tableur peut invoquer un constructeurs de schéma pour analyser ses données; une application peut invoquer un navigateur HTML etc...

En matière de programmation, les composants augmentent les possibilités de réutilisation et de modularisation du code et devraient pour cela être utilisés autant que possible. Ainsi, pour faire des composants une véritable option pratique, KDE les a imaginé simples à programmer et utiliser.

Dans l´environement Gnome, les composants existent au travers de Bonobo, une technologie fondée sur Corba. KDE a par le passé utilisé Corba pour finalement le laisser tomber pour une technologie crée sur mesure: KPart. À l´époque cette décision fut très critiquée avant même que quiconque ait pu en comprendre les raisons et les conséquences. J´estime que fût un bon choix et j'espère pouvoir y revenir dans un autre article.

En bref une technologie pour composants graphiques doit posséder les trois caractéristiques suivantes:

Corba rend difficile la réalisation de ces conditions, alors que cela est facilement obtenu avec KPart. Corba, bien qu´étant une technologie valable, n´est pas à sa place lorsqu´il s´agit de gérer les composants d´une interface graphique. Ce qui suit met en lumière le succès de KPart dans ce domaine.

KPart

Lorsque les développeurs du noyau de KDE réalisèrent que gérer Corba était en passe de devenir un véritable cauchemar ils se mirent très rapidement à définir une technologie de remplacement compacte et efficace: KPart.

KPart prend appui sur les bibliothèques partagées ce qui permet aux composants d´exister immédiatement sous forme d´objet C++. Tous les aspects de ces objets (fonctions membres) sont accessible sans qu´il soit nécessaire d´en définir les paramètres avec un language IDL. Le code à écrire pour utiliser un composant est en de nombreux points similaire à celui dont on a l´habitude en C++ pour les objets. Les bibliothèques partagées ont l´avantage d´être très rapides à invoquer ou à révoquer et permettent au programme de ne pas avoir à se dupliquer pour fourcher ("fork") , puisque le code est réalisé à l´intérieur même de l´application. L´interface de KPart prend en charge 95% du composant même ce qui rend sa création et son utilisation d´une grande simplicité comme je le montre dans les exemples qui suivent. Tout ce qu´il vous faut comprendre se trouve dans la documentation et le tutoriel.

Il y a deux types de KPart: les composants et les extensions (plugins). Les composants peuvent à la fois créer un élément de fenêtre (widget) et compléter le menu des applications avec des éléments qui leurs sont propres. Une extension n´a pas d´élément de fenêtre et sert simplement à présenter des fonctions supplémentaires. Pour que le tout fonctionne, les menus d´une application sont définis par un fichier XML.

1er exemple: Invoquer un composant navigateur HTML

Comme le montre l´exemple suivant où l´on incorpore un navigateur dans une application, utiliser un composant est très facile. Le code même est tiré du modèle d´application génèré par kapptemplate (dont Kurt Granroth est l´auteur).
KTrader::OfferList offers = KTrader::self()->query("text/html",
                                        "'KParts/ReadOnlyPart' in ServiceTypes");

KLibFactory *factory = 0;
// in theory, we only care about the first one.. but let's try all
// offers just in case the first can't be loaded for some reason
KTrader::OfferList::Iterator it(offers.begin());
for( ; it != offers.end(); ++it) {
	KService::Ptr ptr = (*it);

	// we now know that our offer can handle HTML and is a part.
	// since it is a part, it must also have a library... let's try to
	// load that now
	factory = KLibLoader::self()->factory( ptr->library() );
	if (factory) {
    m_html = static_cast<KParts::ReadOnlyPart *>(factory->create(this,
                          ptr->name(), "KParts::ReadOnlyPart"));
	    break;
	}
}

Tous les composants disponibles sont indexés et gardés dans une base de données. Les requêtes sont faites par l'intermédiaire de KTrader qui vous permet de définir les characteristiques souhaitées du composant: son nom, le mimetype qu´il reconnaît et beaucoup d´autres choses encore. Dans ce cas précis on demande un composant qui puisse afficher les documents text/html en lecture seule. KTrader retourne alors une liste de composants disponibles (KService::Ptr), par ordre de préférence. Ces KService::Ptr ont une bibliothèque associée que vous pouvez appeler avec KLibLoader. Cette bibliothèque à une "fabrique" capable de créer un élément de fenêtre qui n´est autre que le composant que vous avez invoqué.

Même si ça peut paraître difficile au premier abord, il n´y a rien d´autre qui viendra compliquer la chose par la suite. Trouvez le service, invoquez sa bibliothèque et sa fabrique et laissez la créer votre élément de fenêtre. C´est aussi simple que ça. Dans notre cas, le composant HTML préféré de l´utilisateur sera activé (khtml probablement; gecko est également disponible par l´intermédiaire de kmozilla). Si vous voulez activer un composant différent, il suffit juste de remplacer text/html par un autre mimetype.

2eme exemple: Appeler l´éditeur texte de choix

Pour appeler un éditeur texte on utilisera la même méthode. Le code, tiré de KTextEditor interface documentation, est plus court mais reste fondamentalement identique au précédent. Ce code est extrait de href="http://developer.kde.org/documentation/library/2.0-api/classref/interfaces/KTextEditor.html">la documentation de l'interface KTextEditor
 KTrader::OfferList offers = KTrader::self()->query( "KTextEditor/Document" );
 ASSERT( offers.count() >= 1 );
 KService::Ptr service = *offers.begin();
 KLibFactory *factory = KLibLoader::self()->factory( service->library() );
 ASSERT( factory );
 m_part = static_cast<KTextEditor::Document *>( factory->create( this, 0, "KTextEditor::Document" ) );
 ASSERT( m_part );
 QWidget * view = m_part->createView( my_parent_widget, 0 );

8 lignes de code dont 3 assertions. Comme je vous l´avais dit, utiliser KPart ne présente guère de difficulté.

3eme exemple: Créer un composant

Imaginez maintenant que vous ayez une application et que vous vouliez la mettre à la disposition d´autres applications comme composant KPart. Le code qui suit, tire du tutoriel KPart de Kurt Granroth sur KPart, vous montre ce qu´il faut faire. Le composant en question correspond à une application appelé aKtion.

Tout ce qu´il y a à faire c´est de créer une fabrique dont la bibliothèque se servira lorsqu´elle sera invoquée. La fabrique elle même ne fait rien d´autre que de créer un objet KPart qui installe les nouvelles options de menu et retourne un élément de fenêtre contenant votre application.

aktion_part.h aktion_part.cpp
#ifndef __aktion_part_h__
#define __aktion_part_h__

#include "kparts/browserextension.h"
#include "klibloader.h"

class KAboutData;
class KInstance;
class AktionBrowserExtension;
class QLabel;

class AktionFactory : public KLibFactory
{
    Q_OBJECT
public:
    AktionFactory() {}
    virtual ~AktionFactory();

    virtual QObject* create(QObject* parent = 0, 
                const char* name = 0, 
                const char* classname = "QObject", 
                const QStringList &args = QStringList());

    static KInstance *instance();
    static KAboutData *aboutData();

private:
    static KInstance *s_instance;
};

class AktionPart: public KParts::ReadOnlyPart
{
    Q_OBJECT
public:
    AktionPart(QWidget *parent, const char *name);
    virtual ~AktionPart() { closeURL(); }

    bool closeURL() { return true; }

protected:
    virtual bool openFile() 
        { widget->setText(m_file); return true; }
    QLabel *widget;
};

#endif
#include "aktion_part.h"
#include "kinstance.h"
#include "klocale.h"
#include "kaboutdata.h"
#include "qlabel.h"

extern "C"
{
    void *init_libaktion()
    {
        return new AktionFactory;
    }
};

/**
 * We need one static instance of the factory for our C 'main'
 * function
 */
KInstance *AktionFactory::s_instance = 0L;

AktionFactory::~AktionFactory()
{
    if (s_instance) {
        delete s_instance->aboutData();
        delete s_instance;
    }
    s_instance = 0;
}

QObject *AktionFactory::create(QObject *parent, 
                        const char *name, const char*, 
                        const QStringList& )
{
    QObject *obj = new AktionPart((QWidget*)parent, name);
    emit objectCreated(obj);
    return obj;
}

KInstance *AktionFactory::instance()
{
    if ( !s_instance ) s_instance = new KInstance( aboutData() );
    return s_instance;
}

KAboutdata *AktionFactory::aboutData()
{
    KAboutData *about = new KAboutData("aktion",
            I18N_NOOP("aKtion"), "1.99");
    return about;
}

AktionPart::AktionPart(QWidget *parent, const char *name)
    : KParts::ReadOnlyPart(parent, name)
{
    setInstance(AktionFactory::instance());

    // create a canvas to insert our widget
    QWidget *canvas = new QWidget(parent);
    canvas->setFocusPolicy(QWidget::ClickFocus);
    setWidget(canvas);

    // as an example, display a blank white widget
    widget = new QLabel(canvas);
    widget->setText("aKtion!");
    widget->setAutoResize(true);
    widget->show();
}

Il y a 80 lignes de code en tout dont 90% sont génériques et parfaitement réutilisables sur un autre composant. Seules les 15 dernières lignes ne s´appliquent qu´au composant aKtion. Si vous utilisez kapptemplate ou kdevelop vous n´aurez même pas à vous préoccuper d´écrire ces 80 lignes puisqu´elles seront générées automatiquement pour vous. :-)

4eme exemple: Konqueror

Tout le monde connaît Konqueror comme le navigateur de KDE2 bien qu´il ne soit au fond qu´une coquille vide qui appelle ses données par l´intermédiaire des esclaves Kio (un autre exemple de technologie KDE) et qui active un composant de visualisation grâce à KPart. C´est l´exemple le plus visible de l´utilisation des composants dans KDE.

Ce qui suit est une liste non-exhaustive de toutes les applications qui offrent un composant KPart et qui peuvent ainsi s´insérer dans Konqueror:

Chacun de ces composants peut être inséré dans une application à l´aide simplement de 8 lignes de code. Et avec la venue de la version 2.2 de KDE la liste ne fait que s´agrandir!

Example 5: KOffice

KDE est connu pour sa suite d´application bureautique, KOffice, qui inclus entre autre un traitement de text (KWord), un tableur (KSpread), un logiciel de présentation (KPresenter), un logiciel de dessin vectoriel (Kontour), un logiciel de graphe (KChart), etc..

Ce que l´on sait moins à propos de KOffice c´est que chaque application est disponible en tant que composant. La classe qui décrit un document KOffice s´appelle KoDocument et descend de KParts::ReadWritePart. Ainsi un document KOffice peut être inséré et visualisé comme n´importe quel autre composant KPart et tous les avantages de KPart s´appliquent aussi aux composants KOffice: utilisation et programmation facile, invocation rapide et compacte, etc.. Les composants KOffice diffèrent légèrement dans leur mode d´activation parce qu´ils peuvent être imbriqués l´un dans l´autre mais d´un point de vue fonctionnel ce sont des KParts.

Konqueror utilise l´insertion de document KOffice pour permettre leur prévisualisation à l´intérieur même du navigateur. L´avantage principal cependant réside dans la capacité qu´à KWord par exemple à insérer une feuille de calcul, une formule mathématique et un graphe dans un document texte. Lorsque vous travaillez sur la partie texte, le composant KWord est actif et lorsque vous travaillez sur la feuille de calcul c´est le composant KSpread qui prend le relais.

Quand KWord ou KSpread est lancé, l´utilisateur peut choisir soit de créer un nouveau document soit d´ouvrir un fichier existant. Si vous observez la fenêtre qui se trouve sous la fenêtre de dialogue activé vous verrez que la barre d´outils est presque vide et que la fenêtre elle-même est identique, que KWord ou KSpread ait été activé. C´est une ¨fenêtre-receptable¨ commune à tous les documents KOffice qui ne contient rien qui n´appartienne spécifiquement à KWord ou KSpread. Ce n´est que lorsqu´un composant KWord est activé par exemple que KWord est matérialisé. La seule chose qui différentie KWord de KSpread lorsque vous lancez l´une ou l´autre application c´est le mimetype que la fenêtre de dialogue d´ouverture est capable d´utiliser.

KWord en train d'ouvrir un document KSpread en train d'ouvrir un document

Créez par exemple un document KWord dans lequel vous insérez un document KSpread. Lorsque vous travaillez sur le document KWord les menus et barres d´outils sont ceux de KWord. Le cadre de KSpread ne contient qu´un tableau statique. Maintenant lorsque vous cliquez sur le cadre de KSpread vous constatez que les menus et barres d´outils de KWord disparaissent pour laisser place à ceux de KSpread. Cet échange est le résultat de la désactivation et activation de composants KPart. Le composant KWord a été désactivé et ses menu et barres d´outils ont été enlevés de la ¨fenêtre-réceptacle¨; pendant un instant la fenêtre de KWord est redevenu la fenêtre vide de KOffice mentionnée plus haut lorsque nous avions parlé de la fenêtre de dialogue d´ouverture. L´instant suivant le composant KSpread est activé et les menus et barres d´outils de KSpread ont été activés avec lui. Le tableau est devenu une feuille de calcul de KSpread prête à être éditée. Comme le montre la troisième capture d´écran, travailler sur cet élément équivaut à travailler directement à partir KSpread parce toutes les fonctions de KSpread sont mises à la disposion de l´utilisateur par l'intermédiaire du composant.

Document KWord contenant une table KSpread
KWord est activé KSpread est activé
Le même document KSpread, mais dans KSpread

L´espace de travail KOffice

Il existe un programme qui illustre là vrai nature modulaire de KOffice. Il s´agit de l´espace de travail KOffice que vous pouvez lancer par la commande ¨koshell¨. La fenêtre principale est identique à la ¨fenêtre-receptable¨ mentionnée plus haut. Vous pouvez activer chacun des composants de KOffice en cliquant sur leurs icônes respectives dans le cadre de gauche. La ¨fenêtre-receptable¨ de KOffice devient alors KWord, KSpread ou n´importe quelle autre application que vous aurez choisi.

Espace de travail KOffice vide

Quelques chiffres sur le code de KOffice

À la différence de OpenOffice, Gnu Spread ou AbiWord qui existaient indépendement de Gnome, KOffice a été entièrement développé en même temps que KDE. KOffice a pu être développé rapidement grâce à la simplicité et à la versatilité de l´architecture et l´infrastructure logicielle de KDE et de KOffice.

Par exemple, le code source de l´espace de travail KOffice dont nous venons de parler ne contient que 600 lignes, l´équivalent de trois jours de travail pour un programmeur moyen.
philippe@werewindle /usr/src/kde-cvs/koffice/koshell $ ls
AUTHORS   Makefile.am  dummy.cc         koshell_shell.cc
CVS/      Makefile.in  koshell.desktop  koshell_shell.h
Makefile  TODO         koshell_main.cc

philippe@werewindle /usr/src/kde-cvs/koffice/koshell $ wc -l *.h *.cc
    121 koshell_shell.h
      1 dummy.cc
     49 koshell_main.cc
    434 koshell_shell.cc

    605 total

Si vous souhaitez travailler au dévelopement d´une application KOffice il vous faut simplement créer une classe qui descende de KoDocument. L´exemple qui accompagne KOffice contient 430 lignes de code.
philippe@werewindle /usr/src/kde-cvs/koffice/example $ ls
CVS/         README           example_factory.cc  example_view.cc
Makefile     configure.in.in  example_factory.h   example_view.h
Makefile.am  example.desktop  example_part.cc     main.cc
Makefile.in  example.rc       example_part.h      x-example.desktop

philippe@werewindle /usr/src/kde-cvs/koffice/example $ wc -l *.h *.cc
     47 example_factory.h
     42 example_part.h
     48 example_view.h
    106 example_factory.cc
     70 example_part.cc
     67 example_view.cc
     49 main.cc

    429 total

Tout ce qu´il faut faire pour transformer cet exemple en un véritable document KOffice c´est de réécrire trois fonctions: ExamplePart::loadXML() pour charger les données de votre document, ExamplePart::saveXML() pour transférer vos données sur un fichier XML et ExamplePart::paintContent() pour faire apparaître le contenu de votre document.

Conclusions

Comme vous l´avez compris KDE est doté d´une architecture modulaire versatile qui permet d´utiliser ses composants à loisir. Ainsi KPart est omniprésent dans KDE. Bonobo est loin d´être aussi facile à utiliser et si je ne me trompe Gnome 2.0 ne sera pas Bonoboisé. Gnome a encore beaucoup de chemin à faire avant d´égaler KDE.

Dans mon prochain article je compte montrer comment KDE utilise IPC/RCP grâce à DCOP.

References


Copyright (c) 2001 Philippe Fremy (phil at freehackers.org )
Translation: Copyright (c) 2001 Sébastien Biot (sebastien.biot at verizon.net)
Last modification : $Date: 2005/07/02 18:03:31 $