Hinweis des Autors
Zugegebem, der Artikel ist alt, aber vielleicht findet jemand einige Hinweise um es auf eine aktuelle Version anzuwenden
Erstellen von Anwendungen mit grafischer Oberfläche
mit dem LINUX Desktop KDE
Inhalt:
1 Einführung
Das Betriebssystem LINUX gewinnt immer mehr an Bedeutung, insbesondere in der Anwendung im professionellen Bereich. Im privaten Bereich ist die Zahl der Benutzer zwar ebenfalls steigend, aber die Zahl der Anwender, die sich an Ihre grafische Oberfläche ?Windows" gewöhnt haben, ist groß.
Unter Linux gibt es eine Reihe von Windowmanagern, die unterschiedliche Entwicklerteams entworfen und auch zur stabilen Lauffähigkeit gebracht haben. Ein System kann jedoch nur dann Erfolg haben, wenn es Anwendungsentwicklern die Möglichkeit gibt, eine Vielzahl von Anwendungen schnell, effektiv und mit hoher Qualität zu erstellen. Nur wenn die Anzahl der Anwendungen hoch ist, werden auch die Endbenutzer dieses System akzeptieren und benutzen.
Der Entwicklung von grafischen Oberflächen kommt daher eine hohe Bedeutung zu und wird in naher Zukunft mit hoher Wahrscheinlichkeit einen LINUX-Boom auslösen.
2 Zielsetzung dieses Dokuments
Es soll in möglichst kompakter Weise ein Leitfaden zur Erstellung von grafischen Anwendungen unter LINUX gegeben werden. Häufig werden zum Thema ?Programmieren" Bücher mit bis zu 1000 Seiten angeboten. Dies soll hier ausdrücklich nicht Ziel sein !
Durch praktische Hinweise und Verweis auf bereits vorhandene Dokumentation soll die Möglichkeit des selbständigen Einarbeitens gegeben werden. Hierbei sollen frühzeitig Erfolgserlebnisse in Form von lauffähigen Programmen erzielt werden. Der Schwerpunkt liegt dabei nicht bei der Vermittlung der Sprache C/C++ sondern bei der Anwendung der Bibliothek ?Qt".
Folgende Vorkenntnisse sollten vorhanden sein:
- Grundlagen UNIX
- Grundlagen in der Sprache C und C++ / Objektorientierung
- Nützlich aber nicht zwingend sind Kenntnisse in "Java"
3 Das Projekt KDE
Mit dem Projekt K-Desktop-Environment KDE (http://www.kde.org) wurde eine grafische Oberfläche geschaffen, die eine hohe Benutzerfreundlichkeit aufweist und auch für ?Windows"-umsteiger gute Möglichkeiten bietet sich zurecht zu finden. Die Verbreitung von KDE nimmt schnell zu. Es ist aber noch nicht abzuschätzen, ob es sich zum Standard durchsetzt.
Aktuelle Version : KDE 1.1.1
4 Das Tool "Qt", eine Klassenbibliothek
Das Tool ?Qt" (http://www.troll.no) ist eine Bibliothek von GUI-Elementen und Funktionen, die eine Entwicklung grafischer Anwendungen sehr ?einfach" ermöglichen, da es auch sehr gut dokumentiert ist.
Wichtige Hinweise:
Wie unter LINUX üblich, sind für viele Programme und Tools keine Lizenzgebühren fällig, oder es gibt - wie hier beim Qt-Tool - eine sogenannte ?Free Edition", welche unter der GPL -Lizenz steht. Wenn die Absicht besteht, Programme für die externe Vermarktung zu erstellen, muß eine ?Professionell Edition" erworben werden. Die Lizenzbedingungen werden jeweils mitgeliefert oder können unter http://www.troll.no eingesehen werden.
Aktuelle Version Qt 1.44
5 Vorbereiten der Entwicklungsumgebung
5.1 Softwareversionen
Dieser Abschnitt beruht auf der Festlegung, das folgende Softwareversionen verwendet werden: (andere Versionen haben ggf. andere Pfadangaben für Programme / Bibliotheken)
- SuSE Distribution 6.1 (Frühjahr 1999)
- KDE-Desktop 1.1.x
- Qt 1.44
5.2 Installation von Paketen in SuSE 6.1
In dieser Distribution wird von SuSE das Tool YaST bereitgestellt, welches die Installation von Paketen durchführt. Es müssen folgende Pakete installiert werden:
Bild 1: Anzeige der Paketliste
Die Paketauswahl erfolgt im YaST unter den Menüpunkten
- Installation festlegen / starten
- Konfiguration ändern / erstellen
5.3 Installation der Bibliotheken und Dokumentation prüfen
Um eine einfache Möglichkeit zu geben, ggf. auf spätere Versionen der Bibliotheken umzustellen, sind hier beispielhaft Dateien angeben worden, die für die Compiliervorgang und das Ausführen der fertigen Programme notwendig sind.
Die Pfadangaben sind in den beigefügten Scripts ?qcc" und ?qccm" als Variablen definiert und müssen ggf. dort geändert werden, damit einwandfrei compiliert werden kann. Diese Scripts testen das Vorhandensein der Pfade und brechen ggf. den Vorgang ab. In diesem Fall sollte nach der nachfolgenden Tabelle das Verzeichnis ermittelt werden, in dem die notwendigen Dateien stehen.
Melden die o.g. Scripte z.B. ?Fatal: Das Verzeichnis /usr/lib/qt/include existiert nicht !", so finden Sie mit dem Befehl:
find / -name qapplication.h -print
das richtige Verzeichnis.
Pfad |
Beispiel für eine vorhandene Datei in diesem Verzeichnis. |
/usr/lib/qt/lib |
-rwxr-xr-x 1 root root 2213612 Mar 19 12:58 libqt.so.1.44 |
/usr/lib/qt/include |
-rw-r-r- 1 root root 6280 Mar 19 12:58 qapplication.h |
/usr/X11R6/include |
In diesem Beispiel keine bestimmten Dateien. |
/usr/lib/gcc-lib/i486-linux/2.7.2.3 |
G++ etc. ?? |
Die Dokumentation des Qt-Tools befindet sich gewöhnlich unter:
/usr/doc/packages/qt/html
Sollte dies nicht der Fall sein, lesen Sie in Ihrer Dokumentation der Linux-Distribution nach, wo sich die Dokumentation befindet. Hilft dies nicht, dann können Sie unter http://www.troll.no eine Dokumentation herunterladen.
5.4 Script für das Compilieren und Linken
Leider ist dem Tool ?Qt" kein Hinweis zum Compilieren der Programme beigefügt. Es werden zwar Beispielsprogramme im Quellcode mitgeliefert, diese enthalten auch "Makefiles", in einem von mir durchgeführten Test wurde jedoch der Linkvorgang abgebrochen. Von http://www.troll.no kann als Unterstützung das Paket "kdesdk" heruntergeladen werden. Dieses Paket erhält Möglichkeiten zum compilieren, die jedoch ebenfalls recht komliziert sind und die Tools "autoconf" und "automake" erfordern. Lesen Sie die entsprechende Dokumentation hierzu.
Es kann aber auch das nachfolgende Shellscript verwendet werden:
#Parameterübergabe prüfen if [unix: $# -ne 1 ] then echo "USAGE $0 <Quelldatei>" >&2 exit 1 fi if [ ! -f ${1}.cpp ] then echo "$0 : Fatal: Datei ${1}.cpp nicht gefunden" >&2 exit 1 fi #Variablen für die Bibliotheken setzen, müssen ggf. geändert #werden, z.B. bei neuen Versionen QT_INCLUDE_PATH=${QTDIR}/include export QT_INCLUDE_PATH QT_LIB_PATH=${QTDIR}/lib export QT_LIB_PATH GCC_LIB_PATH=/usr/lib/gcc-lib/i486-linux/2.7.2.3 ;export GCC_LIB_PATH #Prüfen, ob diese Verzeichnisse existieren for i in $QT_INCLUDE_PATH $QT_LIB_PATH $GCC_LIB_PATH do if [ ! -d ${i} ] then echo "$0 : Fatal: Das Verzeichnis ${i} existiert nicht !" >&2 exit 1 fi done # 1. Nur compilieren echo "Compiliere Datei ${1}.cpp ..." g++ -c -I${QT_INCLUDE_PATH} -pipe -O2 -fno-strength-reduce -I/usr/X11R6/include -o ${1}.o ${1}.cpp if [ $? -ne 0 ] then echo "Compilieren Datei ${1}.cpp fehlgeschlagen" >&2 exit 1 fi # 2. Linken echo "Linke Datei ${1}.o ..." ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o ${GCC_LIB_PATH}/crtbegin.o ${GCC_LIB_PATH}/crtend.o /usr/lib/crtn.o -L${QT_LIB_PATH} -L/usr/X11R6/lib -L${GCC_LIB_PATH} -L/usr/i486-linux/lib -lqt -lX11 -lXext -ljpeg -lstdc++ -lm -lgcc -warn-common -lc ${1}.o -o ${1} if [ $? -ne 0 ] then echo "Linken Datei ${1}.o fehlgeschlagen" >&2 exit 1 fi
ild 2: Das Script qcc
Hierzu einige wichtige Hinweise:
- Der Aufruf des Scripts ist ?qcc <Quelldatei>" , wobei die Quelldatei ohne die Endung ?cpp" eingegeben wird.
- Die Pfadangaben für die Bibliotheken müssen ggf. angepaßt werden. (siehe Abschnitt 5.3 )
- Für das unten behandelte Programm ?pushme" habe ich das Shellscript ?qccm" verwendet, welches eine Weiterentwicklung des qcc-Scripts ist. Hierbei wird auch der Meta-Object-Compiler (MOC) verwendet.(siehe unten im Abschnitt 9)
- In der getesteten Version Qt 1.44 meldet das Script beim Linken eine Warnung, daß die Versionsnummer der Bibliothek nicht richtig währe. Diesen Hinweis kann man ignorieren. Er beruht auf der Tatsache, daß für das compilieren der Bibliotheken wiederum die EGCS-Bibliothek verwendet wurde. Die Verwendung der EGCS-Bibliothek in /usr/lib/gcc-lib/i486-linux/egcs-2.91.66 funktionierte aber nicht.
6 Übersicht über die GUI-Elemente der Qt-Bibliothek
Besondere Unterstützung erhält der Entwickler durch die im Qt-Tool mitgelieferte Objektbibliothek für die grafischen Elemente. Das bedeutet in der Praxis, daß der Entwickler nicht ?das Rad" neu erfinden muß, und für alle auf dieser Plattform entwickelten Programme ein einheitliches Erscheinungsbild existiert.
Die einzelnen Elemente sind sehr gut dokumentiert, d.h. unter dem Verzeichnis /usr/doc/packages/qt/html befinden sich zu jeder Klasse html-Dokumente die wesentliche Informationen liefern. Diese Dokumentationsform erinnert sehr stark an das Java-Development-Kit (JDK).
Zu jeder Klasse werden die Konstruktoren, Destruktoren, die enthaltenen Funktionen mit Aufruf- und Rückgabeparametern, die Membervariablen, sowie die Signale und Slots (vgl. Abschnitt 8) beschrieben.
Wegen dieser guten Dokumentation wird deshalb bei der Beschreibung des Beispielprogramms hierauf nicht näher Bezug genommen.
Die nachfolgenden Abbildungen sind auch Bestandteil der mitgelieferten Dokumentation:
Klassenname |
Abbildung |
|
QListView |
|
|
QPrintDialog |
|
|
QButtonGroup |
|
|
QCheckBox |
|
|
QComboBox |
|
|
QFileDialog |
|
|
QGroupBox |
|
|
QHeader |
|
|
QLabel |
|
|
QLCDNumber |
|
|
QLineEdit |
|
|
QListBox |
|
|
QMainWindow |
|
|
QMenuBar |
|
|
QMultiLineEdit |
|
|
QMessageBox |
|
|
QPopupMenu |
|
|
QProgressBar |
|
|
QProgressDialog |
|
|
QRadioButton |
|
|
QScrollBar |
|
|
QScrollView |
|
|
QSlider |
|
|
QSpinBox |
|
|
QStatusBar |
|
|
QTabBar |
|
|
QTabDialog |
|
|
QTableView |
|
|
7 An "Hello World" kommt man nicht vorbei.
Dieses kleine Programm stellt ein Fenster mit einem einzelnen Button dar, welcher die Beschriftung ?Quit" trägt. Bei Betätigung dieses Buttons wird die Anwendung beendet. Es handelt sich bereits um eine vollständige Anwendung, d.h. es gibt ein Systemmenü und das Fenster kann in seiner Größe geändert werden. Allerdings wird auf diese Aktion noch nicht reagiert. Deshalb wird auch die Größe des Buttons nicht geändert, wenn sich die Größe des Fensters ändert. Dieses Beispiel kann nun mit Hilfe des im Abschnitt 5.4 beschriebenen Scripts compiliert werden und läuft dann nach Aufruf auf dem KDE-Desktop. Durch ?spielerisches Experimentieren" kann mit Hilfe der Qt-Dokumentation das Programm angepasst werden, wodurch weitergehende Funktionalitäten erlernt werden. Beispiele: Anpassen der Farben und Schriftarten, verändern der Positionen der Elemente, etc. #include <qapplication.h> #include <qpushbutton.h> #include <qfont.h> int main(int argc, char **argv) { //QApplication stellt das Anwendungsfenster mit Systemmenue bereit //und nimmt die Argumente aus der Befehlszeile auf QApplication a( argc , argv ); //Ein neuer Button mit dem Titel "Quit" wird erstellt //Der Button erhaelt den Namen "quit" und wird in das aktuelle //Window gesetzt. QPushButton quit("Quit"); //Der Button erhaelt die Groesse 75 Pixel breit, 30 Pixel hoch quit.resize(75,30); //Die Beschriftung des Buttons wird auf die Schriftart "Times" //18 Punkte hoch und Fettdruck eingestellt quit.setFont(QFont("Times",18,QFont::Bold)); //Die Aktion "click" des Buttons "quit" wird mit der Funktion //"quit" der Anwendung "a" verbunden. Dies stellt eine Verbindung //in einer Richtung dar. QObject::connect(&quit,SIGNAL(clicked()),&a,SLOT(quit())); //Der Button "quit" wird zum Hauptelement der Anwendung erklaert //Hierdurch erhaelt das Anwendungsfenster die Groesse des Buttons a.setMainWidget(&quit); //Der Schalter wird angezeigt quit.show(); //Die Anwendung gibt den Returncode der Applikation an die //aufrufende Shell zurück return a.exec(); }
8 Verarbeitung der Anwenderaktionen(Events),
SIGNALe und SLOTs
8.1 Allgemeines über Events
Eine elementare Funktion eines Programms ist die Reaktion auf die Eingaben des Benutzers. Dies sind z.B. Mausklicks, verschieben oder vergrößern von Fenstern, Tastatureingaben, Texterfassung.
Theorie: Alle objektorientierten Systeme mit grafischer Oberfläche besitzen eine sogenannte Message- Queue - eine Warteschlange - in der diese Benutzeraktionen in der Reihenfolge ihres Eintreffens gesammelt und an die zugehörenden Elemente der Anwendung weitergeleitet werden. Elemente sind hierbei die im Abschnitt 6 beschriebenen Klassen.
8.2 Erste Möglichkeit: vordefinierte Funktionen
In der Programmiersprache ?C++" steht die Möglichkeit des ?Überschreibens" von Funktionen zur Verfügung. Hierdurch wird eine Funktion einer höheren Klasse durch eine lokale Funktion gleichen Namens ersetzt. Dies bietet hier die Möglichkeit, feststehende Funktionsnamen für eine Benutzeraktion festzulegen.
Beispiel in der Datei pushme.cpp :
void MainWidget::resizeEvent( QResizeEvent * ) { main_resize(); }
Die Funktion "resizeEvent" wird immer aufgerufen, wenn die Größe des Programmfensters geändert wird. In diesem Fall wird dann die Funktion main_resize() aufgerufen, welche dann die notwendigen Aktionen (verschieben oder Größenänderung der anderen GUI-Elemente) übernimmt.
Vergleichen Sie hierzu die Dokumentation der Klasse Qwidget.
Ein weiteres Beispiel ist die Funktion MainWidget::timerEvent(QTimerEvent *e) welche vom System aufgerufen wird, wenn der eingestellte Zeitintervall erreicht ist. Die Einstellung hierzu erfolgt über die Klasse ?Qtime" und den Befehl ?startTimer(500)". Hierdurch wird der Intervall auf 500msec eingestellt. Diese Funktion steuert die Zeitanzeige.
8.3 Zweite Möglichkeit: SIGNAL- Verarbeitung
Während in Java von Events und Eventhandlern (Aktionen und die Aktion verarbeitende Funktion) gesprochen wird, heißen diese Merkmale im Qt-Tool ?SIGNAL" und ?SLOT", welche aber die gleiche Funktion haben. Ein Anwender löst eine Aktion aus, (Mausklick auf einen Button) in diesem Button wird das SIGNAL clicked() ausgelöst. Durch die ?connect"-Methode kann dieses Signal mit einem SLOT verbunden werden. Der SLOT bildet das Gegenstück zu einem SIGNAL, übernimmt also die Verarbeitung des SIGNALs.
8.3.1 Vorhandene SIGNALe und SLOTs im Qt-Tool
Viele Klassen besitzen bereits ?von Hause aus" entsprechende SIGNALe und SLOTs. Ein Beispiel hierzu ist im Programm ?Hello World" enthalten: Das SIGNAL clicked() des Buttons ?quit" wird mit dem SLOT quit() der Anwendung verbunden.
Die in der Dokumentation des Qt-Tools als ?public slots" oder ?signals" deklarierten Funktionen können verwendet werden. Es muß darauf geachtet werden, daß die beiden Funktionen jeweils die selben Parameter besitzen d.h. wenn das Signal einen Integer-Wert liefert, muß dieser auch vom SLOT aufgenommen werden.
8.3.2 Eigene Entwicklung von SIGNALen und SLOTs
Zur Entwicklung eigener SIGNALe und SLOTs sind mehrere Arbeitsschritte erforderlich. Diese werden anhand des in Abschnitt 11 behandelten Schiebespiels erläutert.
In der Datei pumain.h ist der Prototyp der Hauptanwendungsklasse ?MainWidget" enthalten. Dies muß in einer Headerdatei (*.h) geschehen, da auch andere Dateien hierauf Zugriff haben müssen (d.h. diese Headerdatei includieren). Zu Beginn der Klassendefinition wird das Schlüsselwort ?Q_OBJECT" eingesetzt, welches für den MOC (èAbschnitt 9) den Hinweis gibt, daß hier Informationen über SIGNALe und SLOTs vorhanden sind.
class MainWidget : public QWidget
{ Q_OBJECTpublic:
MainWidget( QWidget *parent=0, const char *name=0 );public slots: void pressed(); void new_game(); void sbar_changed(int);signals: void sig_pressed ();
// Abschnitte protected: und private: sind hier
// aus Vereinfachungsgründen weggelassen.
};
Bild 3: Auszug aus der Datei pumain.h
Die Beschreibung der einzelnen Funktionen geschieht dann in der Datei pumain.cpp . Beispiel:
void MainWidget::new_game()
{
// Inhalt siehe Datei pumain.cpp
}
Anmerkung: Die Funktion sig_pressed wird nicht näher beschrieben. Dies wird vom MOC (è siehe Abschnitt 9) vorgenommen. Das Signal wurde hier nur eingebaut, um ein Beispiel zu geben. Es ist für den Programmablauf des Beispielprogramms ?pushme" ohne Belang. In der Funktion ?pressed()" wird am Ende mit dem Befehl ?emit" das Signal ausgesendet. (Ist hier auskommentiert)
Sind alle Funktionen beschrieben, kann nun (probeweise !) der Compiliervorgang gestartet werden. Wird hierzu das mitgelieferte Script ?qccm" verwendet, wird der MOC (èAbschnitt 9) gestartet. Für spätere eigene Programme kann hier der Hinweis gegeben werden, daß der Compiliervorgang auch ohne den MOC ?scheinbar" erfolgreich verläuft, aber dennoch keine SIGNALe funktionieren werden. Der Compiliervorgang sollte jedoch trotzdem vorher probeweise ohne den MOC durchgeführt werden, damit sichergestellt ist, daß alle Syntaxfehler beseitigt sind.
9 Die Verwendung des Meta-Object-Compilers (MOC)
Mit Hilfe des MOC werden Informationen über die SIGNAL und SLOT- Mechanismen in der Anwendung in eine Datei geschrieben. Diese Datei stellt dann selbst ein cpp-Programm dar, welches mit den anderen Teilen der Anwendung compiliert und gelinkt werden muß.
Konkret werden hier aus der Datei pumain.h Informationen in die Datei moc_pumain.cpp geschrieben.
Hierzu lautet die Befehlszeile (siehe Datei ?qccm"): moc pumain.h -o moc_pumain.cpp
Dieser Befehl wird immer dann ausgeführt, wenn die Datei moc_pumain.cpp nicht existiert, oder diese älter als pumain.h ist.
Die erzeugte Datei moc_pumain.cpp wird dann compiliert und gelinkt.
Weitere Informationen über den MOC werden über den Befehl:
man moc
angezeigt.
10 Möglichkeit der Fehlersuche
Müssen während des Programmablaufs Werte von Variablen überprüft werden, kann dies mit dem Befehl ?debug" erfolgen. Dieser ist genau wie der C-Befehl ?printf" zu verwenden. Beispiel:
debug ("Es wurde der Schalter %s gedrückt",sender()->name());
11 Beispielsprogramm: Ein Schiebespiel
Der Sinn des Spiels ist die Sortierung der Spielsteine in einer numerischen Reihenfolge. Zu Beginn des Spiels werden die Steine in zufälliger Reihenfolge angeordnet. Ein Feld bleibt als Bewegungslücke frei. Klickt der Spieler auf einen an die Lücke angrenzenden Stein, wird dieser mit der Lücke vertauscht.
Während der Spieldauer wird die Spielzeit in einer LCD-Anzeige dargestellt. Events: Ereignisse sind die periodische Aktualisierung der Zeit, ein Klick auf einen Spielstein, ein Klick auf den Schalter ?Neues Spiel" und eine Veränderung des Wertes der Scrollbar. Letzteres führt zu einem neuen Spielbeginn, wodurch die Steine neu gemischt werden und die Spielzeit auf 0 gesetzt wird.
Realisierung:
Kernstück des Programms ist die Steuerung der Spielsteine. Diese bestehen aus Objekten ?QPushButton", welche in einem Array dimensioniert sind. In einem zweidimenionalen Array mit Namen ?playarray [] []" werden die Bezeichnungen der einzelnen Buttons gespeichert. Dies ist wichtig für:
- Die Frage: ist ein Schalter mit diesem Namen bereits vorhanden (function array_exist ()) ?
- den Tausch der Spielsteine bei einem Spielzug
(function swap_button(int,int))
- die noch nicht implementierte Funktion des Spielendes, in dem alle Werte auf ihre richtige Reihenfolge hin geprüft werden.
12 Literaturhinweise
Interessanter Vortrag zum Thema:
http://www.student.uni-kl.de/~blehner/kde-vortrag/material.html
Fragen zur Installation, Konfiguration:
http://www.trolltech.com/developer/faq/install.html
Download für Qt 1.44: (in SuSE 6.1 enthalten)
http://www.trolltech.com/products/download/freelicense/qtfree-dl.html
Buch zum Thema:
?Programming with Qt" von Kalle Dalheimer, O'Reilly & Associates; ISBN: 1565925882 Preis: $26.36
Comment: It is in English Language. Buy it at http://www.amazon.de or http://www.amazon.com