Francais English Chinese

Komponenten von KDE

by Philippe Fremy

Ubersetzung vom Dirk Dörflinger

Einleitung:

KDE 2.0 erschien im September 2000 und stellte einen großen Schritt im Vergleich zu KDE 1.X dar. Die Oberfläche war beinahe komplett neu geschrieben worden, seit dem nutzt sie die Vorteile von mächtigen neuen Technologien: Komponenten, RPC/IPC (Remote Procedure Call/Inter Process Communication), Netzwerktransparenz, Sounddämon,...

Man könnte sich nun vorstellen, daß es eine sehr langwierige Aufgabe ist, eine Anwendung mit diesen neuen Technologien zu entwickeln. Das ist aber nicht der Fall. Diese Technologien sind sehr elegant und sehr einfach zu benutzen. Das ist, das, was dieser Artikel zeigen soll.

Alle Code-Beispiele hier sind unter KDE 2.1 geschrieben und getestet und stammen von verschiedenen Orten: CVS-Beispiele, ein Buch über KDE, EMails, Dokumentationen, Anleitungen oder Anwendungen. Praktisch alles findet sich auf developer.kde.org wieder.

Jede dieser Technologien ist inzwischen sehr ausgereift, da sie schon drei offizielle KDE-Versionen (Kde 2.0, 2.1 und 2.2) ohne große Änderungen durchlaufen haben. Das gesamte KDE basiert zu großen Teilen darauf. Folglich kann man sie getrost als "rock-stable" bezeichnen.

Komponenten der Benutzeroberfläche von KDE

GUI-(Graphical User Interface, Grafische Benutzerschnittstelle) Komponenten sind sehr mächtig. Grundsätzlich erlaubt eine Komponentenarchitektur, daß eine Anwendung ihre Eigenschaften und Schnittstellen nicht nur als Anwendung selbst, sondern auch als eingebetteten Teil einer beliebigen anderen bietet. Jede Komponente kann einzeln benutzt werden, und die bevorzugte Komponente kann für bestimmte Fälle verwendet werden. Typische Beispiele: Ein Email-Programm ruft eine Text-Editor-Komponente auf, um eine neue Mail zu erstellen; eine IDE (Integrated Development Environment, Integrierte Entwicklungsumgebung) benutzt einen Text-Editor (nicht unbedingt den selben wie das Email-Programm), um den Code zu bearbeiten; eine Tabellenkalkulation benutzt eine Diagrammkomponente, um die Daten zu analysieren; eine Anwendung nutzt einen HTML-Browser, um Webseiten darzustellen; usw.

Komponenten erhöhen die Modularität und ermöglichen es, Code noch öfter wiederbenutzen zu können, deshalb solten sie auch so oft wie möglich benutzt werden. Um das zu realisieren, macht es KDE sehr einfach, sie zu schreiben und zu benutzen.

Gnome benutzt als Komponententechnologie Bonobo, das auf CORBA basiert. KDE nutzte anfangs auch CORBA, es wurde aber dann durch eine eigene Technologie ersetzt: KPart. Dieses Umschwenken wurde oft kritisiert, obwohl kaum jemand die Ursache und die Konsequenzen dafür verstand. Es war eine gute Entscheidung, und irgendwann wird ein Artikel erscheinen, der die Gründe beleuchtet.

In aller Kürze hat eine gute GUI-Komponentenarchitektur die folgenden Merkmale:

Mit CORBA ist es schwer, dies zu erfüllen, während es mit KPart sehr einfach ist. CORBA ist eine sehr gute Technologie, allerdings nicht für GUI-Komponenten geeignet. Das Folgende soll zeigen, weshalb KPart hier ideal ist.

KPart

Als die KDE Entwickler bemerkten, daß CORBA zum unbezwingbaren Albtraum wird, schrieben sie in wenigen Tagen eine leichtgewichtige und effiziente Technologie, um es zu ersetzen: KPart.

