Advanced Services | Der GuiBuilder des QtCreators |
Im Gegensatz zu den vorigen beiden Kapiteln legen wir kein leeres Qt Projekt an. Wir wählen jetzt eine Qt-Gui-Anwendung. Wir können nach dieser Entscheidung wählen zwischen einer Qt-Gui-Anwendung ohne Formdatei oder einer Qt-Gui-Anwendung mit Formdatei.
Eine Qt4-Gui-Anwendung ohne Formdatei ist ein mit einem Skelett angereichertes "leeres Qt Projekt". Es wird eine leere Klasse MainWindow angelegt, die von QMainWindow erbt und das Q_OBJECT Makro einbindet.
Man beachte, daß im obigen Screenshot die Checkbox zu "Form-Datei generieren" nicht angehakt ist.
/* mainwindow.h */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QtGui/QMainWindow> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); }; #endif // MAINWINDOW_H
/* mainwindow.cpp */ #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { } MainWindow::~MainWindow() { }
/* main.cpp */ #include <QtGui/QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
Wie bei einem leeren Qt-Projekt muß nun alles weitere von Hand codiert werden.
Eine Qt-Gui-Anwendung mit Formdatei hat einen GuiBuilder, mit dem man die graphische Oberfläche entwerfen kann, indem man die QWidgets mit der Maus auf ein QMainWindow zieht.
Man beachte, daß die Checkbox zu "Form-Datei generieren" jetzt angehakt ist.
Beim Ausführen des Programms zeigt sich ein Fentser mit einer leeren Toolbar.
Es werden die folgende Codedateien angelegt:
Interessant sind die letzten beiden Dateien
Klickt man auf mainwindow.ui, so schaltet der Qtcreator sofort auf die Designeransicht um. Klickt man in der linken Menüspalte auf Editieren, so kann man den Quelltext von mainwindow.ui einsehen. mainwindow.ui ist eine xml-Datei, in der die graphische Konfiguration, die der Benutzer mit dem Gui-Builder einrichtet festgehalten wird. Der Quelltext dieser Datei ist innerhalb des Qtcreators nicht editierbar. In der Theorie sollte das auch nicht notwendig sein.
Beim Kompilieren wird aus mainwindow.ui die Headerdatei ui_mainwindow.h
erzeugt. In diesem Header wird die Klasse Ui_MainWindow vereinbart, die
sämtliche Komponenten enthält, die man mit dem Gui-Builder angelegt hat.
Ui_MainWindow ist eine Toplevelklasse und leitet sich von keiner anderen
gui-Klasse ab, im Gegensatz zu dem, was der Name vermuten läßt.
Die mit Gui-Builder per drag und drop angelegten Komponenten
werden als Zeiger in einem public Datenteil angelegt.
Des weiteren gibt es hier eine public inline-Methode
void setupUi(QMainWindow *MainWindow)
Hier wird nun wirklich ein Zeiger auf ein QMainWindow übergeben. und dieses MainWindow wird mit den Komponenten eingerichtet, die im Datenteil der Klasse Ui_MainWindow stehen. Hier sieht man zunächst noch nicht, woher das eigentliche QMainWindow kommt. In einem eigenen namespace Ui wird dann eine leere Klasse MainWindow angelegt, die public von Ui_MainWindow erbt. Man beachte, daß weder Ui_MainWindow noch Ui::MainWindow Klassen sind, die sich von QWidget ableiten !
namespace Ui { class MainWindow : public Ui_MainWindow {}; } // namespace Ui
Bei jedem Kompilierungsvorgang wird die Datei ui_mainwindow.h erneut aus der xml-datei mainwindow.ui generiert. Es nützt also nichts, sie von Hand zu bearbeiten. Sie ist deshalb auch im Projektfenster nicht sichtbar.
In mainwindow.h wird eine zweite Klasse MainWindow deklariert, die nun tatsächlich von QMainWindow erbt. Sie enthält im private Datenteil einen Zeiger ui vom Typ Ui::MainWindow*.
Das Interessante ist nun der Konstruktor der Klasse MainWindow. Er reicht zum einen natürlich das übergebene QWidget an den Elternkonstruktor weiter, initialisiert dann aber den Zeiger ui sozusagen on the fly durch einen Aufruf von new Ui::MainWindow. Dadurch kann im Rumpf des Konstruktors zu ui die Methode setupUi() aufgerufen werden. Ihr wird ein Zeiger auf das MainWindow-Objekt übergeben (nicht zu verwechseln mit Ui::MainWindow) und so richtet die Methode setupUi() die in Ui_MainWindow vereinarten Komponenten im MainWindow-Objekt ein:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); }
Mit drag und drop erzeugen wir folgendes Layout und versuchen uns dann im signal/slot Eventhandling
Es gibt mehrere Varianten um das Signal/Slot Eventhandling zu realisieren. Eine wichtige Rolle spielt dabei die Methode connect() aus QObject, die folgenden Aufbau hat:
QObject::connect(<SenderObjekt> , SIGNAL(<GesendetesEreignis()>), <Empfängerobjekt>, SLOT(<Reaktionsmethode()>));
Im Fenster unterhalb des Designers schalten wir von Aktionsansicht um auf Signale und Slots.
Mit + erzeugt man eine neue Zeile im Signal/Slot-Fenster unterhalb des graphischen Entwurfs. Hier kann man die gewünschte Verbindung mit der Maus erzeugen, indem man sich die einzelen Objekte, die Signale und die Slots anzeigen läßt und dann auswählt.
Wir haben nun mit der Maus die folgende Codezeile erzeugt:
QObject::connect(exitButton, SIGNAL(clicked()), MainWindow, SLOT(close()));
Allerdings ist die obige Anweisung ein wenig versteckt. Sie wird in die Methode setupUi() eingefügt die sich (s.o.) in der nicht angezeigten Datei ui_mainwindow.h befindet.
Es geht auch ohne Mausclicks. Stattdessen können wir diese Verbindung auch mit der Hand eintragen, indem wir sie in den Konstruktor von MainWindow in der Datei mainWindow.cpp legen, der Zugriff auf den Button muß dann dann über den zeiger ui erfolgen. Da die Methode setupUi() das Fenster einrichtet, muß der Aufruf von connect() nach dem von setupUi() kommen.
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QObject::connect(ui->exitButton, SIGNAL(clicked(bool)), this, SLOT(close())); }
Leider wird eine in mainwindow.h deklarierte und in mainwindow.cpp implementierte eigene slot-Methode auch nach dem Erstellen von Qtcreator nicht erkannt und deshalb auch nicht im Signal/Slot-Fenster angezeigt. Hier muß man einen anderen Weg wählen. Im Folgenden werden zwei Wege beschrieben.
Vom QtCreator wird nun in der xml-Datei mainwindow.ui der folgende Eintrag vorgenommen:
<connection> <sender>exitButton</sender> <signal>clicked()</signal> <receiver>MainWindow</receiver> <slot>close()</slot> ...
Ist man (wieder) im Modus F3 (Widgets bearbeiten), so aktiviert man die betreffende Komponente, holt sich mit der rechten Maustaste das Kontextmenu und wählt "Slot anzeigen". Man erhält ein kleines Fenster mit dem Titel "Go to slot" und bestätigt die gewünschte Auswahl. Sodann wechselt QtCreator zur Datei mainwindow.cpp wo bereits eine Methode (in diesem Fall) mit dem Namen on_exitButton_clicked() erzeugt worden ist. Auch die Deklaration in der Headerdatei hat QtCreator erledigt. Man braucht lediglich den gewünschten Code einzutragen.
Das close()-Ereignis müssen wir bei diesem Weg mit der Hand eintragen.
In diesem Beispiel wollen wir zählen, wie oft der PushButton geklickt worden ist und diese Zahl auf dem Label erscheinen lassen. Für diesen Vorgang müssen wir die Reaktionsmethode selbst schreiben. Weg 4 von vorhin läßt sich sofort auf diese Situation übertragen und liefert uns die Reaktionsmethode on_pushButton_clicked().
In mainwindow.h definieren wir eine private Variable countClicks und zählen Sie in der slot()-Methode hoch. Da auf einem QLabel nur Strings erscheinen können, konvertieren wir die int-Variable in einen QString.