]> Översikt över KDE:s arkitektur Bernd Gehrmann
bernd@kdevelop.org
2001 2002 Bernd Gehrmann &FDLNotice; Den här dokumentationen ger en översikt över KDE-utvecklingsmiljön. KDE arkitektur utveckling programmering
Biblioteksstruktur Bibliotek ordnade efter namn tdecore 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. tdeui Biblioteket tdeui 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. tdeio Biblioteket tdeio 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. kjs Biblioteket kjs tillhandahåller en implementering av Javaskript. tdehtml Biblioteket tdehtml innehåller TDEHTML-delen, en HTML-bläddringskomponent, DOM-gränssnitt och tolk, inklusive gränssnitt till Java och Javaskript. Grupperade klasser Centralt programskelett: klasser som behövs av nästan alla program. <ulink url="kdeapi:tdecore/TDEApplication">TDEApplication</ulink> Initierar och styr ett KDE-program. <ulink url="kdeapi:tdecore/KUniqueApplication">KUniqueApplication</ulink> Försäkrar att bara en instans av ett program kan köra samtidigt. <ulink url="kdeapi:tdecore/TDEAboutData">TDEAboutData</ulink> Innehåller information för dialogrutan Om. <ulink url="kdeapi:tdecore/TDECmdLineArgs">TDECmdLineArgs</ulink> Behandling av kommandoradsväljare. Konfigurationsinställningar: åtkomst till KDE:s hierarkiska konfigurationsdatabas, globala inställningar och programresurser. <ulink url="kdeapi:tdecore/TDEConfig">TDEConfig</ulink> Ger tillgång till KDE:s konfigurationsdatabas. <ulink url="kdeapi:tdecore/KSimpleConfig">KSimpleConfig</ulink> Åtkomst av enkla, icke-hierarkiska konfigurationsfiler. <ulink url="kdeapi:tdecore/KDesktopFile">KDesktopFile</ulink> Åtkomst till .desktop-filer. <ulink url="kdeapi:tdecore/TDEGlobalSettings">TDEGlobalSettings</ulink> Bekväm åtkomst till inställningar som inte är programspecifika. Fil- och webbadresshantering: avkodning av webbadresser, tillfälliga filer, etc. <ulink url="kdeapi:tdecore/KURL">KURL</ulink> Representerar och tolkar webbadresser. <ulink url="kdeapi:tdecore/KTempFile">KTempFile</ulink> Skapar unika filer för tillfällig data. <ulink url="kdeapi:tdecore/KSaveFile">KSaveFile</ulink> Tillåter att filer sparas odelbart. Interprocesskommunikation: DCOP-hjälpklasser och start av underprocesser. <ulink url="kdeapi:tdecore/TDEProcess">TDEProcess</ulink> Startar och styr underprocesser. <ulink url="kdeapi:tdecore/KShellProcess">KShellProcess</ulink> Startar underprocesser via ett skal. <ulink url="kdeapi:tdesu/PtyProcess">PtyProcess</ulink> Kommunikation med underprocesser via en pseudoterminal. <ulink url="kdeapi:tdecore/KIPC">KIPC</ulink> Enkel IPC-mekanism som använder X11-klientmeddelanden. <ulink url="kdeapi:dcop/DCOPClient">DCOPClient</ulink> DCOP-meddelanden. <ulink url="kdeapi:tdecore/KDCOPPropertyProxy">KDCOPPropertyProxy</ulink> En proxyklass som offentliggör Qt-egenskaper via DCOP. <ulink url="kdeapi:tdeui/KDCOPActionProxy">KDCOPActionProxy</ulink> En proxyklass som offentliggör ett DCOP-gränssnitt för åtgärder. Verktygsklasser: minneshantering, reguljära uttryck, stränghantering, slumptal. <ulink url="kdeapi:tdecore/KRegExp">KRegExp</ulink> Matchning av POSIX reguljära uttryck. <ulink url="kdeapi:tdecore/KStringHandler">KStringHandler</ulink> Ett överdådigt gränssnitt för stränghantering. <ulink url="kdeapi:tdecore/TDEZoneAllocator">TDEZoneAllocator</ulink> Effektiv minnestilldelning för stora grupper av små objekt. <ulink url="kdeapi:tdecore/KRandomSequence">KRandomSequence</ulink> Skapa pseudoslumptal. Snabbtangenter: klasser som hjälper till att skapa överensstämmande tangentbindningar över hela skrivbordet. <ulink url="kdeapi:tdecore/TDEAccel">TDEAccel</ulink> Samling av snabbtangenter. <ulink url="kdeapi:tdecore/TDEStdAccel">TDEStdAccel</ulink> Lätt åtkomst till de vanliga snabbtangenterna. <ulink url="kdeapi:tdecore/TDEGlobalAccel"></ulink> Samling av snabbtangenter som gäller för hela systemet. Bildbehandling: ikonladdning och -hantering. <ulink url="kdeapi:tdecore/TDEIconLoader">TDEIconLoader</ulink> Laddar ikoner som överensstämmer med teman. <ulink url="kdeapi:tdecore/TDEIconTheme">TDEIconTheme</ulink> Hjälpklasser för TDEIconLoader. <ulink url="kdeapi:tdecore/KPixmap">KPixmap</ulink> En pixmapp-klass med utökade gittermöjligheter. <ulink url="kdeapi:tdeui/KPixmapEffect">KPixmapEffect</ulink> Pixmappeffekter som toning och mönster. <ulink url="kdeapi:tdeui/KPixmapIO">KPixmapIO</ulink> Snabb konvertering mellan QImage och QPixmap. Drag och släpp: dra objekt för färger och webbadresser. <ulink url="kdeapi:tdecore/KURLDrag">KURLDrag</ulink> Dragobjekt för webbadresser. <ulink url="kdeapi:tdeui/KColorDrag">KColorDrag</ulink> Dragobjekt för färger. <ulink url="kdeapi:tdecore/KMultipleDrag">KMultipleDrag</ulink> Tillåter att dragobjekt skapas från flera andra. Automatisk komplettering <ulink url="kdeapi:tdecore/TDECompletion">TDECompletion</ulink> Generell automatisk komplettering av strängar. <ulink url="kdeapi:tdeio/KURLCompletion">KURLCompletion</ulink> Automatisk komplettering av webbadresser. <ulink url="kdeapi:tdeio/KShellCompletion">KShellCompletion</ulink> Automatisk komplettering för körbara program. Grafiska komponenter: klasser för listvyer, linjaler, färgval, etc. <ulink url="kdeapi:tdeui/TDEListView">TDEListView</ulink> En version av QListView som följer KDE:s systeminställningar. <ulink url="kdeapi:tdeui/TDEListView">TDEListBox</ulink> En version av QListBox som följer KDE:s systeminställningar. <ulink url="kdeapi:tdeui/TDEListView">TDEIconView</ulink> En version av QIconView som följer KDE:s systeminställningar. <ulink url="kdeapi:tdeui/TDEListView">KLineEdit</ulink> En version av QLineEdit med stöd för komplettering. <ulink url="kdeapi:tdeui/KComboBox">KComboBox</ulink> En version av QComboBox med stöd för komplettering. <ulink url="kdeapi:tdeui/TDEFontCombo">TDEFontCombo</ulink> En kombinationsruta för att välja teckensnitt. <ulink url="kdeapi:tdeui/KColorCombo">KColorCombo</ulink> En kombinationsruta för att välja färger. <ulink url="kdeapi:tdeui/KColorButton">KColorButton</ulink> En knapp för att välja färger. <ulink url="kdeapi:tdeui/KURLCombo">KURLCombo</ulink> En kombinationsruta för att välja filnamn och webbadresser. <ulink url="kdeapi:tdefile/KURLRequester">KURLRequester</ulink> En radeditor för att välja filnamn och webbadresser. <ulink url="kdeapi:tdeui/KRuler">KRuler</ulink> En linjalkomponent. <ulink url="kdeapi:tdeui/KAnimWidget">KAnimWidget</ulink> Animeringar. <ulink url="kdeapi:tdeui/KNumInput">KNumInput</ulink> En komponent för att mata in tal. <ulink url="kdeapi:tdeui/KPasswordEdit">KPasswordEdit</ulink> En komponent för att mata in lösenord. Dialogrutor: dialogrutor med fullständig funktion för val av filer, färger och teckensnitt. <ulink url="kdeapi:tdefile/KFileDialog">KFileDialog</ulink> En dialogruta för val av filer. <ulink url="kdeapi:tdeui/KColorDialog">KColorDialog</ulink> En dialogruta för val av färger. <ulink url="kdeapi:tdeui/TDEFontDialog">TDEFontDialog</ulink> En dialogruta för val av teckensnitt. <ulink url="kdeapi:tdefile/TDEIconDialog">TDEIconDialog</ulink> En dialogruta för val av ikoner. <ulink url="kdeapi:tdeui/KKeyDialog">KKeyDialog</ulink> En dialogruta för att redigera tangentbordsbindningar. <ulink url="kdeapi:tdeui/KEditToolBar">KEditToolBar</ulink> En dialogruta för att redigera verktygsrader. <ulink url="kdeapi:tdeui/KTipDialog">KTipDialog</ulink> En dialogruta med dagens tips. <ulink url="kdeapi:tdeui/TDEAboutDialog">TDEAboutDialog</ulink> En dialogruta om ett program. <ulink url="kdeapi:tdeui/KLineEditDlg">KLineEditDlg</ulink> En enkel dialogruta för att mata in text. <ulink url="kdeapi:tdefile/KURLRequesterDlg">KURLRequesterDlg</ulink> En enkel dialogruta för att mata in webbadresser. <ulink url="kdeapi:tdeui/KMessageBox">KMessageBox</ulink> En dialogruta för att meddela fel och varningar. <ulink url="kdeapi:tdeui/KPasswordDialog">KPasswordDialog</ulink> En dialogruta för att mata in lösenord. Åtgärder och grafiskt XML-gränssnitt <ulink url="kdeapi:tdeui/TDEAction">TDEAction</ulink> En abstraktion av en åtgärd som kan anslutas till menyrader och verktygsrader. <ulink url="kdeapi:tdeui/TDEActionCollection">TDEActionCollection</ulink> En samling åtgärder. <ulink url="kdeapi:tdeui/KXMLGUIClient">KXMLGUIClient</ulink> Ett fragment av ett grafiskt gränssnitt som består av en åtgärd och ett DOM-träd som motsvarar dess plats i det grafiska gränssnittet. <ulink url="kdeapi:tdeparts/KPartManager">KPartManager</ulink> Hanterar aktivering av klienter till det grafiska XML-gränssnittet. Insticksprogram och komponenter <ulink url="kdeapi:tdecore/KLibrary">KLibrary</ulink> Representerar ett dynamiskt laddat bibliotek. <ulink url="kdeapi:tdecore/KLibrary">KLibLoader</ulink> Laddning av delade bibliotek. <ulink url="kdeapi:tdecore/KLibFactory">KLibFactory</ulink> Tillverkning av objekt för Insticksprogram. <ulink url="kdeapi:tdeio/KServiceType">KServiceType</ulink> Representerar en tjänsttyp. <ulink url="kdeapi:tdeio/KService">KService</ulink> Representerar en tjänst. <ulink url="kdeapi:tdeio/KMimeType">KMimeType</ulink> Representerar en Mime-typ. <ulink url="kdeapi:tdeio/KServiceTypeProfile">KServiceTypeProfile</ulink> Användarinställningar för tilldelningar av Mime-typer. <ulink url="kdeapi:tdeio/KServiceTypeProfile">TDETrader</ulink> Förfrågningar om tjänster. Grafik Lågnivågrafik med QPainter. Uppritning med QPainter 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. Den centrala grafiska klassen för att rita tvådimensionellt med Qt är QPainter. Den kan rita på en QPaintDevice. Det finns tre möjliga ritenheter implementerade: En är QWidget, som representerar en grafisk komponent på skärmen. Den andra är QPrinter, som representerar en skrivare, och producerar Postskript-utmatning. Den tredje är klassen QPicture, 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. 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. void MinKomponent::paintEvent() { QPainter p(this); // Ställ in // Använd } 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 enhetsmått för att beräkna koordinater. Omvandlingar 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: QPainter::setWorldMatrix() kan användas för att ange den här 3x3 matrisen i ekvationen, som har typen QWMatrix. 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: Förflyttningar 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: Skalning 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: Genom att ge en av parametrarna ett negativt värde, kan man åstadkomma spegling av koordinatsystemet. Skjuvning En förvrängning av koordinatsystemet med två parametrar. En skjuvningsomvandling kan göras genom att anropa m.shear(sh, sv), vilket motsvarar matrisen: Rotation 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: Observera att rotation är ekvivalent med en kombination av skalning och skjuvning. Här är några bilder som visar effekten av de grundläggande omvandlingarna för vår maskot: a) Normal b) Roterad 30 grader c) Skjuvad med 0,4 d) Speglad 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. Ange streckegenskaper 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 QPen-objekt. Egenskaperna som lagras i det är en stil, en färg, en sammanfogningsstil och en ändstil. Pennstilen är en medlem av uppräkningstypen Qt::PenStyle. och kan ha något av följande värden: Sammanfogningsstilen är en medlem av uppräkningstypen Qt::PenJoinStyle. Den anger hur förbindelsen mellan flera linjer som sätts samman ritas. Den kan ha något av följande värden: a) MiterJoin c) BevelJoin b) RoundJoin Ändstilen är en medlem av uppräkningstypen Qt::PenCapStyle och anger hur linjernas ändpunkter ritas. Den antar något värde från följande tabell: a) FlatCap b) SquareCap c) RoundCap Ange fyllnadsegenskaper 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 QBrush-objekt. Borstar kan skapas på fyra olika sätt: QBrush::QBrush(): detta skapar en borste som inte fyller i former. QBrush::QBrush(BrushStyle): detta skapar en svart borste, med ett av de fördefinierade mönstren som visas nedan. QBrush::QBrush(const QColor &, BrushStyle): detta skapar en färgad borste, med ett av de fördefinierade mönstren som visas nedan. QBrush::QBrush(const QColor &, const QPixmap): detta skapar en färgad borste, med det egna mönstret som anges som andra parameter. En standardborststil från uppräkningstypen Qt::BrushStyle. Här är en bild av alla fördefinierade mönster: Ytterligare ett sätt att anpassa en borstes beteende är att använda funktionen QPainter::setBrushOrigin(). Färg Färger har betydelse både när kurvor ritas, och när former fylls i. Färger representeras av klassen QColor 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. 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. Övriga inställningar 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 RasterOp. 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". Rita grafiska primitiver 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 QRect som argument, eller en uppsättning med fyra heltal. Rita en ensam punkt: drawPoint(). Rita linjer: drawLine(), drawLineSegments() och drawPolyLine(). Rita och fylla i rektanglar: drawRect(), drawRoundRect(), fillRect() och eraseRect(). Rita och fylla i cirklar, ellipser och delar av dem: drawEllipse(), drawArc(), drawPie och drawChord(). Rita och fylla i generella polygoner: drawPolygon(). Rita Bezierkurvor: drawQuadBezier() [drawCubicBezier i Qt 3.0]. Rita pixmappar och bilder Qt tillhandahåller två mycket olika klasser för att representera bilder. QPixmap 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 mycket 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 bitBlt för att överföra det till komponenten. För komplexa omritningar, hjälper detta till att undvika flimmer. I motsats till detta, finns QImage-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. Rita text 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 Qt::AlignmentFlags och Qt::TextFlags. 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. Ett mer avancerat sätt att visa text med taggar, är klassen QSimpleRichText. 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 QStyleSheet (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(). Strukturerad grafik med QCanvas 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. Som alternativ innehåller Qt klassen QCanvas, 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 QCanvasItem eller någon av dess mer specialiserade delklasser. En duk kan visas på skärmen genom en eller flera komponenter från klassen QCanvasView, 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. 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: 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. En handledning om hur QCanvas används för att skriva spel baserade på småfigurer finns här. 3D-grafik med OpenGL Lågnivågränssnitt De-facto standarden för att rita upp 3D-grafik nu för tiden är OpenGL. 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 GL-sammanhang, och har ingen interaktion med verktygslådan eller miljön. Qt erbjuder den grafiska komponenten QGLWidget, 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. 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. 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. 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. 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. 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. Qt innehåller några exempel på användning av QGLWidget i demo-exemplen. En samling handledningar finns här, och mer information samt en OpenGL-referens finns på OpenGL:s hemsida. Högnivågränssnitt 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, Coin, som åtföljs av en verktygsanpassning till Qt, som heter SoQt. Det grundläggande konceptet i Open Inventor är en scen. En scen kan laddas från disk, och sparas med ett särskilt format, nära besläktat med VRML. En scen består av en samling objekt som kallas noder. 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. En introduktion till Inventor finns här (i allmänhet kan du ersätta alla SoXt som omnämns i artikeln med SoQt). Användargränssnitt Åtgärdsmönstret Definiera menyer och verktygsrader i XML Inledning Medan åtgärdsmönster 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. Det här problemet löses med en samling klasser som kallas grafiskt XML-gränssnitt. 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 Arkiv Öppna eller Hjälp Om) 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. KDE:s klass för toppnivåfönster, TDEMainWindow, ärver KXMLGUIClient, 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 actionCollection() som förälder. Ett anrop till createGUI() bygger sedan hela uppsättningen menyer och verktygsrader som definieras av programmets XML-fil (vanligtvis med ändelsen ui.rc). Ett exempel: Menyn i Kview I det följande använder vi KDE:s bildvisare Kview som exempel. Den har en ui.rc-fil som heter kviewui.rc, som installeras med ett fragment från Makefile.am rcdir = $(kde_datadir)/kview rc_DATA = kviewui.rc Här är ett utdrag ur filen kviewui.rc. För enkelhetens skull, visar vi bara definitionen för menyn View. <!DOCTYPE kpartgui> <kpartgui name="kview"> <MenuBar> <Menu name="view" > <Action name="zoom50" /> <Action name="zoom100" /> <Action name="zoom200" /> <Action name="zoomMaxpect" /> <Separator/> <Action name="fullscreen" /> </Menu> </MenuBar> </kpartgui> Motsvarande del av att skapa detta i C++ är: KStdAction::zoomIn ( this, SLOT(slotZoomIn()), actionCollection() ); KStdAction::zoomOut ( this, SLOT(slotZoomOut()), actionCollection() ); KStdAction::zoom ( this, SLOT(slotZoom()), actionCollection() ); new TDEAction ( i18n("&Half size"), ALT+Key_0, this, SLOT(slotHalfSize()), actionCollection(), "zoom50" ); new TDEAction ( i18n("&Normal size"), ALT+Key_1, this, SLOT(slotDoubleSize()), actionCollection(), "zoom100" ); new TDEAction ( i18n("&Double size"), ALT+Key_2, this, SLOT(slotDoubleSize()), actionCollection(), "zoom200" ); new TDEAction ( i18n("&Fill Screen"), ALT+Key_3, this, SLOT(slotFillScreen()), actionCollection(), "zoomMaxpect" ); new TDEAction ( i18n("Fullscreen &Mode"), CTRL+SHIFT+Key_F, this, SLOT(slotFullScreen()), actionCollection(), "fullscreen" ); Menyn View som skapas av den här definitionen av det grafiska gränssnittet ser ut som visas av den här skärmbilden: XML-filen börjar med en dokumenttypdeklaration. DTD:n för kpartgui finns i tdelibs-källkoden i tdeui/kpartgui.dtd. 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 ui.rc, ser KDE till att alla anpassade versioner av filen slängs och att den nya filen används istället. Nästa rad, <MenuBar>, innehåller en deklaration av en menyrad. Du kan också infoga hur många <ToolBar>-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. Kview har till exempel en undermeny med rubriken "Image", som deklareras enligt följande: <Menu name="image" > <text>&amp;Image</text> ... </Menu> I KDE:s automatiska byggramverk, plockas sådana rubriker automatiskt ut och placeras i programmets .po-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;". Låt oss återvända till exemplet. Kviews meny Visa innehåller ett antal egna åtgärder zoom50, zoom100, zoom200, zoomMaxpect och fullscreen, deklarerade med elementet <Action>. Skiljelinjen i skärmbilderna motsvarar elementet <Separator>. Du märker att vissa menyalternativ inte har ett motsvarande element i XML-filen. De är standardåtgärder. Standardåtgärder skapas av klassen KStdAction. 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 tdeui/ui_standards.rc i tdelibs-källkoden. Ett exempel: Verktygsrader i Konqueror För beskrivningen av verktygsrader, byter vi till Konquerors definition av grafiskt gränssnitt. Det här utdraget definierar platsraden, som innehåller inmatningsfältet för webbadresser. <ToolBar name="locationToolBar" fullWidth="true" newline="true" > <text>Location Toolbar</text> <Action name="clear_location" /> <Action name="location_label" /> <Action name="toolbar_url_combo" /> <Action name="go_url" /> </ToolBar> Det första vi märker är att det finns många fler egenskaper än för menyrader. De omfattar: fullWidth: 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. newline: 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. noEdit: Normalt kan verktygsrader anpassas av användaren, t.ex. med Inställningar Anpassa verktygsrader i Konqueror. 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. Konquerors bokmärkesverktygsrad. iconText: 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). hidden: Om det här är "true", så visas inte verktygsraden från början, och måste aktiveras av något menyalternativ. position: 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). Dynamiska menyer XML kan naturligtvis bara innehålla en statisk beskrivning av ett användargränssnitt. Ofta finns det menyer som ändras under körning. Konquerors meny Plats innehåller till exempel en uppsättning alternativ Öppna med ..., 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 åtgärdslistor. 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 Konquerors XML-fil: <Menu name="file"> <text>&amp;Location</text> ... <ActionList name="openwith"> ... </Menu> Funktionen KXMLGUIClient::plugActionList() används sedan för att lägga till åtgärder som ska visas, medan funktionen KXMLGuiClient::unplugActionList() tar bort alla anslutna åtgärder. Rutinen som är ansvarig för att göra uppdateringarna ser ut på följande sätt: void MainWindow::updateOpenWithActions() { unplugActionList("openwith"); openWithActions.clear(); for ( /* Snurra för relevanta tjänster */ ) { TDEAction *action = new TDEAction( ...); openWithActions.append(action); } plugActionList("openwith", openWithActions); } Observera att i motsats till statiska åtgärder, så skapas inte 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 openWithActions.setAutoDelete(true) i exemplet ovan. Sammanhangsberoende menyer 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 createGUI() (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: void MainWindow::popupRequested() { QWidget *w = factory()->container("context_popup", this); QPopupMenu *popup = static_cast<QPopupMenu *>(w); popup->exec(QCursor::pos()); } Metoden KXMLGUIFactory::container() 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: ... <Menu name="context_popup"> <Action name="file_add"/> <Action name="file_remove"/> </Menu> ... Tillhandahålla inbyggd hjälp 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: 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. "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å Skift F1, 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. 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. Fördelen med "Vad är det här?" hjälpfönster som de erbjuds av Qt och KDE, är att de kan innehålla formaterad text, dvs. de kan innehålla olika teckensnitt, text med fetstil och kursiv stil, och till och med bilder och tabeller. Ett exempel på "Vad är det här?" hjälp: Till sist, ska alla program ha en handbok. En handbok visas normalt i Hjälpcentralen genom att använda menyn Hjälp. 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 DocBook-taggspråket. 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 QToolTip. QToolTip::add(w, i18n("This widget does something.")) Om menyraderna och verktygsraderna skapas som åtgärdsmönster, hämtas strängen som används som verktygstips från det första argumentet i konstruktorn TDEAction. action = new TDEAction(i18n("&Delete"), "editdelete", SHIFT+Key_Delete, actionCollection(), "del") Här är det också möjligt att tilldela en text som visas i statusraden när motsvarande menyalternativ markeras: action->setStatusText(i18n("Deletes the marked file")) Programmeringsgränssnittet för "Vad är det här?" är mycket likt. Använd följande kod i dialogrutor: QWhatsThis::add(w, i18n("<qt>This demonstrates <b>Qt</b>'s" " rich text engine.<ul>" "<li>Foo</li>" "<li>Bar</li>" "</ul></qt>")) För menyalternativ, använd action->setWhatsThis(i18n("Deletes the marked file")) Start av Hjälpcentralen är inkapslat i klassen TDEApplication. För att visa handboken för programmet, använd bara kapp->invokeHelp() 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 invokeHelp(), som avgör ankaret som bläddraren hoppar till. Komponenter och tjänster KDE-tjänster Vad är KDE-tjänster? Begreppet tjänst ä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 DCOP. Genom att göra anspråk på att vara av en viss tjänsttyp, 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. 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. Några exempel: HTML-uppritningskomponenten som används i Konqueror är en inbäddad komponent som implementerar tjänsttypen KParts/ReadOnlyPart och Browser/View. I senaste versionen av KDevelop, är största delen av funktionerna paketerade i insticksprogram med tjänsttypen KDevelop/Part. 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. Konqueror 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 ThumbCreator. Naturligtvis karaktäriseras en tjänst inte bara av tjänsttypen som den implementerar, utan också av några egenskaper. Till exempel så gör inte en ThumbCreator bara anspråk på att implementera C++ klassen med typen ThumbCreator, 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 handlare, med ett komplext frågespråk, för detta syfte. Definiera tjänsttyper Nya tjänsttyper läggs till genom att installera en beskrivning av dem i katalogen TDEDIR/share/servicetypes. I det automatiska byggramverket, kan det göras med detta fragment från Makefile.am: kde_servicetypesdir_DATA = tdeveloppart.desktop EXTRA_DIST = $(kde_servicetypesdir_DATA) Definitionen tdeveloppart.desktop för en del till KDevelop ser ut som följer: [Desktop Entry] Type=ServiceType X-TDE-ServiceType=KDevelop/Part Name=KDevelop Part [PropertyDef::X-KDevelop-Scope] Type=QString [PropertyDef::X-KDevelop-ProgrammingLanguages] Type=QStringList [PropertyDef::X-KDevelop-Args] Type=QString 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 [PropertyDef::name] i konfigurationsfilen. I gruppen, anger posten Type egenskapens typ. Möjliga typer är allt som kan lagras i en QVariant. Definiera delade bibliotekstjänster Tjänstdefinitioner lagras i katalogen TDEDIR/share/services: kde_servicesdir_DATA = kdevdoxygen.desktop EXTRA_DIST = $(kde_servicesdir_DATA) Innehållet i följande exempelfil, kdevdoxygen.desktop, anger insticksprogrammet KDevDoxygen med tjänsttypen KDevelop/Part: [Desktop Entry] Type=Service Comment=Doxygen Name=KDevDoxygen ServiceTypes=KDevelop/Part X-TDE-Library=libkdevdoxygen X-KDevelop-ProgrammingLanguages=C,C++,Java X-KDevelop-Scope=Project Förutom de vanliga deklarationerna, är en viktig post X-TDE-Library. Den innehåller namnet på libtool-biblioteket (utan filändelsen .la). Det fastlägger också namnet på den exporterade symbolen i biblioteket som returnerar objekttillverkaren (med det inledande prefixet init_). I ovanstående exempel, måste biblioteket innehålla följande funktion: extern "C" { void *init_libkdevdoxygen() { return new DoxygenFactory; } }; Typen för tillverkningsklassen DoxygenFactory beror på den specifika tjänsttyp som tjänsten implementerar. I vårt exempel med ett KDevelop-insticksprogram, måste tillverkaren vara en KDevFactory (som ärver KLibFactory). Vanligare exempel är KParts::Factory som antas skapa objekten KParts::ReadOnlyPart eller i de flesta fall det generella KLibFactory. Använda delade bibliotekstjänster För att kunna använda en delad bibliotekstjänst i ett program, måste du skaffa ett KService-objekt som representerar den. Det här beskrivs i avsnittet om Mime-typer (och i ett avsnitt om handlaren som återstår att skriva :-) Med objektet KService tillgängligt, kan du mycket lätt ladda biblioteket och få en pekare till dess tillverkningsobjekt. 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); } Från det ögonblicket, beror fortsättningen återigen på tjänsttypen. För generella insticksprogram, skapar man objekt med metoden KLibFactory::create(). Med KParts, måste tillverkningspekaren konverteras till det mer specifika KParts::Factory, och dess metod create() måste användas: 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 << "Tjänsten implementerar inte rätt tillverkare" << endl; } Definiera DCOP-tjänster 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 tdeio_uiserver, 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. 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: X-DCOP-ServiceType anger hur tjänsten startas. Värdet Unique (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 TDEApplication::startServiceByName(), 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 KUniqueApplication. Värdet Multi för X-DCOP-ServiceType 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 None (ingen) användas. I detta fall, väntar inte start av tjänsten på att den har registrerats i DCOP. X-TDE-StartupNotify ska normalt anges som "false". Annars visar aktivitetsfältet en startbekräftelse, eller, beroende på användarinställningarna, så ändras markören. Här är definitionen av tdeio_uiserver: [Desktop Entry] Type=Service Name=tdeio_uiserver Exec=tdeio_uiserver X-DCOP-ServiceType=Unique X-TDE-StartupNotify=false Använda DCOP-tjänster En DCOP-tjänst startas med en av flera metoder i klassen TDEApplication: DCOPClient *client = kapp->dcopClient(); client->attach(); if (!client->isApplicationRegistered("tdeio_uiserver")) { QString error; if (TDEApplication::startServiceByName("tdeio_uiserver", QStringList(), &error)) cout << "Start av TDEIO-server misslyckades med meddelandet " << error << endl; } ... QByteArray data, replyData; QCString replyType; QDataStream arg(data, IO_WriteOnly); arg << true; if (!client->call("tdeio_uiserver", "UIServer", "setListMode(bool)", data, replyType, replyData)) cout << "Anrop till tdeio_uiserver misslyckades" << endl; ... 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. I exemplet som ges här, startas tjänsten "med namn", dvs. första argumentet till TDEApplication::startServiceByName() är namnet, som det anges på raden Name i desktop-filen. Ett alternativ är att använda TDEApplication::startServiceByDesktopName(), som använder namnet på desktop-filen som argument, dvs. i det här fallet "tdeio_uiserver.desktop". 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 QString. Om starten av tjänsten misslyckas, tilldelas det här argumentet det översatta felmeddelandet. Mime-typer Vad är Mime-typer? 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: I Konquerors ikonvy, representeras filer av ikoner. Varje Mime-typ har en viss ikon som den hör ihop med, som visas här. När man klickar på en filikon eller ett filnamn i Konqueror, så visas antingen filen i en inbäddad vy, eller så startas ett program som hör ihop med filtypen. 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. 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. 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. 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 foo.jpg ä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 TDEDIR/share/mimelnk/magic, 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 .desktop-fil, och den blir effektivt och bekvämt tillgänglig via KDE-biblioteken. Definiera Mime-typer Låt oss definiera typen "application/x-foo", för vårt nya program foobar. För att göra det, måste filen foo.desktop skrivas, och installeras i TDEDIR/share/mimelnk/application. (Det är den vanliga platsen, som kan variera mellan distributioner). Det här kan göras genom att lägga till följande till Makefile.am: mimedir = $(kde_mimedir)/application mime_DATA = foo.desktop EXTRA_DIST = $(mime_DATA) Filen foo.desktop ska se ut som följer: [Desktop Entry] Type=MimeType MimeType=application/x-foo Icon=fooicon Patterns=*.foo; DefaultApp=foobar Comment=Foo Data File Comment[sv]=Foo-datafil Posten "Comment" är avsedd att översättas. Eftersom .desktop-filen anger en ikon, bör du också installera en ikon fooicon.png, som representerar filen, t.ex. i Konqueror. I KDE-biblioteken motsvarar en sådan typdefinition en instans av klassen KMimeType. Använd det som i följande exempel: KMimeType::Ptr type = KMimeType::mimeType("application/x-foo"); cout << "Typ: " << type->name() < endl; cout << "Ikon: " << type->icon() < endl; cout << "Kommentar: " << type->icon() < endl; QStringList patterns = type->patterns(); QStringList::ConstIterator it; for (it = patterns.begin(); it != patterns.end(); ++it) cout << "Mönster: " << (*it) << endl; Avgöra Mime-typ för data Det snabba sättet att avgöra filtypen är KMimeType::findByURL(). 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 .pl, som skulle ange typen "text/x-perl". Dock är filen som levereras av servern utmatning från skriptet, som normalt är HTML. I sådana fall, returnerar KMimeType::findByURL() Mime-typen "application/octet-stream" (tillgänglig via KMimeType::defaultMimeType()), som anger att den misslyckades med att ta reda på typen. KMimeType::Ptr type = KMimeType::findByURL("/home/bernd/foobar.jpg"); if (type->name() == KMimeType::defaultMimeType()) cout << "Kunde inte avgöra typen" << endl; else cout << "Typ: " << type->name() << endl; (den här metoden har några fler argument, men dessa är inte dokumenterade, så glöm helt enkelt bort dem.) 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 KMimeMagic, som har annorlunda felhantering: KMimeMagicResult *result = KMimeMagic::self()->findFileType("/home/bernd/foobar.jpg"); if (!result || !result->isValid()) cout << "Kunde inte avgöra typen" << endl; else cout << "Typ: " << result->mimeType() << endl; Med en variant av den här funktionen, kan du också avgöra typen för ett minnesblock. Det används till exempel av Kate för att räkna ut färgläggningsläget: QByteArray array; ... KMimeMagicResult *result = KMimeMagic::self()->findBufferType(array); if (!result || !result->isValid()) cout << "Kunde inte avgöra typen" << endl; else cout << "Typ: " << result->mimeType() << endl; Till och med KMimeMagic kan förstås bara avgöra filtypen från innehållet i en lokal fil. För fjärrfiler, finns ytterligare en möjlighet: KURL url("http://developer.kde.org/favicon.ico"); QString type = TDEIO::NetAccess::mimetype(url); if (type == KMimeType::defaultMimeType()) cout << "Kunde inte avgöra typen" << endl; else cout << "Typ: " << type << endl; 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 KMimeType::findByURL() returnerade "application/octet-stream". Å andra sidan, om du inte vill blockera programmet, kan du också uttryckligen starta TDEIO-jobbet och ansluta till några av dess signaler: void FooClass::findType() { KURL url("http://developer.kde.org/favicon.ico"); TDEIO::MimetypeJob *job = TDEIO::mimetype(url); connect( job, SIGNAL(result(TDEIO::Job*)), this, SLOT(mimeResult(TDEIO::Job*)) ); } void FooClass::mimeResult(TDEIO::Job *job) { if (job->error()) job->showErrorDialog(); else cout << "Mime type: " << ((TDEIO::MimetypeJob *)job)->mimetype() << endl; } Tilldela en Mime-typ till ett program eller tjänst När ett program installeras, installerar det en .desktop-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 .desktop-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 KServiceTypeProfile: KService::OfferList offers = KServiceTypeProfile::offers("text/html", "Application"); KService::OfferList::ConstIterator it; for (it = offers.begin(); it != offers.end(); ++it) { KService::Ptr service = (*it); cout << "Namn: " << service->name() << endl; } Returvärdet från funktionen är en lista med tjänsterbjudanden. Ett KServiceOffer-objekt paketerar en KService::Ptr, tillsammans med ett rangordningsnummer. Listan som returneras av KServiceTypeProfile::offers() är ordnad enligt vad användaren föredrar. Användaren kan ändra detta genom att anropa "keditfiletype text/html" eller välja Redigera filtyp i Konquerors sammanhangsberoende meny för en HTML-fil. I exemplet ovan, begärdes en lista med erbjudanden för programmen som stöder text/html. Det omfattar, bland annat, HTML-editorer som Quanta Plus. Du kan också ersätta det andra argumentet "Application" med "KParts::ReadOnlyPart". I det fallet, får du en lista med inbäddbara komponenter för att presentera HTML-innehåll, till exempel TDEHTML. 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: KService::Ptr offer = KServiceTypeProfile::preferredService("text/html", "Application"); if (offer) cout << "Namn: " << service->name() << endl; else cout << "Ingen lämplig tjänst hittades" << endl; För ännu mer komplicerade förfrågningar, finns det en fullständig CORBA-liknande handlare. För att köra en programtjänst med några webbadresser, använd KRun: KURL::List urlList; urlList << "http://www.ietf.org/rfc/rfc1341.txt?number=1341"; urlList << "http://www.ietf.org/rfc/rfc2046.txt?number=2046"; KRun::run(offer.service(), urlList); Diverse I det här avsnittet listar vi några av de programmeringsgränssnitt som på något sätt hör ihop med den föregående beskrivningen. Hämta en ikon för en webbadress. Det här tittar efter webbadressens typ, och returnerar motsvarande ikon. KURL url("ftp://ftp.kde.org/pub/incoming/wibble.c"); QString icon = KMimeType::iconForURL(url); Kör en webbadress. Det här tittar efter webbadressens typ, och startar tillhörande program till typen som användaren föredrar. KURL url("http://dot.kde.org"); new KRun(url); Nätverkstransparens Inledning 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 nätverkstransparens. 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 asynkronism 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. I KDE-biblioteken implementeras nätverkstransparens med TDEIO-programmeringsgränssnittet. Det centrala begreppet i arkitekturen är ett I/O-jobb. 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. Bakgrundsoperationer åstadkoms genom att starta I/O-slavar 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. 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: http://www-com.physik.hu-berlin.de/~bernd/article.tgz#tar:/paper.tex Använda TDEIO 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 result(TDEIO::Job*). Efter signalen har skickats, tar jobbet bort sig självt. Därför ser ett typiskt användarfall ut så här: void FooClass::makeDirectory() { SimpleJob *job = TDEIO::mkdir(KURL("file:/home/bernd/kiodir")); connect( job, SIGNAL(result(TDEIO::Job*)), this, SLOT(mkdirResult(TDEIO::Job*)) ); } void FooClass::mkdirResult(TDEIO::Job *job) { if (job->error()) job->showErrorDialog(); else cout << "mkdir gick bra" << endl; } Beroende på jobbtypen, kan du också ansluta till andra signaler. Här är en översikt av de möjliga funktionerna: TDEIO::mkdir(const KURL &url, int permission) Skapar en katalog, valfritt med vissa rättigheter. TDEIO::rmdir(const KURL &url) Tar bort en katalog. TDEIO::chmod(const KURL &url, int permissions) Ändrar rättigheter för en fil. TDEIO::rename(const KURL &src, const KURL &dest, bool overwrite) Byter namn på en fil. TDEIO::symlink(const QString &target, const KURL &dest, bool overwrite, bool showProgressInfo) Skapar en symbolisk länk. TDEIO::stat(const KURL &url, bool showProgressInfo) Hittar viss information om filen, som storlek, ändringstid och rättigheter. Informationen kan hämtas från TDEIO::StatJob::statResult() efter jobbet har avslutats. TDEIO::get(const KURL &url, bool reload, bool showProgressInfo) Överför data från en webbadress. TDEIO::put(const KURL &url, int permissions, bool overwrite, bool resume, bool showProgressInfo) Överför data till en webbadress. TDEIO::http_post(const KURL &url, const QByteArray &data, bool showProgressInfo) Sänder data. Särskild för HTTP. TDEIO::mimetype(const KURL &url, bool showProgressInfo) Försöker hitta webbadressens Mime-typ. Typen kan hämtas från TDEIO::MimetypeJob::mimetype() efter jobbet har avslutats. TDEIO::file_copy(const KURL &src, const KURL &dest, int permissions, bool overwrite, bool resume, bool showProgressInfo) Kopierar en ensam fil. TDEIO::file_move(const KURL &src, const KURL &dest, int permissions, bool overwrite, bool resume, bool showProgressInfo) Byter namn på eller flyttar en ensam fil. TDEIO::file_delete(const KURL &url, bool showProgressInfo) Tar bort en ensam fil TDEIO::listDir(const KURL &url, bool showProgressInfo) Listar innehållet i en katalog. Varje gång några nya poster blir kända, skickas signalen TDEIO::ListJob::entries(). TDEIO::listRecursive(const KURL &url, bool showProgressInfo) Liknar funktionen listDir(), men den här är rekursiv. TDEIO::copy(const KURL &src, const KURL &dest, bool showProgressInfo) Kopierar en fil eller katalog. Kataloger kopieras rekursivt. TDEIO::move(const KURL &src, const KURL &dest, bool showProgressInfo) Flyttar eller byter namn på en fil eller katalog. TDEIO::del(const KURL &src, bool shred, bool showProgressInfo) Tar bort en fil eller katalog. Katalogposter 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. Följande typer är för närvarande definierade: UDS_SIZE (heltal) - Filens storlek. UDS_USER (sträng) - Användaren som äger filen. UDS_GROUP (sträng): Grupp som äger filen. UDS_NAME (sträng): Filnamnet. UDS_ACCESS (heltal) - Filens rättigheter, som t.ex. lagras av C-biblioteksfunktionen stat() i fältet st_mode. 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. UDS_LINK_DEST (sträng): I fallet med en symbolisk länk, namnet på filen som pekas ut. UDS_MODIFICATION_TIME (heltal) - Tiden (med typen time_t) då filen sist ändrades, som t.ex. lagras av stat() i fältet st_mtime. UDS_ACCESS_TIME (heltal) - Tiden då filen sist användes, som t.ex. lagras av stat() i fältet st_atime. UDS_CREATION_TIME (heltal) - Tiden då filen skapades, som t.ex. lagras av stat() i fältet st_ctime. UDS_URL (sträng) - Tillhandahåller en fils webbadress, om den inte helt enkelt är sammanslagningen av katalogwebbadressen och filnamnet. UDS_MIME_TYPE (sträng): Filens Mime-typ 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. Även om sättet att lagra information om filer i en UDSEntry ä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 m_uds är UDS_MIME_TYPE. Som tur är, finns ett programmeringsgränssnitt som är mycket enklare att använda: klassen KFileItem. Synkron användning 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: KURL source, target; source = ...; target = ... TDEIO::NetAccess::copy(source, target); 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. En särskilt intressant kombination av funktioner är download() tillsammans med removeTempFile(). 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. Om webbadressen är lokal, laddas inte filen ner, utan istället sätts det andra argumentet till det lokala filnamnet. Funktionen removeTempFile() 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: KURL url; url = ...; QString tempFile; if (TDEIO::NetAccess::download(url, tempFile) { // ladda filen med namnet tempFile TDEIO::NetAccess::removeTempFile(tempFile); } Metadata 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: void FooClass::reloadPage() { KURL url("http://www.kdevelop.org/index.html"); TDEIO::TransferJob *job = TDEIO::get(url, true, false); job->addMetaData("cache", "reload"); ... } Samma teknik används åt andra hållet, dvs. för kommunikation från slaven till programmet. Metoden Job::queryMetaData() frågar efter värdet på en viss nyckel som levereras av slaven. För HTTP-slaven, är ett sådant exempel nyckeln "modified" (ä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: void FooClass::printModifiedDate() { KURL url("http://developer.kde.org/documentation/kde2arch/index.html"); TDEIO::TransferJob *job = TDEIO::get(url, true, false); connect( job, SIGNAL(result(TDEIO::Job*)), this, SLOT(transferResult(TDEIO::Job*)) ); } void FooClass::transferResult(TDEIO::Job *job) { QString mimetype; if (job->error()) job->showErrorDialog(); else { TDEIO::TransferJob *transferJob = (TDEIO::TransferJob*) job; QString modified = transferJob->queryMetaData("modified"); cout << "Senaste ändring: " << modified << endl; } Schemaläggning 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. 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. 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 direkt. 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. Så det finns ett annat sätt. Man kan schemalägga 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: KURL url("http://developer.kde.org/documentation/kde2arch/index.html"); TDEIO::TransferJob *job = TDEIO::get(url, true, false); TDEIO::Scheduler::scheduleJob(job); En tredje möjlighet är anslutningsorienterat. 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: KURL baseUrl("imap://bernd@albert.physik.hu-berlin.de"); TDEIO::Slave *slave = TDEIO::Scheduler::getConnectedSlave(baseUrl); TDEIO::TransferJob *job1 = TDEIO::get(KURL(baseUrl, "/INBOX;UID=79374")); TDEIO::Scheduler::assignJobToSlave(slave, job1); TDEIO::TransferJob *job2 = TDEIO::get(KURL(baseUrl, "/INBOX;UID=86793")); TDEIO::Scheduler::assignJobToSlave(slave, job2); ... TDEIO::Scheduler::disconnectSlave(slave); Du kan bara koppla ner slaven efter alla jobb som tilldelats den garanterat har avslutats. Definiera en I/O-slav 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: protocoldir = $(kde_servicesdir) protocol_DATA = ftp.protocol EXTRA_DIST = $(mime_DATA) Innehållet i filen ftp.protocol är följande: [Protocol] exec=tdeio_ftp protocol=ftp input=none output=filesystem listing=Name,Type,Size,Date,Access,Owner,Group,Link, reading=true writing=true makedir=true deleting=true Icon=ftp Posten "protocol" anger vilket protokoll som slaven ansvarar för. "exec" ä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 "tdeinit", 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. Raderna "input" och "output" används inte för närvarande. Återstående rader i filen .protocol 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 FTP-protokollet), kan TDEIO läsa källfilen och sedan skriva data till destinationsfilen. För att detta ska fungera, måste .protocol annonsera åtgärderna som slaven stöder. 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 kdemain(). 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: extern "C" { int kdemain(int argc, char **argv); } int kdemain(int argc, char **argv) { TDELocale::setMainCatalogue("tdelibs"); TDEInstance instance("tdeio_ftp"); (void) TDEGlobal::locale(); if (argc != 4) { fprintf(stderr, "Usage: tdeio_ftp protocol " "domain-socket1 domain-socket2\n"); exit(-1); } FtpSlave slave(argv[2], argv[3]); slave.dispatchLoop(); return 0; } Implementera en I/O-slav Slavar implementeras som delklasser till TDEIO::SlaveBase (FtpSlave i exemplet ovan). På så sätt motsvarar åtgärderna i .protocol vissa virtuella funktioner i TDEIO::SlaveBase som implementeringen av slaven måste implementera om. Här är en lista med möjliga åtgärder och motsvarande virtuella funktioner: läsa: Läser data från en webbadress void get(const KURL &url) skriva: Skriver data till en webbadress och skapar filen om den inte ännu finns. void put(const KURL &url, int permissions, bool overwrite, bool resume) flytta: Byter namn på en fil. void rename(const KURL &src, const KURL &dest, bool overwrite) ta bort: Tar bort en fil eller katalog. void del(const KURL &url, bool isFile) lista: Listar innehållet i en katalog. void listDir(const KURL &url) skapa katalog: Skapar en katalog. void mkdir(const KURL &url, int permissions) Dessutom finns det funktioner som kan implementeras om, och inte listas i filen .protocol. För dessa åtgärder, avgör TDEIO automatiskt om de stöds eller inte (dvs. standardimplementationen returnerar ett fel). Levererar information om en fil, liknar C-funktionen stat(). void stat(const KURL &url) Ändrar åtkomsträttigheter för en fil. void chmod(const KURL &url, int permissions) Avgör Mime-typ för en fil. void mimetype(const KURL &url) Kopierar en fil. copy(const KURL &url, const KURL &dest, int permissions, bool overwrite) Skapar en symbolisk länk. void symlink(const QString &target, const KURL &dest, bool overwrite) Alla dessa implementationer ska sluta med ett av två anrop: Om åtgärden lyckades, ska de anropa finished(). Om ett fel uppstod, ska de anropa error() med en felkod som första argument och en sträng som andra. Möjliga felkoder listats som uppräkningstypen TDEIO::Error. Det andra argumentet är oftast webbadressen i fråga. Den används t.ex. i TDEIO::Job::showErrorDialog() för att parametrisera felmeddelandet som är läsbart av användaren. För slavar som motsvarar nätverksprotokoll, kan det vara intressant att implementera om metoden SlaveBase::setHost(). 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 SlaveBase::metaData(). Du kan kontrollera om metadata med en viss nyckel finns med SlaveBase::hasMetaData(). Kommunicera tillbaka till programmet Diverse åtgärder som implementeras i en slav, behöver något sätt att skicka tillbaka data till programmet som använder slavprocessen. get() skickar datablock. Det görs med data(), som använder argumentet QByteArray. Du behöver förstås inte skicka all data på en gång. Om du skickar en stor fil, anropa data() med mindre datablock, så att programmet kan behandla dem. Anropa finished() när överföringen är klar. listDir() rapporterar information om posterna i en katalog. Anropa listEntries() med en TDEIO::UDSEntryList som argument, för detta syfte. På motsvarande sätt som data(), kan du anropa den flera gånger. När du är klar, anropa listEntry() med andra argumentet satt till true. Du kan också anropa totalSize() för att rapportera totalt antal katalogposter, om det är känt. stat() rapporterar information om en fil, som storlek, Mime-typ, etc. Sådan information paketeras i en TDEIO::UDSEntry, som beskrivs nedan. Använd statEntry() för att skicka ett sådant objekt till programmet. mimetype() anropar mimeType() med ett strängargument. get() och copy() kan vilja tillhandahålla förloppsinformation. Det görs med metoderna totalSize(), processedSize() och speed(). Den totala storleken och behandlade storleken rapporteras som byte, och hastigheten som byte per sekund. Du kan skicka godtyckliga nyckel/värdepar av metadata med setMetaData(). Kommunikation med användaren 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. infoMessage(): 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 TDEIO::Job::infoMessage(). warning(): Visar en varning i en meddelanderuta med KMessageBox::information(). Om en meddelanderuta fortfarande visas från ett tidigare anrop av warning() från samma underprocess, händer ingenting. messageBox(): 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 SlaveBase::MessageBoxType som referens. openPassDlg(): Visar en dialogruta för att mata in användarnamn och lösenord. Licenser &underFDL; &underGPL;