KPart basiert auf gemeinsam benutzen Bibliotheken. Deshalb erscheint eine Komponente direkt als C++-Objekt. Es ist nicht nötig, die Eigenschaften und Schnittstellen mit Hilfe einer IDL (Interface Description Language) zu beschreiben, alles ist ohne besonderen Aufwand erreichbar. Deshalb lassen sich Komponenten mehr oder weniger genauso Programmieren wie ein C++-Objekt, das für die Anwendung sowieso erzeugt werden muß. Gemeinsam benutzte Bibliotheken sind ebenfalls sehr schnell geladen oder wieder deaktiviert. Es ist noch nichteinmal ein fork notwendig, da der Code direkt in der Anwendung laeuft. 95% der komponentenspezifischen Arbeit erledigt die KPart API. Deshalb ist es sehr einfach, Komponenten zu schreiben und zu verwenden, wie die folgenden Beispiele zeigen werden. Nach dem Lesen der kurzen Dokumentation sowie der Anleitung sollte eigentlich alles klar sein.

Beispiel 1:Verwendung eines HTML-Browsers

Es ist ganz einfach, ein Part zu verwenden. Hier ist als Beispiel ein Stück Code angeführt, das verwendet werden kann, um einen HTML-Browser in einer Anwendung zu verwenden. Der Code stammt aus den Anwendungsvorlagen, die kapptemplate generiert (Der Vorlagengenerator von Kurt Granroth).

KTrader::OfferList offers = KTrader::self()->query("text/html",
                                        "'KParts/ReadOnlyPart' in ServiceTypes");

KLibFactory *factory = 0;
// theoretisch interessiert uns nur der erste.. aber wir probieren alle,
// falls der erste aus irgendeinem Grund nicht geladen werden kann.
KTrader::OfferList::Iterator it(offers.begin());
for( ; it != offers.end(); ++it) {
		KService::Ptr ptr = (*it);

		// wir wissen, daß unsere "offer" mit HTML umgehen kann und ein "Part" ist.
		// da es ein Part ist, muß es eine Bibliothek haben, also versuchen wir sie zu laden.
		factory = KLibLoader::self()->factory( ptr->library() );
		if (factory) {
		m_html = static_cast(factory->create(this,
			ptr->name(), "KParts::ReadOnlyPart"));
			break;
		}
}
Alle erreichbaren Komponenten sind gekennzeichnet und in einer Datenbank abgelegt. Mittels den angegebenen Charakteristiken fragt KTrader diese Datenbank nach den Komponenten ab: Dem Namen der Komponente, dem MIME-Typen, den die Komopnente verarbeiten kann, oder viele andere Parameter. Hier suchen wir nach einer Komponente, die text/html anzeigen kann. KTrader liefert eine Liste der erreichbaren Komponente (KService::Ptr), sortiert nach der Voreinstellung. Diese KService::PTR haben eine zugehörige Bibliothek, die von KLibLoader geladen wird. Die Bibliothek wiederum hat eine sog. factory, die das zugehörige Widget aufbaut. Dieses Widget ist schlußendlich die gesuchte Komponente.

Es mag vielleicht anfangs etwas kompliziert klingen, aber es wird nicht mehr komplizierter, und auch nicht mehr anders. Suchen der Komponente, Bibliothek laden, factory bekommen und Widget aufbauen lassen. Das war's. In unserem Fall wird der von Benutzer bevorzugte HTML-Renderer aktiviert (vermutlich khtml, aber dank kmozilla ist auch gecko möglich). Um eine andere Komponente zu erhalten, einfach text/html durch einen anderen MIME-Type ersetzen.

Beispiel 2: Verwenden des bevorzugten Texteditors

Auf die selbe Art und Weise läßt sich auch ein Texteditor verwenden. Der Code ist kompakter, aber prinzipiell unverändert. Dieser Code stammt aus der Dokumentation zum 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( factory->create( this, 0, "KTextEditor::Document" ) );
 ASSERT( m_part );
 QWidget * view = m_part->createView( my_parent_widget, 0 );

8 Zeilen Code mit 3 asserts. Es ist wirklich ganz einfach, einen Part in einer Anwendung zu verwenden, wir haben nicht zuviel versprochen.

Beispiel 3: Eine Komponente anbieten

