Wer sich schon mit der GUI-Programmierung befasst hat, wird bei dieser Überschrift an sogenannte Callback-Funktionen denken. Doch Qt geht hier einen anderen Weg und verwendet ein Signal-Slot-Konzept. Wie dieses genau funktioniert, wollen wir im folgenden Kapitel näher beschreiben.

2

Signale und Slots

Im Grunde verläuft die GUI-Programmierung immer nach demselben Prinzip: Wenn sich bspw. ein Widget verändert hat (bspw. bei Anklicken eines Buttons), will man ein anderes Widget darüber informieren. Widgets werden also im Programm mit einer Funktion verknüpft, die ausgeführt wird, sobald der Anwender dieses etwa durch einen Mausklick aktiviert hat. Drückt der Anwender z. B. auf einen »Öffnen«-Button, wird eine entsprechende Funktion aufgerufen, die ggf. einen neuen Dialog zum Öffnen einer Datei präsentiert. Widgets Das Wort ins Deutsche zu übersetzen ist wohl wenig sinnvoll (Widget=»Dingsbums«). Als Widgets (Fensterkontrollelemente) bezeichnet man alle Interaktionsbzw. Steuerelemente in grafischen Benutzeroberflächen. Abhängig von der GUIBibliothek gibt es hierbei einfache Widgets, woraus sich der Programmierer seine eigenen Dialoge zusammenbasteln kann. Häufig findet man auch komplexere Widgets wie bspw. einen fertigen Dialog zum Öffnen/Speichern einer Datei.

Viele grafische Toolkits verwenden zur Kommunikation zwischen den Widgets oft eine Callback-Funktion. Ein solcher Callback ist nichts als ein simpler Zeiger auf eine Funktion. Allerdings haben solche Callbacks zwei kleine Makel. Zum einen sind sie nicht typensicher (man ist nie sicher, dass die gerade ausführende Funktion den Callback mit den richtigen Argumenten aufruft). Zweitens ist der Callback mit der auszuführenden Funktion fest verbunden, weil die auszuführende Funktion wissen muss, welcher Callback aufgerufen werden soll. Mit dem Signal-und-Slot-Konzept geht Qt hier einen etwas anderen Weg. Dieses Konzept hat den Vorteil, dass Qt die Verbindung automatisch trennt, wenn eines der kommunizierenden Objekte zerstört wird. Dadurch vermeidet man viele

27

2

Signale und Slots

Abstürze, weil Versuche, auf ein nichtvorhandenes Objekt zuzugreifen, nicht mehr möglich sind.

2.1

Grundlagen

Am einfachsten lässt sich das Signal-und-Slot-Konzept an unserem »Hallo Welt«Beispiel beschreiben. Das Beispiel wollen wir nun erweitern, indem wir beim Anklicken des Buttons die Anwendung beenden. 01 // beispiele/signalslot1/main.cpp 02 #include 03 #include 04 int main(int argc, char *argv[]) { 05 QApplication app(argc, argv); 06 QPushButton hello("Ende"); 07 hello.resize(100, 30); 08 hello.show(); 09 QObject::connect( &hello, SIGNAL( clicked() ), &app, SLOT( quit() ) ); 10 return app.exec(); 11 }

Verglichen mit dem ersten Beispiel wurde hier, abgesehen von einer anderen Bezeichnung des Buttons, mit der Zeile 9 eine neue statische Methode aufgerufen. Und zwar connect() von der Klasse QObject. Hinweis Die Funktion connect() von der Klasse QObject hat nichts mit der gleichnamigen Socketfunktion von Berkeley zu tun. connect() ist eine statische (static) Methode, die eine Verbindung zwischen einem Signal und einem Slot herstellt. Hierzu die genaue Syntax: bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection ) [static]

Mit den ersten beiden Argumenten (sender, signal) bezeichnet man das Objekt, das ein Signal aufnimmt und an den Empfänger sendet – das Sender-Objekt also. Die beiden anderen Argumente (receiver, method) legen das Objekt des Emp-

28

Grundlagen

fängers des entgegenzunehmenden Slots fest. Genauer gesagt: Empfängt das Objekt sender das Signal signal, wird es an den Slot gebunden. Dies nimmt das Objekt receiver vom Slot entgegen und führt method aus. Auf die folgende Zeile bezogen, heißt das: QObject::connect( &hello, SIGNAL( clicked() ), &app, SLOT( quit() ) );

Hiermit verbinden Sie das Objekt hello von der Klasse QPushButton mit dem Objekt app von der Klasse QApplication. Sollte beim Button das Signal clicked() eingehen, legen Sie als Aktion fest, dass bei der Klasse QApplication die Methode quit() ausgeführt wird, was in diesem Fall das Ende des Programms bedeuten würde (siehe Abbildung 2.1).

QPushButton hello

QApplication app

clicked()

quit()

Abbildung 2.1

Zwei Objekte verbinden

Die Funktion QObject::connect() gibt bei erfolgreicher Verbindung true, ansonsten false zurück. Außerdem müssen Sie die Makros SIGNAL() und SLOT() verwenden, wenn Sie das Signal und die Methode festlegen wollen, weil für die beiden Argumente eine Zeichenkette vorgesehen und diese beiden Makros dafür sorgen, dass eine korrekte Zeichenkette eingesetzt wird. Hinweis Das letzte Argument type von QObject::connect() ist optional und beschreibt den Typ der Verbindung, auf dem diese aufbaut.

Somit handelt es sich bei den Slots um ganz normale Methoden einer Klasse die auf Signale einer anderen Klasse reagieren, wobei die Signale im Grunde wiederum nur einfache Methoden einer Klasse sind. Natürlich ist es auch möglich, einen Sender mit mehreren Empfängern und umgekehrt zu verknüpfen. Ist bspw. ein Signal mit zwei oder mehreren Slots verbunden, werden die Slots der Reihe nach ausgeführt, so wie sie im Code geschrieben wurden. Jede Unterklasse von QObject kann somit solche Signale und Slots definieren.

29

2.1

2

Signale und Slots

2.1.1

Signale und Slots ermitteln

Sicherlich stellt sich für Sie jetzt die Frage: Woher mit den Signalen und Slots? Hierzu sind zwar tiefere Kenntnisse von Qt und deren Klassen von Vorteil, doch kann man die einzelnen Signale und Slots unmöglich auswendig kennen. Für solche Fälle ist es ratsam, den Assistant von Qt zu verwenden, der bei der Qt-Distribution mit installiert wurde (Abbildung 2.2). Im Grunde wird hier nur das Ableiten und Vererben von Klassen bzw. Methoden verwendet, was für C++ typisch ist.

Abbildung 2.2

QtAssistant von Trolltech

Wollen Sie bspw. ermitteln, welche Signale bei der Klasse QPushButton auftreten können, müssen Sie zunächst im Indexverzeichnis von Qt-Assistant nach der entsprechenden Klasse suchen (Abbildung 2.3). Als Ergebnis erhalten Sie die Klassen-Referenz von QPushButton, mit all ihren Eigenschaften, Funktionen, Signalen und Slots. Wichtig hierbei ist auch der Inhalt von vererbten Mitgliedern (Abbildung 2.4).

30

Grundlagen

Abbildung 2.3

Auf der Suche nach der entsprechenden Klasse

Abbildung 2.4

Klassen-Referenz von QPushButton

31

2.1

2

Signale und Slots

Bei der Durchsicht der Klassen-Referenz von QPushButton fällt auf, dass sich hier zwar öffentliche Slots befinden, aber keine direkten Signale. Erst bei den vererbten Mitgliedern finden Sie einen Eintrag (mit Verweis) wie bspw. »4 signals inherited from QAbstractButton«. Also folgen Sie dem Link zur Klasse QAbstractButton, und siehe da: Wir finden die gewünschten Signale, die ja auch an QPushButton weitervererbt wurden (da public). Neben clicked() finden Sie hier auch die Signale pressed(), released() und toogled(), des Weiteren zwei weitere Signale, die von QWidget und von QObject geerbt wurden (Abbildung 2.5).

Abbildung 2.5

Vererbte Signale von QAbstractButton an QPushButton

Wollen Sie noch mehr zu dem Signal erfahren, folgen Sie einfach wieder dem entsprechenden Verweis. Im Beispiel zuvor haben wir auch einen Slot (quit()) der Klasse QApplication verwendet. Um hier ebenfalls an den entsprechenden Slot(s) einer Klasse zu kommen, gehen Sie wie bei den mit dem Assistant bereits beschriebenen Signalen vor. Auf der Klassen-Referenz von QApplication finden Sie dann unter dem Eintrag »Public Slots« die entsprechenden Slots (Abbildung 2.6). Zwar sind im vorliegenden Fall drei vorhandene Slots definiert (aboutQt(), closeAllWindows() und setStyleSheet()), aber der Slot quit() ist nicht direkt

in der Klasse QApplication definiert. Auch in diesem Fall wurde der Slot quit() von einer anderen Klasse, hier von QCoreApplication, geerbt. Dies können Sie

32

Grundlagen

dem entsprechenden Verweis »1 public slot inherited from QCoreApplication« entnehmen.

Abbildung 2.6

Klassen-Referenz von QApplication

Abbildung 2.7

In der Referenz von QcoreApplication gefundener Slot

33

2.1

2

Signale und Slots

Die Verwendung des Assistant von Qt ist unerlässlich für jeden seriösen Qt-Programmierer. Das Prinzip ist immer dasselbe: Man sucht nach der entsprechenden Klasse (Widget) im Indexverzeichnis und anschließend nach dem entsprechenden Signal, dem Slot, der Eigenschaft oder eben der entsprechenden Methode, die man mit einem Widget ausführen will. Hierzu soll ein weiteres Beispiel erstellt werden, das folgendes Signal-Slot-Konzept verwendet (siehe Abbildung 2.8).

QPushButton but1

QApplication app

clicked()

aboutQt()

quit() QPushButton but2

clicked() QWidget win QPushButton but3 showNormal() clicked() showMaximized()

QPushButton but4

showMinimized()

clicked()

Abbildung 2.8

Signale und Slots verbinden

Die Zeichnung liest sich einfach. Auf der linken Seite wurden alle Objekte angegeben, die ein Signal empfangen und es an die Objekte der rechten Seite senden.

34

Grundlagen

