Wenn die Englisch-, C++- und Qt-Kenntnisse ausreichen, macht der Qt Designer grafische Benutzeroberflächen (GUIs) per Mausklick möglich.
Anspruchsvolle Software mit grafischer Benutzeroberfläche erstellt heutzutage fast niemand mehr in Form von Programmen, die in pixelgenauer Kleinarbeit Linie für Linie und Punkt für Punkt “auf den Bildschirm malen”. Stattdessen kommen GUI-Bibliotheken zum Einsatz, die in der Art eines Werkzeugkastens vorgefertigte Elemente wie Fenster, Werkzeugleisten, Rollbalken, Knöpfe oder Menüs bereitstellen.
Eine der verbreitetsten (u.a. für KDE eingesetzten) GUI-Bibliotheken heißt Qt und stammt von der norwegischen Firma Trolltech. Ihr großer Vorteil besteht darin, nicht nur für Linux u.a. Unix-Betriebssysteme, sondern auch für Windows und (seit Version 3.0) für MacOS X erhältlich zu sein. Solange der Quellcode keine betriebssystemspezifischen Systemaufrufe enthält, lassen sich Qt-Programme auf allen unterstützten Plattformen ohne Änderung übersetzen.
Außer der Bibliothek selbst bringt das Qt-GUI-Toolkit eine Reihe Hilfsprogramme mit, die das Leben der Programmiererin durchaus erleichtern. Dazu zählt der GUI-Builder Qt Designer. Wie auch andere Programme seiner Art kann er fehlende Kenntnisse in den Programmiersprachen C und C++ zwar nicht kompensieren, aber er sorgt für rasche Erfolgserlebnisse beim Entwerfen grafischer Programmoberflächen. Die mit Qt 3.0.x gelieferten Versionen machen den Einstieg insofern einfacher, als dass sie zusätzlich zum interaktiven Erstellen der Programmfenster automatisch die main()-Routine der Qt-Applikation erzeugen können. So kommt man fast ohne C++-Kenntnisse (aber nicht ohne Qt-Kenntnisse und das Wissen um Klassen und Objekte) zu einem lauffähigen Programm, das allerdings hinter seiner schönen Fassade bestenfalls ein wenig Funktionalität birgt. Der Designer erzeugt XML-Beschreibungen von GUI-Elementen, die mithilfe des “User Interface Compilers” uic in C++-Klassen umgewandelt werden.
So schön die Qt-Welt sein mag – ohne Englischkenntnisse kommt man in ihr nicht weiter. Zwar sind auf Qt basierende Programme vergleichsweise leicht zu lokalisieren, doch geht Trolltech selbst nicht gerade mit gutem Beispiel voran: Weder der Designer oder sonstige mitgelieferte Tools, noch die Dokumentation sind in anderen Sprachen als Englisch erhältlich.
Kasten 1: Lizenzen, Installation und häufige Fehler
Für Open-Source-Projekte ist Qt für X11 als Free-Version unter der GPL oder der (Trolltech-eigenen Open-Source-Lizenz) QPL erhältlich und kann von [1] heruntergeladen werden. Solange die verschiedenen Linux-Distributionen die aktuelle Version 3.0.1 (oder auch nur 3.0) nicht als fertig gepackte rpm-Pakete mitliefern, muss man allerdings selbst kompilieren. Dazu packt man den Qt-3.0.1-Tarball mit
tar -xzvf qt-x11-free-3.0.1.tar.gz
im Installationsverzeichnis (vorzugsweise /usr/local) aus und legt einen Symlink…
ln -s /usr/local/qt-x11-free-3.0.1 /usr/local/qt
… an, mit dem man die Umgebungsvariable QTDIR füttert:
export QTDIR=/usr/local/qt
Anschließend wechselt man mit cd QTDIR ins entsprechende Verzeichnis, tippt ./configure ein, antwortet yes[Return] auf die Frage “Do you accept the terms of either license? ” und wartet darauf, dass configure fertig wird. Anschließend leitet make einen sehr langwierigen Kompilierungsvorgang ein. Sollte dieser irgendwann in der Mitte mit einer Fehlermeldung abbrechen, vergewissere man sich, dass noch genügend Plattenplatz zur Verfügung steht. Qt kompiliert auf den meisten Linux-Systemen problemlos; treten dennoch Compilerfehler auf, sind die in der Regel so speziell, dass zu ihrer Beseitigung einige Erfahrung gehört.
Außer der Variablen QTDIR, die besagt, wo die zu benutzende Qt-Version liegt, müssen die Suchpfade für Programme (PATH), Manpages (MANPATH) und Bibliotheken (LD_LIBRARY_PATH) angepasst werden, ehe man Qt und die mitgelieferten Programme ohne Verrenkungen benutzen kann. Dazu tragen Bash-Nutzer/innen die entsprechenden Variablenzuweisungen in die Datei ~/.bash_profile und/oder ~/.bashrc ein:
export QTDIR=/usr/local/qt export PATH=$QTDIR/bin:$PATH export MANPATH=$QTDIR/doc/man:$MANPATH export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
Sollte sich das Programm qmake (siehe Text) weigern, Makefiles zu erstellen, kann man hier noch die Zeile
export QMAKEPATH=$QTDIR/mkspecs/linux-g++
ergänzen. Diese sorgt dafür, dass qmake die nötigen Informationen bekommt, wie Qt-Programme unter Linux mit dem C++-Compiler g++ aus der GCC-Compiler-Suite gebaut werden. Alle export– Kommandos können natürlich auch auf der Kommandozeile aufgerufen werden, um die Einstellungen für die aktuelle Shell zu setzen, die von nachträglichen Änderungen an den Bash-Startdarteien nicht beeinflusst wird.
Wenn die Shell, qmake oder der Compiler nötige Hilfsprogramme, Informationen, Dateien und Klassen nicht finden und mit einer entsprechenden Fehlermeldung aussteigen, liegt das sehr oft an falsch gesetzten Umgebungsvariablen. Beliebt ist etwa die Fehlermeldung, eine bestimmte, mit Q beginnende Klasse fehle. Dann enthält QTDIR meist den Pfad zu einem älteren Qt, etwa Qt 1.44 unter /usr/lib/qt, wo es die entsprechende Klasse tatsächlich nicht gibt.
Die wichtigsten Informationen zum Selbstkompilieren der Qt-Library und der mitgelieferten Hilfsprogramme können Sie in der Datei INSTALL im Sourcen-Verzeichnis nachlesen. Sollten Sie Closed-Source-Software oder Qt-Programme für und/oder unter Windows bzw. MacOS X programmieren wollen, informiert die Trolltech-Homepage [2] über Lizenzen und Kosten. Die englischsprachige Mailingliste qt-interest[3] bietet Unterstützung bei technischen Fragen.
Gestatten: Designer
Wenn die Suchpfade für Programme und Qt selbst stimmen (vgl. Kasten 1), lässt sich der Designer mit dem Befehl designer & aus einem X-Terminal heraus starten. Welche Fenster die Arbeitsfläche anzeigt, legen Sie im Menüpunkt Window / Views fest: Das “File Overview“-Window ermöglicht die Navigation innerhalb aller zu einem Projekt gehörigen Dateien. Object Overview gibt im -Tab einen Überblick darüber, aus welchen Objekten eine im Designer erzeugte GUI-Klasse besteht. Der Reiter Source wiederum enthält grob gesagt ein Abbild der Klassendeklaration. Die wichtige Aufgabe, die Eigenschaften von Widgets festzulegen, übernimmt der Property Editor. Sobald diese Fenster gefüllt sind, lohnt es sich, mit der rechten Maustaste auf einzelne Einträge zu drücken – oft kommt man so zu einem nützlichen Kontextmenü. Das Output Window gibt Aufschluss darüber, was der Designer tut, und kann manchmal bei der Fehlersuche recht nützlich sein.
Zieht man ein Fenster an der Doppellinie auf die leere Fläche, löst es sich aus dem Hauptfenster und lässt sich auf dem aktuellen virtuellen Desktop beliebig verschieben (Abbildung 1). Führt man ein solches herausgelöstes Fenster so an eine Fensterkante des Designers heran, dass der Cursor genau auf dem Rand zu liegen kommt, klinkt es sich wieder ein.
Sollte der Platz weiterhin knapp werden, können (auf demselben Weg verschiebbare) Werkzeugleisten unter Window / Toolbars abgeschaltet werden. Alle diese Änderungen merkt sich das Programm in der Datei ~/.designerrctb2, die sich auch von Hand modifizieren oder löschen lässt, sofern man es beim Fensterjonglieren zu bunt getrieben hat.
Neue Projekte
Jedes Programmierprojekt beginnt im Designer damit, dass eine neue Projektdatei angelegt wird. Aus dieser einfachen ASCII-Datei (vgl. z.B. [4]) erzeugt qmake ein passendes Makefile, das per Hand zu erstellen recht anspruchsvoll ist. So wählt man zu Projektbeginn File / New… und im darauf erscheinenden Dialog (Abbildung 2) C++ Project.
Ein neuer Dialog namens Project Settings erwartet hinter Project File: den Namen der neuen Projektdatei; der rechts daneben stehende Button mit den drei Punkten führt zu einem File-Auswahl-Dialog. Um die Projektdokumentation nicht gleich zu Anfang zu vernachlässigen, bietet sich ein beschreibender Text im Description-Feld an.
File / New… /Main Window sorgt nun dafür, dass das neue Projekt mit Leben, in diesem Fall dem Hauptfenster der geplanten GUI-Applikation, gefüllt wird. Sofern man das Feld Insert into: im “New File“-Dialog (Abbildung 2 oben) nicht verändert, schlägt der Designer neu zu erzeugende Dialoge, Wizards, C++-Dateien und Hauptfenster dem aktuellen Projekt zu. Jedes über diesen Dialog erzeugte GUI-Fenster wird später zu einer C++-Klasse.
Ein Wizard sorgt nun dafür, dass das Main-Window-Widget sofort mit Actions, Benutzeraktionen wie “Datei speichern”, “Anwendung schließen”, “Kopiere markierten Text in den Zwischenspeicher” etc., ausgestattet wird. Solche Aktionen kann die Programmiererin als Menüeinträge, Tastaturbelegungen und Werkzeugleisteneinträge verwenden – der Wizard (Abbildung 3) fragt so zunächst nach, ob allgemeingebräuchliche Aktionen im Hauptmenü und/oder in der Toolbar angelegt werden sollen. Kreuzt man an, dass der Designer Slots und Verbindungen (“Connections”) für die Aktionen anlegen soll, erstellt er ein Code-Gerüst für die Implementation der entsprechenden Actions und sorgt dafür, dass die Auswahl eines entsprechenden Menüpunkts oder Toolbar-Icons bereits zur Ausführung dieses (meist noch zu schreibenden) Codes führt.
Ein Druck auf den Next>-Button führt zum nächsten Wizard-Dialog. Hier darf man aus den Kategorien, bei denen das Toolbar-Feld in Abbildung 3 angewählt war, die Werkzeugleiste gestalten. Um hier nichts zu übersehen, sollte man das Category-Popdown-Menü komplett durchsehen. Transportiert man den Eintrag <Separator> mit dem Pfeil-nach-rechts-Knopf in die Toolbar-Auswahl, wird an der entsprechenden (durch die Pfeile nach oben und unten veränderbaren) Position ein Trennstrich in Menü oder Toolbar eingefügt. Ein Finish im letzten Wizard-Fenster erlaubt es nun, mit der richtigen Arbeit am Hauptfenster-Widget zu beginnen (Abbildung 4).
Eigenschaften und mehr
Zwei neue Fenster sind hinzugekommen: ein Formular, auf dem GUI-Elemente aus den Werkzeugleisten platziert werden, und der Action Editor. Je nachdem, welches Widget auf dem Formular markiert ist, ändert sich der Inhalt des Properties-Reiters.
Properties sind Eigenschaften einer Klasse, die sich mit set-Funktionen ändern lassen. Die Funktionstaste [F1] liefert eine Kurzbeschreibung. Ein Klick auf die Hyperlinks ruft das externe Hilfsprogramm assistant mit dem entsprechenden Ausschnitt der API-Dokumentation auf. Beim “Container-Fenster”, per Default als Form x bezeichnet, wird die Property name zum Klassennamen der gesamten Klasse; sonst legt man damit den Variablennamen des jeweiligen Objekts fest.
Betätigt man über einem markierten Formularbestandteil die [F1]-Taste, erscheint ebenfalls der Qt Assistant mit der passenden API-Dokumentation. Dessen Start kann auf etwas leistungsschwächeren Rechner einige Zeit dauern. Leider geschieht einfach nur nichts, wenn der Designer das assistant-Programm mangels passend gesetzter Pfadangabe o.a. nicht aufrufen kann.
Möchte ein Qt-GUI-Objekt signalisieren, dass jetzt etwas passieren soll, sendet es ein Signal aus. So setzt ein Pushbutton-Objekt das Signal clicked() in die Welt, sobald der Nutzer auf dieses GUI-Element klickt. Nun muss sich nur noch ein entsprechendes Objekt finden, das dieses Signal bearbeiten will, sonst verhallt es unerhört. Eine Klasse, deren Objekte auf bestimmte Signale reagieren sollen, enthält daher Funktionen (Slots), die mit den entsprechenden Signalen verbunden (“connected”) werden. Wie das genau aussieht, ist in [4] oder auch im Qt-Tutorial [5] genauer erklärt.
Mit dem Connection-Werkzeug, in der Werkzeugleiste über das Icon mit dem rot-blauen Balken aufzurufen, lassen sich zwei Widgets auf dem Formular “verbinden”. Der aufploppende Dialog (Abbildung 5) gibt die Möglichkeit, ein Signal des zuerst markierten Objekts mit einem Slot zu verknüpfen. Das etwas irreführend mit Slots bezeichnete Dropdown-Menü ermöglicht die Auswahl des Zielobjekts. Dessen Slots können nun markiert werden.
Unter Edit / Slots erhält man die Möglichkeit, Slots für die auf dem Formular entstehende GUI-Klasse zu definieren. Wer in C++ sicher ist, kann diese neuen Funktionen gleich mit dem beispielsweise über das Kontextmenü des Formulars unter Source… zu erreichenden Code-Editor implementieren. Leider lässt sich der nicht aus dem Hauptfenster lösen, sodass die Arbeit mit ihm etwas beschwerlich ist. Fügt man hier eine neue Funktion von Hand ein, wird diese als neuer Slot auch in den Source-Reiter eingetragen (Abbildung 6). Dort trägt man über das Kontextmenü übrigens auch Header-Dateien nach, die für die Implementation gebraucht werden. Während die GUI-Beschreibung der neuen Klasse beim Speichern (etwa mit File / Save All) in einer XML-Datei mit der Endung .ui landet, wird die Implementation in einem entsprechenden .ui.h-File abgelegt.
Layout-Management
Wenn schon die grafischen Elemente des neuen GUIs per Mausklick platzierbar sind, sollte sich der Designer auch ohne händische Friemelei um das harmonische Gesamtbild kümmern. Dafür ist die Layout-Management-Toolbar (Abbildung 7) zuständig. Wie bei allen anderen Icons auch erschließt sich deren Bedeutung über die Tool-Tipps, gelbe “Zettelchen”, die erscheinen, wenn die Maus ein wenig über dem Icon verweilt. Ins Formular eingefügte Federn sorgen für ausgewogenen Platz zwischen markierten Elementen, wenn man sie mit den entsprechenden Werkzeugen in ein Gitter bzw. ein horizonales oder vertikales “Layoutraster” einpasst. Bei Splitter-Layouts kann der Anwender des fertigen GUIs den Platz, den die betroffenen Widgets einnehmen, nach Gutdünken zwischen ihnen aufteilen.
Das Ergebnis der Bemühungen lässt sich über das Preview-Menü in verschiedenen Outfits von Windows bis Motif betrachten. Einzelelemente innerhalb eines layouteten “Containers” können sich übrigens nur dann in ihrer Größe verändern, wenn das Layout vorher aufgehoben (Break Layout) wurde.
Abbildung 7: Eistellung der Optimalgröße von Widgets u.a. Layout-Möglichkeiten
Auf dem Weg zum vollständigen Programm
Über den Dialog File / New lassen sich nicht nur weitere Dialoge und Wizards anlegen, sondern auch C++-Header- und Implementationsdateien ins Projekt einfügen. Während die Programmiererin bei “normalen” C++-Dateien auf die eigenen Programmierkenntnisse angewiesen bleibt, erzeugt das Template mit dem Namen C++ Main-File (main.cpp) (Abbildung 2) die Datei mit der main()-Hauptfunktion des Qt-Programms, das eines der im Projekt enthaltenen Widgets als Hauptfenster (Main-Form) benutzt (Abbildung 8).
Um das bislang erzeugte Programm-Gerüst zu kompilieren, erzeugt man zunächst im Projektverzeichnis das passende Makefile:
qmake -o Makefile projektdatei.pro
und kompiliert mit make. Doch nach diesem ersten Erfolgserlebnis gilt es meist noch, das neuerstellte GUI mit weiterer Funktionalität auszustatten. Spätestens dann heißt es, zum Texteditor der Wahl zu greifen.
Glossar
-
main()
-
Die Hauptfunktion eines C-, C++- oder Java-Programms. Bei GUI-Programmen zeichnet sie das Hauptfenster der Applikation auf den Bildschirm. Anders als bei nicht-grafischen Programmen, die mehr oder weniger linear abgearbeitet werden, ist das Programm damit aber nicht fertig – die eigentliche Arbeit beginnt erst, wenn der User dieses Fenster benutzt. Die main()-Funktion eines GUI-Programms startet daher eine sogenannte “Event-Loop” (Ereignisschleife). Erst wenn diese beendet wird, endet das gesamte Programm.
-
$
-
Vor den Namen einer Shell-Variablen gesetzt, sorgt das Dollarzeichen dafür, dass diese ihren Inhalt preisgibt.
-
Widget
-
Buttons, Fenster, Menüleisten u.a. Bestandteile einer grafischen Benutzeroberfläche.
-
Klassendeklaration
-
Will man Objekte einer Klasse erzeugen oder auf solche zugreifen, sollte man nicht die Klassen-Implementation durchlesen müssen. Stattdessen erstellt man innerhalb der C++-class-Anweisung ein “Application Programmer’s Interface” (API), das beschreibt, welche Funktionen und Variablen die Klasse kennt, welche davon von anderen Drittobjekten aus zugänglich sind, welche Art Argumente die Klassenfunktionen annehmen etc. Der Kopf der class-Anweisung legt den Klassennamen fest und welche Klasse der Neuling ggf. beerbt. Diese Deklaration wird in der Regel in eine eigene “Header-Datei” ausgelagert.
Infos
[1] ftp://ftp.troll.no/pub/qt/source/
[3] http://qt-interest.trolltech.com/
[4] Patricia Jung: “Vorfreude, schönste Freude” (Adventskalender mit C++ und Qt), LinuxUser 12/2001, S. 85 ff.
[5] Qt-Tutorial (englisch): http://doc.trolltech.com/3.0/tutorial.html