Nun stellen wir uns vor, wir haben eine tolle Anwendung geschrieben und möchten sie auch anderen Anwendungen als eine KPart Komponente zur Verfügung stellen. Hier ist der dafür notwendige Code, als Auszug aus dem KPart tutorial von Kurt Granroth. Dieser KPart ist für eine Anwendung namens aKtion.

Prinzipiell bieten wir eine factory, die benutzt wird, wenn die Bibliothek geladen wird (siehe oben). Der KPart installiert dann die neuen Menüeinträge und liefert ein Widget zurück, das unsere Anwendung enthalten sollte.
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();
}

	
Wir haben nun 80 Codezeilen, 90% davon sind generisch und für jede andere Komponente verwendbar. Nur die letzten 15 Zeilen sind speziell für unseren aKtion Part. Falls wir jetzt kapptemplate oder kdevelop verwendet hätten, hätten wir uns sogar die 80 Zeilen ersparen können, da sie automatisch erzeugt worden wären :-)

Beispiel 4: Konqueror

Konqueror ist bekannt als der Webbrowser von KDE 2. Aber er ist eigentlich kein Webbrowser, sondern einfach eine Hülle, die Daten über Kioslaves (eine andere KDE-Technologie) empfängt und das dazu passende Anzeigeprogramm als KPart anzeigt. Konqueror ist wohl das beste Beispiel für die Verwendung von Komponenten in KDE.

Hier ist eine - nicht komplette - Liste der Anwendungen, die einen KPart bieten und sich dadurch in Konqueror einbetten lassen:

Wie schon gesagt, jede dieser hier aufgeführten Komponenten läßt sich mit 8 Zeilen Coden in eine Anwendung einbauen. Und mit KDE 2.2 werden es noch mehr sein!

Beispiel 5: KOffice

KDE ist auch sehr bekannt für seine Officesuite. Diese enthält eine Textverarbeitung (KWord), eine Tabellenkalkulation (KSpread), ein Präsentationsprogramm (KPresenter), ein Vektorzeichenprogramm (kontour), ein Diagrammtool (KChart) und vieles mehr.

Was nicht so bekannt ist, ist die Tatsache, dass jede KOffice-Anwendung ist als Komponente verfügbar ist. Die Klasse, die ein KOffice-Dokument repräsentiert, heißt KoDocument und ist von KParts::ReadWritePart abgeleitet. Dadurch kann jedes KOffice-Dokument genau wie ein KPart eingebettet und angezeigt werden. KOffice genießt dadurch alle Vorteile von KPart: einfaches schreiben und benutzen der Komponenten, schnelles und sparsames laden usw. KOffice-Komponenten fügen noch Feinheiten hinzu, da sie verschachtelt sein koennen. Aber in ihrer Funktionalität sind sie KParts.

Konqueror benutzt die Fähigkeit des Einbettens von KOffice, um KOffice-Dokumente im Browser anzuzeigen. Der Hauptnutzen liegt allerdings darin, z.B. in ein KWord-Dokument eine KSpread-Tabelle, eine Formel und ein Diagramm einzubetten. Während der Text bearbeitet wird, ist die KWord-Komponente aktiv. Sobald die Tabelle bearbeitet werden soll, wird die KSPread-Komponente aktiviert (Siehe Screenshots)