Auf der linken Seite befinden sich also die Signale (mit deren Klassen) und auf der rechten Seiten die Slots (mit deren Klassen). Das Beispiel verwendet vier Buttons der Klasse QPushButton, ein Widget namens QWidget, welches den Rahmen darstellt, und natürlich die QApplication-Klasse, ohne die keine GUI-Anwendung auskommt. Der Button but1 wurde hier mit zwei Slots verbunden. Erhält dieser Button das Signal clicked(), wird zunächst der Slot aboutQt() der Klasse QApplication ausgeführt und anschließend der Slot showNormal() von der Klasse QWidget. aboutQt() ist eine einfache Nachrichtenbox über Qt und showNormal() entspricht dem Systemmenüeintrag Wiederherstellen des Fensters. Wurde bspw. die Größe des Fensters verändert, sorgt dieser Slot dafür, dass das Fenster wieder in die ursprüngliche Größe versetzt wird. Zwei weitere Buttons wurden ebenfalls mit Slots der Klasse QWidget verbunden. Erhält der Button but2 das Signal clicked(), wird bei QWidget der Slot showMaximized() ausgeführt. Hierbei wird das Fenster in der Größe des Desktops maximiert. Selbiges gilt für Button but3, nur dass hierbei das Fenster minimiert wird. Der letzte Button (but4) wurde wieder mit QApplication und dessen Slot quit() verbunden. Empfängt also but4 das Signal clicked(), wird die Anwendung beendet. Im Folgenden das komplette Listing zu Abbildung 2.8: 00 01 02 03 04

// beispiele/signalslot2/main.cpp #include #include #include #include

05 int main(int argc, char *argv[]) { 06 QApplication app(argc, argv); 07 QWidget* win = new QWidget; 08 QVBoxLayout* layout = new QVBoxLayout(win); 09 QPushButton* but1 = new QPushButton("Wiederherstellen"); 10 QPushButton* but2 = new QPushButton("Maximieren"); 11 QPushButton* but3 = new QPushButton("Minimieren"); 12 QPushButton* but4 = new QPushButton("Schließen"); 13 14 15 16 17

// Größe der Buttons but1->resize(100, 30); but2->resize(100, 30); but3->resize(100, 30); but4->resize(100, 30);

35

2.1

2

Signale und Slots

18 19 20 21 22

// Widgets zur vertikalen Box hinzufügen layout->addWidget(but1); layout->addWidget(but2); layout->addWidget(but3); layout->addWidget(but4);

23 24

// Signal-und-Slot-Verbindungen herstellen QObject::connect( but1, SIGNAL( clicked() ), &app, SLOT( aboutQt() ) ); QObject::connect( but1, SIGNAL( clicked() ), win, SLOT( showNormal() ) ); QObject::connect( but2, SIGNAL( clicked() ), win, SLOT( showMaximized() ) ); QObject::connect( but3, SIGNAL( clicked() ), win, SLOT( showMinimized() ) ); QObject::connect( but4, SIGNAL( clicked() ), &app, SLOT( quit() ) ); // Fenster anzeigen win->show(); return app.exec();

25 26 27 28 29 30 31 32 }

Neben dem bereits erwähnten neuen Widget QWidget wurde hier auch die Klasse QVBoxLayout verwendet, womit einzelne GUI-Elemente vertikal angeordnet hinzugefügt werden können. Als Argument erhält der Konstruktor die Adresse des Eltern-Widgets, was im Beispiel das Hauptfenster der Anwendung (QWidget) ist. Die einzelnen Widgets können Sie anschließend mit der Methode addWidget() und dem Widget als Argument hinzufügen. Das Thema Layout und die Anordnung einzelner Widgets ist ein sehr wichtiges Thema, so dass Sie hierzu einen eigenen Abschnitt (4.2) finden. Hier nun das Programm bei seiner Ausführung:

Abbildung 2.9

36

Ein Fenster mit vier Buttons

Grundlagen

Abbildung 2.10

2.1.2

Button »Wiederherstellen« wurde angeklickt

Gegenseitiges Signal- und Slot-Konzept

Außerdem kommt es häufig vor, dass zwei Widgets voneinander abhängig sind. Wird bspw. ein Widget verändert, muss das andere ebenfalls an diese Veränderung angepasst werden – und umgekehrt genauso. Am besten sehen Sie sich hierzu folgende Abbildung (2.11) an.

QSpinBox spin

QSlider slider

valueChanged(int)

valueChanged(int)

setValue(int)

setValue(int)

Abbildung 2.11

Anpassen von zwei Widgets bei Veränderungen

37

2.1

2

Signale und Slots

In diesem Beispiel haben Sie ein Klasse QSpinBox und eine Klasse QSlider, die beide jeweils bei einer Veränderung den Wert des anderen Widgets anpassen. Wurde bspw. bei der Spinbox der Wert verändert, wird das Signal valueChanged() ausgelöst. Ist dies der Fall wird der Schieberegler (QSlider) entsprechend der Spinbox mit dem Slot setValue() angepasst. Anders betrachtet, haben Sie denselben Fall. valueChanged() ist jeweils das Signal und setValue() der Slot der beiden Widgets. Wie Sie weitere Signale und Slots in Erfahrung bringen können, wurde in Abschnitt 2.1.1 mit dem Assistant von Qt bereits beschrieben. Hinweis Es lässt sich leider nicht ganz vermeiden, dass ich ein wenig auf spätere Kapitel vorgreife, anders lässt sich das Signal-Slot-Konzept nicht beschreiben.

Um die beiden Widgets miteinander zu verbinden, sind im Grunde nur zwei Verbindungen mit connect() nötig. Hier das entsprechende Programmbeispiel: 00 01 02 03 04

// beispiele/signalslot3/main.cpp #include #include #include #include

05 int main(int argc, char *argv[]) { 06 QApplication app(argc, argv); 07 QWidget* win = new QWidget; 08 QVBoxLayout* layout = new QVBoxLayout(win); 09 QSpinBox* spin = new QSpinBox; 10 QSlider* slider = new QSlider(Qt::Horizontal); 11 12 13 14 15 16 17 18 19 20 21 22

38

// Minimum-Maximum Wert für Spinbox spin->setMinimum(0); spin->setMaximum(100); // Minimum-Maximum Wert für Slider slider->setMinimum(0); slider->setMaximum(100); // Widgets hinzufügen layout->addWidget(spin); layout->addWidget(slider); // Signal-und-Slot Verbindung QObject::connect( spin, SIGNAL( valueChanged(int) ), slider, SLOT( setValue(int) ) ); QObject::connect( slider, SIGNAL( valueChanged(int) ), spin, SLOT( setValue(int) ) );

Grundlagen

23 24 25 }

win->show(); return app.exec();

Das Programm bei der Ausführung:

Abbildung 2.12

Die beiden Widgets, immer up to date

Verändern Sie hierbei den Schieberegler, wird automatisch auch die Spinbox mit dem entsprechenden Wert versehen/verändert. Verändern Sie die Spinbox mit den Pfeilen oder als gültigen Wert durch die Tastatureingabe, wird auch der Schieberegler an die Position entsprechend angepasst.

2.1.3

Argumentenlisten von Signal-Slot-Verbindungen

Im Beispiel zuvor hatten die Signal-Slot-Verbindungen beide dieselben Argumente. Also sowohl valueChanged(int) und setValue(int) waren vom Typ int. Dies muss unbedingt beachtet werden, weil bei einer solchen Signal-SlotVerbindung keine automatische Typenumwandlung erfolgt. Benötigen Sie bspw. einen String anstelle eines Integer-Werts, müssen Sie von der Klasse ableiten und den entsprechenden Slot implementieren. Hierbei konvertiert man im Grunde nur das Argument und ruft anschließend den eigentlichen Slot auf. Seit Qt 4 kann man nicht mehr einfach beliebige Funktionen als Slots verwenden. Slots müssen Sie als solche kennzeichnen um von Qt akzeptiert zu werden. Sollte der Slot weniger Argumente enthalten als das Signal, ist dies ebenfalls gestattet. Überflüssige Argumente werden hierbei ignoriert. Bspw. ist es möglich, eine Verbindung zwischen signalFunc(int) und slotFunc() aufzubauen, obwohl der Slot keine Argumente enthält. Bei mehr als einem Parameter des Signals gilt dieselbe Regel wie bei der Funktionsüberladung von C++. So lässt sich bspw. mit signalFunc(int, float) mit den Slots slotFunc(), slotFunc(int) und slotFunc(int, float) eine Verbindung aufbauen. Nicht aber mit dem Slot slotFunc(float). Anders herum ist es nicht möglich, eine Verbindung herzustellen, wenn ein Slot mehr Argumente erwartet, als das Signal beinhaltet.

39

2.1

2

Signale und Slots

Einfachstes Beispiel: QObject::connect( button, SIGNAL( clicked() ), label, SLOT( setText("Neuer Text") ) );

Hier wird versucht, eine Verbindung zwischen einem Button und einem Textlabel herzustellen. Sollte der Button gedrückt werden, dann soll der Textlabel mit einem neuen Text versehen werden. Allerdings funktioniert dies nicht, weil das Signal clicked() keine Argumente enthält, der Slot setText()aber eines erwartet. Ärgerlich ist hierbei die Tatsache, dass der Compiler Ihnen dies ohne Probleme übersetzt.

2.1.4

Eigene Klasse mit Signalen und Slots definieren bzw. erweitern

Hinweis Ab diesem Punkt muss ich anmerken, dass das Thema für den Einstieg schon recht komplex ist, zumal die Basisklassen von Qt bis jetzt noch nicht genauer beschrieben wurden. Allerdings wollte ich das Thema »Signale und Slots« nicht im ganzen Buch verstreuen, so dass Sie als Leser jederzeit beim Nachschlagen alles in einem Kapitel wiederfinden und nicht im ganzen Buch danach suchen müssen. Ggf. können Sie zu diesem Abschnitt später zurückkehren, sollten Sie diesen Abschnitt nicht auf Anhieb verstehen oder nur überfliegen.

Um eine Klasse mit eigenen Signalen und Slots zu versehen, gilt es folgende Regeln einzuhalten: 왘

Es kann keine eigene Ereignisklasse definiert werden, die nur aus Signalen und Slots besteht. Die Signale und Slots müssen immer einen Teil der Klasse darstellen.



Eigene Signale und Slots können nur in Klassen definiert werden, die von der Klasse QObject abgeleitet sind.



In der neu definierten Klasse muss das Makro Q_OBJECT gesetzt werden (mehr dazu in Kürze).



