Kapitel 4. Komponenter og tjenester

Indholdsfortegnelse

KDE-tjenester
Mime-typer
Netværkstransparens

KDE-tjenester

Hvad er KDE-tjenester?

Begrebet tjeneste er en central idé i KDE's modulære arkitektur. Der er ingen strikt teknisk implementering koblet til benævnelsen: tjenester kan være indstiksprogrammer i form af delte biblioteker, eller programmer som styres via DCOP. Ved at påstå at være af en vis tjenestetype, lover en tjeneste at implementere visse programmeringsgrænseflader eller funktioner. I C++ sprogbrug, kan man forestille sig en tjenestetype som en abstrakt klasse, og en tjeneste som en implementering af grænsefladen.

Fordelen ved denne opdeling er åbenbar: Et program som udnytter en tjenestetype behøver ikke kende til mulige implementeringer af den. Den bruger kun programmeringsgrænsefladen som hører sammen med tjenestetypen. På denne måde kan tjenesten som bruges ændres uden at påvirke programmet. Desuden kan brugeren indstille hvilke tjenester han foretrækker til bestemte funktioner.

Nogle eksempler:

  • HTML-fremvisningskomponenten som bruges i Konqueror er en indlejret komponent som implementerer tjenestetypen KParts/ReadOnlyPart og Browser/View.

  • I de seneste versioner af KDevelop, er størstedelen af funktionerne pakkede i indstiksprogrammer med tjenestetypen KDevelop/Part. Ved opstart, indlæses alle tjenester af denne type, så du kan udvide det integrerede udviklingsmiljø på en meget smidig måde.

  • Konqueror kan vise miniaturer af billeder, HTML-sider, PDF- og tekstfiler, hvis det aktiveres. Denne formåen kan udvides. Hvis du vil vise forhåndsvisningsbilleder af egne datafiler af en vis Mime-type, kan du implementere en tjeneste med tjenestetypen ThumbCreator.

Naturligvis karakteriseres en tjeneste ikke kun af tjenestetypen som den implementerer, men også af nogle egenskaber. For eksempel så gør en ThumbCreator ikke kun krav på at den implementerer C++ klassen med typen ThumbCreator, den har også en liste med Mime-typer som den er ansvarlig for. På samme måde har KDevelop-dele programsproget de understøtter som en egenskab. Når et program beder om en tjenestetype, kan den også angive begrænsninger for tjenestens egenskaber. I eksemplet ovenfor, når KDevelop indlæser indstiksprogrammer for et Java-projekt, spørger det kun efter indstiksprogrammer som har egenskaben Java som programmeringssprog. KDE indeholder en fuldstændig CORBA-lignende handler, med et komplekst forespørgselssprog, til dette formål.

Definition af tjenestetyper

Nye tjenestetyper tilføjes ved at installere en beskrivelse af dem i mappen KDEDIR/share/servicetypes. I det automatiske byggeskelet, kan det gøres med dette fragment fra Makefile.am:

kde_servicetypesdir_DATA = kdeveloppart.desktop
EXTRA_DIST = $(kde_servicetypesdir_DATA)

Definitionen kdeveloppart.desktop for en part til KDevelop ser ud som følger:

[Desktop Entry]
Type=ServiceType
X-KDE-ServiceType=KDevelop/Part
Name=KDevelop Part

[PropertyDef::X-KDevelop-Scope]
Type=QString

[PropertyDef::X-KDevelop-ProgrammingLanguages]
Type=QStringList

[PropertyDef::X-KDevelop-Args]
Type=QString

Foruden de sædvanlige indgange, demonstrerer dette eksempel hvordan man angiver at en tjeneste har visse egenskaber. Hver definition af en egenskab svarer til en gruppe [PropertyDef::name] i konfigurationsfilen. I gruppen, angiver indgangen Type egenskabens type. Mulige typer er alt som kan opbevares i en QVariant.

Definition af delte bibliotekstjenester

Tjenestedefinitioner opbevares i mappen KDEDIR/share/services:

kde_servicesdir_DATA = kdevdoxygen.desktop
EXTRA_DIST = $(kde_servicesdir_DATA)

Indholdet i følgende eksempelfil, kdevdoxygen.desktop, angiver indstiksprogrammet KDevDoxygen med tjenestetypen KDevelop/Part:

[Desktop Entry]
Type=Service
Comment=Doxygen
Name=KDevDoxygen
ServiceTypes=KDevelop/Part
X-KDE-Library=libkdevdoxygen
X-KDevelop-ProgrammingLanguages=C,C++,Java
X-KDevelop-Scope=Project

Foruden de almindelige deklarationer, er en vigtig indgang X-KDE-Library. Den indeholder navnet på libtool-biblioteket (uden filendelsen .la). Det fastlægger også navnet på det eksporterede symbol i biblioteket som returnerer objekttilvirkeren (med det indledende præfiks init_). I ovenstående eksempel, skal biblioteket indeholde følgende funktion:

extern "C" {
    void *init_libkdevdoxygen()
    {
        return new DoxygenFactory;
    }
};

Typen for tilvirkningsklassen DoxygenFactory afhænger af den specifikke tjenestetype som tjenesten implementerer. I vort eksempel med et KDevelop-indstiksprogram, skal tilvirkeren være en KDevFactory (som arver KLibFactory). Et mere almindeligt eksempel er KParts::Factory som antages at oprette objekterne KParts::ReadOnlyPart eller i de fleste tilfælde den generiske KLibFactory.

