Qt für C++ Anfänger: Das Grundgerüst
Aus Wikibooks
Inhaltsverzeichnis |
[Bearbeiten] Die Erstellung des GUIs
Der Taschenrechner soll später mal so aussehen:
Er besteht also aus einem Fenster (als Template bitte Main Window wählen) mit drei Textfeldern, drei "Eingabezeilen" und einem Knopf, um die Berechnung zu starten. Die Elemente die ihr im Qt-Designer dazu zusammen klicken müsst, sind die folgenden, bitte auch genau mit diesen technischen Namen:
QLabel: LabelA, LabelB, LabelC QLineEdit: InputA, InputB, ResultC QPushButton: Calculate
Bitte auf Groß/Kleinschreibung achten. Die technischen Namen sind nicht die, die man im obigen Screenshot sieht, sondern die, die im Fenster "Property Editor" im Bereich QObject oben stehen; das Feld heißt objectName. Damit haben wir dann ein ui-File; wir speichern es unter Taschenrechner.ui in einem Verzeichnis, wo wir auch in Zukunft alle Files des Projektes speichern. Wo das Verzeichnis liegt ist egal, es aber bitte "Taschenrechner" nennen.
[Bearbeiten] Das Code-Grundgerüst
Damit wir nun auch wirklich ein C++-Programm erstellen können, brauchen wir ein Code-Gerüst, an dem wir später weitere Arbeiten vornehmen. Das erste Gerüst wird es uns erlauben, das Programm zu übersetzen und auszuführen - allerdings noch ohne weitere Funktionalität.
Wir werden drei C++-Files haben:
//--- main.cpp - start ---
#include "Taschenrechner.h"
#include <QApplication>
int main( int argc, char* argv[]){
QApplication a(argc, argv);
Taschenrechner w;
w.show();
return a.exec();
}
//--- main.cpp end ---
//--- Taschenrechner.h - start ---
#ifndef TASCHENRECHNER_H
#define TASCHENRECHNER_H
#include "ui_Taschenrechner.h"
class Taschenrechner : public QMainWindow, public Ui::MainWindow{
Q_OBJECT
public:
Taschenrechner (QMainWindow *parent = 0);
~Taschenrechner();
};
#endif //TASCHENRECHNER_H
//--- Taschenrechner.h - end ---
//--- Taschenrechner.cpp - start ---
#include "Taschenrechner.h"
Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){
setupUi(this);
}
Taschenrechner::~Taschenrechner(){
}
//--- Taschenrechner.cpp - end ---
Auch hier bitte auf Groß- und Kleinschreibung achten, insbesondere wird das Wort "Taschenrechner" immer mit einem großen "T" geschrieben. Bitte erstellt die Files so wie hier angegeben; es macht erstmal nichts, wenn man den Code hier nicht versteht; die Erklärungen im Detail folgen weiter unten.
[Bearbeiten] Das erste Übersetzen
Bitte in einer Shell in unser Projektverzeichnis wechseln und nach einander folgende Befehle ausführen:
qmake -project qmake make ./Taschenrechner
[Bearbeiten] qmake -project
Erzeugt ein File .pro das so genannte Project File. Der Name des Files ist der Name des Project Verzeichnisses. In diesem File stehen im wesentlichen alle Files die zu unserem Projekt gehören. Wenn man also neue Files erstellt, also keine bestehenden ändert, muss dieser Befehl erneut aufgerufen werden. Man kann das pro-File mit einem Texteditor öffnen und bekommt eine ganz gute Vorstellung was darin steht.
[Bearbeiten] qmake
qmake erzeugt das Makefile.
[Bearbeiten] make
make übersetzt am Ende unsere Sourcen basierend auf dem Makefile. Es werden auch noch Qt-spezifische Header-Files erstellt. Das File ui_Taschenrechner.h enthält den Qt-Code für das UI. Das Endergebnis ist ein ausführbares File mit dem Namen des Projekts, welches identisch mit dem Name unseres Projektverzeichnisses ist.
[Bearbeiten] Das Grundgerüst im Detail
[Bearbeiten] Taschenrechner.h
Ist das Header File für unsere Klasse Taschenrechner. In diesem Header File finden wir die so genannten Prototypen unsere Funktionen. Also die Beschreibung welche Funktionen zur Verfügung stehen, hier wird aber keine Funktion implementiert.
Nun zu den Details:
01 #ifndef TASCHENRECHNER_H 02 #define TASCHENRECHNER_H <...> 14 #endif //TASCHENRECHNER_H
sorgt dafür, dass der Header nur einmal eingebunden wird. Dabei handelt es sich um Makro die vom Präprozessor abgearbeitet werden bevor der Compiler das Programm übersetzt. Zeile 01 schaut nach ob das Flag TASCHENRECHNER_H gesetzt ist, ist es nicht gesetzt wird der ganze Code bis zur Zeile 15 zum übersetzen "frei gegeben", das endif in Zeile 15 schließt diese if-Bedingung ab. ifndef steht hier für "if not defined". Gesetzt wird ein solches Flag über die Präprozessor Anweisung #define in Zeile 02. Beim allerersten auffinden des obigen Blocks ist das Flag TASCHENRECHNER_H also noch nicht gesetzt und wird abgearbeitet, durch diese Abarbeitung wird das Flag in Zeile 02 nun gesetzt. Trifft der Präprozessor nun ein weiteres mal auf diesen Code wird einfach nicht mehr beachtet, was auch nicht mehr nötig ist da alle Prototypen schon bekannt sind. Somit wird also verhindert, dass Klassenmethoden mehrfach deklariert werden. Die Form der Namensgebung dieses Flag ist die übliche Vereinbarung, im Prinzip würde es auch jeder andere Name tun - was aber nicht empfohlen ist.
04 #include "ui_Taschenrechner.h"
Dieses Header-File wurde von als erstes von make erzeugt, wenn man dort rein schaut erkennt man sehr leicht, daß hier alle UI Elemente zu finden sind.
06 class Taschenrechner : public QMainWindow, public Ui::MainWindow{
07 Q_OBJECT
08
09 public:
10 Taschenrechner (QMainWindow *parent = 0);
11 ~Taschenrechner();
12 };
Dies ist die Klassendeklaration unser Hauptklasse. In Zeile 06 wird der Klassenname "Taschenrechner" angegeben und dass unsere Klasse abgeleitet ist von QMainWindow ": public QMainWindow". Wollen wir beispielsweise einen QDialog erstellen wäre hier die Ableitung auf QDialog zu ändern.
Zeile 07 ist ein weiteres Makro welches immer bei Qt angegeben werden muss. (Hat jemand eine Anfängererklärung was es macht?). Unsere Klasse hat zur Zeit zwei Methoden deklariert, beide sind "public" können also von einer beliebigen Funktion aufgerufen werden. Bei den Methoden handelt sich um den Konstruktor und Desktruktor. Die Methode die den gleichen Namen hat wie die Klasse selbst (Zeile 10) ist immer der Konstruktor die mit dem gleichen Namen aber einem vorgestellten ~ immer der Destruktor (Zeile 11). Diese Methoden werden immer beim Anlegen oder Zerstören einer Klasse aufgerufen. Dem Konstuktor wird hier noch ein Parameter übergeben. (Hat jemand eine Anfängererklärung wozu das da ist?)
[Bearbeiten] Taschenrechner.cpp
01 #include "Taschenrechner.h"
02
03 Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){
04 setupUi(this);
05 }
06
07 Taschenrechner::~Taschenrechner(){
08 }
Hier werden die oben deklarierten Funktionen wirklich implementiert. In Zeile 01 wird das Header-File inkludiert damit bekannt ist welche Funktionen es geben wird. In Zeile 03 - 05 wird der Konstruktor implementiert. Die einzige Anweisung die für den Konstruktor implementiert ist in Zeile 04 gegeben. Es handelt sich um eine Qt spezifische Funktion die in dem File ui_Taschenrechner.h zu finden ist. Der Aufruf dieser Funktion legt letztendlich das UI an. In den Zeilen 07 und 08 ist der Destruktur definiert, der Desktruktor ist leer, sprich es werden keine speziellen Funktionen ausgeführt. Um die Speicherfreigabe nach dem Zerstören des Objekts kümmert sich Qt von alleine. Man beachte hier, dass beide Funktionen keinen Returntype haben, noch nicht mal "void". Das fehlen des Returntyps ist spezifisch für Konstruktoren und Destruktoren, hat also nichts speziell mit Qt zu tun.
[Bearbeiten] main.cpp
01 #include "Taschenrechner.h"
02 #include <QApplication>
03
04 int main( int argc, char* argv[]){
05 QApplication a(argc, argv);
06 Taschenrechner w;
07 w.show();
08 return a.exec();
09 }
Die Main-Funktion, der Startpunkt eines C++ Programms, enthält nicht sehr viel. Es wird sich auch im Laufe der Zeit nicht viel an dem Aussehen ändern. Zeile 01 inkludiert das Headerfile damit die Klasse "Taschenrechner" bekannt ist, das wird in Zeile 06 wichtig sein. Zeile 04 und 09 stellen das Gerüst der main Funktion dar. In Zeile 05 wird ein Qt Objekt "QApplication" (durch Zeile 02 eingebunden) erstellt, die Kommandozeilenparamter werden an diese Objekt übergeben, der Objektname ist einfach "a". In Zeile 06 wird eine Instanz der Klasse "Taschenrechner" erstellt, diese Instanz wird einfach "w" genannt. in Zeile 07 rufen wir die Funktion show() auf, diese Funktion haben wir nicht expliziet implementiert, durch die Ableitung von QMainWindow steht uns aber diese Funktion zur Verfügung. QMainWindow hat diesen "Slot" von QWidget vererbt bekommen. Diese Funktion sorgt dafür dass das Widget angezeigt wird. Sprich lässt man sie weg würde das Programm immer noch "funktionieren" man würde bloß nichts sehen. Zeile 08 übergibt die Kontrolle des Programms. Die Kontrolle heißt dabei, dass der Aufruf a.exec() Qt anweist auf Events zu hören, ohne diese Anweisung wäre also keine Userinteraktion möglich. a.exec() wird erst beendet wenn das Qt Programm beendet wird, d.h. also dass das "return" in Zeile 08 erst ausgeführt wird wenn das Qt Programm beendet wird.
[Bearbeiten] Weiter
zum nächsten Kapitel: Erste UI-Erweiterungen