Standard-Parameter sind für eigene Klassen bei den Signalen und Slots nicht erlaubt. Hinweis Für den Fall, dass Ihnen Themen wie das Ableiten von Klassen in C++ fremd sind, ist es nun an der Zeit, dieses Wissensdefizit zu beheben, weil im Grunde die gesamte Qt-Programmierung darauf basiert.

40

Grundlagen

Um zu beschreiben, wie Sie eigene Signale und Slots in einer abgeleiteten Klasse definieren, soll zunächst die folgende normale C++-Klasse als Vergleich herhalten: // typische Form einer einfachen C++-Klasse class MyClass { public: // Konstruktor MyClass(); // Zugriffsmethoden int value() const { return val; } void setValue(int); private: int val; };

Damit daraus eine waschechte Qt-Klasse entsteht, sind folgende (fett hervorgehobene) Änderungen nötig: 00 // eine echte Qt-Klasse 01 class MyClass : public QObject { 02 Q_OBJECT 03 public: 04 // Konstruktor 05 MyClass(); 06 // Zugriffsmethoden 07 int value() const { return val; } 08 public slots: 09 void setValue(int); 10 signals: 11 void valueChanged(int); 12 private: 13 int val; 14 };

Nachdem Sie Ihre Klasse in Zeile 1 von QObject abgeleitet haben, können Sie Signale und Slots im Grunde wie normale Methoden deklarieren. Allerdings dürfen die deklarierten Signal- und Slot-Elementefunktionen keinen Rückgabetyp haben und müssen daher void sein. Als Argumente können Sie hingegen eine beliebige Anzahl und auch beliebige Typen verwenden. Wichtig ist auch das Makro Q_OBJECT aus Zeile 2 (Achtung, ohne Semikolon!). In der Deklaration der Signal- und Slot-Elementfunktionen werden auch die neuen Spezifizierer signals (Zeile 10) und slots (Zeile 8) verwendet. Je nach

41

2.1

2

Signale und Slots

erwünschter Sichtbarkeit nach außen bzw. der Weitervererbbarkeit werden noch die Spezifizierer private, public und protected vor signals und slots gesetzt. In Ihrer Eigenschaft als Programmierer definieren Sie nur die Slots als gewöhnliche Elementfunktionen (Methoden). Einsteiger in Qt, die Quellcode studieren, sind häufig verwirrt, weil sie die Definition zur Signal-Elementefunktion nicht finden. Der Code für Signal-Elementfunktionen wird aber nicht vom Programmierer, sondern vom Meta Object Compiler (kurz MOC) geschrieben. Hierzu ein Beispiel, das zunächst allerdings noch keine GUI-Elemente verwendet. Wir erstellen eine einfache Klasse mit einem Integerwert als Eigenschaft und implementieren in dieser Klasse das Signal valueChanged(int), welches ausgelöst wird, sobald man den Integerwert des Objekts verändert. Natürlich implementieren wir hierbei auch den entsprechenden Slot setValue(int), um zwei Objekte der Klasse mit connect() verknüpfen zu können. Hier zunächst die Deklaration der Klasse MyClass: 00 01 02 03

// beispiele/signalslot4/myclass.h #ifndef MY_CLASS_H #define MY_CLASS_H #include

04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20

// Eine Klasse, die Signals und Slots besitzt. class MyClass : public QObject { Q_OBJECT public: MyClass(); // Konstruktor int value() const { return val; } public slots: // Der Wert von "val" wird geändert. void setValue( int ); signals: // Das Signal soll ausgesandt werden, // wenn "val" geändert wird. void valueChanged( int ); private: int val; }; #endif

Die Definition der Klasse ist ebenso einfach. Da wir das Signal nicht definieren müssen, reichen in diesem Fall der Konstruktor und die Slot-Elementefunktion setValue(int) aus.

42

Grundlagen

00 // beispiele/signalslot4/myclass.cpp 01 #include "myclass.h" 02 MyClass::MyClass() { 03 val = 0; 04 } 05 void MyClass::setValue( int v ) { 06 // val wird nur neu gesetzt, 07 // wenn tatsächlich ein anderer Wert übergeben wird. 08 if ( v != val ) { 09 val = v; 10 emit valueChanged(v); 11 } 12 }

Auffällig ist hier die Zeile 10 mit dem emit-Signalbezeichner. Mit dem Bezeichner emit machen Sie deutlich, dass es sich bei dem Aufruf um keinen normalen Funktionsaufruf handelt, sondern um einen Signalaufruf. Allerdings erreichen Sie selbiges auch ohne Bezeichner emit. Der Bezeichner ist nur dafür gedacht, sofort erkennen zu können, ob es sich um einen Signalaufruf handelt. Hierzu folgt noch die Hauptfunktion, die abgesehen von den Nachrichtenboxen (QMessageBox) auch ohne GUI-Elementen auskommt und das eigene Signal-SlotKonzept demonstriert. 00 01 02 03 04

// beispiele/signalslot4/main.cpp #include "myclass.h" #include #include #include

05 // simple Nachrichten-Box 06 void MyMessageBox(MyClass& a,MyClass& b,QString title) { 07 QString Qstr, Qval; 08 // String zusammenbasteln 08 Qstr.append(title); Qstr.append("\na: "); 09 Qval.setNum(a.value()); Qstr.append(Qval); 10 Qstr.append("\nb: "); Qval.setNum(b.value()); 11 Qstr.append(Qval); 12 QMessageBox::information( NULL, "MyClass Information", Qstr, QMessageBox::Ok); 13 } 14 int main(int argc, char *argv[])

{

43

2.1

2

Signale und Slots

15 16 17 18

QApplication app(argc, argv); // Zwei MyClass-Objekte MyClass *a = new MyClass(); MyClass *b = new MyClass();

19 20 21

// Das Signal des einen wird mit dem Slot // des anderen Objekts verbunden. QObject::connect( a, SIGNAL( valueChanged(int) ), b, SLOT( setValue(int) ) );

22 23 24 25 27 28 29 30 31 32 }

// b.val bekommt den Wert 100 b->setValue( 100 ); MyMessageBox(*a, *b, "b->setValue(100)"); // a.val bekommt den Wert 99. Durch die Signal-Slot// Verknüpfung bekommt jetzt auch b.val den Wert 99 a->setValue( 99 ); // Der Beweis MyMessageBox(*a, *b, "a->setValue(99)"); return 0;

Die Zeilen 5 bis 13 können Sie hierbei ignorieren. Damit wird nur eine Nachrichtenbox auf dem Bildschirm ausgegeben mit dem Inhalt der Integerwerte der beiden Objekte, die in den Zeilen 17 und 18 neu erzeugt werden. In Zeile 21 verknüpfen wir diese Objekte. Verändert man den Wert von Objekt a, wird das Signal valueChanged() ausgelöst und mit Objekt b und dem Slot setValue() verbunden. Sobald Sie also den Wert von Objekt a verändern, wird der Wert von Objekt b mit unserem selbst erstellten Slot setValue() angepasst. In diesem Beispiel erhält das Objekt b durch setValue() denselben Wert wie Objekt a. In der Zeile 23 bekommt das Objekt b mit 100 einen neuen Wert zugewiesen. Wir haben keine Signal-Slot-Verknüpfung eingerichtet, a und b besitzen folgende Werte (siehe Abbildung 2.13):

Abbildung 2.13

44

Nachdem Objekt b einen Wert erhalten hat

Grundlagen

In der Zeile 28 weisen wir dann dem Objekt a mit 99 einen neuen Wert zu. Und hierbei wird auch gleich unsere eingerichtete Signal-Slot-Verbindung aktiv, so dass auch Objekt b denselben Wert erhält, wie Objekt a. Dadurch ergeben sich folgende Werte (siehe Abbildung 2.14):

Abbildung 2.14

Signal wurde ausgelöst

MOC – Meta Object Compiler Dank qmake brauchen Sie sich nicht mehr groß um den Meta Object Compiler (MOC) zu kümmern. Mit dem folgenden typischen Dreisatz muss man sich um (fast) nichts mehr Gedanken machen: $ qmake –project $ qmake $ make qmake erstellt uns hier den entsprechenden Makefile, der auch den Meta Object

Compiler automatisch verwendet. Normalerweise müssen Sie MOC also nicht direkt aufrufen. Dennoch ist es theoretisch möglich, den MOC anders zu verwenden. Hierzu kann man entweder die mit dem MOC erzeugte Datei der Klassendefinition in einem Schritt zu einer Objektdatei kompilieren und beim Linken der ausführbaren Datei hinzufügen. Der Vorgang würde in etwa wie folgt aussehen: $ moc myclass.h -o moc_myclass.cpp $ g++ moc_myclass.cpp -o moc_myclass.o ---( zunächst wird die moc Datei gesondert übersetzt )--$ g++ myclass.cpp -o myclass.o $ g++ myclass.o moc_myclass.o main.o -o main -L... -l...

Man kann aber auch das mit MOC erzeugte Programm der Klassendefinition mittels einer Include-Anweisung in den Quellcode mit einfügen. Hinweis Sollten Sie mehr über die Verwendung des Meta Objects Compiler erfahren wollen, können Sie den Assistant von Qt (unter »Tools/All Tools« zu finden) verwenden.

45

2.1

2

Signale und Slots

Als Programmierer werden Sie sich sicherlich fragen, wozu denn überhaupt ein zusätzlicher Meta Object Compiler nötig ist, um ein eigenes Signal-Slot-Konzept zu realisieren. Nun – das liegt vor allem daran, dass das Signal-Slot-Konzept kein reines C++ ist, sondern eher eine Erweiterung vom C++-Standard. Der Meta Object Compiler verwandelt daher Signale und Slots in echten C++-konformen Standard. Somit ist MOC also ein Programm, das einen C++-Quelltext einliest und die C++-Erweiterung von Qt verwaltet. Trifft MOC auf eine oder mehrere Deklarationen mit dem Makro Q_OBJECT, produziert dieser eine andere C++-Quelltextdatei. Der so produzierte Code enthält dann den Meta-Object Code für die Klassen, die das Q_OBJECT benutzen. Dieser Meta-Object Code wird für den Signal-Slot-Mechanismus, für Typeninformationen zur Laufzeit und das dynamische Eigenschaftssystem (Property-System) benötigt. Kurz gesagt, benötigt man MOC immer dann, wenn QObject als Basisklasse dient. Der MOC muss dabei jede Datei, die eine Klassendefinition einer entsprechenden Klasse enthält, bearbeiten (ähnlich wie der Präprozessor), bevor der C++-Compiler den Code erhält. Alle von MOC erzeugten Dateien weisen das Präfix moc_auf. In unserem Beispiel wird bspw. aus den Dateien myclass.h und myclass.cpp die Datei moc_myclass.cpp erzeugt. Diese Quelltextdatei muss noch extra kompiliert und mit der Implementierung der Klasse verlinkt werden. qmake erledigt dies alles zum Glück automatisch.

