Wieder einmal steht die Adventszeit völlig unerwartet vor der Tür. Doch zum Glück zaubern Gideon und ein wenig C++ schnell einen hübschen KDE-Adventskalender für Freunde, Verwandte und Bekannte, während man selbst ein wenig programmieren lernt.
Ob für große oder kleine Kinder – ein Adventskalender muss sein, und ein selbstgebastelter macht doppelt soviel Freude, vor allem dann, wenn man dabei ganz nebenbei in die KDE-Programmierung einsteigt. Als Bastelwerkzeug benötigen wir anstelle von Papier, Schere und Klebstoff KDevelop 3.0, die KDE-3.1.x- und Qt-3.1.x-Bibliotheken (zum Programmieren natürlich einschließlich der dev(el)-Pakete) sowie einen Compiler wie zum Beispiel den gcc 3.3.
Die KDE-Entwicklungsumgebung KDevelop wird in der neuen Version 3.0 offiziell zwar erst mit KDE 3.2 ausgeliefert, doch lässt sie sich auch jetzt schon unter dem Codenamen gideon aus dem Internet herunterladen [1] oder von der Heft-CD installieren. Bei älteren Distributionen steht allerdings erst einmal ein komplettes KDE-Update (die Dateien finden sich z. B. auf [6]) auf dem Plan.
SuSE-8.1-User müssen zusätzlich ein wenig tricksen, um gideon-3.0a4a-1_suse81.i586.rpm von der Heft-CD zu installieren: Waren alle Abhängigkeiten befriedigt, stand im Test als letztes noch eine fehlende Datei /srv/www/cgi-bin/susehelp/susesearch.cgi im Raum. Diese gab es zwar, aber unter /srv/www/cgi-bin. Setzt man einen entsprechenden Link
mkdir /srv/www/cgi-bin/susehelp ln -s /srv/www/cgi-bin/susesearch.cgi /srv/www/cgi-bin/susehelp/susesearch.cgi
braucht man bei der Koste-es-was-es-wolle-Installation mit rpm -ivh --nodeps kein schlechtes Gewissen haben.
Aller Anfang ist leicht
Allen des Englischen Mächtigen gibt [2] eine gute Anleitung zur Arbeit mit KDevelop und Hilfsprogrammen wie dem WYSIWYG-Tool QtDesigner [3] zur Gestaltung grafischer Oberflächen mit dem Qt-Toolkit. Damit kann es schon losgehen.
Zunächst erstellen wir ein neues Projekt unter Projekt bzw. Project / New Project.... Für eine einfache KDE-Applikation wie den Adventskalender wählen wir C++ / KDE / Simple KDE Application. Unter Anwendungsname taufen wir das neue Programm adventskalender, passen die Vorgaben bei Lizenz, E-Mail und Autor an, drücken dreimal auf Nächster (Next) und dann auf Abschließen (Finish). Damit ist das Grundgerüst des neuen Programms auch schon fertig und tröstet darüber hinweg, dass KDevelop 3.0 im gegenwärtigen Entwicklungsstand noch nicht besonders viel Deutsch spricht. Auch die originalen, englischsprachigen Beschriftungen wechseln von Vorversion zu Vorversion an der einen oder anderen Stelle ihren Wortlaut.
Damit es später beim Konfigurieren und Kompilieren keine Probleme gibt, legen wir unter Projekt / Project Options... im Punkt Configure Options das Installationsoberverzeichnis (das sogenannte Präfix, siehe auch Kasten 2) und das Oberverzeichnis der zu verwendenden Qt-Installation im Punkt "configure"-Argumente ab:
--prefix=/opt/kde --with-qt-dir=/usr/local/qt
Wer KDE nicht in /opt/kde installiert hat, muss hier natürlich das richtige Installationsverzeichnis (etwa /opt/kde3) angeben. Die beiden Ordner variieren je nach Distribution und Paketersteller ein wenig: So installiert sich Qt 3.1.2 für SuSE 8.1 aus dem KDE-3.1.3-Verzeichnis von [6] nach /usr/lib/qt3.
Die bereits von KDevelop erstellten Dateien finden wir über den (vertikal gestellten) Punkt File Tree (in manchen Versionen auch File Selector genannt) an der linken Seite (Abbildung 1). Sämtliche Quelltexte finden sich im Ordner src, dessen Name kurz für “source”, also Quelltexte, steht.
Unsere erste Änderung betrifft die Programmbeschreibung, die unsere Lieben wie bei jedem KDE-Programm im sogenannten About-Dialog unter Hilfe / Über Programmname nachlesen können. Den finden wir bereits fix und fertig programmiert in der Datei main.cpp. Dort ersetzen wir – je nach Version – den Text "A KDE APPLICATION" bzw. "A KDE KPart Application" in runden Klammern gegen "Fröhliche Weihnachten wünscht KDE":
static const char description[] =
I18N_NOOP("Fröhliche Weihnachten wünscht KDE");
Doch so wie unser Spruch nun im Quelltext steht, wird er nicht zwangsweise korrekt dargestellt. Das liegt daran, dass wir Umlaute benutzen, die die weltumspannend verstandene 7-Bit-ASCII-Kodierung nicht enthält. Diese Probleme umgehen wir, indem wir die Kodierung UTF-8 benutzen. Wählen wir im Menü Ansicht / Kodierung festlegen (View / Set Encoding) den Punkt UnicodeUTF-8 aus, kodiert KDevelop die gerade geöffnete Datei nach UTF-8 um. Dies sollten wir auf jeden Fall für die Dateien adventskalender.cpp und main.cpp machen.
Das Help-Menü generiert KDevelop automatisch, sobald die Applikation ein zweites Menü enthält. Tatsächlich sollten die Benutzerschnittstellen (wie auch Variablen- und Funktionsnamen) von KDE-Programmen in Englisch gehalten werden; erst in einem zweiten Schritt käme die Lokalisierung, die Übersetzung in andere Sprachen, in gesonderten Dateien an die Reihe. Um nicht zu sehr abzuschweifen, sparen wir uns diesen komplexen Schritt an dieser Stelle und beschriften das Userinterface auf Deutsch. Nur bei den Menüs müssen wir, besagter Automatik wegen, Kompromisse eingehen.
Um ein Menü namens File zu erstellen, das einen Eintrag zum Schließen des Programms enthält, modifizieren wir die Datei adventskalenderui.rc. Hierbei handelt es sich um eine Benutzerschnittstellenbeschreibung (ui.rc – “user interface record”) in XML, aus der KDevelop später C++-Code generiert. Zwischen den Tags <MenuBar ...> und </MenuBar> ersetzen wir alles durch die Definition aus Listing 1.
Listing 1
Definition des Datei-Menüs in XML
<MenuBar> <Menu name="file" noMerge="1">&File <Action name="file_quit"/> </Menu> </MenuBar>
Sie beschreibt ein Menü namens file innerhalb der Menüleiste (englisch: menubar), das mit dem Text File beschriftet wird und bei dessen Anwahl der Unterpunkt (die “Aktion”) file_quit sichtbar wird. Das kaufmännische Und-Zeichen & vor dem F sorgt dafür, dass sich das Menü später mit dem Tastenkürzel [Alt-F] öffnen lässt.
Noch fehlt allerdings eine Funktion, die die Menüeinträge auch im Menü erscheinen lässt. Hierfür deklarieren wir in der Header-Datei adventskalender.h, dass das adventskalender-Fenster, was ein Objekt der Klasse adventskalender ist, eine Funktion setupActions() kennen soll:
private: void setupActions();
void bedeutet, dass setupActions() nur etwas tut, aber keine Werte zurückgibt.
Da das Menü gleich bei der “Geburt” des adventskalender-Objekts eingebaut werden soll, schreiben wir den Funktionsaufruf in den Konstruktor adventskalender::adventskalender() der Klasse in der Datei adventskalender.cpp:
setupActions();
adventskalender-Objekte erben Eigenschaften und Funktionen der KMainWindow-Klasse, einer sehr komplexen KDE-Klasse zum Erstellen von Hauptfenstern. Damit setupActions() die Applikation überhaupt beendet, implementieren wir sie – weiterhin in adventskalender.cpp – so, dass sie eine KDE-Standard-Aktion aus den KDE-Bibliotheken aufruft, die die KDE-Anwendung schließt:
void adventskalender::setupActions()
{
KStdAction::quit(this, SLOT(close()), actionCollection());
}
Wenn wir den Menüpunkt Erstellen / Programm ausführen (Build / Execute Program) auswählen, baut KDevelop eine Applikation wie in Abbildung 2 zusammen, die sich über das File-Menü schließen lässt. Help / About zeigt den Dialog aus Abbildung 3.
Gibt der Programm-Erstellungsprozess (den der Nachrichten-Reiter unten links in KDevelop dokumentiert) vorzeitig auf, liegt das meist an fehlenden Entwicklungstools, Bibliotheken und Dev(el)-Paketen. Unter SuSE 8.1 meckerte KDevelop zum Beispiel die fehlende Datei libart_lgpl_2.la an:
libtool: link: cannot libart_lgpl_2.la
Diese liefern die beiden von SuSE geschnürten libart:lgpl-Pakete tatsächlich nicht mit; Abhilfe schafft ein Update auf die entsprechenden Archive vom KDE-Server [6].
Ohne Schokolade
Jetzt fehlen nur noch
- 24 durchnummerierte Törchen mit Bildchen,
- 24 Überraschungen, die sich dahinter verstecken und
- ein Knecht Ruprecht, der aufpasst, dass kein Törchen zu früh geöffnet wird.
Für die Törchen nehmen wir Objekte der Klasse KPushButton, also Knöpfe. Um diese gleichmäßig auf 24 Plätzen zu verteilen, benutzen wir ein QGridLayout, ein Gitterlayout. Damit dieses nicht mit dem KMainWindow-eigenen Layout kollidiert (was uns bestenfalls eine Compiler-Warnung, schlimmstenfalls einen Programmabsturz einbrächte), betten wir das QGridLayout in ein QWidget namens dummy ein, ein generelles GUI-Element, das als Attrappe dient:
QWidget *dummy = new QWidget(this); QGridLayout *layout = new QGridLayout (dummy, 4, 6);
Dem neuen (new) QGridLayout namens layout sagen wir in der runden Klammer, dass es sich ins eben erzeugte QWidget-Objekt dummy einfügen soll. Als weitere Argumente übergeben wir dem QGridLayout-Konstruktor die Information, dass wir 4 Reihen und 6 Spalten brauchen. Damit das überhaupt geht, müssen wir die QGridLayout-Definition aus der Header-Datei qlayout.h am Anfang von adventskalender.cpp einbinden (englisch: “to include”):
#include <qlayout.h>
Die spitzen Klammern besagen, dass die Datei in den systemweiten include-Verzeichnissen statt im Projektverzeichnis liegt. Unseren dummy setzen wir nun als zentrales Element ins KMainWindow: Es nimmt so den ganzen Platz im Hauptfenster ein und stellt ihn indirekt dem QGridLayout zur Verfügung:
this->setCentralWidget(dummy);
Das Stichwort this sagt, dass sich die Aktion auf das Objekt der Klasse adventskalender bezieht, deren Code wir gerade schreiben. Für eine weihnachtliche Stimmung färben wir das QGridLayout rot ein (Abbildung 4). Rot heißt in der Rot-Grün-Blau-(RGB-)Darstellung 255,0,0 (volle Ladung Rot, null Grün, kein Blau):
this->setBackgroundColor(QColor(255,0,0));
24 Törchen
Die 24 Törchen, die das Gitterlayout bevölkern sollen, erzeugen wir in einer for-Schleife, damit wir nicht 24 Mal denselben Code für KPushButtons schreiben müssen. Zunächst brauchen wir einen Startpunkt, an dem wir den ersten KPushButton einsetzen, am besten links oben:
int row = 0, col = 0;
Die for-Schleife soll in der Hilfsvariablen i vom Startwert 0 (int i=0) bis zur Abbruchbedingung i<24 durchzählen (++i addiert jeweils 1 zu i):
for (int i = 0; i<24; ++i) {}
Innerhalb der geschweiften Klammern erstellen wir nun je Schleifendurchlauf einen KPushButton der festen Größe 128×128 Pixel. Dem Knopf übergeben wir bei seiner Erstellung den Parameter dummy, damit er über das QWidget ins QGridLayout eingefügt wird:
KPushButton *b = new KPushButton (0, dummy); b->setFixedSize (128, 128);
Um nun ein Bild auf den Button zu ziehen und ihn zu nummerieren, müssen wir ein wenig in die Trickkiste greifen. Vom Bild dragon5.jpg selbst benötigen wir ein QImage, das wir auf die richtige Größe (nämlich Button-Breite b->width() minus zehn, multipliziert mit der Button-Höhe – ebenfalls 128 – weniger zehn) skalieren. Übernähmen wir die Originalgröße des Knopfs unverändert, wäre das Bild zu groß für den Button und damit unsichtbar. Dummerweise lassen sich QImages nicht bemalen, weshalb wir unseres in eine (nicht skalierbare) QPixmap umwandeln:
QPixmap pixmap ;
QImage image(locate("appdata","dragon5.jpg"));
image = image.smoothScale( ( b->width() - 10 ), ( b->height() - 10 ), QImage::ScaleFree );
pixmap.convertFromImage( image, 0 );
Anderenfalls müssten wir mit Gimp etc. vorab 24 durchnummerierte Bilder anfertigen.
Da wir für KDevelop noch gar nicht definiert haben, wo sich dragon5.jpg befindet (den Fundort appdata spezifizieren wir später in der Datei Makefile.am näher), schaut unser Programm derzeit noch etwas leer aus (Abbildung 5). Nun malen wir mit einem QPainter den Tag auf die QPixmap-“Leinwand” auf:
QPainter painter ( &pixmap ); painter.drawText ( (int)(b->width() / 1.5 ), (int)(b->height() / 1.25 ), QString::number(i+1) ); b->setPixmap( pixmap ); b->show();
(int)(b->width() / 1.5 ) und (int)(b->height() / 1.25 ) berechnen die Koordinaten des Orts, an dem der Text auf dem Bild landen soll. Mit (int) sagen wir, dass unsere Berechnung ein ganzzahliges Ergebnis (“Integer”) haben soll. Die Breite des Buttons b->width() teilen wir durch 1,5, die Höhe durch 1,25. An die so errechnete Stelle schreiben wir die Zahl i+1, also den Tag, in Form einer Zeichenkette (QString). Mit setPixmap() kleben wir das nummerierte Bild auf den Knopf, show() macht es sichtbar.
layout->addWidget(b, row, col);
fügt nun den jeweils aktuellen Knopf b an die entsprechende Stelle im Gitter ein. Am Ende des Schleifendurchlaufs haben wir 24 Törchen fertig, 24 KPushButtons, die immer dasselbe Bild enthalten und mit dem Tag nummeriert sind.
Wenn wir uns schon einmal in der for-Schleife befinden, sorgen wir gleich dafür, dass ein Klick auf den Knopf eine Funktion aufruft, die das Törchen, so erlaubt, öffnet. Auf Mausklick schickt der Knopf automatisch ein Signal durch die Gegend, das die entsprechende Funktion, ein sogenannter Slot, auffängt. Um die Signale aller 24 KPushButtons mit diesem Slot zu verbinden, benutzen wir einen QSignalMapper. Ohne diesen müssten wir für jeden KPushButton einen eigenen Slot schreiben. Unseren Signal-Mapper erstellen wir außerhalb der for-Schleife, und zwar bereits bevor wir in unserem Konstruktor die Funktion setupActions(); aufrufen, also an allererster Stelle:
mSignalMapper = new QSignalMapper(this);
Die Variable mSignalMapper haben wir allerdings bisher noch gar nicht deklariert. Damit wir keinen Fehler beim Kompilieren bekommen, schreiben wir ihre Existenz als private, also nur innerhalb der adventskalender-Klasse nutzbare Variable in der Header-Datei adventskalender.h im Abschnitt private fest:
QSignalMapper * mSignalMapper;
Damit wir Objekte vom Typ QSignalMapper überhaupt erstellen können, lesen wir die Datei qsignalmapper.h am Anfang der Header-Datei ein. Am unteren Ende der Törchen-produzierenden for-Schleife benutzen wir den neuen Mapper dann wie folgt:
mSignalMapper->setMapping(b, i+1);
connect( b, SIGNAL(clicked()), mSignalMapper, SLOT(map()));
col++;
if ( col == 6 ) { col = 0; row++; }
Die erste Zeile sorgt mit der Törchennummer (i+1) dafür, dass der Mapper bei einem clicked()-Signal des KPushButtons b den Weg zur richtigen Funktionalität findet. Dazu verbinden (englisch: “connect”) wir das Klick-Signal zunächst mit einer Funktion des Signalmappers.
Die letzten beiden Zeilen beschreiben, was geschieht, wenn wir am Ende der for-Schleife angelangt sind: Wir erhöhen die Spaltennummer col um den Summand eins (col++). Kommen wir dabei bei sechs an (if ( col == 6 )), setzen wir die Spalte zurück auf Null und erhöhen dafür die Reihe row um eins. So durchlaufen wir alle 24 Felder des QGridLayouts. Jede Runde fügt einen KPushButton ein, so dass wir nach der 24. 24 Knöpfe mit Bild haben.
Bis jetzt findet der QSignalMapper allerdings noch keinen Slot, der beschreibt, was passiert, wenn wir auf einen Button klicken. Den nennen wir openDoor(); er soll eine Integer-Variable (also die Törchennummer) als Argument auswerten:
connect( mSignalMapper, SIGNAL( mapped(int) ), this, SLOT( openDoor(int) ) );
Diese Zeile verbindet das vom SignalMapper ausgesandte Signal mapped() mit dem Slot, der zu dieser (englisch: “this”) Klasse gehört. Bevor wir uns dem Türöffner-Slot widmen, fügen wir noch schnell eine letzte Zeile ans Ende unseres adventskalender-Konstruktors ein, die dafür sorgt, dass unser Hauptfenster immer maximiert dargestellt wird und wir so immer alle 24 Törchen sehen:
this->showMaximized();
Törchen öffnen oder nicht?
In openDoor(int) implementieren wir nun auch gleich unseren Knecht Ruprecht, damit garantiert kein Törchen zu früh geöffnet wird. Zunächst deklarieren wir den Slot in der Header-Datei adventskalender.h. Dort fügen wir zuvor eine neue “Rubrik” nur innerhalb der Klasse sichtbarer Slot-Funktionen ein:
private slots: void openDoor(int);
Zurück in adventskalender.cpp setzen wir direkt vor der allerletzten Zeile #include adventskalender.moc den Slot ein:
void adventskalender::openDoor(int day){
}
Innerhalb der geschweiften Klammern speichern wir das aktuelle Datum ab, um dieses mit dem Datum des angeklickten Törchens zu vergleichen. Dazu brauchen wir die Klasse QDate und eine entsprechende include-Anweisung für qdatetime.h.
QDate date; date=QDate::currentDate();
Die QDate-Methode currentDate() liefert das aktuelle Datum. Um zu regeln, wann das angeklickte Törchen geöffnet werden darf, nutzen wir eine verschachtelte if-else-Anweisung. Darf man das Törchen noch nicht öffnen, geben wir eine entsprechende Nachricht in einer KMessageBox auf dem Bildschirm aus (Listing 2).
Listing 2
Mit KMessageBoxes gegen Schnüffler
if ( date.month() != 12 )
KMessageBox::sorry ( this, "Es ist noch kein Dezember!! Gucken ist noch längst nicht erlaubt!!", "Zu früh…");
else
{
if (date.day() >= day)
{
behindDoor *door = new behindDoor ( day );
door->show();
}
else
KMessageBox::sorry (this, "Den Tag haben wir gar nicht. Hier gibt es nichts zu sehen!!", "Also ehrlich…");
}
In der ersten if-Anweisung überprüft Knecht Ruprecht, ob das aktuelle Datum in der Variablen date überhaupt im Dezember liegt. Wer bereits im November schnüffelt, bekommt die Quittung in Form der KMessageBox aus Abbildung 6. Dieser übergeben wir drei Argumente: this besagt, dass die Box vom KMainWindow unseres Programms kontrolliert wird. Als zweites übergeben wir die Abfuhr an die Schnüffler und zum Schluss noch einen Text, der in der Titelleiste des Fensters erscheint.
Haben wir bereits Dezember, gehen wir in die else-Anweisung hinein, in der wir eine zweite if-Anweisung finden, also einen weiteren Knecht Ruprecht: Dieser überprüft, ob der aktuelle Tag dem des Törchens entspricht oder ob wir ein Törchen öffnen wollen, dessen Datum bereits vergangen ist. Trifft eine dieser beiden Bedingungen zu, öffnet sich das Törchen, sonst erscheint erneut eine KMessageBox, diesmal jedoch mit einem anderen Text (Abbildung 7).
Blick hinter’s Törchen
Wenn wir das Törchen wirklich öffnen dürfen, erstellen wir ein Objekt der Klasse behindDoor. Diese Klasse existiert bisher noch nicht, weshalb wir sie zunächst in unserer Header-Datei unterhalb der Klasse adventskalender deklarieren. Sie soll von der KDE-Klasse KDialog erben, die Dialog-Fenster erstellen kann. Dazu müssen wir oben in adventskalender.h die Header-Datei kdialog.h einbinden. Die Klassen-Deklaration für behindDoor sieht dann wie folgt aus:
class behindDoor : public KDialog
{
public:
behindDoor( int );
virtual ~behindDoor();
};
Dabei deklarieren wir den Konstruktor und einen virtuellen Destruktor: Prinzipiell besteht jede Klasse aus einem Konstruktor und einem Destruktor, der aufräumt, wenn ein Objekt der Klasse aufhört zu existieren. Bei unserer Klasse adventskalender erstellte KDevelop für uns den Destruktor. Jetzt schreiben wir selber eine Klasse und müssen ihn daher selbst deklarieren.
Ein virtueller Destruktor ermöglicht es erfahrenen Programmierer/innen, mit virtuellen Funktionen zu einer eleganteren Lösung als der im folgenden beschriebenen zu kommen. Wir beschäftigen uns lieber mit einfacheren Dingen und kümmern uns um den Konstruktor. Er wünscht sich als Argument eine positive oder negative ganze Zahl (int ).
Nun füllen wir diesen Konstruktor in adventskalender.cpp mit Leben, und zwar gleich am Dateianfang, direkt unter den eingebundenen Header-Dateien:
behindDoor::behindDoor( int day ) : KDialog( 0, "dialog" ){ }
Mit day können wir nun auf das Integer-Argument zugreifen.
Unter diesem Konstruktor erstellen wir direkt den virtuellen Destruktor. Dieser bleibt jedoch leer:
behindDoor::~behindDoor(){ }
24 Überraschungen
Im Konstruktor nutzen wir eine switch-Anweisung. Sie besteht aus 24 Fällen (“cases”), in denen wir für jeden Tag eine andere Textdatei einbinden, die sich als Überraschung hinter dem Törchen versteckt. Hierfür brauchen wir 24 Textdateien mit Gedichten, Rezepten u. ä., die wir mit der Namensendung .txt abspeichern.
Zunächst benötigen wir allerdings ein QFile-Objekt, das diese Textdateien auslesen und sichtbar machen kann:
QFile file;
Natürlich müssen wir hierfür die qfile.h-Datei einbinden. file setzen wir in jedem case der switch-Anweisung auf den Namen und den Pfad unserer Textdatei. Für letzteren darf einmal mehr der Platzhalter appdata herhalten. Das sieht für den 1. Dezember so aus:
case 1:
{
file.setName(locate("appdata","adventskranz.txt" ));
break;
}
Hinter jedem case geben wir den Tag in Form einer Zahl von 1 bis 24 als Bedingung an. Eine break-Anweisung gehört ans Ende jedes fertig bearbeiteten Falls. Damit verlassen wir die switch-Anweisung und verhindern so, dass sich alle Törchen öffnen, obwohl wir nur eins angeklickt haben. Den Code für den ersten case kopieren wir und fügen ihn weitere 23 Mal ein. Anschließend brauchen wir nur noch den jeweiligen Tag hinter case und den Namen der auszulesenden Textdatei ändern.
Die gefundene Datei müssen wir nach der switch-Anweisung noch in einem Fenster sichtbar machen. Dafür öffnen wir sie mit file.open( IO_ReadOnly ) im Nur-Lese-Modus. Gelingt dies (wir prüfen es mit einer if-Anweisung), benötigen wir einen “Texteditor” in Form von QTextEdit, der den Dateiinhalt darstellt:
QTextEdit *edit = new QTextEdit( file.readAll(), QString::null, this );
Dieser steht auf die include-Anweisung für qtextedit.h hin zur Verfügung. Mit file.readAll() lesen wir das ganze Dokument ein.
Diesem QTextEditor sagen wir nun, dass er nur gelesen, nicht jedoch verändert werden darf. Außerdem frieren wir ihn auf eine feste Größe ein und legen mit Hilfe eines RGB-Werts seine Hintergrundfarbe fest:
edit->setReadOnly( true ); edit->setFixedSize( this->width(), this->height() ); edit->setPaletteBackgroundColor ( QColor (255, 235, 235 ) );
Wenn wir die ausgelesene Datei nicht mehr benötigen, schließen wir sie:
file.close();
Den Text sehen wir ja bereits im entsprechenden Fenster. Damit darin kein hässlicher grauer Rahmen entsteht, brauchen wir ein Layout, in dem unser Rezept oder Gedicht dargestellt wird. Dazu benutzen wir ein QVBoxLayout mit vertikaler Ausrichtung. Wir erstellen es mit
QVBoxLayout *l = new QVBoxLayout(this);
und fügen den Texteditor mit dem ausgelesenen Text ein:
l->addWidget(edit);
Kasten 1: Der Schokoladenüberzug
Um unsere Rezepte und Gedichte noch ein wenig zu versüßen, können wir sie mit HTML-Tags versehen. Erlaubt sind allerdings nur grundlegende Formatierungen wie
<b></b>
für Fettdruck oder
<i></i>
für kursiven Text. Dann muss man das Dokument allerdings mit
<qt></qt>
umschließen und mit
<p></p>
oder
<br>
in einzelne Absätze unterteilen:
QTextEdit
behandelt Zeilenumbrüche in solchen “Richtext”-Dateien wie bei echten HTML-Seiten wie Leerzeichen.
Wo ist der Inhalt?
Mit dem Programmieren sind wir nun fertig; wir müssen jedoch noch zwei Dateien anpassen, damit das Bild auf unseren KPushButtons und die 24 Textdateien gefunden und angezeigt werden. Dabei handelt es sich um Makefile.am-Dateien, die dafür sorgen, dass das Programm richtig installiert wird.
Zunächst verändern wir Makefile.am aus dem Projekt-Ordner adventskalender. Dort fügen wir direkt unter der Zeile SUBDIRS=$(TOPSUBDIRS)
myapppicdir=$(kde_datadir)/adventskalender myapppic_DATA=pics/dragon5.jpg
ein. Dadurch stellen wir sicher, dass unser Bild, was wir auf den KPushButton gezogen haben, nach dem Installieren des Programmes sichtbar ist: Es soll im Ordner pics gespeichert werden und dragon5.jpg heißen.
Nun widmen wir uns dem Makefile.am aus dem src-Verzeichnis, in dem sich all unsere Quelltexte befinden. Hier listen wir alle 24 Textdateien am Ende der Datei auf (Listing 3).
Listing 3
Wo befinden sich die Überraschungen?
myapppicdir = $(kde_datadir)/adventskalender
myapppic_DATA =aachener_printen.txt erste_schnee.txt spritzgebäck.txt \
advent.txt feuerzangenbowle.txt stille_nacht.txt \
adventskranz.txt knechtruprecht.txt tannengeflüster.txt \
adventsstollen.txt leise_rieselt.txt weihnachten.txt \
alle_jahre.txt makronen.txt weihnachtslied.txt \
berliner_brot.txt mandelhörnchen.txt weihnachtsmann.txt \
butterplätzchen.txt ros_entsprungen.txt weihnachtszeit.txt \
christkind.txt schneefloeckchen.txt winterwalde.txt
Wenn wir nun unser Programm mit Build / Build Project kompilieren und wie in Kasten 2 gezeigt installieren, ist unser Adventskalender fertig (Abbildung 8). Nur das Einpacken mit Geschenkpapier gestaltet sich ein wenig schwierig, denn wir können nicht einfach den Quelltext auf eine CD-ROM brennen oder Diskette packen.
Kasten 2: Spicken erlaubt!
Den vollständigen Quellcode des Adventskalenders samt Bild und Überraschungen finden Sie selbstverständlich auf der Heft-CD. Wenn Sie das Archiv adventskalender.tar.gz mit tar -xzvf entpacken, erwartet Sie im neu angelegten Verzeichnis adventskalender nicht nur der Sourcecode unterhalb von src, sondern auch die Projektdatei adventskalender.kdevelop, die Sie in KDevelop über Projekt / Open Project... laden.
Passen Sie die configure-Optionen unter Projekt / Project Options... / Configure Options wie im Text erwähnt an: Vom Wert für --prefix= hängt ab, ob Sie den Kalender mit Build / Install aus KDevelop heraus installieren dürfen: Steht hier ein systemweites Verzeichnis wie /opt/kde3, geht das nicht, da Sie in diesem Fall root-Rechte brauchen. Fügen Sie hingegen ein Unterverzeichnis Ihres eigenen Home-Verzeichnisses (etwa /home/pjung/adventskalender) an, können Sie den Kalender nach dem Installieren anschließend mit dem Kommandozeilenbefehl
KDEDIR=/home/pjung/adventskalender /home/pjung/adventskalender/bin/adventskalender &
testen. (Patricia Jung)
Geschenk einpacken
Das Konfigurieren und Kompilieren hat nämlich seine Spuren hinterlassen, von denen wir den Code zunächst befreien müssen. Dazu geben wir auf der Linux-Kommandozeile im adventskalender-Projekt-Verzeichnis den Befehl make clean ein.
Mit tar -czvf o. ä. zusammengepackt, bekommen die Beschenkten ein nettes Tar-Archiv, zu dem ein Begleitschreiben nichts schadet. Nach dem Auspacken müssen sie nämlich ins adventskalender-Verzeichnis gehen und
make -f Makefile.cvs
eingeben. Dieser Befehl generiert mit Hilfe unserer Makefile.am-Dateien Makefile.ins, die der nächste Schritt, das Konfigurieren, in richtige Makefiles umsetzt. Dem configure-Skript dürfen dabei Optionen übergeben werden, etwa
./configure --prefix=/opt/kde --with-qt-dir=/usr/local/qt
Bei diesen beiden Konfigurationsangaben handelt es sich um die gleichen, die wir bei der Projektdefinition in KDevelop eingegeben haben; da es sich auf den Rechnern der Beschenkten um andere Verzeichnisse handeln kann, gilt es aufzupassen.
Läuft configure ohne Probleme durch, setzt ein einfaches make den Kompilationsprozess in Gang. Ging auch hier alles glatt, kann root das Programm für alle Nutzer auf dem System mit
su -c "make install"
einspielen. Nun gibt es auch im K-Menü unter Utilities einen Eintrag, der zu unserem Adventskalender führt. Da bleibt uns nur noch, fröhlichen Advent zu wünschen!
Glossar
- UTF-8
- Das “8-Bit Unicode Transformation Format” ist eine Kodierung für Unicode-Zeichen. Dieser internationale Standard hat zum Ziel, einen Code für jedes Zeichen aller bekannten Schriftkulturen und Zeichensysteme festzulegen.
- XML
- “Extensible Markup Language”, ein Standard zur Definition von Auszeichnungssprachen. Grundgedanke ist es, Daten und ihre Repräsentation zu trennen. So können verschiedene Anwendungen die gleiche Datenbasis im XML-Format nutzen, um dem jeweiligen Einsatzzweck angepasste, unterschiedliche Ansichten zu generieren.
- virtuellen Funktionen
- Weist ein Zeiger vom Typ einer Oberklasse auf Objekte einer davon abgeleiteten Subklasse, kommt von virtuellen Funktionen automatisch die Version aus der Unterklasse (statt der aus der Oberklasse) zum Einsatz.
Infos
[1] http://kdevelop.org/index.html?filename=download.html
[2] http://women.kde.org/articles/tutorials/kdevelop3/
[3] Patricia Jung: “Programmfenster auf Mausklick”, LinuxUser 02/2002, S. 30 ff., http://www.linux-user.de/ausgabe/2002/02/030-designer/designer.html
[4] Qt-Referenz: http://doc.trolltech.com/
[5] KDE-Klassen-Dokumentation: http://developer.kde.org/documentation/library/cvs-api/
[6] KDE-Mirror der FH Heilbronn: ftp://ftp.fh-heilbronn.de/pub/mirrors/kde/stable/