Wenn KWord oder KSpread gestartet werden, zeigen sie einen Dialog, um ein neues Dokument zu erstellen oer dein schon bestehendes zu öffnen. Ein Blick auf das Fenster hinter diesem Dialog (siehe Screenshots) zeigt, daß die Symbolleiste fast leer ist und die Fenster von KWord und KSpread identisch sind. Dieses Fenster ist eine allgemeine KOffice-Hülle und enthält nichts spezifisches von KWOrd oder KSpread. Erst die Aktivierung einer KWord-Komponente macht das Fenster zu einem KWord-Fenster, bzw. einer KSPread-Komponente zu einem KSpread-Fenster. Das einzige, was beim Starten KWord von KSpread unterscheidet, ist der MIME-Type, den der Startdialog verwaltet.
öffnen eines Dokuments mit KWord öffnen eines Dokuments mit KSPread
Nun wollen wir ein KWord-Dokument erstellen, das eine KSpread-Tabelle beinhaltet. Beim Bearbeiten des KWord-Dokuments werden die Menüs und Symbolleisten von KWord angezeigt. Die Tabelle enthält nichts außer statischem Text. Wenn nun auf die Tabelle geklickt wird, verschwinden die KWord-Menüs und -Symbolleisten und werden durch die von KSpread ersetzt. Was ist hier passiert? Komponenten wurden aktiviert und deaktiviert. Die KWord-Komponente wurde deaktiviert, ihre Menüs und Symbolleisten wurden vom Hauptfenster entfernt. Für einen kurzen Moment war nur ein leeres KOfficefenster (das selbe wie beim Start im Hintergrund) zu sehen. Im nächsten Moment wurde die KSpread-Komponente aktiviert und alle Menüs und Symbolleisten von KSpread angezeigt. Die reien Texttabelle wurde durch eine KSpread-Tabelle ersetzt, die nun bearbeitet werden kann. Da alle Eigenschaften von KSpread auch in seiner Komponente enthalten sind, ist es exakt das selbe, als ob die Tabelle dirket in KSpread bearbeitet wurde (siehe 3. Screenshot).

Ein KWord-Dokument mit einem KSpread Rahmen.
KWord aktiviert KSpread aktiviert
Die selbe KSpread-Tabelle in Kspread

Der KOffice-Arbeitsplatz

Es gibt ein Programm, das die wahre Komponentennatur von KOffce verdeutlicht: Der KOffice Arbeitsplatz, der mit dem Befehl koshell gestartet wird. Das Hauptfenster ist die schon erwähnte KOffice-Hülle. Die einzelnen KOffice-Komponenten können durch einen Klick auf das betreffende Symbol auf der linken Seite gestartet werden. Die KOffice-Hülle ändert sich dann sofort in KWord, KSpread oder ein beliebiges anderes KOffice-Programm.

Der leere KOffice Arbeitsplatz
*** Screenshot KOffice Workspace ***

KOffice Codebeispiele

Im Gegensatz zu OpenOffice, Gnu Spread oder AbiWord, die vor Gnome existierten, wurde KOffice von Anfang an gemeinsam mit KDE entwickelt. Es war möglich, alles recht schnell zu entwickeln, da die Architektur und die die gesamte Umgebung von KDE und KOffice sehr mächtig und einfach zu benutzen sind.

So besteht zum Beispiel der KOffice Arbeitsplatz, den wir soeben betrachtet haben, aus gerade einmal 600 Zeilen Code. Das bedeutet ungefähr dre Tage arbeit für einen durchschnittlichen Programmierer.
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

Um eine neue KOffice-Anwendung zu schreiben, ist das Wichtigste, am Anfang eine neue Klasse zu erzeugen, die sich von KoDocument ableitet. Das Beispiel, das in KOffice enthalten ist, hat 430 Zeilen 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

Um dieses Beispiel in einen vollständigen KOffice-Part {alternatively: ...in ein vollständiges KOffice-Dokument.... ;depends on an answer I'm awaiting from Philippe Fremey} zu verwandeln, müssen lediglich drei Funktionen umgeschrieben werden: ExamplePart::loadXML(), ExamplePart::saveXML() und ExamplePart::paintContent(), die entsprechend das Dokument aus einer XML-Datei lädt, es in eine XML-Datei speichert oder den Inhalt des Dokuments anzeigt.

Zusammenfassung

Wie wir gesehen haben, basiert KDE auf einer mächtigen Komponentenstruktur, die es ermöglicht, die Komponenten vielfältig zu nutzen. KPart wird tatsächlich überall in KDE benutzt. Bonobo ist noch weit davon entfernt, diese Einfachheit zu bieten und es ist verständlich, daß Gnome 2.0 nicht "bonobiert" wird. Gnome hat noch einen Weiten weg vor sich, um zu KDE aufzuholen.

Der nächste Artikel wird über IPC/RPC innerhalb von KDE handeln, was von DCOP gemanaged wird.

Quellen

Copyright (c) 2001 Philippe Fremy (phil at freehackers.org)
Translation: Copyright (c) 2001 Dirk Dörflinger (ddoerflinger at web dot de)
Last modification : $Date: 2005/07/02 18:03:31 $ - $Author: philippe $