2.1.5

Widget mit eigenem Slot

Häufig wird man zu seinen Widgets eigene Slots hinzufügen wollen. Ein gerne zitiertes Beispiel zeigt der folgende Codeausschnitt: QObject::connect( button, SIGNAL( clicked() ), label, SLOT( setText("neuer Text") ) );

Hierbei haben wir eine versuchte Signal-Slot-Verknüpfung, die keinerlei Effekt haben wird. Der Programmierer wollte hier wohl erreichen, dass beim Anklicken eines Buttons (bspw. QPushButton) der Text des Labels (QLabel) verändert wird. Der Button verwendet dabei das Signal clicked() und der Label den Slot setText(). Warum dies nicht funktioniert, wurde in Abschnitt 2.1.3 bereits näher beschrieben. Die Argumentenliste von clicked() und setText() stimmt nicht überein. clicked() gibt hierbei keine Argumente an den Slot setText(). Um das folgende Beispiel zu realisieren, leiten wir unsere neue Klasse MyWindow von QWidget ab. Zunächst die Headerdatei mit der Klasse:

46

Grundlagen

00 01 02 03 04 05 06 07

// beispiele/signalslot5/mywindow.h #ifndef MYWINDOW_H #define MYWINDOW_H #include #include #include #include #include

08 class MyWindow : public QWidget { 09 Q_OBJECT 10 public: 11 MyWindow(QWidget *parent = 0); 12 private: 13 QLabel *label; 14 QPushButton *button0; 15 QPushButton *button1; 16 QVBoxLayout* layout; 17 private slots: 18 void setText(); 19 }; 20 #endif

Verglichen mit dem Beispiel zuvor, in dem wir eigene Signale und Slots verwendeten, finden Sie auch hier nichts Neues, außer dass wir jetzt von der Klasse QWidget (Zeile 8) – statt von QObject – ableiten und hier nur ein Slot (Zeile 17 und 18) verwendet wird. Die Definition des Konstruktors und der Slot-Ereignisfunktion finden Sie in der folgenden Quelldatei wieder: 00 // beispiele/signalslot5/mywindow.cpp 01 #include "mywindow.h" 02 MyWindow::MyWindow(QWidget *parent): QWidget(parent) { 03 label = new QLabel("alter Text"); 04 button0 = new QPushButton ("Label aktualisieren"); 05 button1 = new QPushButton ("Beenden"); 06 layout = new QVBoxLayout(this); 07 layout->addWidget(button0); 08 layout->addWidget(button1); 09 layout->addWidget(label); 10 setLayout(layout); 11 connect( button0, SIGNAL( clicked() ), this, SLOT( setText() ) ); 12 connect( button1, SIGNAL( clicked() ), this, SLOT( quit() ) );

47

2.1

2

Signale und Slots

13 } 14 void MyWindow::setText() { 15 label->setText("neuer Text"); 16 }

Zunächst werden wie gewohnt die einzelnen Widgets wie Buttons (QPushButton) und Label (QLabel) erzeugt und der vertikalen Box (QVBoxLayout) hinzugefügt. Anschließend (Zeile 11 und 12) werden die Signal-Slot-Verknüpfungen eingerichtet. Ein besonderes Augenmerk sei hierbei auf den Slot von Zeile 11 geworfen. Hierbei handelt es sich nicht mehr um den von QLabel zur Verfügung gestellten Slot setText(), sondern um unseren selbst definierten Slot ohne Argumente, dessen Implementierung Sie in Zeile 14 bis 16 vorfinden. Klicken Sie nun im Beispiel auf den Button mit dem Label »alter Text«, wird das Signal clicked() ausgelöst. Dieses Signal ist allerdings nicht mit dem Label verbunden, sondern mit dem Fenster bzw. QWidget. Daher finden Sie hierbei auch den this-Zeiger vor. Erst mit der Slot-Ereignisfunktion MyWindow::setText() wird der Text-Label verändert. Jetzt fehlt nur noch die Hauptfunktion, die kaum noch Code enthält: 00 // beispiele/signalslot5/main.cpp 01 #include 02 #include "mywindow.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWindow* window = new MyWindow; 06 window->show(); 07 return app.exec(); 08 }

Das Programm bei der Ausführung:

Abbildung 2.15

48

Programm beim Start

Grundlagen

Abbildung 2.16

2.1.6

Nach dem Drücken von »Label aktualisieren«

Widget mit eigenem Signal

Natürlich will ich Ihnen Ähnliches auch mit einem Signal demonstrieren. Hierfür wollen wir die QPushButton-Klasse um das Signal clicked(int) erweitern. Ursprünglich gibt es bei dieser Klasse ja nur das Signal clicked() ohne Argument. Wir wollen ein neues clicked(int) mit Argument hinzufügen, welches beim Drücken eines Buttons ein Signal mit einer Identifikationsnummer (ID) des Buttons mitschickt. Hierzu zunächst die Klasse MyButton, die wir von der Klasse QPushButton ableiten: 00 01 02 03 04 05

// beispiele/signalslot6/mybutton.h #ifndef MYBUTTON_H #define MYBUTTON_H #include #include #include

06 class MyButton : public QPushButton { 07 Q_OBJECT 08 public: 09 MyButton( const QString& text, int id, QWidget* parent = NULL ); 10 MyButton( const QString& text, QWidget* parent = NULL ); 11 private: 12 static int nextId; 13 int m_id; 14 public slots: 15 //click() überschreiben 16 void click(); 17 signals: 18 //ein neues Signal hinzufügen

49

2.1

2

Signale und Slots

19 void clicked(int id); 20 }; 21 #endif

Zusätzlich zu einem neuen Signal, das hier in Zeile 17 bis 19 festgelegt wird, überschreiben wir den in der Klasse QAbstractButton definierten Slot click(). Der Slot click() führt wortwörtlich einen Klick durch. Natürlich benötigen Sie nach wie vor keine Definition des Signals, da dies, wie bereits beschrieben, der Meta Object Compiler für uns übernimmt. Um den Vorgang besser zu verstehen, benötigen Sie die Implementierung der Klasse MyButton: 00 // beispiele/signalslot6/mybutton.cpp 01 #include "mybutton.h" 02 int MyButton::nextId = 0; 03 MyButton::MyButton( const QString& text, int id, QWidget* parent ) : QPushButton(text, parent), m_id(id) { 04 connect(this, SIGNAL(clicked()), this, SLOT(click())); 05 } 06 MyButton::MyButton(const QString& text, QWidget* parent ) : QPushButton(text, parent), m_id(nextId++) { 07 connect(this, SIGNAL(clicked()), this, SLOT(click())); 08 } 09 void MyButton::click() { 10 emit clicked(m_id); 11 }

Außerdem fallen die beiden connect()in den Konstruktoren auf (Zeile 04 und 07). Hiermit verbinden wir das alte (oder nennen Sie es das echte) clicked()-Signal mit dem eigenen, hausgebackenen Slot click() der Klasse MyButton. Hierbei ist maßgebend entscheidend, dass wirklich die Slot-Elementfunktion von MyButton (Zeile 9 bis 11) ausgeführt wird. Würde die Supermethode QPushButton::click() verwendet, hätte dies eine Endlos-Rekursion zur Folge, weil QPushButton::click() wieder das Signal clicked() auslöst. Sobald der Slot MyButton::click() ausgelöst wurde, können Sie das neue Signal mit der Identifikationsnummer (ID) verschicken (Zeile 10). Um aus diesem Beispiel ein stilvolles Qt-Programm zu machen, wollen wir hierzu noch eine Klasse MyWindow (abgeleitet von QWidget) mit unseren eigenen Buttons

50

Grundlagen

(MyButton) und einem Slot implementieren, der sich um die Ausgabe auf dem Bildschirm kümmert. Hier die Headerdatei unserer Klasse mywindow.h: 00 01 02 03 04

// beispiele/signalslot6/mywindow.h #ifndef MYWINDOW_H #define MYWINDOW_H #include #include "mybutton.h"