Brug af delte bibliotekstjenester

For at kunne bruge en delt bibliotekstjeneste i et program, skal du skaffe et KService-objekt som repræsenterer den. Dette beskrives i afsnittet om Mime-typer (og i et afsnit om handleren som endnu ikke er skrevet :-)

Med objektet KService tilgængeligt, kan du meget let indlæse biblioteket og få en peger til dets tilvirkningsobjekt.

KService *service = ...
QString libName = QFile::encodeName(service->library());
KLibFactory *factory = KLibLoader::self()->factory(libName);
if (!factory) {
    QString name = service->name();
    QString errorMessage = KLibLoader::self()->lastErrorMessage();
    KMessageBox::error(0, i18n("There was an error loading service %1.\n"
                               "The diagnostics from libtool is:\n%2")
                          .arg(name).arg(errorMessage);
}

Fra dette øjeblik, afhænger fortsættelsen igen af tjenestetypen. For generelle indstiksprogrammer, laver man objekter med metoden KLibFactory::create(). Med KParts, skal tilvirkningspegeren konverteres til det mere specifikke KParts::Factory, og dets metode create() skal bruges:

if (factory->inherits("KParts::Factory")) {
    KParts::Factory *partFactory = static_cast<KParts::Factory*>(factory);
    QObject *obj = partFactory->createPart(parentWidget, widgetName, 
                                           parent, name, "KParts::ReadOnlyPart");
    ...
} else {
    cout << "Tjenesten implementerer ikke de rette tilvirkere" << endl;
}

Definition af DCOP-tjenester

En DCOP-tjeneste implementeres oftest som et program som startes når det behøves. Det går derefter ind i en løkke og lytter efter DCOP-forbindelser. Programmet kan være interaktivt, men det kan også køre som en dæmon i baggrunden under hele eller dele af sin livstid, uden at brugeren mærker det. Et eksempel på en sådan dæmon er kio_uiserver, som implementerer vekselvirken med brugeren som fremgangsdialoger for KIO-biblioteket. Fordelen ved en sådan central dæmon er at f.eks. nedtagningsforløbet for flere forskellige filer kan vises i et vindue, selvom nedtagningerne startedes fra forskellige programmer.

En DCOP-tjeneste defineres på en anden måde end en tjeneste i et delt bibliotek. Naturligvis angiver den ikke et bibliotek, men i stedet et kørbart program. Desuden angiver en DCOP-tjeneste ikke linjen med tjenestetype, eftersom den startes med navn. Den indeholder yderligere to linjer med yderligere egenskaber:

X-DCOP-ServiceType angiver hvordan tjenesten startes. Værdien Unique (unik) angiver at tjenesten ikke må startes mere end én gang. Det betyder at hvis du forsøger at starte tjenesten (f.eks. via KApplication::startServiceByName(), kontrollerer KDE om den allerede er registreret i DCOP, og bruger tjenesten som kører. Hvis den ikke allerede er registreret, starter KDE den og venter til den er registreret. Derfor kan du med det samme sende DCOP-kald til tjenesten. I dette tilfælde, skal tjenesten implementeres som KUniqueApplication.

Værdien Multi for X-DCOP-ServiceType angiver at flere instanser af tjenesten kan eksistere samtidigt, så hvert forsøg på at starte tjenesten skaber en ny proces. Som en sidste mulighed kan værdien None (ingen) bruges. I dette tilfælde, venter starten af tjenesten ikke på at den er registreret i DCOP.

X-KDE-StartupNotify skal normalt angives som "false". Ellers viser aktivitetsfeltet en startbekræftelse, eller, afhængig af brugerindstillingerne, så ændres markøren.

Her er definitionen af kio_uiserver:

[Desktop Entry]
Type=Service
Name=kio_uiserver
Exec=kio_uiserver
X-DCOP-ServiceType=Unique
X-KDE-StartupNotify=false

Brug af DCOP-tjenester

En DCOP-tjeneste startes med en af flere metoder i klassen KApplication:

DCOPClient *client = kapp->dcopClient();
client->attach();
if (!client->isApplicationRegistered("kio_uiserver")) {
    QString error;
    if (KApplication::startServiceByName("kio_uiserver", QStringList(), &error))
        cout << "Start af KIO-server mislykkedes med meddelelsen " << error << endl;
}
...
QByteArray data, replyData;
QCString replyType;
QDataStream arg(data, IO_WriteOnly);
arg << true;
if (!client->call("kio_uiserver", "UIServer", "setListMode(bool)", 
                  data, replyType, replyData))
    cout << "Kald til kio_uiserver mislykkedes" << endl;
...

Bemærk at eksemplet med et DCOP-kald som gives her udtrykkelig bruger sammensætning af argumenter. Ofte vil man i stedet bruge en prototype som laves af dcopidl2cpp, eftersom det er meget enklere, med mindre risiko for fejl.

I eksemplet som gives her, startes tjenesten "med navn", dvs. første argument til KApplication::startServiceByName() er navnet, som det angives på linjen Name i desktop-filen. Et alternativ er at bruge KApplication::startServiceByDesktopName(), som bruger navnet på desktop-filen som argument, dvs. i dette tilfælde "kio_uiserver.desktop".

Alle disse kald har en liste med URL'er som andet argument, som gives til tjenesten på kommandolinjen. Det tredje argument er en peger til en QString. Hvis starten af tjenesten mislykkes, tildeles dette argument til en oversat fejlmeddelelse.