<listitem><para>Biblioteket tdecore är det grundläggande programramverket för alla KDE-baserade program. Det ger tillgång till konfigurationssystemet, hantering av kommandoraden, laddning och hantering av ikoner, vissa särskilda sorters interprocesskommunikation, filhantering och diverse andra verktyg. </para></listitem>
<listitem><para>Biblioteket <literal>tdeui</literal> tillhandahåller många grafiska komponenter och standarddialogrutor som Qt inte har eller som har fler funktioner än motsvarande i Qt. Det innehåller också flera grafiska komponenter som är delklasser av de i Qt, men är bättre integrerade med KDE-skrivbordet genom att de respekterar användarinställningar. </para></listitem>
<listitem><para>Biblioteket <literal>tdeio</literal> innehåller funktioner för asynkron, nätverkstransparent I/O och åtkomst till hantering av Mime-typer. Det tillhandahåller också KDE:s fildialogruta och dess hjälpklasser. </para></listitem>
<listitem><para>Biblioteket <literal>tdehtml</literal> innehåller TDEHTML-delen, en HTML-bläddringskomponent, DOM-gränssnitt och tolk, inklusive gränssnitt till Java och Javaskript. </para></listitem>
<para>Qt:s lågnivåritmodell är baserad på de möjligheter som erbjuds av X11 och andra fönstersystem där en version av Qt finns. Men den utökar också dem genom att implementera ytterligare funktioner som godtyckliga affina omvandlingar för text och pixmappar. </para>
<para>Den centrala grafiska klassen för att rita tvådimensionellt med Qt är <ulink url="kdeapi:qt/QPainter">QPainter</ulink>. Den kan rita på en <ulink url="kdeapi:qt/QPaintDevice">QPaintDevice</ulink>. Det finns tre möjliga ritenheter implementerade: En är <ulink url="kdeapi:qt/QWidget">QWidget</ulink>, som representerar en grafisk komponent på skärmen. Den andra är <ulink url="kdeapi:qt/QPrinter">QPrinter</ulink>, som representerar en skrivare, och producerar Postskript-utmatning. Den tredje är klassen <ulink url="kdeapi:qt/QPicture">QPicture</ulink>, som spelar in ritkommandon och kan spara dem till disk, och sedan spela upp dem. Ett möjligt lagringsformat för ritkommandon är W3C-standarden SVG. </para>
<para>Alltså är det möjligt att återanvända uppritningskoden som du använder för att visa en grafisk komponent för utskrift, med stöd för samma funktioner. Naturligtvis används koden i praktiken i ett något annorlunda sammanhang. Rita på en grafisk komponent görs nästan enbart i metoden paintEvent() i en komponentklass. </para>
<para>Vid ritning på en skrivare, måste du försäkra dig om att använda QPrinter::newPage() för att avsluta en sida, och börja på en ny: något som inte är relevant för att rita grafiska komponenter. Vid utskrift vill du kanske också använda <ulink url="kdeapi:qt/QPaintDeviceMetrics">enhetsmått</ulink> för att beräkna koordinater. </para>
<para>Normalt när QPainter används, ritar den i det naturliga koordinatsystemet som används av enheten. Det betyder att om du ritar en linje med längden 10 enheter, ritas den som en horisontell linje på skärmen med längden 10 bildpunkter. Dock kan QPainter använda godtyckliga affina omvandlingar innan former och kurvor verkligen ritas upp. En affin omvandling överför x- och y-koordinater linjärt till x' och y' enligt: </para>
<para>QPainter::setWorldMatrix() kan användas för att ange den här 3x3 matrisen i ekvationen, som har typen <ulink url="kdeapi:qt/QWMatrix">QWMatrix</ulink>. Normalt är detta identitetsmatrisen, dvs. m11 och m22 är ett, och övriga värden är noll. Det finns i grunden tre olika grupper av omvandlingar: </para>
<para>Dessa flyttar ett objekts alla punkter med ett fast värde i någon riktning. En förflyttningsmatris kan erhållas genom att anropa metoden m.translate(dx, dy) med en QWMatrix. Det motsvarar matrisen: </para>
<para>Dessa förstorar eller förminskar ett objekts koordinater, och gör det större eller mindre utan att förvränga det. En skalningsomvandling kan göras för en QWMatrix genom att anropa m.scale(sx, sy). Det motsvarar matrisen: </para>
<para>En förvrängning av koordinatsystemet med två parametrar. En skjuvningsomvandling kan göras genom att anropa m.shear(sh, sv), vilket motsvarar matrisen: </para>
<para>Detta roterar ett objekt. En rotationsomvandling kan göras genom att anropa m.rotate(alfa). Observera att vinkeln måste anges i grader, inte som en matematisk vinkel! Motsvarande matris är: </para>
<para>Omvandlingar kan kombineras genom att multiplicera grundläggande matriser. Observera att matrisoperationer inte i allmänhet är kommutativa, och därför beror den kombinerade effekten av en sammansättning på ordningen som matriserna multipliceras med. </para>
<para>Uppritning av linjer, kurvor och polygonkanter kan ändras genom att ange en särskild penna med QPainter::setPen(). Argumentet till den här funktionen är ett <ulink url="kdeapi:qt/QPen">QPen</ulink>-objekt. Egenskaperna som lagras i det är en stil, en färg, en sammanfogningsstil och en ändstil. </para>
<para>Pennstilen är en medlem av uppräkningstypen <ulink url="kdeapi:qt/Qt#PenStyle-enum">Qt::PenStyle</ulink>. och kan ha något av följande värden: </para>
<para>Sammanfogningsstilen är en medlem av uppräkningstypen <ulink url="kdeapi:qt/Qt#PenJoinStyle-enum">Qt::PenJoinStyle</ulink>. Den anger hur förbindelsen mellan flera linjer som sätts samman ritas. Den kan ha något av följande värden: </para>
<para>Ändstilen är en medlem av uppräkningstypen <ulink url="kdeapi:qt/Qt#PenCapStyle-enum">Qt::PenCapStyle</ulink> och anger hur linjernas ändpunkter ritas. Den antar något värde från följande tabell: </para>
<para>Fyllnadsstilen för polygoner, cirklar eller rektanglar kan ändras genom att ange en särskild borste med QPainter::setBrush(). Den här funktionens argument är ett <ulink url="kdeapi:qt/QBrush">QBrush</ulink>-objekt. Borstar kan skapas på fyra olika sätt: </para>
<para>En standardborststil från uppräkningstypen <ulink url="kdeapi:qt/Qt#BrushStyle-enum">Qt::BrushStyle</ulink>. Här är en bild av alla fördefinierade mönster: </para>
<para>Färger har betydelse både när kurvor ritas, och när former fylls i. Färger representeras av klassen <ulink url="kdeapi:qt/QColor">QColor</ulink> i Qt. Qt stöder inte avancerade grafikfunktioner som ICC-färgprofiler och färgkorrektion. Färger skapas oftast genom att ange deras röda, gröna och bläa komponenter, eftersom RGB-modellen är sättet som bildpunkter sätts samman på en bildskärm. </para>
<para>Det är också möjligt att använda färgton, färgmättnad och värde. Den här HSV-representationen är den som används i GTK:s färgdialogruta, t.ex. i GIMP. Där motsvarar färgtonen en vinkel i färghjulet, medan färgmättnaden motsvarar avståndet från cirkelns mitt. Värdet väljs med ett särskilt skjutreglage. </para>
<para>Normalt när du ritar på en ritenhet, så ersätter bildpunkterna de som tidigare fanns där. Det betyder om du fyller ett visst område med röd färg, och sedan fyller samma område med blå färg, så är bara den blåa färgen synlig. Qt:s bildmodell tillåter inte genomskinlighet, dvs. ett sätt att blanda förgrunden som ritas med bakgrunden. Det finns dock ett enkelt sätt att kombinera bakgrund och förgrund med Booleska operationer. Metoden QPainter::setRasterOp() anger operationen som används, som kommer från uppräkningstypen <ulink url="kdeapi:qt/Qt#RasterOp-enum">RasterOp</ulink>. </para>
<para>Standardvärdet är CopyROP, som ignorerar bakgrunden. Ett annat populärt val är XorROP. Om du ritar en svart linje med den operationen på en färgad bild, så inverteras området som täcks. Den här effekten används till exempel för att skapa gummibandsmarkeringar i bildbehandlingsprogram, som är kända under namnet "vandrande myror". </para>
<para>I det följande listar vi de grundläggande grafiska elementen som stöds av QPainter. De flesta av dem finns i flera överlastade versioner som har olika antal argument. Metoder som hanterar rektanglar, har till exempel oftast en <ulink url="kdeapi:qt/QRect">QRect</ulink> som argument, eller en uppsättning med fyra heltal. </para>
<para>Qt tillhandahåller två mycket olika klasser för att representera bilder. </para>
<para><ulink url="kdeapi:qt/QPixmap">QPixmap</ulink> motsvarar direkt pixmappsobjekt i X11. En pixmapp är ett objekt på serversidan och kan, med ett modernt grafikkort, till och med lagras direkt i kortets minne. Det gör det <emphasis>mycket</emphasis> effektivt att överföra en pixmapp till skärmen. En pixmapp fungerar också som en motsvarighet till grafiska komponenter utanför skärmen. QPixmap-klassen är en delklass till QPaintDevice, så det går att rita på den med en QPainter. Elementära ritoperationer accelereras ofta av modern grafik. Därför är ett vanligt användningsmönster att använda en pixmapp för dubbelbuffring. Detta betyder att istället för att rita direkt på en grafisk komponent, ritar man på ett tillfälligt pixmappsobjekt och använder funktionen <ulink url="kdeapi:qt/QPaintDevice#bitBlt-1">bitBlt</ulink> för att överföra det till komponenten. För komplexa omritningar, hjälper detta till att undvika flimmer. </para>
<para>I motsats till detta, finns <ulink url="kdeapi:qt/QImage">QImage</ulink>-objekt på klientsidan. Deras huvuduppgift är att ge direkt åtkomst till bildpunkterna i bilden. Det gör dem användbara för bildhantering, och saker som att ladda och spara till disk (Metoden load() för QPixmap använder QImage som ett mellansteg). Å andra sidan, så blir uppritning av en bild på en grafisk komponent en ganska krävande åtgärd, eftersom det innebär en överföring till X-servern, vilket kan ta en viss tid, särskilt för stora bilder och fjärrservrar. Beroende på färgdjupet, kan konvertering från QImage till QPixmap också kräva användning av gitter. </para>
<para>Text kan ritas med en av de överlastade varianterna av metoden QPainter::drawText(). De ritar en QString, antingen vid en given punkt eller inne i en given rektangel, med teckensnittet som ställts in med QPainter::setFont(). Det finns också en parameter som tar en ELLER-kombination av vissa flaggor från uppräkningstyperna <ulink url="kdeapi:qt/Qt#AlignmentFlags-enum">Qt::AlignmentFlags</ulink> och <ulink url="kdeapi:qt/Qt#TextFlags-enum">Qt::TextFlags</ulink>. </para>
<para>Med början i version 3.0, hanterar Qt fullständig textlayout också för språk som skrivs från höger till vänster. </para>
<para>Ett mer avancerat sätt att visa text med taggar, är klassen <ulink url="kdeapi:qt/QSimpleRichText">QSimpleRichText</ulink>. Objekt från klassen kan skapas med ett textstycke som använder en delmängd av HTML-taggarna, som är ganska omfattande och till och med erbjuder tabeller. Textstilen kan anpassas genom att använda <ulink url="kdeapi/qt/QStyleSheet">QStyleSheet</ulink> (taggarnas dokumentation finns också här). Så fort textobjektet har skapats, kan det ritas upp på en grafisk komponent eller en annan ritenhet med metoden QSimpleRichText::draw(). </para>
<para>QPainter erbjuder en kraftfull ritmodell för att rita på grafikska komponenter och pixmappar. Dock kan den vara omständlig att använda. Varje gång komponenten tar emot en rithändelse, måste den analysera QPaintEvent::region() eller QPaintEvent::rect() för det som måste ritas om. Därefter måste den ställa in en QPainter, och rita alla objekt som överlappar det området. Tänk dig till exempel ett vektorritprogram som tillåter att objekt som polygoner, cirklar och grupper av dem att dras omkring. Varje gång objekten flyttas lite grand, aktiverar komponentens mushändelsehantering en rithändelse för hela området som täcks av objektens gamla plats och deras nya plats. Att räkna ut nödvändiga omritningar, och att göra dem på ett effektivt sätt, kan vara svårt, och kan också vara i konflikt med programkodens objektorienterade struktur. </para>
<para>Som alternativ innehåller Qt klassen <ulink url="kdeapi:qt/QCanvas">QCanvas</ulink>, där man lägger till grafiska objekt, som polygoner, text eller pixmappar. Man kan också skapa ytterligare objekt genom att skapa en delklass av <ulink url="kdeapi:qt/QCanvasItem">QCanvasItem</ulink> eller någon av dess mer specialiserade delklasser. En duk kan visas på skärmen genom en eller flera komponenter från klassen <ulink url="kdeapi:qt/QCanvas">QCanvasView</ulink>, som man måste skapa en delklass av för att hantera interaktion med användaren. Qt tar hand om all omritning av objekt i vyn, vare sig de orsakas av att komponenten visas, nya objekt skapas eller ändras, eller andra orsaker. Genom att använda dubbelbuffring, kan detta göras på ett effektivt och flimmerfritt sätt. </para>
<para>Objekt på duken kan överlappa varandra. I detta fall, så beror vilken som syns på z-ordningen, som kan tilldelas med QCanvasItem::setZ(). Objekt kan också göras synliga eller osynliga. Man kan också tillhandahålla en bakgrund som ska ritas "bakom" alla objekt, och en förgrund. För att associera mushändelser med objekt på duken, finns metoden QCanvas::collisions(), som returnerar en lista med objekt som överlappar med en given punkt. Här visar vi en skärmbild av en dukvy i arbete: </para>
<para>Här ritas rutmönstret upp i bakgrunden. Dessutom finns ett QCanvasText-objekt och en violett QCanvasPolygon. Fjärilen är en QCanvasPixmap. Den har genomskinliga områden, så du kan se underliggande objekt genom den. </para>
<para>En handledning om hur QCanvas används för att skriva spel baserade på småfigurer finns <ulink url="http://zez.org/article/articleview/2/1/">här</ulink>. </para>
<para>De-facto standarden för att rita upp 3D-grafik nu för tiden är <ulink url="http://www.opengl.org">OpenGL</ulink>. Implementeringar av standarden levereras med Microsoft Windows, Mac OS X och XFree86, och de stöder ofta funktioner för hårdvaruacceleration som erbjuds av moderna grafikkort. OpenGL själv hanterar bara uppritning på ett angivet område i rambuffern genom ett <emphasis>GL-sammanhang</emphasis>, och har ingen interaktion med verktygslådan eller miljön. </para>
<para>Qt erbjuder den grafiska komponenten <ulink url="kdeapi:qt/QGLWidget">QGLWidget</ulink>, som kapslar in ett fönster med tillhörande GL-sammanhang. I grunden används det genom att skapa en delklass av det och implementera om några metoder. </para>
<listitem><para>Istället för att implementera om paintEvent(), och använda QPainter för att rita komponentens innehåll, överskrider man paintGL() och använder GL-kommandon för att rita upp en scen. QGLWidget tar hand om att göra sitt GL-sammanhang det aktuella innan paintGL() anropas, och tömmer det efteråt. </para></listitem>
<listitem><para>Den virtuella metoden initializeGL() anropas en gång innan den första gången innan resizeGL() eller paintGL() anropas. Det kan användas för att skapa visningslistor för objekt, och göra alla initieringar. </para></listitem>
<listitem><para>Istället för att implementera om resizeEvent(), överskrider man resizeGL(). Detta kan användas för att ställa in vyområdet på ett lämpligt sätt. </para></listitem>
<listitem><para>Istället för att anropa update() när scenens tillstånd har ändrats, till exempel om du animerar det med ett tidur, ska man anropa updateGL(). Då aktiveras en omritning. </para></listitem>
<para>I allmänhet beter sig QGLWidget som vilken annan grafisk komponent som helst, dvs. man kan till exempel hantera mushändelser som vanligt, ändra storlek på komponenten och kombinera den med andra i en layout. </para>
<para>Qt innehåller några exempel på användning av QGLWidget i <literal>demo</literal>-exemplen. En samling handledningar finns <ulink url="http://www.libsdl.org/opengl/intro.html">här</ulink>, och mer information samt en OpenGL-referens finns på <ulink url="http://www.opengl.org">OpenGL:s hemsida</ulink>. </para>
<para>OpenGL är ett gränssnitt på ganska låg nivå för att rita 3D-grafik. På samma sätt som QCanvas ger programmeraren ett gränssnitt på högre nivå som hanterar objekt och deras egenskaper, finns det också gränssnitt på högre nivå för 3D-grafik. Ett av de mest populära är Open Inventor. Ursprungligen var det en teknologi som utvecklades av SGI, men idag finns också en implementering med öppen källkod, <ulink url="http://www.coin3d.org">Coin</ulink>, som åtföljs av en verktygsanpassning till Qt, som heter SoQt. </para>
<para>Det grundläggande konceptet i Open Inventor är en <emphasis>scen</emphasis>. En scen kan laddas från disk, och sparas med ett särskilt format, nära besläktat med <ulink url="http://www.vrml.org">VRML</ulink>. En scen består av en samling objekt som kallas <emphasis>noder</emphasis>. Inventor tillhandahåller redan en omfattande samling med återanvändbara noder, som kuber, cylindrar och rutnät. Dessutom finns ljuskällor, material, kameror, etc. Noder representeras av C++ klasser, och kan kombineras och delklasser kan skapas. </para>
<para>En introduktion till Inventor finns <ulink url="http://www.motifzone.com/tmd/articles/OpenInventor/OpenInventor.html">här</ulink> (i allmänhet kan du ersätta alla SoXt som omnämns i artikeln med SoQt). </para>
<para>Medan <link linkend="userinterface-actionpattern">åtgärdsmönster</link> tillåter att åtgärder som aktiveras av användaren kapslas in i ett objekt, som kan "anslutas" någonstans i menyraderna eller verktygsraderna, löser det inte ensamt problemet med att skapa själva menyerna. I synnerhet måste du bygga alla sammanhangsberoende menyer i C++ kod, och uttryckligen infoga åtgärderna i en viss ordning, med hänsyn taget till stilguiden för standardåtgärder. Det gör det rätt svårt att låta användaren anpassa menyerna eller ändra snabbtangenter så att de passar hans behov, utan att ändra källkoden. </para>
<para>Det här problemet löses med en samling klasser som kallas <literal>grafiskt XML-gränssnitt</literal>. I grunden skiljer de åtgärderna (kodade i C++) från deras utseende i menyrader och verktygsrader (kodade i XML). Utan att ändra någon källkod, kan menyer enkelt anpassas genom att justera en XML-fil. Dessutom hjälper det till att försäkra att standardåtgärder (som <menuchoice><guimenu>Arkiv</guimenu> <guimenuitem>Öppna</guimenuitem></menuchoice> eller <menuchoice><guimenu>Hjälp</guimenu> <guimenuitem>Om</guimenuitem></menuchoice>) visas på platserna som föreslås av stilguiden. Grafiska XML-gränssnitt är särskilt viktiga för modulära program, där alternativen i menyraderna kan komma från många olika insticksprogram eller delar. </para>
<para>KDE:s klass för toppnivåfönster, <ulink url="kdeapi:tdeui/TDEMainWindow.html">TDEMainWindow</ulink>, ärver <ulink url="kdeapi:tdeui/KXMLGUIClient.html">KXMLGUIClient</ulink>, och stöder därför grafiska XML-gränssnitt från början. Alla åtgärder som skapas inne i det måste ha klientens <literal>actionCollection()</literal> som förälder. Ett anrop till <literal> createGUI()</literal> bygger sedan hela uppsättningen menyer och verktygsrader som definieras av programmets XML-fil (vanligtvis med ändelsen <literal>ui.rc</literal>). </para>
<para>I det följande använder vi KDE:s bildvisare <application>Kview</application> som exempel. Den har en <literal>ui.rc</literal>-fil som heter <filename>kviewui.rc</filename>, som installeras med ett fragment från <filename>Makefile.am</filename> </para>
<para>Här är ett utdrag ur filen <filename>kviewui.rc</filename>. För enkelhetens skull, visar vi bara definitionen för menyn <guimenu>View</guimenu>. </para>
<para>XML-filen börjar med en dokumenttypdeklaration. DTD:n för kpartgui finns i tdelibs-källkoden i <filename>tdeui/kpartgui.dtd</filename>. Det yttersta elementet i filen innehåller programmets instansnamn som en egenskap. Det kan också innehålla ett versionsnummer på formen "version=2". Det är användbart när du ger ut nya versioner av ett program med ändrad menystruktur, t.ex. med flera funktioner. Om du räknar upp versionsnumret i filen <literal>ui.rc</literal>, ser KDE till att alla anpassade versioner av filen slängs och att den nya filen används istället. </para>
<para>Nästa rad, <literal><MenuBar></literal>, innehåller en deklaration av en menyrad. Du kan också infoga hur många <literal><ToolBar></literal>-deklarationer som helst, för att skapa några verktygsrader. Menyn innehåller en undermeny, med namnet "view". Det namnet är redan fördefinierat, och därför skulle den översatta versionen av ordet "View" kunna visas. Om du deklarerar undermenyer, måste du uttryckligen lägga till rubriken. <application>Kview</application> har till exempel en undermeny med rubriken "Image", som deklareras enligt följande: </para>
<para>I KDE:s automatiska byggramverk, plockas sådana rubriker automatiskt ut och placeras i programmets <ulink url="tde-i18n-howto.html"><literal>.po</literal></ulink>-fil, så att det hanteras av översättare. Observera att du måste skriva markeringen av snabbtangenten "&" på en form som följer XML-syntaxen "&amp;". </para>
<para>Låt oss återvända till exemplet. <application>Kview</application>s meny <guimenu>Visa</guimenu> innehåller ett antal egna åtgärder <literal>zoom50</literal>, <literal>zoom100</literal>, <literal>zoom200</literal>, <literal>zoomMaxpect</literal> och <literal>fullscreen</literal>, deklarerade med elementet <literal><Action></literal>. Skiljelinjen i skärmbilderna motsvarar elementet <literal><Separator></literal>. </para>
<para>Du märker att vissa menyalternativ inte har ett motsvarande element i XML-filen. De är <emphasis>standardåtgärder</emphasis>. Standardåtgärder skapas av klassen <ulink url="kdeapi:tdeui/KStdAction.html">KStdAction</ulink>. När du skapar sådana åtgärder i ditt program (som i C++ exemplet ovan), infogas de automatiskt på en föreskriven plats, och möjligen med en ikon och en snabbtangent. Du kan slå upp de här platserna i filen <filename>tdeui/ui_standards.rc</filename> i tdelibs-källkoden. </para>
<para>För beskrivningen av verktygsrader, byter vi till <application>Konquerors</application> definition av grafiskt gränssnitt. Det här utdraget definierar platsraden, som innehåller inmatningsfältet för webbadresser. </para>
<listitem><para><literal>fullWidth</literal>: Talar om för det grafiska XML-gränssnittet att verktygsraden har samma bredd som toppnivåfönstret. Om detta är "false", upptar verktygsraden bara så mycket plats som nödvändigt, och ytterligare verktygsrader placeras på samma rad. </para></listitem>
<listitem><para><literal>newline</literal>: Det här hör ihop med ovanstående alternativ. Om newline är "true", så placeras verktygsraden på en ny rad. Annars kan den placeras i en rad tillsammans med den föregående verktygsraden. </para></listitem>
<listitem><para><literal>noEdit</literal>: Normalt kan verktygsrader anpassas av användaren, t.ex. med <menuchoice><guimenu>Inställningar</guimenu> <guimenuitem>Anpassa verktygsrader</guimenuitem></menuchoice> i <application>Konqueror</application>. Sätts alternativet till "true", markeras verktygsraden så att den inte går att redigera. Det är viktigt för verktygsrader som fylls med objekt när programmet kör, t.ex. <application>Konqueror</application>s bokmärkesverktygsrad. </para></listitem>
<listitem><para><literal>iconText</literal>: Talar om för det grafiska XML-gränssnittet att visa åtgärdens text intill ikonen. Normalt visas texten bara som ett verktygstips när musmarkören hålls över ikonen en stund. Möjliga värden för egenskapen är "icononly" (visar bara ikonen), "textonly" (visar bara texten), "icontextright" (visar texten till höger om ikonen) och "icontextbottom" (visar texten under ikonen). </para></listitem>
<listitem><para><literal>hidden</literal>: Om det här är "true", så visas inte verktygsraden från början, och måste aktiveras av något menyalternativ. </para></listitem>
<listitem><para><literal>position</literal>: Standardvärdet för den här egenskapen är "top", vilket betyder att verktygsraden placeras under menyraden. För program med många verktyg, som grafikprogram, kan det vara intressant att ersätta det här med "left" (vänster), "right" (höger) eller "bottom" (under). </para></listitem>
<para>XML kan naturligtvis bara innehålla en statisk beskrivning av ett användargränssnitt. Ofta finns det menyer som ändras under körning. <application>Konqueror</application>s meny <guimenu>Plats</guimenu> innehåller till exempel en uppsättning alternativ <guimenuitem>Öppna med ...</guimenuitem>, med program som kan ladda en fil med en given Mime-typ. Varje gång dokumentet som visas ändras, uppdateras listan med menyalternativ. Det grafiska XML-gränssnittet är förberett för att hantera sådana fall med begreppet <emphasis>åtgärdslistor</emphasis>. En åtgärdslista deklareras som ett objekt i XML-filen, men består av flera åtgärder som ansluts till menyn när programmet kör. Ovanstående exempel implementeras med följande deklaration i <application>Konqueror</application>s XML-fil: </para>
<para>Funktionen <function>KXMLGUIClient::plugActionList()</function> används sedan för att lägga till åtgärder som ska visas, medan funktionen <function>KXMLGuiClient::unplugActionList()</function> tar bort alla anslutna åtgärder. Rutinen som är ansvarig för att göra uppdateringarna ser ut på följande sätt: </para>
<para>Observera att i motsats till statiska åtgärder, så skapas <emphasis>inte</emphasis> de här med åtgärdssamlingen som förälder, och du ansvarar själv att de tas bort. Det enklaste sättet att åstadkomma det är genom att använda <literal>openWithActions.setAutoDelete(true)</literal> i exemplet ovan. </para>
<para>Exemplen ovan innehåller bara klasser där ett huvudfönsters menyrad och verktygsrader skapas. I de fallen är processen som skapar behållarna helt dold för dig inne i anropet av funktionen <function>createGUI()</function> (utom om du har egna behållare). Det finns dock fall då du vill skapa andra behållare och befolka dem med grafiska gränssnittsdefinitioner från XML-filen. Ett sådant exempel är sammanhangsberoende menyer. För att få en pekare till en sammanhangsberoende meny, måste du fråga klientens tillverkare efter den: </para>
<para>Metoden <function>KXMLGUIFactory::container()</function> som används ovan, ser efter om den hittar en behållare i XML-filen med det angivna namnet. Alltså kan en möjlig definition se ut på följande sätt: </para>
<para>Att göra ett program lätt och intuitivt att använda omfattar en stor mängd funktioner, som ofta kallas inbyggd hjälp. Inbyggd hjälp har flera, delvis motstridiga, mål: å ena sidan ska den ge användaren svar på frågan "Hur kan jag utföra en viss uppgift?", å andra sidan ska den hjälpa användaren utforska programmet och hitta funktioner som han inte ännu känner till. Det är viktigt att inse att det här bara kan åstadkommas genom att erbjuda flera hjälpnivåer: </para>
<listitem><para>Verktygstips är små etiketter som dyker upp över gränssnittselement när musen blir kvar där en längre stund. De är särskilt viktiga för verktygsrader, där ikonerna inte alltid räcker till för att förklara syftet med en knapp. </para></listitem>
<listitem><para>"Vad är det här?" hjälp är ofta en längre och mer utförlig förklaring av en komponent eller menyalternativ. Den är också knepigare att använda. I dialogrutor kan den visas på två olika sätt: antingen genom att trycka på <keycombo><keycap>Skift</keycap> <keycap>F1</keycap></keycombo>, eller genom att klicka på frågetecknet i namnlisten (stöd för det här beror på fönsterhanteraren). Muspekaren ändras då till en pil med ett frågetecken, och ett hjälpfönster visas när ett element i användargränssnittet klickas. "Vad är det här?" hjälp för menyer aktiveras oftast med en knapp i verktygsraden som innehåller en pil och ett frågetecken. </para></listitem>
<listitem><para>Problemet med den här ansatsen är att användaren inte kan se om en grafisk komponent tillhandahåller hjälp eller inte. När användaren aktiverar knappen med frågetecken och inte får något hjälpfönster vid klick på ett element i användargränssnittet, blir han mycket snart frustrerad. </para>
<para>Fördelen med "Vad är det här?" hjälpfönster som de erbjuds av Qt och KDE, är att de kan innehålla <ulink url="kdeapi:qt/QStyleSheet"> formaterad text</ulink>, dvs. de kan innehålla olika teckensnitt, text med fetstil och kursiv stil, och till och med bilder och tabeller. </para>
<para>Ett exempel på "Vad är det här?" hjälp: </para>
<listitem><para>Till sist, ska alla program ha en handbok. En handbok visas normalt i <application>Hjälpcentralen</application> genom att använda menyn <guimenu>Hjälp</guimenu>. Det betyder att ett helt nytt program dyker upp och avleder användaren från arbetet. Följaktligen ska det bara vara nödvändigt att rådfråga handboken om andra funktioner, som verktygstips och vad är det här hjälp, inte räcker till. Naturligvis har en handbok fördelen att den inte förklarar enskilda isolerade aspekter av användargränssnittet. Den kan istället förklara vissa av programmets aspekter i ett större sammanhang. Handböcker för KDE skrivs med användning av <ulink url="http://i18n.kde.org">DocBook</ulink>-taggspråket. </para></listitem>
<para>Från programmerarens synvinkel, erbjuder Qt ett enkelt gränssnitt för inbyggd hjälp. För att tilldela ett verktygstips till en grafisk komponent, använd klassen <ulink url="kdeapi:qt/QToolTip">QToolTip</ulink>. </para>
<para>Om menyraderna och verktygsraderna skapas som <ulink url="actionpattern.html">åtgärdsmönster</ulink>, hämtas strängen som används som verktygstips från det första argumentet i konstruktorn <ulink url="kdeapi:tdeui/TDEAction.html">TDEAction</ulink>. </para>
<para>Start av <application>Hjälpcentralen</application> är inkapslat i klassen <ulink url="kdeapi:tdecore/TDEApplication">TDEApplication</ulink>. För att visa handboken för programmet, använd bara </para>
<para>Det här visar första sidan med innehållsförteckningen. När du bara vill visa ett visst avsnitt av handboken, kan du ge ytterligare ett argument till <function>invokeHelp()</function>, som avgör ankaret som bläddraren hoppar till. </para>
<para>Begreppet <emphasis>tjänst</emphasis> är ett central idé i KDE:s modulära arkitektur. Det finns ingen strikt teknisk implementering kopplat till benämningen: tjänster kan vara insticksprogram i form av delade bibliotek, eller program som styrs via <ulink url="dcop.html">DCOP</ulink>. Genom att göra anspråk på att vara av en viss <emphasis>tjänsttyp</emphasis>, lovar en tjänst att implementera vissa programmeringsgränssnitt eller funktioner. Med C++ språkbruk, kan man föreställa sig en tjänsttyp som en abstrakt klass, och en tjänst som en implementering av gränssnittet. </para>
<para>Fördelen med den här uppdelningen är uppenbar: Ett program som utnyttjar en tjänsttyp behöver inte känna till möjliga implementeringar av den. Det använder bara programmeringsgränssnittet som hör ihop med tjänsttypen. På detta sätt kan tjänsten som används ändras utan att påverka programmet. Dessutom kan användaren anpassa vilka tjänster som han föredrar för vissa funktioner. </para>
<listitem><para>HTML-uppritningskomponenten som används i <application>Konqueror</application> är en inbäddad komponent som implementerar tjänsttypen <literal>KParts/ReadOnlyPart</literal> och <literal>Browser/View</literal>. </para></listitem>
<listitem><para>I senaste versionen av <application>KDevelop</application>, är största delen av funktionerna paketerade i insticksprogram med tjänsttypen <literal>KDevelop/Part</literal>. Vid start, laddas alla tjänster av den här typen, så att du kan utöka den integrerade utvecklingsmiljön på ett mycket smidigt sätt. </para></listitem>
<listitem><para><application>Konqueror</application> kan visa miniatyrbilder av bilder, HTML-sidor, PDF- och textfiler, om det aktiveras. Den här förmågan kan utökas. Om du vill visa förhandsgranskningsbilder av egna datafiler med en viss Mime-typ, kan du implementera en tjänst med tjänsttypen <classname>ThumbCreator</classname>. </para></listitem>
<para>Naturligtvis karaktäriseras en tjänst inte bara av tjänsttypen som den implementerar, utan också av några <emphasis>egenskaper</emphasis>. Till exempel så gör inte en ThumbCreator bara anspråk på att implementera C++ klassen med typen <classname>ThumbCreator</classname>, den har också en lista med Mime-typer som den är ansvarig för. På samma sätt har KDevelop-delar programspråket de stöder som en egenskap. När ett program begär en tjänsttyp, kan den också ange begränsningar för tjänstens egenskaper. I exemplet ovan, när KDevelop laddar insticksprogram för ett Java-projekt, frågar det bara efter insticksprogram som har egenskapen Java som programspråk. KDE innehåller en fullständig CORBA-liknande <emphasis>handlare</emphasis>, med ett komplext frågespråk, för detta syfte. </para>
<para>Nya tjänsttyper läggs till genom att installera en beskrivning av dem i katalogen <filename>TDEDIR/share/servicetypes</filename>. I det automatiska byggramverket, kan det göras med detta fragment från <filename>Makefile.am</filename>: </para>
<para>Förutom de vanliga posterna, förevisar det här exemplet hur man anger att en tjänst har vissa egenskaper. Varje definition av en egenskap motsvarar en grupp <literal>[PropertyDef::name]</literal> i konfigurationsfilen. I gruppen, anger posten <literal>Type</literal> egenskapens typ. Möjliga typer är allt som kan lagras i en <ulink url="kdeapi:qt/QVariant">QVariant</ulink>. </para>
<para>Innehållet i följande exempelfil, <filename>kdevdoxygen.desktop</filename>, anger insticksprogrammet <literal>KDevDoxygen</literal> med tjänsttypen <literal>KDevelop/Part</literal>: </para>
<para>Förutom de vanliga deklarationerna, är en viktig post <literal>X-TDE-Library</literal>. Den innehåller namnet på libtool-biblioteket (utan filändelsen <literal>.la</literal>). Det fastlägger också namnet på den exporterade symbolen i biblioteket som returnerar objekttillverkaren (med det inledande prefixet <literal>init_</literal>). I ovanstående exempel, måste biblioteket innehålla följande funktion: </para>
<para>Typen för tillverkningsklassen <classname>DoxygenFactory</classname> beror på den specifika tjänsttyp som tjänsten implementerar. I vårt exempel med ett KDevelop-insticksprogram, måste tillverkaren vara en <classname>KDevFactory</classname> (som ärver <classname>KLibFactory</classname>). Vanligare exempel är <ulink url="kdeapi:tdeparts/KParts::Factory">KParts::Factory</ulink> som antas skapa objekten <ulink url="kdeapi:tdeparts/KParts::ReadOnlyPart">KParts::ReadOnlyPart</ulink> eller i de flesta fall det generella <ulink url="kdeapi:tdecore/KLibFactory">KLibFactory</ulink>. </para>
<para>För att kunna använda en delad bibliotekstjänst i ett program, måste du skaffa ett <ulink url="kdeapi:tdeio/KService.html">KService</ulink>-objekt som representerar den. Det här beskrivs i <ulink url="mime.html">avsnittet om Mime-typer</ulink> (och i ett avsnitt om handlaren som återstår att skriva :-) </para>
<para>Med objektet <classname>KService</classname> tillgängligt, kan du mycket lätt ladda biblioteket och få en pekare till dess tillverkningsobjekt. </para>
<para>Från det ögonblicket, beror fortsättningen återigen på tjänsttypen. För generella insticksprogram, skapar man objekt med metoden <ulink url="kdeapi:tdecore/KLibFactory.html#ref3">KLibFactory::create()</ulink>. Med KParts, måste tillverkningspekaren konverteras till det mer specifika KParts::Factory, och dess metod create() måste användas: </para>
<para>En DCOP-tjänst implementeras oftast som ett program som startas när det behövs. Det går därefter in i en snurra och lyssnar efter DCOP-anslutningar. Programmet kan vara interaktivt, men det kan också köra som en demon i bakgrunden under hela eller delar av sin livstid, utan att användaren märker det. Ett exempel på en sådan demon är <literal>tdeio_uiserver</literal>, som implementerar växelverkan med användaren som förloppsdialogrutor för TDEIO-biblioteket. Fördelen med en sådan central demon är att t.ex. nerladdningsförloppet för flera olika filer kan visas i ett fönster, även om nerladdningarna startades från olika program. </para>
<para>En DCOP-tjänst definieras på annat sätt än en tjänst i ett delat bibliotek. Naturligtvis anger den inte ett bibliotek, utan istället ett körbart program. Dessutom anger inte en DCOP-tjänst raden med tjänsttyp, eftersom den startas med namn. Den innehåller ytterligare två rader som ytterligare egenskaper: </para>
<para><literal>X-DCOP-ServiceType</literal> anger hur tjänsten startas. Värdet <literal>Unique</literal> (unik) anger att tjänsten inte får startas mer än en gång. Det betyder att om du försöker starta tjänsten (t.ex. via <ulink url="kdeapi:tdecore/TDEApplication.html#startServiceByName"> TDEApplication::startServiceByName()</ulink>, kontrollerar KDE om den redan har registrerats i DCOP, och använder tjänsten som kör. Om den inte redan är registrerad, startar KDE den och väntar till den har registrerats. Därför kan du omedelbart skicka DCOP-anrop till tjänsten. I detta fall, ska tjänsten implementeras som <ulink url="kdeapi:tdecore/KUniqueApplication.html">KUniqueApplication</ulink>. </para>
<para>Värdet <literal>Multi</literal> för <literal>X-DCOP-ServiceType</literal> anger att flera instanser av tjänsten kan existera samtidigt, så varje försök att starta tjänsten skapar en ny process. Som en sista möjlighet kan värdet <literal>None</literal> (ingen) användas. I detta fall, väntar inte start av tjänsten på att den har registrerats i DCOP. </para>
<para><literal>X-TDE-StartupNotify</literal> ska normalt anges som "false". Annars visar aktivitetsfältet en startbekräftelse, eller, beroende på användarinställningarna, så ändras markören. </para>
<para>Här är definitionen av <literal>tdeio_uiserver</literal>: </para>
<para>Observera att exemplet med ett DCOP-anrop som ges här använder uttrycklig sammansättning av argument. Ofta vill man istället använda en prototyp som skapas av dcopidl2cpp, eftersom det är mycket enklare, och mindre felbenäget. </para>
<para>I exemplet som ges här, startas tjänsten "med namn", dvs. första argumentet till <function>TDEApplication::startServiceByName() </function> är namnet, som det anges på raden <literal>Name</literal> i desktop-filen. Ett alternativ är att använda <function>TDEApplication::startServiceByDesktopName()</function>, som använder namnet på desktop-filen som argument, dvs. i det här fallet <literal>"tdeio_uiserver.desktop"</literal>. </para>
<para>Alla dessa anrop har en lista med webbadresser som andra argument, vilket ges till tjänsten på kommandoraden. Det tredje argumentet är en pekare till en <classname>QString</classname>. Om starten av tjänsten misslyckas, tilldelas det här argumentet det översatta felmeddelandet. </para>
<para>Mime-typer används för att beskriva typ av innehåll för filer eller datafragment. Ursprungligen infördes de för att tillåta att bilder eller ljudfiler, etc. kunde skickas med e-post (Mime betyder "Multipurpose Internet Mail Extensions"). Senare användes systemet också av webbläsare för att avgöra hur data som skickades av en webbserver skulle visas för användaren. En HTML-sida har till exempel Mime-typen "text/html", och en Postskript-fil "application/postscript". I KDE används den här idén på många skilda platser: </para>
<listitem><para>I <application>Konqueror</application>s ikonvy, representeras filer av ikoner. Varje Mime-typ har en viss ikon som den hör ihop med, som visas här. </para></listitem>
<listitem><para>När man klickar på en filikon eller ett filnamn i <application>Konqueror</application>, så visas antingen filen i en inbäddad vy, eller så startas ett program som hör ihop med filtypen. </para></listitem>
<listitem><para>När du drar och släpper någon data från ett program till ett annat (eller inom samma program), kan målet välja att bara acceptera vissa datatyper. Dessutom hanteras bilddata på annat sätt än textdata. </para></listitem>
<listitem><para>Data på klippbordet har en Mime-typ. Traditionellt hanterade X-program bara pixmappar eller text, men med Qt finns det ingen begränsning av datatypen. </para></listitem>
<para>Det är klart från ovanstående exempel, att Mime-hantering är en komplex sak. Först måste en tilldelning av filnamn till Mime-typer göras. KDE går ytterligare ett steg, och låter till och med filinnehåll tilldelas till Mime-typer, i de fall då filnamnet inte är tillgängligt. Därefter måste Mime-typer tilldelas till program eller bibliotek som kan visa eller redigera en fil av en viss typ, eller skapa en miniatyrbild av den. </para>
<para>Det finns en mängd olika programmeringsgränssnitt för att räkna ut Mime-typen för data eller filer. I allmänhet måste man göra en avvägning mellan hastighet och tillförlitlighet. Man kan hitta en filtyp genom att bara titta på filnamnet (i de flesta fallen filändelsen). Filen <filename>foo.jpg</filename> är till exempel normalt "image/jpeg". I de fall där filändelsen har tagits bort är det här inte säkert, och man måste verkligen titta i filens innehåll. Det är förstås långsammare, särskilt för filer som först måste laddas ner via HTTP. Den innehållsbaserade metoden använder filen <filename>TDEDIR/share/mimelnk/magic</filename>, och är därför svår att utöka. Men i allmänhet kan information om Mime-typer lätt göras tillgängligt för systemet, genom att installera en <literal>.desktop</literal>-fil, och den blir effektivt och bekvämt tillgänglig via KDE-biblioteken. </para>
<para>Låt oss definiera typen <literal>"application/x-foo"</literal>, för vårt nya program <application>foobar</application>. För att göra det, måste filen <filename>foo.desktop</filename> skrivas, och installeras i <filename>TDEDIR/share/mimelnk/application</filename>. (Det är den vanliga platsen, som kan variera mellan distributioner). Det här kan göras genom att lägga till följande till <filename>Makefile.am</filename>: </para>
<para>Posten <literal>"Comment"</literal> är avsedd att översättas. Eftersom <filename>.desktop</filename>-filen anger en ikon, bör du också installera en ikon <filename>fooicon.png</filename>, som representerar filen, t.ex. i <application>Konqueror</application>. </para>
<para>I KDE-biblioteken motsvarar en sådan typdefinition en instans av klassen <ulink url="kdeapi:tdeio/KMimeType.html">KMimeType</ulink>. Använd det som i följande exempel: </para>
<programlisting>KMimeType::Ptr type = KMimeType::mimeType("application/x-foo");
<para>Det snabba sättet att avgöra filtypen är <function>KMimeType::findByURL()</function>. Det tittar efter webbadressen och avgör i de flesta fall typen från filändelsen. Med vissa protokoll (t.ex. http, man, info), används inte den mekanismen. CGI-skript på webbservrar som skrivs i Perl, har till exempel ofta ändelsen <literal>.pl</literal>, som skulle ange typen <literal>"text/x-perl"</literal>. Dock är filen som levereras av servern utmatning från skriptet, som normalt är HTML. I sådana fall, returnerar <function>KMimeType::findByURL()</function> Mime-typen <literal>"application/octet-stream"</literal> (tillgänglig via <function>KMimeType::defaultMimeType()</function>), som anger att den misslyckades med att ta reda på typen. </para>
<programlisting>KMimeType::Ptr type = KMimeType::findByURL("/home/bernd/foobar.jpg");
<para>Man kan vilja ta reda på en Mime-typ från filens innehåll i stället för filnamnet. Det är tillförlitligare, men också långsammare, eftersom det kräver att en del av filen läses. Det görs med klassen <ulink url="kdeapi:tdeio/KMimeMagic.html">KMimeMagic</ulink>, som har annorlunda felhantering: </para>
<para>Med en variant av den här funktionen, kan du också avgöra typen för ett minnesblock. Det används till exempel av <application>Kate</application> för att räkna ut färgläggningsläget: </para>
<para>Det här startar ett TDEIO-jobb för att ladda ner en del av filen, och kontrollera detta. Observera att den här funktionen kanske är riktigt långsam och blockerar programmet. Normalt vill man bara använda det om <function>KMimeType::findByURL()</function> returnerade <literal>"application/octet-stream"</literal>. </para>
<para>Å andra sidan, om du inte vill blockera programmet, kan du också uttryckligen starta TDEIO-jobbet och ansluta till några av dess signaler: </para>
<title>Tilldela en Mime-typ till ett program eller tjänst</title>
<para>När ett program installeras, installerar det en <literal>.desktop</literal>-fil, som innehåller en lista med MIME-typer som programmet kan ladda. På samma sätt gör komponenter, som en KPart, den här informationen tillgänglig med sina <literal>.desktop</literal>-tjänstfiler. Alltså finns i allmänhet flera program och komponenter som kan behandla en given MIME-typ. Du kan skaffa en sådan lista från klassen <classname>KServiceTypeProfile</classname>: </para>
<para>Returvärdet från funktionen är en lista med tjänsterbjudanden. Ett <classname>KServiceOffer</classname>-objekt paketerar en KService::Ptr, tillsammans med ett rangordningsnummer. Listan som returneras av <function>KServiceTypeProfile::offers()</function> är ordnad enligt vad användaren föredrar. Användaren kan ändra detta genom att anropa <command>"keditfiletype text/html"</command> eller välja <guimenuitem>Redigera filtyp</guimenuitem> i <application>Konqueror</application>s sammanhangsberoende meny för en HTML-fil. </para>
<para>I exemplet ovan, begärdes en lista med erbjudanden för programmen som stöder <literal>text/html</literal>. Det omfattar, bland annat, HTML-editorer som <application>Quanta Plus</application>. Du kan också ersätta det andra argumentet <literal>"Application"</literal> med <literal>"KParts::ReadOnlyPart"</literal>. I det fallet, får du en lista med inbäddbara komponenter för att presentera HTML-innehåll, till exempel TDEHTML. </para>
<para>I de flesta fall är du inte intresserad av listan med alla erbjudanden om tjänster för en kombination av Mime-typ och tjänsttyp. Det finns en bekvämlighetsfunktion som bara ger dig tjänsterbjudandet som föredras högst: </para>
<para>För ännu mer komplicerade förfrågningar, finns det en fullständig CORBA-liknande <ulink url="kdeapi:tdeio/TDETrader.html">handlare</ulink>. </para>
<para>Under Internetåldern är det ytterst viktigt att skrivbordsprogram kan komma åt resurser via Internet: De ska kunna ladda ner filer från en webbserver, skriva filer till en FTP-server eller läsa e-post från en e-postserver. Ofta kallas möjligheten att komma åt filer oberoende av plats för <emphasis>nätverkstransparens</emphasis>. </para>
<para>I det förflutna implementerades olika ansatser för att nå målet. Det gamla NFS-filsystemet är ett försök att implementera nätverkstransparens på POSIX-gränssnittsnivå. Medan denna ansats fungerar riktigt bra i lokala, tätt kopplade nätverk, skalas det inte för resurser med otillförlitlig och möjligen långsam åtkomst. Här är <emphasis>asynkronism</emphasis> viktig. Medan du väntar på att webbläsaren ska ladda ner en sida, ska inte användargränssnittet blockeras. Dessutom ska inte siduppritningen börja när hela sidan är tillgänglig, utan den ska uppdateras regelbundet medan data anländer. </para>
<para>I KDE-biblioteken implementeras nätverkstransparens med TDEIO-programmeringsgränssnittet. Det centrala begreppet i arkitekturen är ett I/O-<emphasis>jobb</emphasis>. Ett jobb kan kopiera filer, ta bort filer och liknande saker. Så fort ett jobb har startats, fungerar det i bakgrunden och blockerar inte programmet. All kommunikation från jobbet tillbaka till programmet, som att leverera data eller förloppsinformation, görs integrerat i Qt:s händelsesnurra. </para>
<para>Bakgrundsoperationer åstadkoms genom att starta <emphasis>I/O-slavar</emphasis> för att utföra vissa uppgifter. I/O-slavar startas som separata processer, och kommunikation sker via Unix domänuttag. På detta sätt behövs inget flertrådssystem, och instabila slavar kan inte krascha programmet som använder dem. </para>
<para>Filplatser uttrycks med webbadresser som har en omfattande användning. Men i KDE, utökar webbadresser inte bara området med tillgängliga filer utanför det lokala filsystemet. De går också i motsatt riktning, t.ex. kan man bläddra i tar-arkiv. Det åstadkoms genom att nästla webbadresser. En fil i ett tar-arkiv på en HTTP-server skulle kunna ha webbadressen: </para>
<para>I de flesta fall skapas jobb genom att anropa funktioner i TDEIO-namnrymden. Dessa funktioner har en eller två webbadresser som argument, och möjligen också andra nödvändiga parametrar. När jobbet är avslutat, skickar det signalen <literal>result(TDEIO::Job*)</literal>. Efter signalen har skickats, tar jobbet bort sig självt. Därför ser ett typiskt användarfall ut så här: </para>
<listitem><para>Hittar viss information om filen, som storlek, ändringstid och rättigheter. Informationen kan hämtas från TDEIO::StatJob::statResult() efter jobbet har avslutats. </para></listitem>
<listitem><para>Försöker hitta webbadressens Mime-typ. Typen kan hämtas från TDEIO::MimetypeJob::mimetype() efter jobbet har avslutats. </para></listitem>
<para>Båda jobben TDEIO::stat() och TDEIO::listDir() returnerar sina resultat med typerna UDSEntry och UDSEntryList. Den senare är definierad som QValueList<UDSEntry>. Akronymen UDS betyder "Universal directory service" (Allmän katalogtjänst). Principen bakom detta är att katalogposten bara innehåller information som en I/O-slav kan tillhandahålla, inte mer. Till exempel tillhandahåller inte HTTP-slaven någon information om åtkomsträttigheter eller ägare av filer. Istället är en UDSEntry en lista med UDSAtoms. Varje objekt tillhandahåller viss information. Den består av en typ som lagras i m_uds, och antingen ett heltalsvärde i m_long, eller ett strängvärde i m_str, beroende på typen. </para>
<listitem><para>UDS_ACCESS (heltal) - Filens rättigheter, som t.ex. lagras av C-biblioteksfunktionen stat() i fältet st_mode. </para></listitem>
<listitem><para>UDS_FILE_TYPE (heltal): Filtypen, som t.ex. lagras av stat() i fältet st_mode. Därför kan du använda vanliga makron från C-biblioteket, som S_ISDIR, för att kontrollera värdet. Observera att data som tillhandahålls av I/O-slavar motsvarar stat(), inte lstat(), dvs. i fallet med symboliska länkar, så är filtypen här typen på filen som länken pekar ut, inte själva länken. </para></listitem>
<listitem><para>UDS_LINK_DEST (sträng): I fallet med en symbolisk länk, namnet på filen som pekas ut. </para></listitem>
<listitem><para>UDS_MODIFICATION_TIME (heltal) - Tiden (med typen time_t) då filen sist ändrades, som t.ex. lagras av stat() i fältet st_mtime. </para></listitem>
<listitem><para>UDS_ACCESS_TIME (heltal) - Tiden då filen sist användes, som t.ex. lagras av stat() i fältet st_atime. </para></listitem>
<listitem><para>UDS_CREATION_TIME (heltal) - Tiden då filen skapades, som t.ex. lagras av stat() i fältet st_ctime. </para></listitem>
<listitem><para>UDS_URL (sträng) - Tillhandahåller en fils webbadress, om den inte helt enkelt är sammanslagningen av katalogwebbadressen och filnamnet. </para></listitem>
<listitem><para>UDS_GUESSED_MIME_TYPE (sträng): Mime-typ för filen som gissats av slaven. Skillnaden mot föregående typ är att den som tillhandahålls här inte ska betraktas som tillförlitlig (eftersom att avgöra den på ett tillförlitligt sätt skulle vara för dyrt). Klassen KRun kontrollerar till exempel uttryckligen Mime-typen, om den inte har tillförlitlig information. </para></listitem>
<para>Även om sättet att lagra information om filer i en <classname>UDSEntry</classname> är flexibelt och praktiskt ur en I/O-slavs synvinkel, är det rörigt att använda för den somskriver programmet. För att till exempel ta reda på Mime-typen för filen, måste du snurra igenom hela innehållet och kontrollera om <literal>m_uds</literal> är <literal>UDS_MIME_TYPE</literal>. Som tur är, finns ett programmeringsgränssnitt som är mycket enklare att använda: klassen <classname>KFileItem</classname>. </para>
<para>Ofta är det TDEIO:s asynkrona programmeringsgränssnitt för komplext att använda, och därför är inte implementering av fullständig asynkronism prioriterat. I ett program som till exempel bara kan hantera en dokumentfil åt gången, finns det ändå inte mycket som kan göras medan programmet laddar ner en fil. I dessa enkla fall, finns ett mycket enklare programmeringsgränssnitt, i form av ett antal statiska funktioner i TDEIO::NetAccess. För att till exempel kopiera en fil, använd: </para>
<para>Funktionen returnerar efter hela kopieringsprocessen har avslutats. Ändå så tillhandahåller den här metoden en förloppsdialogruta, och den försäkrar att programmet behandlar omritningshändelser. </para>
<para>En särskilt intressant kombination av funktioner är <function>download()</function> tillsammans med <function>removeTempFile()</function>. Den första laddar ner en fil från en given webbadress, och lagrar den i en tillfällig fil med ett unikt namn. Namnet lagras som det andra argumentet. <emphasis>Om</emphasis> webbadressen är lokal, laddas inte filen ner, utan istället sätts det andra argumentet till det lokala filnamnet. Funktionen <function>removeTempFile()</function> tar bort filen som anges av argumentet, om filen skapades av den föregående nerladdningen. Om det inte är fallet, gör den ingenting. På så sätt får man ett mycket enkelt kodfragment för att ladda filer, oberoende av deras plats: </para>
<para>Som märks ovan, är gränssnittet för I/O-jobb ganska abstrakt och hanterar inte något utbyte av information mellan program och I/O-slav som är protokollspecifikt. Det är inte alltid lämpligt. Man kan till exempel ge vissa parametrar till HTTP-slaven för att styra dess cachebeteende eller skicka en mängd kakor tillsammans med begäran. För detta behov har ett koncept med metadata införts. När ett jobb skapas, kan man anpassa det genom att lägga till metadata till det. Varje metadataobjekt består av ett par med nyckel och värde. För att till exempel förhindra HTTP-slaven från att ladda en webbsida från cachen, kan du använda: </para>
<para>Samma teknik används åt andra hållet, dvs. för kommunikation från slaven till programmet. Metoden <function>Job::queryMetaData()</function> frågar efter värdet på en viss nyckel som levereras av slaven. För HTTP-slaven, är ett sådant exempel nyckeln <literal>"modified"</literal> (ändrad), som innehåller datumet då webbsidan sist ändrades (i form av en sträng). Ett exempel på hur det kan användas är följande: </para>
<para>När TDEIO-programmeringsgränssnittet används, behöver du oftast inte hantera detaljerna med att starta I/O-slavar och kommunicera med dem. Det normala användningsfallet är att starta ett jobb med några parametrar, och hantera signalerna som jobbet skickar. </para>
<para>Bakom ridån är scenariot mycket mer komplicerat. När du skapar ett jobb, läggs det i en kö. När programmet går tillbaka till händelsesnurran, tilldelar TDEIO slavprocesser för jobben i kön. För det första jobbet som startas, är detta trivialt: en I/O-slav för lämpligt protokoll startas. Efter jobbet (som en nerladdning från en HTTP-server) har avslutats, tas det dock inte omedelbart bort. Istället läggs det i en grupp med lediga slavar och tas bort efter en viss tid utan aktivitet (för närvarande tre minuter). Om en ny begäran för samma värddator och protokoll anländer, återanvänds slaven. Den uppenbara fördelen är att vid en serie jobb med samma värddator, sparar man in kostnaden för att skapa nya processer, och möjligen också att genomgå en behörighetskontroll. </para>
<para>Naturligtvis är återanvändning bara möjlig när den befintliga slaven redan har avslutat sitt tidigare jobb. Om en ny begäran anländer medan en befintlig slavprocess fortfarande kör, måste en ny process startas och användas. Med användningen i exemplen ovan av programmeringsgränssnittet, finns det ingen begränsning för att skapa nya slavprocesser: om man startar en serie nerladdningar av 20 olika filer i rad, skapar TDEIO 20 slavprocesser. Den här metoden att tilldela slavar till jobb kallas <emphasis>direkt</emphasis>. Det är inte alltid den mest lämpliga metoden, eftersom den kan behöva mycket minne och ge hög last både på klient- och serverdatorn. </para>
<para>Så det finns ett annat sätt. Man kan <emphasis>schemalägga</emphasis> jobb. Om man gör det, skapas bara ett begränsat antal (för närvarande tre) slavprocesser för ett protokoll. Om du skapar fler jobb än så, läggs de i en kö och processas när en slavprocess blir ledig. Det görs på följande sätt: </para>
<para>En tredje möjlighet är <emphasis>anslutningsorienterat</emphasis>. Till exempel för IMAP-slaven, är det inte vettigt att starta flera processer för samma server. Bara en IMAP-anslutning åt gången får upprätthållas. I detta fall måste programmet uttryckligen hantera slavbegreppet. Det måste tilldela en slav för en viss anslutning och sedan tilldela alla jobb som ska gå genom samma anslutning till samma slav. Det kan återigen enkelt åstadkommas genom att använda TDEIO::Scheduler: </para>
<para>I det följande beskriver vi hur du kan lägga till en ny I/O-slav i systemet. På liknande sätt som tjänster, annonseras I/O-slavar för systemet genom att installera en liten konfigurationsfil. Följande fragment av Makefile.am installerar FTP-protokollet: </para>
<para>Posten <literal>"protocol"</literal> anger vilket protokoll som slaven ansvarar för. <literal>"exec"</literal> är (i motsats mot vad man naivt kan förvänta sig) namnet på biblioteket som implementerar slaven. När det är meningen att slaven ska starta, startas programmet <command>"tdeinit"</command>, som i sin tur laddar biblioteket i sitt adressrum. I praktiken kan du betrakta slaven som kör som en separat process, även om den är implementerad som ett bibliotek. Fördelen med den här mekanismen är att den sparar mycket minne, och reducerar tiden som behövs för länkning under körning. </para>
<para>Raderna "input" och "output" används inte för närvarande. </para>
<para>Återstående rader i filen <literal>.protocol</literal> anger vilka förmågor slaven har. I allmänhet är de funktioner som slaven måste implementera mycket enklare än de funktioner som TDEIO-programmeringsgränssnittet tillhandahåller programmet. Orsaken till detta är att komplexa jobb schemaläggs som en följd av deljobb. För att till exempel lista en katalog rekursivt, startas ett jobb för toppnivåkatalogen. För varje underkatalog som rapporteras tillbaka, startas nya underjobb. Schemaläggning i TDEIO försäkrar att inte för många jobb är aktiva samtidigt. På liknande sätt, för att kopiera en fil med ett protokoll som inte stöder kopiering direkt (som <literal>FTP</literal>-protokollet), kan TDEIO läsa källfilen och sedan skriva data till destinationsfilen. För att detta ska fungera, måste <literal>.protocol</literal> annonsera åtgärderna som slaven stöder. </para>
<para>Eftersom slavar laddas som delade bibliotek, men utgör fullständiga program, ser deras kodramverk något annorlunda ut jämfört med normala delade biblioteksinsticksprogram. Funktionen som anropas för att starta slaven kallas <function>kdemain()</function>. Den här funktionen gör en del initieringar, och hoppar sedan till en händelsesnurra och väntar på begäran från programmet som använder den. Det här ser ut som följer: </para>
<programlisting>extern "C" { int kdemain(int argc, char **argv); }
<para>Slavar implementeras som delklasser till <classname>TDEIO::SlaveBase</classname> (FtpSlave i exemplet ovan). På så sätt motsvarar åtgärderna i <literal>.protocol</literal> vissa virtuella funktioner i <classname>TDEIO::SlaveBase</classname> som implementeringen av slaven måste implementera om. Här är en lista med möjliga åtgärder och motsvarande virtuella funktioner: </para>
<para>Dessutom finns det funktioner som kan implementeras om, och inte listas i filen <literal>.protocol</literal>. För dessa åtgärder, avgör TDEIO automatiskt om de stöds eller inte (dvs. standardimplementationen returnerar ett fel). </para>
<para>Alla dessa implementationer ska sluta med ett av två anrop: Om åtgärden lyckades, ska de anropa <literal>finished()</literal>. Om ett fel uppstod, ska de anropa <literal>error()</literal> med en felkod som första argument och en sträng som andra. Möjliga felkoder listats som uppräkningstypen <type>TDEIO::Error</type>. Det andra argumentet är oftast webbadressen i fråga. Den används t.ex. i <function>TDEIO::Job::showErrorDialog()</function> för att parametrisera felmeddelandet som är läsbart av användaren. </para>
<para>För slavar som motsvarar nätverksprotokoll, kan det vara intressant att implementera om metoden <function>SlaveBase::setHost()</function>. Den anropas för att tala om för slavprocessen om värddator och port, och användarnamn och lösenord att använda för inloggning. I allmänhet kan metadata som anges av programmet hämtas med <function>SlaveBase::metaData()</function>. Du kan kontrollera om metadata med en viss nyckel finns med <function>SlaveBase::hasMetaData()</function>. </para>
<listitem><para><function>get()</function> skickar datablock. Det görs med <function>data()</function>, som använder argumentet <classname>QByteArray</classname>. Du behöver förstås inte skicka all data på en gång. Om du skickar en stor fil, anropa <function>data()</function> med mindre datablock, så att programmet kan behandla dem. Anropa <function>finished()</function> när överföringen är klar. </para></listitem>
<listitem><para><function>listDir()</function> rapporterar information om posterna i en katalog. Anropa <function>listEntries()</function> med en <classname>TDEIO::UDSEntryList</classname> som argument, för detta syfte. På motsvarande sätt som <function>data()</function>, kan du anropa den flera gånger. När du är klar, anropa <function>listEntry()</function> med andra argumentet satt till true. Du kan också anropa <function>totalSize()</function> för att rapportera totalt antal katalogposter, om det är känt. </para></listitem>
<listitem><para><function>stat()</function> rapporterar information om en fil, som storlek, Mime-typ, etc. Sådan information paketeras i en <classname>TDEIO::UDSEntry</classname>, som beskrivs nedan. Använd <function>statEntry()</function> för att skicka ett sådant objekt till programmet. </para></listitem>
<listitem><para><function>mimetype()</function> anropar <function>mimeType()</function> med ett strängargument. </para></listitem>
<listitem><para><function>get()</function> och <function>copy()</function> kan vilja tillhandahålla förloppsinformation. Det görs med metoderna <function>totalSize()</function>, <function>processedSize()</function> och <function>speed()</function>. Den totala storleken och behandlade storleken rapporteras som byte, och hastigheten som byte per sekund. </para></listitem>
<listitem><para>Du kan skicka godtyckliga nyckel/värdepar av metadata med <function>setMetaData()</function>. </para></listitem>
<para>Ibland måste en slav kommunicera med användaren. Exempel kan vara informativa meddelanden, dialogrutor för behörighetskontroll och bekräftelsedialogrutor när en fil håller på att skrivas över. </para>
<listitem><para><function>infoMessage()</function>: Det här är för informativ återmatning, som meddelandet "Hämtar data från <värddator>" från HTTP-slaven, som ofta visas i programmets statusrad. På programsidan, motsvarar metoden signalen <function>TDEIO::Job::infoMessage()</function>. </para></listitem>
<listitem><para><function>warning()</function>: Visar en varning i en meddelanderuta med <function>KMessageBox::information()</function>. Om en meddelanderuta fortfarande visas från ett tidigare anrop av warning() från samma underprocess, händer ingenting. </para></listitem>
<listitem><para><function>messageBox()</function>: Den här är utförligare än den tidigare metoden. Den tillåter att en meddelanderuta med text och rubrik och några knappar visas. Se uppräkningstypen <type>SlaveBase::MessageBoxType</type> som referens. </para></listitem>
<listitem><para><function>openPassDlg()</function>: Visar en dialogruta för att mata in användarnamn och lösenord. </para></listitem>