05 class MyWindow : public QWidget { 06 Q_OBJECT 07 public: 08 MyWindow(QWidget* parent = NULL); 09 private: 10 MyButton* myButton[3]; 11 public slots: 12 // Slot für die Ausgabe 13 void myMessageBox(int id); 14 }; 15 #endif

Unseren Button finden Sie in Zeile 10 MyButton mit eigenem implementierten Signal wieder. In Zeile 11 bis 13 deklarieren wir noch einen eigenen Slot für die Klasse MyWindow. Die Slot-Elementfunktion ist im Grunde eine einfache Nachrichtenbox (QMessageBox), welche die Identifikationsnummer des Signals clicked(int) unserer Buttons zurückgibt. Hierzu die Implementierung der Klasse MyWindow: 00 01 02 03 04

// beispiele/signalslot6/mywindow.cpp #include "mywindow.h" #include "mybutton.h" #include #include

05 MyWindow::MyWindow(QWidget* parent) : QWidget(parent) { 06 myButton[0] = new MyButton(tr("Drück mich")); 07 myButton[1] = new MyButton(tr("Mich auch")); 08 myButton[2] = new MyButton(tr("Und mich")); 09 QVBoxLayout* layout = new QVBoxLayout(this); 10 for (int i = 0; i < 3; ++i) { 11 layout->addWidget(myButton[i]); 12 connect( myButton[i], SIGNAL( clicked(int) ), this, SLOT( myMessageBox(int) ) ); 14 } 15 }

51

2.1

2

Signale und Slots

16 void MyWindow::myMessageBox(int id) { 17 QMessageBox::information( this, tr("Ein Button wurde betätigt"), QString(tr("Button ID: %1")).arg(id), QMessageBox::Ok ); 18 }

In Zeile 12 wird unser neues clicked(int)-Signal mit dem Ausgabe-Slot myMessageBox verbunden. Sobald also unser neues Signal eintrifft, wird eine Nachrich-

tenbox mit der ID des Buttons ausgegeben. Zum Schluss noch unser Hauptprogramm zur Demonstration: 00 // beispiele/signalslot6/main.cpp #include #include "mywindow.h" #include "mybutton.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); MyWindow* window = new MyWindow; window->show(); return app.exec(); }

Das Programm bei der Ausführung:

Abbildung 2.17

Programm nach dem Start

Abbildung 2.18

Button »Mich auch« mit ID 1 wurde betätigt

52

Grundlagen

2.1.7

Zusammenfassung

Der Umfang dieses Kapitels war zwar nicht enorm, dafür aber sehr detailreich, so dass wir hier die wichtigsten Regeln zusammenfassen, die in Bezug auf das Signal-Slot-Konzept im Allgemeinen zutreffen. 왘

Die Deklaration von Signalen und Slots ist nur in Klassen erlaubt. Eine Deklaration außerhalb von Klassen würde auch nicht in das Klassenkonzept von C++ passen.



Klassen, die eigene Signale bzw. Slots enthalten, müssen von QObject abgeleitet sein. Da bei Qt die meisten GUI-Klassen von QWidget abgeleitet sind, muss man sich diesbezüglich keine großen Gedanken machen, da QWidget wiederum von QObject abgeleitet ist (mehr dazu im nächsten Kapitel).



Verwenden Sie Klassen mit eigenen Signalen bzw. Slots, müssen Sie das Makro Q_OBJECT verwenden. Hinter dem Makro wird kein Semikolon verwendet.



Slots können Sie wie eine gewöhnliche C++-Methode deklarieren und implementieren. Im Grunde sind Slots ja gewöhnliche Methoden, die sich auch außerhalb einer Signal-Slot-Verknüpfung mit connect() direkt verwenden lassen.



Bei der Definition von Slots müssen Sie zuvor das Schlüsselwort slots mit entsprechendem Spezifizierer (private, public) hinzufügen. Wollen Sie einen Slot als virtual deklarieren, können Sie auch protected vor dem Schlüsselwort slots setzen.



Es ist nicht gestattet, Slots als static zu deklarieren, was bei gewöhnlichen C++-Methoden verwendet werden darf.



Wie Methoden können auch Slots Parameter besitzen. Hierbei muss beachtet werden, dass bei einem mit connect() angegebenen Signal dieselben Parametertypen wie beim entsprechenden Slot verwendet werden. Wie bei einer Funktionsüberladung ist auch erlaubt, dass ein Slot weniger Parameter haben darf als das Signal.



Bei der Definition von Signalen in einer Klasse muss zuvor das Schlüsselwort signals angegeben werden. Ansonsten entspricht auch die Deklaration von Signalen der Deklaration gewöhnlicher Methoden. Wichtig ist hierbei allerdings, dass Signale nur deklariert und niemals direkt implementiert werden dürfen.



Zum Senden von Signalen in einer Klassen-Komponente wird gewöhnlich das Qt-Schlüsselwort emit platziert. emit dient nur der besseren Erkennbarkeit und muss nicht verwendet werden.

53

2.1

2

Signale und Slots

Die Signale und Slots werden mit der statischen Funktion QObject::con-



nect() verbunden.

Wichtig: Bei den Routinen der Makros SIGNAL und SLOT sind nur Typen und keine Werte als Parameter erlaubt. Sie können also nicht bei einer Signalroutine wie SIGNAL(valueChanged( 5 )) einen Wert als Parameter verwenden, sondern dürfen hierzu nur einen Typ wie bspw. SIGNAL(valueChanged( int )) verwenden.



Hinweis Natürlich ist es auch möglich, ein Signal von seiner Verbindung, die mittels connect() erstellt wurde, mit QObject::disconnect() wieder zu lösen.

54

Das Qt-Framework umfasst mittlerweile weit über 400 Klassen. Hierbei die Übersicht zu behalten, ist für Einsteiger zunächst nicht ganz einfach. Qt basiert auf einer Ableitungshierarchie. Um sich mit Qt eingehender zu befassen, müssen wir daher erst die Wurzeln und den Aufbau des Frameworks kennen. Sobald Sie wissen, wie Qt »tickt«, werden Sie sich schnell zurechtfinden. Dieses theoretische Kapitel geht also kurz auf den Aufbau des Qt-Framework ein.

3

Basisklassen und Bibliotheken von Qt

Wie Sie bereits ein Kapitel zuvor erfahren haben, fordert auch das Signal-SlotKonzept, dass die beteiligten Klassen von der Klasse QObject abstammen. Aber auch viele andere nichtgrafische Klassen basieren auf QObject als Basisklasse. Bspw. werden Klassen, die der Kommunikation zwischen den Prozessen dienen (z. B. QThread), von QObject abgeleitet, damit diese über Signale und Slots kommunizieren können.

3.1

Basisklasse: QObject

QObject wurde im vorigen Kapitel im Zusammenhang mit dem Signal-Slot-Konzept bereits erwähnt. Dabei werden Sie sicherlich festgestellt haben, dass es sich hierbei um eine ziemlich wichtige Klasse in Qt handelt. In der Tat ist QObject eine Basisklasse vieler Qt-Klassen. Alle auf dem Bildschirm dargestellten Widgets leiten bspw. von der Klasse QWidget ab. QWidget wiederum wurde von der Klasse QObject abgeleitet.

3.2

Qt-Klassenhierarchie

Natürlich gibt es auch viele weitere Klassen, die nicht von QObject abgeleitet wurden. Dies sind im Grunde alle Klassen, die kein Signal-und-Slot-Konzept und keine automatische Speicherverwaltung benötigen. Bekanntester Vertreter dürfte hier QString sein. QString ist das, was für den C++-Programmierer die Klasse string und für die Verwendung von Zeichenketten zuständig ist.

55

3

Basisklassen und Bibliotheken von Qt

Hinweis Es wird empfohlen, in Qt die Klasse QString der Standardklasse string vorzuziehen. QString speichert und verarbeitet den Text im Unicode-Format und erlaubt somit, fast alle Schriftsysteme dieser Erde zu verwenden. Oder kurz: Mit QString müssen Sie sich nicht um die Dekodierung des Zeichensatzes kümmern.

Den besten Überblick zur Klassenhierarchie von Qt bekommen Sie hierbei wieder mit Assistant von Qt. (vgl. den Abschnitt »API Reference« unter »Inheritance Hierarchy«; siehe Abbildung 3.1). Hierbei werden Sie feststellen, dass QObject den größten Anteil von abgeleiteten Klassen besitzt.

Abbildung 3.1

Klassenhierarchie von Qt

Trotzdem werden Sie Klassen wie QPaintDevice entdecken, wovon auch QWidget wiederum einen Teil ererbt hat und somit bei den sichtbaren GUI-Elementen

wieder eine Rolle spielt. Ebenso sieht dies etwa bei der Klasse QLayout aus. Diese Klasse wurde bspw. ebenfalls von QObject abgeleitet, hat aber auch die Super-

56

Qt-Klassenhierarchie

basisklasse QLayoutItem. Die Klasse QLayout beinhaltet Widgets, die zur Anordnung der GUI-Elemente auf dem Fenster benötigt werden. In QVBoxLayout haben Sie bereits einen solchen Abkömmling beider Klassen kennengelernt. Der folgenden Abbildung (3.2) können Sie einen kurzen Überblick zum Aufbau der Qt-Klassenhierarchie entnehmen. Hierbei erkennt man leicht, dass viele Klassen zum Teil von mehreren Klassen abgeleitet wurden. Andere Klassen wiederum, wie bspw. QString, haben gar keine Basisklasse. QLayoutItem

QObject

QLayout

QPaintDevice

QWidget

QString

QPrinter

... ...

... QBoxLayout

...

QDialog

... ... Abbildung 3.2

...

...

Schematischer Überblick der Qt-Klassenhierarchie

Gerade auf Einsteiger wirken solch gewaltige Frameworks zunächst ziemlich abschreckend. Sie erfuhren aber bereits in Abschnitt 2.1.1, wie man die ganze Klassenhierarchie mit dem Assistant von Qt nach oben arbeiten kann. Um nochmals auf den Button QPushButton zurückzukommen: Bei der Klassenreferenz von QPushButton im Assistant finden Sie ganz zu Beginn eine Zeile wie »Inherits QAbstractButton«, also von QabstractButton abgeleitet. Klicken Sie auf den Verweis von QAbstractButton. Bei dieser Klassenreferenz erfahren Sie auch gleich, welche Kinder-Widgets aus QAbstractButton erstellt wurden. Dies steht in »Inherited by«, was in diesem Fall die Klassen Q3Button, QCheckBox, QPushButton, QRadioButton und QToolButton wären. Das Eltern-Widget (Inherits) ist hingegen QWidget. Auch finden Sie hier (bei QWidget) gleich endlos viele GUI-Elemente, die aus QWidget abgeleitet wurden. QWidget wiederum wurde von QObject und QPaintDevice (siehe Abbildung 3.3) abgeleitet. Dieses Durchlaufen der Klassen mit dem Assistant von Qt ist unerlässlich, wenn es darum geht, nach Funktionen, Methoden, Eigenschaften, Slots oder Signalen bestimmter Klassen zu suchen oder eben um eigene Widgets zu implementieren. Ich weiß, dass ich mich wiederhole, doch kann man dies nicht oft genug erwähnen.

57

3.2

3

Basisklassen und Bibliotheken von Qt

QObject

QPaintDevice

QWidget

QAbstractButton

QPushButton

Abbildung 3.3

3.3

Klassenhierarchie von QPushButton

Speicherverwaltung von Objekten

Da C++ keine automatische Speicherverwaltung wie bspw. Java besitzt, haben wir in den Beispielen des vorigen Kapitels des Öfteren den new-Operator zum Erzeugen neuer Objekte verwendet. Im Abschnitt zuvor lernten Sie die Klassenhierarchie von Qt kennen. Sie sahen (Abbildung 3.3), wie aus QObject abgeleitete Klassen eine Baumhierarchie gebildet wird. Durch eine solche Hierarchie entsteht eine Eltern-Kind-Beziehung. Wird bspw. ein Eltern-Widget gelöscht, werden alle davon abgeleiteten KinderObjekte ebenfalls gelöscht. Die Kinder dieser Kinder-Objekte wiederum löschen auch ihre Nachkommen. Auf diese Weise nimmt uns Qt einen Teil der Speicherverwaltung ab, und Sie müssen nicht jedes einzelne Objekt mit delete per Hand löschen. Damit man die Qt-Klassen verwenden kann, wird bei der Initialisierung immer die Übergabe eines Zeigers auf das aufrufende Elternobjekt verlangt. Dadurch ist gewährleistet, dass die Elternklasse Kenntnis über ihre Kindobjekte besitzt und diese bei der eigenen Löschung auch aus dem Speicher zerstört. Aus diesem Grund muss jedes Qt-Objekt im Heap erzeugt werden, was der Operator new ja leistet. Mit einem Klassen-Konstruktor auf dem Stack gelegte Objekte werden nach der Abarbeitung wieder gelöscht. Das Widget wird zwar kurz erzeugt, aber nie angezeigt.

58

Speicherverwaltung von Objekten

Hinweis Als Beispiel nenne ich Ihnen ein altbekanntes Problem: die Rückgabe einer globalen Variablen aus einer normalen Funktion.

Hierzu bspw. folgende Zeilen: 01 02 03 04 05

QWidget* win = new QWidget; QVBoxLayout* layout = new QVBoxLayout(win); QPushButton* but01 = new QPushButton("Label"); // Widgets zur vertikalen Box hinzufügen layout->addWidget(but01);

QVBoxLayout wird hier als Kind von QWidget gezeugt. Der Button hingegen ist bei der Zeugung noch elternlos und würde jetzt noch nicht angezeigt werden. Erst beim Aufruf von addWidget() wird dafür gesorgt, dass jemand die Elternschaft für den Button übernimmt. Allerdings kommt dabei oft auch ein wenig Verwirrung auf, da nicht die Klasse QVBoxLayout die Elternschaft von QPushButton übernimmt, sondern QWidget. Betrachtet man die Widget-Hierarchie, wird man feststellen, dass QPushButton und QVBoxLayout in der Hierarchie etwa gleichgestellt sind. Dabei würde die ganze Speicherverwaltung natürlich nicht mehr funktionieren. Deswegen müssen die Kinderelemente eines Widgets immer GUI-Elemente eines übergeordneten Widgets sein. Durch den Codeausschnitt ergibt sich die in Abbildung 3.4 abgedruckte Hierarchie.

QWidget

QPushButton Abbildung 3.4

QVBoxLayout

Eltern-Kind-Elemente

Allerdings ist es auch möglich, das Eltern-Widget gleich bei der Erzeugung mit anzugeben. Bezogen auf den eben gezeigten Codeausschnitt würde dies folgendermaßen aussehen: 01 02 03

QWidget* win = new QWidget; // Eltern-Widget als zweites Argument angeben QPushButton* but01 = new QPushButton( "Label", win );

59

3.3

3

Basisklassen und Bibliotheken von Qt

3.4

Programm-Bibliotheken von Qt

Wenn Sie mit dem Qt-Assistant ein wenig die Klassen betrachtet haben, werden Sie feststellen, dass Qt in mehrere Pakete (Module) aufgeteilt ist. Neben der Erstellung grafischer Oberflächen bietet Qt ja noch viel mehr für die Entwicklung von Applikationen. Hier zunächst ein Überblick über die in den nächsten Unterkapiteln näher erörterten Qt-Bibliotheken: 왘

QtCore enthält die grundlegenden (core) Funktionen und Klassen ohne grafische Oberfläche, die von den anderen Bibliothken verwendet werden.



QtGui erweitert die Bibliothek QtCore und umfasst die gesamte GUI-Funktionalität.



QtNetwork enthält alle Klassen und Funktionen, die nötig sind, um bspw. TCP/IP (und UDP) Server- und/oder Clientanwendungen zu schreiben.



QtOpenGL enthält Klassen, die es einfach machen, die OpenGL-Schnittstelle in Qt-Anwendungen zu verwenden.



QtSql zuständig für die Einbindung von SQL-Datenbanken in den Qt-Anwendung.



QtSvg stellt Klassen zum Anzeigen von SVG-Grafiken (Vektorformat) zur Verfügung.



QtXml enthält C++-Implementationen von SAX- und DOM-Klassen.



QtDesigner beinhaltet Klassen, mit denen Sie Plugins (Widgets) für den QtDesigner erstellen können.



QtUiTools Dieser Teil der Bibliothek enthält Klassen (im Augenblick eigentlich nur eine), die es Ihnen ermöglichen, Standalone-Anwendungen (Benutzerschnittstellen; User Interfaces (UI)) dynamisch zur Laufzeit zu erzeugen und in einer .ui-Datei abzuspeichern.



QtAssistant ermöglicht die Verwendung der Online-Hilfe des Assistant von Qt.



Qt3Support enthält Klassen, um Anwendungen von Version 3 auf Version 4 zu portieren.



QtTest wird verwendet, um Qt-Anwendungen und -Bibliotheken zu testen.

Die kommerzielle Version von Qt unter Windows bietet mit QAxContainer und QAxServer außerdem zwei Programm-Bibliotheken für ActiveX-Controls und ActiveX-Server an. Für alle Unix-Plattformen (und Qt-Editionen) stehen mit der Bibliothek QtDBus noch Klassen für die Interprozesskommunikation mit D-BUS zur Verfügung.

60

Programm-Bibliotheken von Qt

Hinweis D-BUS ist ein Software-System, das mehrere Möglichkeiten anbietet, Programme miteinander kommunizieren zu lassen.

Wenn Sie qmake verwenden, um Ihre Projekte zu bauen, werden QtCore (core) und QtGui (gui) standardmäßig hinzugefügt. Wollen Sie bspw., dass bei Ihrem Projekt nur QtCore gelinkt wird, müssen Sie in der .pro-Datei (Projektdatei) folgende Zeile ändern bzw. hinzufügen: QT -= gui

Die Verwendung entspricht den erweiterten Operatoren von C/C++. Wollen Sie bspw. dem Projekt die Bibliothek QtNetwork hinzufügen, müssen Sie nur Folgendes in die .pro-Datei einfügen: QT += network

Die zu linkende Bibliothek legt qmake mit QT fest. Wenn Sie eine Qt-Bibliothek hinzufügen wollen, müssen Sie nur den +=-Operator vor der gewünschten Bibliothek setzen. Soll eine Bibliothek entfernt werden, dann erledigen Sie dies mit -=-Operator. In der folgenden Tabelle (3.1) finden Sie die möglichen Werte, die Sie für die QTVariable der .pro-Datei (Projektdatei) verwenden können, und welche Bibliothek Sie dabei hinzulinken. QT-Wert (Option)

Bibliothek

core (per Standard implementiert)

QtCore

gui (per Standard implementiert)

QtGui

network

QtNetwork

opengl

QtOpenGL

sql

QtSql

svg

QtSvg

xml

QtXml

designer

QtDesigner

uitools

QtUiTools

qt3support

Qt3Support

qtestlib

QtTest

Tabelle 3.1

Bibliotheken hinzulinken

Natürlich ist es auch möglich, mehrere Bibliotheken dem Projekt hinzuzulinken. Zu diesem Zweck muss man der .pro-Datei nur mehrere Bibliotheken, getrennt von einem Leerzeichen, hinzufügen:

61

3.4

3

Basisklassen und Bibliotheken von Qt

QT += network opengl sql svg xml qt3support

Hiermit hätten Sie praktisch alle Bibliotheken (gui und core sind per Standard dabei) hinzugelinkt.

3.4.1

QtCore

QtCore ist die grundlegende Basisbibliothek, auf der Qt basiert, und enthält im Grunde nur Klassen, die mit keiner grafischen Ausgabe verbunden sind. QtCore beinhaltet auch die von C++-Programmierern so bezeichnete STL (Standard Template Library) – und zwar mitsamt der Funktionalität und der Art der Verwendung. Alle Qt-eigenen Container-Klassen (und auch Strings) verwenden hierbei die Methode »copy on write«. Damit lassen sich die Klassen ohne großen Aufwand als Funktionswert zurückgeben. Auch die Klasse QString (zur Behandlung von Zeichenketten) ist ein Teil von QtCore. Es wurde bereits erwähnt, dass die einzelnen Zeichen hierbei im Unicode-Standard (UTF-16) kodiert werden. Anstelle eines char kommt hierbei ein short int (16 Bits) zum Zuge. Natürlich enthält QString auch alles Nötige für Konvertierungen unterschiedlicher Zeichensatzsysteme wie ASCII, UTF, verschiedener ISO-Standards usw. QtCore ist auch für die Klassen QObject und QCoreApplication (Basisklasse für QApplication) verantwortlich. Insgesamt beinhaltet QtCore mehr als 200 Klas-

sen. Die grundlegenden und in der Praxis gängigsten seien hier stichpunktartig notiert: 왘

die grundlegenden Datentypen QString und QByteArray



die grundlegenden Datenstrukturen (wie bei STL) QList, QVector, QHash QMap, usw.



Klassen für die Ein-/Ausgabe wie QFile, QDir, QTextStream usw.



Klassen, um Multithreads wie QThread, QMutex, QWaitCondition usw. zu programmieren



Klassen für Datum und Uhrzeit wie QDate, QTime und QDateTime



Laden von Bibliotheken und Plugins zur Laufzeit wie QLibrary und QPluginLoader



Die Klasse QObject, Hauptträger vieler Konzepte wie Kommunikation zwischen Signalen und Slots, Speicherverwaltung und Objekteigenschaften.

QtCore wird häufig auch alleine ohne QtGui verwendet, um Client-/Server-Programme mit Multithreads oder Sockets zu schreiben. Häufig ist hierfür keine grafische Oberfläche nötig.

62

Programm-Bibliotheken von Qt

3.4.2

QtGui

Zunächst setzt QtGui die Bibliothek QtCore voraus. QtGui enthält alle Klassen, die etwas auf dem Bildschirm zeichnen. Dabei wird die Klasse QtWidget erweitert. QtWidget allein zeichnet im Grunde nur ein leeres Rechteck auf den Bildschirm. Auch neue GUI-Elemente werden mithilfe von QWidget erstellt, indem man die neue Klasse davon ableitet. QtGui ist wohl der Hauptgrund, weshalb viele Anwender Qt verwenden und wird wohl auch den Großteil des Buches ausmachen. Daher zählen wir im Folgenden die gängigsten Teile von QtGui stichpunktartig auf 왘

Klassen, um vorgefertigte Dialoge zu verwenden (bspw. QFileDialog (Dateiauswahl), QPrinterDialog (Dokument drucken), QColorDialog (Farbauswahl), usw.);



Klassen, um die Widgets anzuordnen – sogenannte Layout-Klassen (bspw. QVBoxLayout, QHBoxLayout, QGridLayout, usw.);



die Klasse QWidget und unzählige davon abgeleitete GUI-Klassen;



Klassen zum Zeichnen auf dem Bildschirm (bspw. QBrush, QPainter, QPen, usw.);



die Klasse QApplication.

3.4.3

QtNetwork

Die Klasse QtNetwork bietet eine Menge Klassen an, mit denen Sie TCP/IP (sowie UDP)-Client-/Server-Anwendungen schreiben können. Neben den Klassen QTcpSocket, QTcpServer und QUdpSocket, die ja in einer tieferen Ebene operieren, bietet QtNetwork mit QHttp und QFtp Klassen der höheren Ebene an, womit das HTTP- und FTP-Protokoll verwendet werden kann. QtNetwork benötigt ebenfalls die Bibliothek QtCore, nicht aber QtGui.

3.4.4

QtOpenGL

Mit dieser Bibliothek können Sie die OpenGL-API in Ihrer Qt-Anwendung verwenden. OpenGL ist eine Portable Standard-Schnittstelle zum Rendern von 3DGrafik (ähnlich wie DirectX). Mit QtOpenGL können Sie OpenGL verwenden wie jedes andere Qt-Widget.

3.4.5

QtSql

Die Bibliothek QtSql beinhaltet Klassen für die Unterstützung SQL-Datenbanken. In der folgenden Tabelle (3.2) sind Client-APIs aufgelistet, für die Qt einen passenden Treiber zur Verfügung stellt.

63

3.4

3

Basisklassen und Bibliotheken von Qt

Treibername

Datenbanksystem

QIBASE

Borland InterBase

QMYSQL

MySQL

QODBC

Open Database Connectivity (ODBC) (wird von Microsoft SQL-Server verwendet)

QPSQL

PostgresSQL

QSQLITE2

SQLite (Version 2)

QSQLITE

SQLite (Version 3)

Tabelle 3.2

Datenbanken, für die Qt einen Treiber mitliefert

Diese Treiber sind auf jeden Fall in der Open-Source-Edition von Qt enthalten. Weitere Datenbanksysteme bzw. Treiber, die sich mit GPL nicht unter einen Hut bringen lassen, stehen nur bei der kommerziellen Version von Qt zur Verfügung (siehe Tabelle 3.3). Treibername

Datenbanksystem

QDB2

IBM DB2

QOCI

Oracle Call Interface Treiber

QTDS

Sybase Adaptive Server

Tabelle 3.3

DBMS (nur in der kommerziellen Version von Qt)

Besonders beeindruckend an QtSql ist die Tatsache, dass man nur ein entsprechendes Datenbanksystem auszuwählen braucht und gleich danach mit dem QtCode auf jede DB-Schnittstelle zugreifen kann.

3.4.6

QtSvg

Die Bibliothek QtSvg unterstützt Klassen, mit denen sich Vektorgrafiken mit dem SVG-Format anzeigen lassen. SVG (Scalable Vector Graphics, »Skalierbare Vektorgrafiken«) ist ein Standard zur Beschreibung zweidimensionaler Vektorgrafiken in der XML-Syntax. SVG wurde im September 2001 vom W3C als Empfehlung veröffentlicht. Viele aktuelle Webbrowser sind von Haus aus in der Lage, einen Großteil des Sprachumfangs darzustellen, wie z. B. Mozilla Firefox oder Opera. Bisher unterstützt QtSvg die Profile SVG-Basic und SVG-Tiny. ECMAScripts und DOM-Manipulationen werden von Qt zurzeit noch nicht unterstützt.

3.4.7

QtXml

Die Bibliothek QtXml stellt einen einfachen XML-Parser zur Verfügung. Dieser lässt sich direkt über die SAX2-Schnittstelle (SAX = Simple API for XML) verwen-

64

Meta-Include-Headerdatei

den. In QtXml ist außerdem der DOM-Standard (DOM = Document Object Model) implementiert. Damit lässt sich ein XML-Dokument parsen und die entsprechende Baumstruktur verändern bzw. anpassen. Das so veränderte Dokument kann natürlich wiederum als XML-Dokument ausgegeben werden. DOM erlaubt es sogar, ein neues XML-Dokument daraus zu erstellen.

3.4.8

Qt3Support

Da Qt4 gegenüber Qt3 enorm erweitert und verbessert wurde und tlw. inkompatibel geworden ist, liefert Trolltech diese Bibliothek mit. Wir beschreiben allerdings Qt Version 4 und gehen nicht näher darauf ein.

3.4.9

Der Rest

Auf die weiteren Bibliotheken wie QtDesigner, QtUiTools, QtTest und die kommerziellen Bibliotheken wird in diesem Buch nicht näher eingegangen. Zudem existiert eine Migrationslösung in Qt4 für MFC-, Motif- und Xt-basierende Anwendungen. Auch hierbei handelt es sich um eine kommerzielle Lösung, die Trolltech separat unter dem Namen QtSolution anbietet.

3.5

Meta-Include-Headerdatei

Alle Programmbibliotheken aus dem Kapitel zuvor sind auch als Meta-IncludeHeaderdatei enthalten, um bspw. die einzelnen Klassen aus der GUI-Bibliothek QtGui wie folgt zu inkludieren: #include #include #include #include #include #include ...



Statt der vielen Inkludierungen der Headerdateien können Sie auch die MetaHeaderdatei QtGui inkludieren: #include

Wenn Sie sich diese Meta-Include-Datei ansehen, werden Sie feststellen, dass alle Klassen der GUI-Bibliothek inkludiert sind. Ein Nachteil dieser Inkludierung kann sein, dass die Kompilierung etwas mehr Zeit in Anspruch nimmt. Im

65

3.5

3

Basisklassen und Bibliotheken von Qt

Grunde trifft dies aber nur auf ältere Compiler zu, die keine vorkompilierten Headerdateien unterstützen. In den Buch-Beispielen verwenden wir zunächst noch die Klassen-Headerdateien und führen dann allmählich die Meta-Include-Datei ein. Im Prinzip entspricht der Klassenname bei Qt auch dem Headernamen. Lautet der Klassenname bspw. QPushButton, müssen Sie nur den Include hinzufügen (oder eben die Meta-Include-Datei ). Für alle anderen Bibliotheken gilt dasselbe wie für die Bibliothek QtGui mit ihrem Meta-Include. Somit können Sie der Bibliothek QtXml anstelle der unzähligen XML-Include-Dateien auch nur den Meta-Include hinzufügen.

66

Index A Aktionen QAction 329 QActionGroup 337 Algorithmen 606, 613 kopieren 616 sortieren 613 suchen 614 assert Q_ASSERT() 748 Ausgabe Debugging 743

B Baummodell 284 Behälter-Widget 152 Bibliotheken erstellen 737 linken 61 Bildbearbeitung 674 Binäre Daten 425 Button 130 QButtonGroup 152 QCheckBox 142 QPushButton 137 QRadioButton 148 QToolButton 151

C Check-Boxen 142 Condition-Variable 575 .config-Eintrag 357 Container 606 QHash 612 QLinkList 608 QList 607 QMap 611 QMultiHash 612 QMultiMap 611 QQueue 610 QSet 612 QStack 609

Container (Forts.) QVector 608 Container-Widget 152 QFrame 167 QGroupBox 152 QTabWidget 156 QToolBox 173 Container-Widget 씮 Behälter-Widget

D Datei Ein- und Ausgabe 415 Informationen 466 QFile 415 QTemporaryFile 423 Temporäre Dateien 423 Datenbanken 583 Daten abfragen 589, 595 Daten ändern 588, 594 Daten hinzufügen 587, 594 Daten löschen 595 Item-View 598 QSqlDatabase 585 QSqlQuery 586 QSqlTableModel 593, 598 QtSql 583 SQL-Anweisungen ausführen 586 Transaktionen 590 Treiber 583 Verbindung herstellen 585 Datentypen Qt 601 Datum 231, 253, 618 D-BUS 61 Debugging Ausgabe 743 Designer 752 Dialog erstellen 753 Hauptfenster erstellen 775 Dialoge Benutzerfreundlichkeit 106 Dateiauswahl 118 Druckerdialog 129 Eingabe 123

787

Index

Dialoge (Forts.) Erstellen 96 Farbauswahl 128 Layout 70 QColorDialog 128 QFileDialog 118 QFontDialog 127 QMessageBox 110 QPrintDialog 129 Schriftauswahl 127 vorgefertigte 109 Direkthilfe 312 DLL erstellen 737 Dock-Widget 352 DOM 705 API- 713 Drag & Drop 639 benutzerdefinierte MIME-Typen 651 Drag-Seite 648 Drop-Seite 643 Kodierung 640 QMimeData 640 Drag & Drop 씮 Ziehen und Fallenlassen Drucken 683 Grafik 683 HTML 688 PDF 683 PDF erstellen 687 Druckerdialog 129 Dynamische Bibliotheken 737 Laden 739 QLibrary 739

E Ein- und Ausgabe 411 Binäre Daten 425 Datei 415 Dateiinformationen 466 Puffer 455 QBuffer 455 QDataStream 425 QDir 457 QFile 415 QFileInfo 466 QIODevice 411 QTemporaryFile 423 QTextStream 438

788

Ein- und Ausgabe (Forts.) Streams 425 Temporäre Datei 423 Textdaten 438 Verzeichnisse 457 Zwischenspeicher 455 Eingabedialog 123 Eingabe-Widget 196 QAbstractSlider 196 QAbstractSpinBox 225 QCalendarWidget 253 QComboBox 215 QDateTimeEdit 231 QDial 201 QDoubleSpinBox 230 QFontComboBox 225 QLineEdit 205 QScrollBar 204 QSlider 199 QSpinBox 229 QTextEdit 239 Einstellungen speichern 357 Embeeded Progamming Qtopia 743 Ereignis-Handler 621 Ereignisschleife 619 Ereignisverarbeitung 619 Drag & Drop 639 Drag-Seite 648 Drop-Seite 643 Filter implementieren 628 Handler 621 Maus 639 Multithreads 633 neu implementieren 627 optimieren 636 QEvent 619 QPaintEvent 658 Tastatur 621 Übersicht 631 Verwaltung 631

F Farbauswahl 128 Fenster aufteilen QSplitter 394 QSplitterHandle 398

Index

forever 582 FTP-Protokoll 537

G Grafik Bild laden 675 Bild speichern 675 Bildbearbeitung 674 Bildformate 675 Bildinformationen 676 Bild-Transformationen 676 draw...() 658 Drucken 683 Matrix 673 OpenGL 693 Pinsel 662 Pixel manipulieren 677 QPainter 657 QPaintEvent 658 Scherung 665 Schrift 662 Skalieren 665 Stift 663 SVG 702 Transformationen 665 Vektorgrafik 702 Versetzen 667 Grafik 씮 Zeichnen

H Hauptfenster 317 Aktionen 329 Aktionsgruppe 337 aufteilen 394 Dock-Widget 352 MDI (Multi Document Interface) 377 Menü-Elemente 321 Menüleiste 321 QAction 329 QActionGroup 337 QDockWidget 352 QMainWindow 317 QMenu 321 QMenuBar 321 QScrollArea 403 QSettingsBar 357 QStatusBar 339

Hauptfenster (Forts.) QSToolBar 346 QWorkspace 377 Scrollbereich 403 Statusleiste 339 Verschiebbares Fenster 352 Werkzeugleiste 346 Zustand sichern 357 HTTP-Protokoll QHttp 521 QHttpHeader 527

I Interantionale Anwendungen 727 Internationale Anwendungen Linguist 729 Interprozesskommunikation QProcess 474 Item-View 259 Baummodell 284 Listenmodell 274 Model-View 297 QListWidget 274 QListWidgetItem 274 QTableWidget 260 QTableWidgetItem 260 QTreeWidget 284 QTreeWidgetItem 284 Tabellenmodell 260

K Klassenhierarchie Qt 55 Kodierung QMimeData 640 Kommandozeile auswerten 751 Konsolenprogramm erstellen 752

L Layout 70 eigenen Manager erstellen 94 Programmbeispiel 72 QBoxLayout 80 QGridLayout 71, 84

789

Index

Layout (Forts.) QHBoxLayout 71 QLayout 76 QSpacerItem 92 QStackedLayout 88 QStackedWidget 89 Qt 93 QVBoxLayout 71 QWidgetItem 92 Widget 70 Linguist Übersetzen mit- 729 Listenmodell 274 Lizenzierung 16 lupdate 728

M make 24 MDI-Anwendungen 377 Menüleiste 321 Meta Object Compiler 45 Meta-Include-Datei 65 MOC 45 Modell-Präsentation 297 QDirModel 299 QSortFilterProxyModel 307 QStringListModel 305 Stringliste 305 Verzeichnishierarchien 299 vordefinierte 298 Model-View 297 Multi Document Interface 377 Multithreads 554 Condition 575 Daten an Thread binden 578 Ereignisverarbeitung 633 Mutex 564, 566 QMutex 564 QMutexLocker 566 QReadWriteLock 567 QSemaphore 569 QWaitCondition 575 Semaphore 569 Mutex 564, 566

790

N Netzwerkkommunikation 489 QAbstractSocket 490 QFtp 537 QHostAdress 515 QHostInfo 515 QHttp 521 QHttpHeader 527 QHttpRequestHeader 527 QHttpResponseHeader 527 QNetworkProxy 553 QTcpServer 497 QTcpSocket 496 QUdpSocket 507 QUrl 528 TCP 496, 497 UDP 507

O Online-Hilfen 311 Direkthilfe 312 Qt Assistant 316 QTextBrowser 314 Statuszeile 311 Tooltipps 311 OpenGL 693 MesaGL 694 QGLWidget 695 Spezifikation 694 Open-Source 16

P PDF aus HTML erstellen 689 drucken 687 erstellen 687 Plugins 742 .pro 23 Programm starten 24 Projektdatei 23, 61 Prozess QProcess 474 starten 474

Index

Q Q_ASSERT() 748 Q_CHECK_PTR() 748 QAbstractButton 130 QAbstractSlider 196 QAbstractSocket 490 QAbstractSpinBox 225 QAction 329 QActionGroup 337 qApp 69 QApplication 750 QAssistantClient 316 QBoxLayout 80 QBrush 662 QButtonGroup 152 QByteArray 605 QCalendarWidget 253 QChar 604 QCheckBox 142 QClipboard 653 QColorDialog 128 QComboBox 215 QCoreApplication 750 QDataStream 425 QDate 618 QDateTime 618 QDateTimeEdit 231 QDial 201 QDialog 96 QDir 457 QDirModel 299 QDockWidget 352 QDoubleSpinBox 230 QEvent 619 QFile 415 QFileDialog 118 QFileInfo 466 QFont 662 QFontComboBox 225 QFontDialog 127 QFrame 167 QFtp 537 QGLWidget 695 QGridLayout 71, 84 QGroupBox 152 QHash 612 QHBoxLayout 71 QHostAdress 515

QHostInfo 515 QHttp 521 QHttpHeader 527 QHttpRequestHeader 527 QHttpResponseHeader 527 QImage 674 Bild laden 675 Bild speichern 675 Bildformate 675 Bildinformationen 676 Bild-Transformationen 676 Pixel manipulieren 677 QInputDialog 123 QInputEvent 621 QIODevice 411 QKeyEvent 621 QLabel 189 QLayout 76 QLCDNumber 184 QLibrary 740 QLineEdit 205 QLinkList 608 QList 607 QListWidget 274 QListWidgetItem 274 QMainWindow 317, 318 qmake 22, 61, 738 make 24 Projektdatei 23 QMap 611 QMatrix 673 QMenu 321, 325 QMenuBar 321, 323 QMessageBox 110 QMimeData 640 benutzerdefinierte MIME-Typen 651 QMultiHash 612 QMultiMap 611 QMutex 564 QMutexLocker 566 QNetworkProxy 553 QObject 55, 727 QPainter 657 draw...() 658 Matrix 673 Pinsel 662 Scherung 665 Schrift 662 skalieren 665

791

Index

QPainter (Forts.) Stift 663 Transformationen 665 versetzen 667 QPaintEvent 658 QPen 663 QPrintDialog 129 QPrinter 683 QProcess 474 QProgressBar 179 QProgressDialog 183 QPuffer 455 QPushButton 137 QQeue 610 QRadioButton 148 QReadWriteLock 567 QScrollArea 403 QScrollBar 204 QSemaphore 569 QSet 612 QSettings 357 QSlider 199 QSortFilterProxyModel 307 QSpacerItem 92 QSpinBox 229 QSplitter 394 QSplitterHandle 398 QSqlDatabase 585 QSqlQuery 586 QSqlTableModel 593, 598 QStack 609 QStackedLayout 88 QStackedWidget 89 QStatusBar 339 QString 602 QStringList 603 QStringListModel 305 QStyle 748 QSvgWidget 702 Qt 93 Bibliotheken 60 Datentypen 601 Designer 752 Hallo Welt 20 installieren 17 Klassenhierarchie 55 Lizenzierung 16 Meta Object Compiler 45 Programm starten 24

792

Qt (Forts.) qmake 22 Quellcode übersetzen 22 Ressourcen-System 785 Speicherverwaltung 58 Styles 748 Typendefinitionen 601 QT_TR_NOOP() 734 Qt3Support 60, 65 QTableWidget 260 QTableWidgetItem 260, 266 QTableWidgetSelectionRange 273 QTabWidget 156 QtAssistant 60 QtCore 60, 62 QTcpServer 497 QTcpSocket 496 QtDBus 60 QtDesigner 60 QTemporaryFile 423 QTextBrowser 253, 314 QTextEdit 239 QTextStream 438 65 QtGui 60, 63 QThread 554 QThreadStorage 578 QTime 618 QTimer 187, 633 QtNetwork 60, 63 QToolBar 346 QToolBox 173 QToolButton 151 QtOpenGL 60, 63 Qtopia 743 QTreeWidget 284 QTreeWidgetItem 284 QtSql 60, 63, 583 Treiber 583 QtSvg 60, 64 QtTest 60 QtUiTools 60 QtXml 60, 64, 705 QUdpSocket 507 Quellcode übersetzen 22 QUrl 528 QVariant 606 QVBoxLayout 71 QVector 608

Index

QWaitCondition 575 QWidgetItem 92 QWorkspace 377

R Radio-Button 148 RAD-Tool Qt Designer 752 Registry-Eintrag 357 Ressourcen-System 785

S SAX 705 API- 706 Schriftauswahl 127 Scrollbereich 403 Semaphore 569 Signale Argumentenliste 39 Grundlagen 28 Slots Argumentenliste 39 Grundlagen 28 Sockets 489 QAbstractSocket 490 QTcpServer 497 QTcpSocket 496 QUdpSocket 507 Speicherverwaltung 58 SQL 583 Statusleiste 339 normale Meldungen 340 permanente Meldungen 342 temporäre Meldungen 339 Statuszeilentipp 311 Strings 602 Styles Qt- 748 SVG 702

T Tabellenmodell 260, 598 mit SQL 598 TCP-Sockets 496 Threads 554 Toolbar 346

Tooltipps 311 tr() 727

U UDP-Sockets 507 Uhrzeit 231, 618 Union QVariant 606

V Verzeichnishierarchien anzeigen 299 Verzeichnisse QDir 457

W Werkzeugleiste 346 Widget Button 130 Container 152 Datum 231, 253 Eingabe-Widget 196 Layout 70 mit eigenem Signal 49 mit eigenem Slot 46 QAbstractButton 130 QAbstractSlider 196 QAbstractSpinBox 225 QButtonGroup 152 QCalendarWidget 253 QCheckBox 142 QComboBox 215 QDateTimeEdit 231 QDial 201 QDoubleSpinBox 230 QFontComboBox 225 QFrame 167 QGroupBox 152 QLabel 189 QLCDNumber 184 QLineEdit 205 QProgressBar 179 QProgressDialog 183 QPushButton 137 QRadioButton 148 QScrollBar 204 QSlider 199

793

Index

Widget (Forts.) QSpinBox 229 QTabWidget 156 QTextBrowser 253 QTextEdit 239 QToolBox 173 QToolButton 151 Uhrzeit 231 Zustandsanzeige 179

X XML DOM-API 713 DOM-Baum durchsuchen 724 Einführung 705 SAX-API 706 SAX-Handler 708

794

Z Zeichen QChar 604 Zeichenketten QByteArray 605 QString 602 QStringList 603 Zeichnen 657 Ziehen und Fallenlassen 639 Zustandsanzeige 179 QLabel 189 QLCDNumber 184 QProgressBar 179 QProgressDialog 183 Zwischenablage QClipboard 653