Qt für C++-Anfänger/ Druckversion

Aus Wikibooks
Zur Navigation springen Zur Suche springen
Qt für C++-Anfänger



Wikibooks

Übersicht[Bearbeiten]

Einleitung[Bearbeiten]

Dieses Tutorial richtet sich an diejenigen, die einen schnellen und einfachen Einstieg in die Qt-Programmierung suchen. Dieses Tutorial setzt Grundkenntnisse in der C++-Programmierung voraus. Neben der Programmierung soll auch das Lesen der Qt-Dokumentation geübt werden, was für die Entwicklung umfangreicher Qt-Anwendungen unverzichtbar ist.

Dieses Tutorial bezieht sich auf die Qt-Version 4, welche zur Version 3 nicht kompatibel ist. Wer jedoch mit der älteren Version von Qt gearbeitet hat, wird einen schnellen Einstieg in die neue Version finden, da sich in der grundlegenden Programmstruktur nicht viel geändert hat. Die GUI soll dabei mittels des Qt-Designers erstellt werden. Dazu wäre es vorteilhaft, wenn man sich vorher mit dem Qt-Designer befasst. Die Qt-Designer-Tutorials im Web, die ich kenne, haben alle diverse Schwächen, weswegen ich sie für ungeeignet halte, um mit ihnen ein lauffähiges Programm zu erstellen. Sie sind in erster Linie für Qt3 geschrieben, das – wie oben schon angedeutet – zu Qt4 inkompatibel ist. Außerdem wird anhand dieser Tutorials nur eine Maske erzeugt und kein lauffähiges Programm. Den Umgang mit dem Qt Designer können sie trotzdem nahebringen.

Die Befehle in diesem Tutorial beziehen sich auf den Qt-Designer unter Linux. Ich nehme an, dass sie unter Windows und anderen Betriebssystem identisch sind. Ansonsten ist das jetzt ein guter Zeitpunkt, um Linux zu installieren. ;-)

Die Anwendung, die wir in diesem Tutorial erzeugen werden, wird ein Taschenrechner zum Addieren und Subtrahieren sein. Extrem simpel, aber man hat hinterher ein Grundgerüst, welches man benutzen kann, um mit seinen eigenen Anwendungen loszulegen.


Das Grundgerüst[Bearbeiten]

Die Erstellung des GUIs[Bearbeiten]

Die grafische Benutzeroberfläche wird mit dem Tool "Qt Designer" erstellt. Unter Linux lässt es sich mit dem Kommando "designer" bzw. "designer-qt4" aufrufen.

Der Taschenrechner soll später mal so aussehen:

Qt Beispiel Taschenrechner.png

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 sollte aber bitte "Taschenrechner" genannt werden.

Das Code-Grundgerüst[Bearbeiten]

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:


 1 //--- main.cpp - start ---
 2 
 3 #include "Taschenrechner.h"
 4 #include <QApplication>
 5 
 6 int main( int argc, char* argv[])
 7 {
 8 	QApplication a(argc, argv);
 9 	Taschenrechner w;
10 	w.show();
11 	return a.exec();
12 }
13 
14 //--- main.cpp - end ---


 1 //--- Taschenrechner.h - start ---
 2 
 3 #ifndef TASCHENRECHNER_H
 4 #define TASCHENRECHNER_H
 5 
 6 #include "ui_Taschenrechner.h"
 7 
 8 class Taschenrechner : public QMainWindow, public Ui::MainWindow{
 9 	Q_OBJECT
10 
11 	public:
12 		Taschenrechner (QMainWindow *parent = 0);
13 		~Taschenrechner();
14 };
15 #endif //TASCHENRECHNER_H
16 
17 //--- Taschenrechner.h - end ---


 1 //--- Taschenrechner.cpp - start ---
 2 
 3 #include "Taschenrechner.h"
 4 
 5 Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){
 6 	setupUi(this);
 7 }
 8 
 9 Taschenrechner::~Taschenrechner(){
10 }
11 
12 //--- 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.

Das erste Übersetzen[Bearbeiten]

Wechsle in einer Shell in unser Projektverzeichnis und führe nacheinander folgende Befehle aus:

qmake -project
qmake
make
./Taschenrechner

Beachte: Bei manchen Distributionen heißt der Befehl qmake-qt4.

qmake -project[Bearbeiten]

Erzeugt eine .pro-Datei; die so genannte Projekt-Datei. Der Name des Files ist der Name des Projektverzeichnisses. In dieser Datei stehen im wesentlichen alle Dateien, die zu unserem Projekt gehören. Wenn man neue Dateien erstellt, also nicht nur die bestehenden ändert, muss dieser Befehl erneut aufgerufen werden. Man kann die .pro-Datei mit einem Texteditor öffnen und bekommt eine ganz gute Vorstellung, was darin steht.

qmake[Bearbeiten]

qmake erzeugt das Makefile.

make[Bearbeiten]

make übersetzt am Ende unsere Quelldateien basierend auf dem Makefile. Es werden auch noch Qt-spezifische Header-Dateien erstellt. Die Datei ui_Taschenrechner.h enthält den Qt-Code für die Benutzerschnittstelle (ui steht für engl. user interface). Das Endergebnis ist eine ausführbare Datei mit dem Namen des Projekts, welche identisch mit dem Namen unseres Projektverzeichnisses ist. Ist unter Windows der Aufruf von make nicht erfolgreich, muss der komplette Pfad zur make.exe eingegeben werden: z.B. <QT-Verzeichnis>\QT\Symbian\SDKs\Symbian3Qt474\epoc32\tools\make.

Das Grundgerüst im Detail[Bearbeiten]

Taschenrechner.h[Bearbeiten]

ist das Header File für unsere Klasse Taschenrechner. In diesem Header File finden wir die so genannten Prototypen unserer Funktionen. Also die Beschreibung welche Funktionen zur Verfügung stehen, hier wird aber keine Funktion implementiert.

Nun zu den Details:

03 #ifndef TASCHENRECHNER_H
04 #define TASCHENRECHNER_H
<...>
15 #endif //TASCHENRECHNER_H

sorgt dafür, dass der Header nur einmal eingebunden wird. Dabei handelt es sich um sogenannte Includewächter. Das sind Makros, die vom Präprozessor abgearbeitet werden, bevor der Compiler das Programm übersetzt. In Zeile 03 wird nachgeschaut, ob das Flag TASCHENRECHNER_H gesetzt ist. Ist es nicht gesetzt, wird der ganze Code bis zur Zeile 15 zum Übersetzen "freigegeben". 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 04. 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 04 nun gesetzt. Trifft der Präprozessor nun ein weiteres Mal auf diesen Code, wird dieser einfach nicht mehr beachtet, was auch nicht mehr nötig ist, da alle Prototypen schon bekannt sind. Somit wird also verhindert, dass Definitionen vom Compiler mehrfach verarbeitet werden, was zu Fehlern beim Übersetzen führen würde. Die Form der Namensgebung dieses Flags (als eine Ableitung aus dem Dateinamen) ist eine weit verbreitete Konvention. Im Prinzip würde es jeder andere Name auch tun – was aber nicht zu empfehlen ist. Es würde Verwechslungen wahrscheinlicher machen.

06 #include "ui_Taschenrechner.h"

Dieses Header-File wurde von make als erstes erzeugt, wenn man dort rein schaut, erkennt man sehr leicht, dass hier alle UI Elemente zu finden sind.

08 class Taschenrechner : public QMainWindow, public Ui::MainWindow {
09	Q_OBJECT
10
11 public:
12	Taschenrechner (QMainWindow *parent = 0);
13	~Taschenrechner();
14 };

Dies ist die Klassendeklaration unserer Hauptklasse. In Zeile 08 wird der Klassenname "Taschenrechner" angegeben; außerdem zeigt dies an, dass unsere Klasse abgeleitet ist von QMainWindow ": public QMainWindow". Wollen wir beispielsweise einen QDialog erstellen, wäre hier die Ableitung auf QDialog zu ändern. In "ui_Taschenrechner.h" findet die Definition der Taschenrechnermaske "MainWindow" im Namensraum "Ui" statt. "Ui::MainWindow" ist seinerseits nur ein Container bzw. eine Ableitung der eigentlichen Maskendefinition in "Ui_MainWindow".

Zeile 09("Q_OBJECT") ist ein weiteres Makro, welches immer bei Qt in der Klasse, die Signals und Slots behandelt, angegeben werden muss. Diese werden dann durch den Meta-Object Compiler ausgewertet. Vergisst man das Makro, werden Signale und Slots nicht behandelt. Unsere Klasse hat zur Zeit zwei Methoden deklariert. Beide sind "public", können also von einer beliebigen Funktion aufgerufen werden. Bei den Methoden handelt es sich um den Konstruktor und Destruktor. Die Methode, die den gleichen Namen hat wie die Klasse selbst (Zeile 12), ist immer der Konstruktor und die mit dem gleichen Namen, aber einer vorangestellten Tilde (~), ist immer der Destruktor (Zeile 13). Diese Methoden werden immer beim Anlegen oder Zerstören eines Objekts der Klasse aufgerufen. Dem Konstruktor wird hier noch ein Parameter übergeben, der dem Objekt ein Elternwidget zuweist. In unserem Beispiel ist der Standard bei nicht-übergebenem Argument, parent = 0.

Taschenrechner.cpp[Bearbeiten]

01 #include "Taschenrechner.h"
02
03 Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){	
04 	setupUi(this);
05 }
06
07 Taschenrechner::~Taschenrechner(){
08 }

Hier werden die deklarierten Funktionen aus der Headerdatei wirklich implementiert. In Zeile 01 wird das Header-File inkludiert. Damit ist bekannt, welche Funktionen es geben wird. In Zeile 03 – 05 wird der Konstruktor implementiert. Das ": QMainWindow(parent)" nach dem Konstruktor sorgt dafür, dass der Konstruktor der Klasse QMainWindow, von der unsere Klasse abgeleitet worden ist, vor dem Aufruf mit dem angegebenen Argument aufgerufen wird. Die einzige Anweisung, die für den Konstruktor implementiert ist, 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 Destruktor definiert; dieser ist leer. Es werden keine speziellen Funktionen ausgeführt. Um die Speicherfreigabe nach dem Zerstören des Objekts kümmert sich Qt von selbst. Man beachte hier, dass das Fehlen des Returntyps (bspw. "void") spezifisch für Konstruktoren und Destruktoren ist! Dies hat nichts mit Qt zu tun, sondern tritt bei vielen objektorientierten Programmiersprachen auf.

main.cpp[Bearbeiten]

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 bindet das Headerfile ein, 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 und die Kommandozeilenparamter werden an dieses Objekt übergeben. Der Objektname ist einfach "a". In Zeile 06 wird eine Instanz der Klasse "Taschenrechner" erstellt, diese Instanz wird "w" genannt, (kommt wohl von Window oder Widget) man könnte aber jeden beliebigen Namen wählen. In Zeile 07 rufen wir die Funktion show() auf. Diese Funktion haben wir nicht explizit implementiert, durch die Ableitung von QMainWindow steht uns aber diese Funktion zur Verfügung. QMainWindow hat diesen "Slot" von QWidget geerbt. Diese Funktion sorgt dafür, dass das Widget angezeigt wird. Sprich: Ließe 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. Das heißt also, dass das "return" in Zeile 08 erst ausgeführt wird, wenn das Qt-Programm beendet wird.


Erste UI-Erweiterungen[Bearbeiten]

Als erste Erweiterung sollen am UI ein paar Veränderungen vorgenommen werden – ohne den Qt-Designer. In den Inputfeldern von A und B soll ein Startwert von "0" vorgegeben werden.

Dazu muss als erstes herausgefunden werden, wie man in dem QLineEdit-Objekt einen Text eintragen kann. Dazu schaut man in der QLineEdit-Dokumentation nach einer Funktion, die sich so anhört, als ob man sie gebrauchen könnte. Nach einiger Zeit hat man darin etwas Übung. Schaut man in die Dokumentation, findet man zwei Funktionen, die passen könnten, setText() und insert(). Beide würden das machen, was wir hier erreichen wollen. Wir wählen setText(). Die Syntax der Funktion ist laut Dokumentation:

void setText ( const QString & )

Die Funktion liefert keinen Rückgabewert, da dieser mit "void" angegeben ist. Als Input-Parameter wird ein "const QString &" erwartet. Man erkennt leicht, dass vom Typ her ein QString, das ist im wesentlichen ein normaler String, erwartet wird. Bleibt noch zu klären, was "const" und "&" bedeuten. Das "const" gibt an, dass sich der Wert der Variable innerhalb der Funktion nicht ändert bzw. nicht geändert werden darf. Würde man sie innerhalb der Funktion versuchen zu verändern, würde es einen Compiler-Error geben. Da wir die Funktion nicht selber schreiben, sondern sie von Qt vorgegeben ist, muss einen das "const" nicht weiter stören. Ferner erlaubt das "const", dass man explizite Werte, also z.B. "Ich bin ein String", direkt mitgeben kann. Ohne das "const" wäre das nicht möglich. Das "&" gibt an, dass man ein so genanntes "Passing by reference" macht. Man gibt beim Aufruf der Funktion nur die Variable an, aber implizit wird die Adresse, an der die Variable steht, weitergereicht. Sprich, sollte die Variable innerhalb der Funktion verändert werden, so hat diese Änderung auch außerhalb der Funktion Gültigkeit. Da wir oben gesehen haben, dass die Variable nicht geändert wird, erscheint das erstmal als rein theoretisches Konstrukt. Hintergrund ist, dass es so schneller in der Ausführung ist, als wenn man ein "Passing by value" machen würde. Kurz zusammen gefasst sagt uns obiges Argument einfach nur, dass man ein QString-Objekt übergeben muss.

Als nächstes muss man sich überlegen, wie der Befehl konkret aussieht:

InputA -> setText("0");

InputA ist der Name, den wir dem QLineEdit-Objekt im Designer gegeben haben. InputA ist aber nur ein Pointer auf diese Instanz, will man eine Funktion einer Instanz aufrufen, nutzt man den Operator "->".

Jetzt ist nur noch die Frage offen, wo der Code für InputA und InputB eingefügt werden muss. Da die Eigenschaften von Anfang an gegeben sein sollen, ist der geeignete Platz der Konstruktor. Die einzige Datei, welche wir ändern müssen, ist also "Taschenrechner.cpp".

//--- Taschenrechner.cpp - start ---

#include "Taschenrechner.h"

Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){	
	setupUi(this);
	InputA -> setText("0");
	InputB -> setText("0");
}

Taschenrechner::~Taschenrechner(){
}

//--- Taschenrechner.cpp - end ---

Nun einfach erneut "make" ausführen und das Programm starten, um die Änderung zu sehen. Als nächstes könnte man die beiden neuen Zeilen gegen diese austauschen:

	InputA -> insert("0");
	InputB -> insert("0");


Signale und Slots[Bearbeiten]

Signale und Slots sind ein Mechanismus von Qt, wie sich verschiedene GUI-Elemente oder Aktionen unterhalten können. Jemand sendet ein Signal aus und ein anderer empfängt dieses. Ein Signal kann z.B. beim Drücken eines Buttons ausgesendet werden. Ein oder mehrere Empfänger, die so genannten Slots, empfangen das Signal und rufen daraufhin eine entsprechende Funktion auf, die z.B. irgendeine Berechnung startet. Als erstes müssen wir uns überlegen, wie man Signale und Slots miteinander verbindet.

Diese Verbindung wird über das connect-Statement hergestellt. connect ist dabei ein Qt-spezifisches Makro, welches vom Präprozessor in echtes C++ umgesetzt wird. Die Syntax sieht wie folgt aus:

connect(Calculate, SIGNAL(clicked()), this, SLOT(addAB()));

"Calculate" ist das Qt-Objekt, das ein Signal aussendet. Calculate ist der Name des QPushButton, den wir im Qt Designer festgelegt hatten.

"SIGNAL(clicked())" bezeichnet das Signal, welches abgefangen werden soll. Objekte können unterschiedliche Signale aussenden. In der Qt-Dokumentation können die für das jeweilige Qt-Objekt verfügbaren Signale nachgeschlagen werden.

"this" bezeichnet die Instanz der Klasse, deren aufzurufende Methode im Folgenden angegeben wird.

"SLOT(addAB())" bezeichnet die Funktion, die aufgerufen werden soll. Für diese Funktion muss natürlich, wie für jede andere C++ Funktion auch, eine Deklaration erstellt werden.

Dieser connect-Befehl muss im Konstruktor unserer Klasse stehen, damit er gleich am Anfang ausgeführt wird. Die Deklaration des Slots addAB() findet im Headerfile statt. Das Headerfile sieht damit folgendermaßen aus:

//--- 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();
private slots:
    void addAB();
};

#endif //TASCHENRECHNER_H

//--- Taschenrechner.h - end ---

Wie man sieht, findet die Deklaration der Funktion addAB() innerhalb der Klasse statt. Es handelt sich also um eine Memberfunktion. Es wird noch angegeben, dass es sich um eine private Funktion handelt. Mit dem Makro "slots" wird gesagt, dass es sich bei dieser Funktion um einen Slot handelt. Die Datei Taschenrechner.cpp mit der eigentlichen Implementation sieht dann so aus:

//--- Taschenrechner.cpp - start ---

#include "Taschenrechner.h"

Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent) {	
    setupUi(this);
    InputA -> setText("0");
    InputB -> setText("0");

    connect(Calculate, SIGNAL(clicked()), this, SLOT(addAB()));
}

Taschenrechner::~Taschenrechner() { }

void Taschenrechner::addAB() {
    //Hier wird im nächsten Kapitel die Funktion implementiert
}

//--- Taschenrechner.cpp - end ---

Alle Slots und Funktionen, die benötigt werden, werden also einfach in der Datei Taschenrechner.cpp nacheinander aufgelistet. Mit der Angabe "Taschenrechner::" wird mitgeteilt, dass es sich um eine Funktion der Klasse Taschenrechner handelt. Der Funktionskopf muss ansonsten genau so aussehen wie im zugehörigen Headerfile.

Wenn man das Programm nun übersetzt und ausführt, wird man erst einmal keine Veränderung feststellen, da die Funktion addAB() noch leer ist. Als letztes sei angemerkt, dass die Funktion addAB() keinen Wert zurück gibt (void). Ein "return;" schadet zwar nicht, man kann sich die Tipparbeit aber sparen. Nur, wenn es mehrere Ausstiege aus der Funktion gibt, was zum Beispiel bei Schleifen der Fall sein kann, muss ein return verwendet werden.

Siehe auch[Bearbeiten]


Die erste Version[Bearbeiten]

Die Funktion addAB()[Bearbeiten]

Jetzt muss noch die Funktion addAB() implementiert werden und unser Taschenrechner ist in der ersten Version fertig. Die Implementation dieser Funktion besteht im Wesentlichen im Aufruf von Qt Funktionen. Hierbei kann man also ein bisschen üben, sich in der Qt Dokumentation zurecht zu finden.

Auslesen der Felder[Bearbeiten]

Als erstes müssen wir die Eingabefelder auslesen. Die Eingabefelder sind QLineEdit Objekte. Wir suchen also eine Funktion in der Doku zu QLineEdit, die passen könnte. Wir finden die passenden Funktionen displayText() und text(). Beide Funktionen sind gleich, ein Unterschied ergibt sich lediglich in dem Fall, dass das Eingabefeld zur Eingabe von Passworten verwendet wird. Wir entscheiden uns für text(). Die Syntax für text() ist:

QString text() const

Man ruft die Funktion also ohne Parameterübergabe auf und bekommt einen QString zurück. Das "const" am Ende der Syntaxdefinition besagt, daß das Objekt, auf das man die Funktion anwendet (also QLineEdit und damit der darin gespeicherte Text) nicht verändert wird. Wie schon beim Setzen der Default-Werte wird die Funktion über den Operator "->" aufgerufen, da es sich bei InputA um einen Pointer handelt.

QString a;
a = InputA -> text();

Jetzt haben wir in dem QString a den eingegebenen Text stehen. Analoges kann man für InputB machen. Um mit den Zahlen rechnen zu können, müssen wir aus dem QString ein Double machen.

QString in Double wandeln[Bearbeiten]

Um eine entsprechende Funktion zu finden, müssen wir jetzt die Doku zu QString nach einer passenden Funktion durchsuchen. Die passende Funktion ist toDouble(). Die Syntax hier ist etwas komplizierter:

double QString::toDouble ( bool * ok = 0 ) const

Man sieht direkt, dass der Rückgabewert ein double ist. Als Parameter zum Aufruf kann ein Pointer (*) auf eine boolsche Variable mitgegeben werden, die den internen Namen "ok" bekommt. Der Parameter ist optional. Das heißt, wenn kein Parameter vorhanden ist, wird der Default-Wert "0" übergeben. In dieser boolschen Variable finden wir später das Ergebnis, ob die Konvertierung also geklappt hat (ok=true) oder nicht (ok=false). Das "const" besagt wieder, dass das Objekt, auf das die Funktion angewendet wird, nicht verändert wird. In diesem Fall existiert also der QString, den wir in Double umwandeln, auch nach der Wandlung noch unverändert.

double a;
a = (InputA -> text()).toDouble();

Hier wird jetzt nicht der "->" Operator verwendet, da text() einen QString zurückliefert und nicht einen Pointer auf einen QString. Wollen wir die Wandlung prüfen, so müsste der Code so aussehen:

double a;
bool ok;
a = (InputA -> text()).toDouble(&ok);

ok ist eine boolsche Variable, die Funktion will aber einen Pointer auf eine boolsche Variable haben, sprich die (Speicher-)Adresse, wo die boolsche Variable zu finden ist. Die Adresse einer Funktion wird über den "&" Operator bestimmt. Wir werden die erste Version ohne Prüfung verwenden, auch wenn es schlechter Stil ist. Unser Code sieht also dann wie folgt aus:

double a, b, c;
a = (InputA -> text()).toDouble();
b = (InputB -> text()).toDouble();
c = a + b;

Als nächstes muss c in das QLineEdit Feld ResultC geschrieben werden. Das Problem ist, dass die bekannte Funktion setText() einen QString als Parameter erwartet und kein Double. Es muss also wieder eine Umwandlung vorgenommen werden.

Wandeln von Double nach QString[Bearbeiten]

Da wir wieder etwas mit einem QString machen wollen, müssen wir wieder die Doku zu QString durchsuchen. Die Funktion, die wir benötigen, lautet: arg(). Die Funktion arg() gibt es in der Dokumentation zu QString mehrfach. Dies sind so genannte überladene Funktionen. Der Compiler sucht sich an Hand der Parameter (Anzahl und Typen) die passende Funktion heraus. Wir müssen also die richtigen Parameter übergeben. Die Funktion, die wir brauchen, ist

QString QString::arg ( double a, int fieldWidth = 0, char format = 'g', 
                     int precision = -1, 
                     const QChar & fillChar = QLatin1Char( ' ' ) ) const

Warum gerade diese? Nunja, das ist die einzige, die als Übergabeparameter ein Double erlaubt. Die Funktionssyntax sieht relativ kompliziert aus. Der einzige verpflichtende Parameter ist jedoch "double a", alle anderen Übergabeparameter sind optional, da dort ein Defaultwert angegeben ist. Hierbei ist zu beachten, dass man bei der Übergabe eines Parameters, der vom Defaultwert abweichen soll, alle anderen Parameter "links" von ihm ebenfalls übergeben muss, selbst wenn diese ihre Defaultwerte behalten sollen. Das "const" ganz am Ende der Funktionsdefinition kennen wir ja bereits: Es besagt, dass der QString, auf den die Funktion angewendet wird, selber nicht verändert wird. Der zurückgegebene QString ist also ein neu erzeugter QString.

double a: Der Wert, der umgewandelt werden soll.

int fieldWidth: Der Wert gibt an, wie viele Stellen mit dem Parameter fillChar aufgefüllt werden. (Da es sich um eine überladene Funktion handelt, ist der Parameter nicht an dieser Stelle in der Doku erklärt sondern bei der arg() Funktion, wo er zum ersten Mal auftritt.)

char format: Gibt an, in welcher Schreibweise die Zahl umgewandelt werden soll, also z.B. mit Exponenten.

int precision: Gibt an, wie viele Stellen nach dem Komma dargestellt werden sollen.

const QChar &: Gibt das Zeichen an, welches zum Auffüllen verwendet wird, wenn fieldWidth ungleich Null ist.

Wir wollen Format "f" (floating point) verwenden mit 4 Nachkommastellen. Unser Aufruf ist also arg(a+b,0,'f',4). Damit ist unser Code wie folgt:

double a, b;
a = (InputA -> text()).toDouble();
b = (InputB -> text()).toDouble();
ResultC -> setText(QString("%1").arg(a+b,0,'f',4));

%1 ist ein Platzhalter, welcher durch die arg Funktion ersetzt wird. Auch diese Erklärung findet man weiter oben in der Doku, wo sie zum ersten Mal benutzt wird. Es lohnt sich also bei Funktionen, die mehrfach vorkommen, auch die Dokumentation von Versionen anzuschauen, die man eigentlich nicht braucht. Was wir nicht vergessen dürfen ist das Einbinden der QString library in der die Funktion enthalten ist. Das Einbinden erfolgt hierbei mit #include <QString>

Die Datei Taschenrechner.cpp sieht damit so aus:

//--- Taschenrechner.cpp - start ---

#include "Taschenrechner.h"
#include <QString>

Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){	
	setupUi(this);
	InputA -> setText("0");
	InputB -> setText("0");

	connect(Calculate, SIGNAL (clicked()), this, SLOT(addAB()));
}

Taschenrechner::~Taschenrechner(){
}

void Taschenrechner::addAB(){
	double a, b;
	a = (InputA -> text()).toDouble();
	b = (InputB -> text()).toDouble();
	ResultC -> setText(QString("%1").arg(a+b,0,'f',4));
}

//--- Taschenrechner.cpp - end ---

Nach dem erneuten Übersetzen ist der Taschenrechner fertig zum Addieren. Man könnte jetzt sehr einfach eine Funktion subAB() und einen zusätzlichen Button implementieren, um die Subtraktion einzuführen. Hier soll jedoch einen Schritt weiter gegangen werden: Das Ganze soll übersichtlich in verschiedenen Quellcodedateien implementiert werden.


Weitere UI-Erweiterungen[Bearbeiten]

UI File zur Subtraktion[Bearbeiten]

Um den Taschenrechner nicht nur zur Addition, sondern auch zur Subtraktion verwenden zu können, müssen einige Änderungen vorgenommen werden. Das UI soll so aussehen, dass es ein Fenstermenü gibt, über das man die gewünschte Rechenoperation auswählen kann. Je nachdem, welche Operation gewählt wird, soll sich der Inhalt des UIs (Labels, Eingabefelder und der Button) ändern. Das Wechseln des UIs beim Auswählen des entsprechenden Menüeintrags geschieht über ein so genanntes "Stacked Widget". Ein Stacked Widget kann man sich wie einen Block Papier vorstellen und je nach dem, welche Seite gewählt wird, sieht man eine entsprechende Ansicht.

Änderungen im UI-File[Bearbeiten]

Das bestehende UI-File kann als Grundlage genommen werden. Hierin wird ein neues StackedWidget-Element erstellt. Alle schon vorhandenen UI-Elemente werden auf dessen erste Seite verschoben, danach werden die Elemente nochmals auf die zweite Seite kopiert. Dabei wird noch das Textlabel "A+B" umbenannt in "A-B". Durch das Kopieren ändern sich die technischen Namen der kopierten Objekte: Sie heißen wie die ursprünglichen Objekte, enden aber mit "_2". Will man die Objekte hier also manuell anlegen, sollte dieser Name gewählt werden. Der technische Name des Stacked Widget in unserem Tutorial ist "stackedWidget". Geht man im Qt-Designer in den Preview-Modus (Strg+R), so sieht man für das Stacked Widget ein paar kleine Pfeile, mit denen man die verschiedenen Ansichten durchblättern kann.

Als letztes muss nun noch das Menü erstellt werden. Sollte der UI Designer keine leere Menüleiste am oberen Ende des Reißbrettes anzeigen, kann man durch Klick auf eine leere Stelle im Hintergrund das Kontextmenü aufrufen und "Create Menu Bar" wählen. Einträge können direkt durch einen Mausklick auf die Menüleiste hinzugefügt werden. Das Hauptmenü soll aus den zwei Einträgen "Main" und "Choose" bestehen. Dabei enthält das erste Hauptmenü nur den Eintrag "Quit" zum Beenden des Programms und das zweite Menü die Einträge "Add" und "Sub". Die technischen Namen der Einträge sind per Default "action<Menüeintrag>", also z.B. "actionQuit". Änderungen an diesen Namen können über den Property Editor vorgenommen werden. Eventuell zeigt der Property Editor nicht die richtigen Daten an, wenn man auf den gewünschten Menüeintrag klickt. In diesem Fall müsste man über den Object Inspector gehen. Dieser kann über das Menü im Qt Designer eingeschaltet werden.

Man kann mit dem neuen UI-File das Programm nun wieder übersetzen und ausführen. Man sollte die Menüleiste und deren Einträge sehen aber sonst noch keine Veränderungen.

Menüs mit Funktionen belegen[Bearbeiten]

Nun sollen die drei Menüs mit Funktionen belegt werden. Das ist ganz einfach. Wenn man in der Doku ein wenig sucht, findet man die passenden Funktionen. Hier wird nun eine etwas kürzere Beschreibung gegeben als im Kapitel zuvor. Für jeden Menüeintrag muss ein connect String in den Konstruktor eingebaut werden. Der connect String hat die Form

connect(action<Menüeintrag>,SIGNAL (triggered()), this, SLOT(<Funktion>));

action<Menüeintrag> ist dabei der technische Name des Menüs und <Funktion> ist eine zu implementierende Funktion, die gerufen werden soll, wenn der Benutzer den entsprechenden Eintrag auswählt. Für jede dieser Funktionen muss im Header-File Taschenrechner.h eine entsprechende Deklaration eingeführt und diese in der Quelldatei Taschenrechner.cpp implementiert werden.

Menü Quit[Bearbeiten]

Der connect String sieht hier so aus

connect(actionQuit,SIGNAL (triggered()), this, SLOT(slotClose()));

Anzumerken sei hier, dass der Slot "slotClose()" genannt wurde, man mag geneigt sein, ihn einfach nur "close()" zu nennen. close() ist aber eine Funktion, die es in Qt schon gibt. Die Benutzung von close() als Slotname würde zu einem unerwünschten Verhalten führen. Generell sollte man bei der Wahl der Funktionsnamen etwas aufpassen und zu "simple" Namen meiden. Weitere Beispiele von Funktionsnamen, die nicht verwendet werden können, sind add und sub. Add und Sub könnten aber benutzt werden. Die Groß- und Kleinschreibung spielt hier also eine Rolle. Die Deklaration der Funktion slotClose() ist sehr einfach:

void slotClose();

Die Implementation der Funktion ist ebenfalls sehr simpel

void Taschenrechner::slotClose(){ 
	close();
}

Zur Erinnerung: ganz am Anfang hatten wir in main.cpp eine Code-Zeile "return a.exec()" implementiert. Wenn jetzt close() aufgerufen wird, schließt sich das aktuelle Fenster. Ist das aktuelle Fenster das letzte Fenster, so endet a.exec() und das Programm ebenfalls. Der return-Befehl gibt den Status von a.exec() zurück. Die Funktion close() bekommt QApplication von QWidget vererbt, ist also unter QWidget zu finden. Möchte man nicht nur das Fenster schließen, sondern die ganze Applikation beenden (falls noch andere Fenster offen sind), so gibt es die Funktionen QCoreApplication::quit() und QCoreApplication::exit().

Nun kann die Applikation wieder neu übersetzt und dann geprüft werden, ob "Quit" wirklich die Anwendung schließt. Die Funktion slotClose() ist natürlich sehr kurz, da nur close() aufgerufen wird, man kann natürlich einen Slot auch direkt in dem connect String verarbeiten.

connect(actionQuit,SIGNAL (triggered()), qApp, SLOT(quit()));

qApp ist ein Makro, welches die aktuelle Instanz von QApplication herausfindet.

Menü Add[Bearbeiten]

Der connect String sieht wie folgt aus

connect(actionAdd, SIGNAL(triggered()), this, SLOT(showAdd()));

Die Deklaration von showAdd() wie folgt

void showAdd();

Für die Implementation von showAdd() muss man sich überlegen wie man zwischen den Seiten des stacked Widget umschalten kann. In der Doku findet man die passende Funktion, damit sieht die Implementation wie folgt aus:

void Taschenrechner::showAdd(){
	stackedWidget -> setCurrentIndex(0);
}

Man beachte, daß die Zählung der Seiten bei Null beginnt.

Menü Sub[Bearbeiten]

Das Menü Sub wird implementiert wie das Menü Add, man ersetze einfach Add durch Sub. Ferner muss darauf geachtet werden, dass der Index von Null auf Eins geändert wird, damit auch wirklich die richtige Seite angezeigt wird.

Beim folgenden Coding ist noch zu beachten, dass sich main.cpp nicht geändert hat und dass analog zum vorherigen Kapitel noch Defaultwerte für die Sub-Seite gesetzt wurden.

//--- 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();
private slots:
	void addAB();
	void slotClose();
	void showAdd();
	void showSub();
};

#endif //TASCHENRECHNER_H

//--- Taschenrechner.h - end ---
//--- Taschenrechner.cpp - start ---
#include "Taschenrechner.h"

Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){	
	setupUi(this);
	InputA -> setText("0");
	InputB -> setText("0");
	InputA_2 -> setText("0");
	InputB_2 -> setText("0");

	connect(Calculate, SIGNAL (clicked()), this, SLOT(addAB()));
	connect(actionQuit,SIGNAL (triggered()), this, SLOT(slotClose()));
	connect(actionAdd, SIGNAL(triggered()), this, SLOT(showAdd()));
	connect(actionSub, SIGNAL(triggered()), this, SLOT(showSub()));
}

Taschenrechner::~Taschenrechner(){
}

void Taschenrechner::addAB(){
	double a, b;
	a = (InputA -> text()).toDouble();
	b = (InputB -> text()).toDouble();
	ResultC -> setText(QString("%1").arg(a+b,0,'f',4));	
}

void Taschenrechner::slotClose(){
	close();
}

void Taschenrechner::showAdd(){
	stackedWidget -> setCurrentIndex(0);
}

void Taschenrechner::showSub(){
	stackedWidget -> setCurrentIndex(1);
}

//--- Taschenrechner.cpp - end ---

Zu guter Letzt könnte man jetzt eine Funktion subAB implementieren und fertig wäre der Taschenrechner. Da dies identisch ist mit der Implementierung von addAB(), kann auf eine weitere Erklärung hier verzichtet werden. Wenn man dies macht, wird die Datei Taschenrechner.cpp sehr schnell sehr unübersichtlich (es gibt ja noch mehr Rechenarten zu implementieren). Daher soll im folgenden Kapitel der Quellcode auf verschiedene Dateien verteilt werden.


Aufteilung des Quellcodes[Bearbeiten]

Der Quellcode soll jetzt auf mehrere Dateien verteilt werden, um eine bessere Übersicht zu behalten. Dazu sollen die Funktionen zum Addieren und zum Subtrahieren in einer jeweils eigenen Datei liegen. Ferner wird es eine Datei geben, die für übergreifende Funktionen (z.B. das UI) zuständig ist. Jede Funktion wird in eine eigene Klasse ausgegliedert, jede Klasse besteht aus zwei Dateien:

<Klassenname>.cpp
<Klassenname>.h

Insgesamt werden wir die folgenden Dateien haben:

myAdd.cpp
myAdd.h

mySub.cpp
mySub.h

Taschenrechner.cpp
Taschenrechner.h

main.cpp

Taschenrechner.ui

Eine Anmerkung sei gemacht, die neuen Klassen wurden myAdd und mySub genannt. Auch hier muss wieder mit der Namensgebung aufgepasst werden, da bestimmte Namen schon belegt sind. Wir hätten sie nicht add und sub, sehr wohl aber Add und Sub nennen können. Die Dateien myAdd.cpp/.h und mySub.cpp/.h sehen natürlich sehr ähnlich aus und wir werden nur die Klasse myAdd im Detail besprechen, für mySub wird der Quellcode ohne weiteren Kommentar zur Verfügung gestellt.

myAdd.h[Bearbeiten]

Hierbei handelt es sich wieder um eine Headerdatei, die im Prinzip nichts Ungewöhnliches enthält.

//--- myAdd.h - start ---
#ifndef MYADD_H
#define MYADD_H

#include <QWidget>

class Taschenrechner;

class myAdd : public QWidget{
	Q_OBJECT

Taschenrechner *myTaschenrechner;

public:
	myAdd (Taschenrechner*);
	~myAdd();
private slots:

};

#endif //MYADD_H
//--- myAdd.h - end ---

Die Klasse myAdd ist abgeleitet von der Klasse QWidget. Dies ist notwendig, damit die Klasse myAdd alle grundsätzlichen Qt Eigenschaften besitzt. Es gibt eine Klassenvariable myTaschenrechner von Typ Taschenrechner, die unsere Hauptklasse ist. Damit diese Variable erzeugt werden kann, muss der Typ Taschenrechner deklariert werden, sprich der Compiler muss wissen, dass es eine Klasse Taschenrechner gibt. Dies passiert mittels der forward declaration

class Taschenrechner;

Diese Variable muss beim Anlegen einer Instanz der Klasse myAdd belegt werden. Erzwungen wird das, da im Konstruktor von myAdd diese Variable übergeben werden muss. Notwendig ist dies, da wir aus der Klasse myAdd heraus auf Objekte (=UI Elemente) der Klasse Taschenrechner zugreifen wollen. Diese UI Elemente liegen gemäß unseres Designs (die Qt Designer ui-Datei wird aus Taschenrechner.cpp heraus dargestellt) in dieser Klasse und könnten ohne Zugriff auf diese Klasse nicht gefunden/angesprochen werden.

myAdd.cpp[Bearbeiten]

Im Grundgerüst der Klasse myAdd werden erstmals nur der Konstruktor und der Destruktor implementiert. Für den Konstruktor muss überlegt werden, wie der Pointer auf die Klasse Taschenrechner übergeben werden kann. Eine Möglichkeit ist es, beim Aufruf des Konstruktors den Wert an einen Pointer TRechner zu übergeben und diesen dann an myTaschenrechner zu übergeben.

//--- myAdd.cpp - start ---
#include "myAdd.h"
#include "Taschenrechner.h"

myAdd::myAdd(Taschenrechner *TRechner){
	myTaschenrechner = TRechner; 
}

myAdd::~myAdd(){
}
//--- myAdd.cpp - end ---

Es sei angemerkt, dass etwas in der Art von

myAdd::myAdd(Taschenrechner *myTaschenrechner){
}

nicht funktioniert, man muss also wirklich eine Variable übergeben und diese dann weiter reichen. Das ist sicherlich nicht sehr elegant, daher gibt es eine andere Möglichkeit Klassenvariablen zu initialisieren. Dabei handelt es sich um die so genannten Initialisierungslisten.

//--- myAdd.cpp - start ---
#include "myAdd.h"
#include "Taschenrechner.h"

myAdd::myAdd(Taschenrechner *TRechner):myTaschenrechner(TRechner){
}

myAdd::~myAdd(){
}
//--- myAdd.cpp - end ---

Man erkennt im Code wohl, wie diese Liste funktioniert, die Variable wird mit einem ":" angehangen und der Wert zur Initialisierung wird in runden Klammern übergeben. Auf diese Art und Weise können auch mehrere Variablen initialisiert werden.

Erzeugen von Instanzen der neuen Klassen[Bearbeiten]

Die neuen Klassen wurden jetzt definiert. Sie haben bisher noch keine Funktionen implementiert. Das wird später kommen. Die Frage ist jetzt noch, wie man diese Klassen wirklich nutzt. Nur vom Erstellen der Header und cpp Files werden die Klassen noch nicht verwendet. Man muss im bisherigen Coding eine Instanz der neuen Klassen anlegen, dies geschieht über den "new" Operator innerhalb des Konstruktors der Klasse Taschenrechner.

myAdd *addWidget = new myAdd(this);
mySub *subWidget = new mySub(this);

Hier wird ein Pointer addWidget/subWidget vom Typ myAdd/mySub angelegt (myAdd *addWidget). Der Pointer wird durch die Funktion myAdd() initialisiert. Dazu muss bei Klassen der Operator "new" verwendet werden im Gegensatz zu intrinsischen Datentypen, z.B. int a = 1, bei denen kein "new" verwendet wird. Die Funktion myAdd() ist der Konstruktor der Klasse myAdd. So, wie diese Klasse definiert ist, muss immer ein Parameter übergeben werden. Dieser Parameter ist der Pointer auf die Instanz der Klasse Taschenrechner. Diesen Pointer kann man im Konstruktor mit der Anweisung "this" übergeben.
Wichtig: Damit diese Funktionen in Taschenrechner.cpp bekannt sind, darf man nicht vergessen, die Header-Dateien der Klassen mittels #include einzubinden.

Bevor jetzt der Quellcode mittels make übersetzt werden kann, muss berücksichtigt werden, dass neue Dateien erzeugt wurden. Diese sind im Makefile nicht angegeben, so dass es beim Übersetzen zu einer Fehlermeldung kommen wird. Einfach mal ausprobieren. Damit das Makefile angepasst wird, nochmals

qmake -project
qmake

ausführen. Jetzt sollte das Programm mittels make übersetzt werden können. Es ist allerdings noch keine neue Funktionalität enthalten. Weiterhin wird eine Warnung ausgegeben, dass addWidget und subWidget nicht benutzt werden. Diese Warnung kann ignoriert werden, da wirklich mit den Objekten noch nichts passiert.


Finale Implementierung[Bearbeiten]

Nun müssen die Funktionen zum Addieren und Subtrahieren noch in die anderen Dateien verschoben werden. Als erstes können aus der Klasse Taschenrechner die Deklaration und Implementierung der Methode addAB() entfernt werden, da diese nicht mehr gebraucht werden.

Aus Taschenrechner.h muss nur die Deklaration der Funktion addAB() entfernt werden, ansonsten ändert sich nichts.

//--- 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();
private slots:
	void showAdd();
	void showSub();
	void slotClose();
};

#endif //TASCHENRECHNER_H
//--- Taschenrechner.h - end ---

In Taschenrechner.cpp müssen zwei Änderungen vorgenommen werden. Die Datei sieht nun wie folgt aus:

//--- Taschenrechner.cpp - start ---
#include "Taschenrechner.h"
#include "myAdd.h"
#include "mySub.h"

Taschenrechner::Taschenrechner(QMainWindow *parent) : QMainWindow(parent){	
	setupUi(this);

	myAdd *addWidget = new myAdd(this);
	mySub *subWidget = new mySub(this);

	InputA -> setText("0");
	InputB -> setText("0");
	InputA_2 -> setText("0");
	InputB_2 -> setText("0");



	connect(Calculate, SIGNAL(clicked()), addWidget, SLOT(addAB()));
	connect(Calculate_2, SIGNAL(clicked()), subWidget, SLOT(subAB()));
	connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
	connect(actionAdd, SIGNAL(triggered()), this, SLOT(showAdd()));
	connect(actionSub, SIGNAL(triggered()), this, SLOT(showSub()));
}

Taschenrechner::~Taschenrechner(){
}

void Taschenrechner::showAdd(){
	stackedWidget -> setCurrentIndex(0);
}

void Taschenrechner::showSub(){
	stackedWidget -> setCurrentIndex(1);
}
//--- Taschenrechner.cpp - end ---

Als erstes wurde die Implementierung der Funktion addAB() entfernt. Als nächsten Schritt müssen die connect Anweisungen angepasst werden. Diese sehen nun so aus:

connect(Calculate, SIGNAL (clicked()), addWidget, SLOT(addAB()));
connect(Calculate_2, SIGNAL (clicked()), subWidget, SLOT(subAB()));

Wenn der QPushButton "Calculate" das Signal clicked() erhält, ist der Empfänger nicht mehr "this" sondern das neu angelegte Objekt addWidget. Dies geschieht, da nun gerade in addWidget die Funktion zum Addieren enthalten ist. Der Slot heißt wieder addAB(), ist aber ein Slot der Klasse myAdd. Für die Subtraktion ist der connect String im wesentlichen identisch, der QPushButton hier hat einen anderen Namen, da es technisch ein anderer Knopf ist. Empfänger ist hier die Funktion subAB() der Instanz subWidget der Klasse mySub.

Die Anpassungen in myAdd.h und myAdd.cpp sind im wesentlichen wieder identisch wie die in mySub.h und mySub.cpp. myAdd.h hat nun folgendes Aussehen:

//--- myAdd.h - start ---
#ifndef MYADD_H
#define MYADD_H

#include <QWidget>

class Taschenrechner;

class myAdd : public QWidget{
	Q_OBJECT

Taschenrechner *myTaschenrechner;

public:
	myAdd (Taschenrechner*);
	~myAdd();
private slots:
	void addAB();
};

#endif //MYADD_H
//--- myAdd.h - end ---

Einzig die Deklaration des Slots addAB() ist eingefügt worden. In myAdd.cpp muss diese Funktion als Nächstes implementiert werden:

//--- myAdd.cpp - start ---
#include "myAdd.h"
#include "Taschenrechner.h"


myAdd::myAdd(Taschenrechner *TRechner):myTaschenrechner(TRechner){
}


myAdd::~myAdd(){
}

void myAdd::addAB(){
	double a, b;
	a = (myTaschenrechner -> InputA -> text()).toDouble();
	b = (myTaschenrechner -> InputB -> text()).toDouble();
	myTaschenrechner -> ResultC -> setText(QString("%1").arg(a+b,0,'f',4));	
}
//--- myAdd.cpp - end ---

Die Implementierung ist im Prinzip identisch mit der Implementierung, als dieser Slot noch zur Klasse Taschenrechner gehörte. Das einzige Problem, welches sich ergibt, ist, dass die ganzen UI Objekte zur Klasse Taschenrechner gehören und hier nicht bekannt sind. Die Instanz der Klasse Taschenrechner, die hier benutzt wird, wurde beim Erzeugen von addWidget angegeben und in myTaschenrechner gespeichert. Wenn man nun auf Objekt im UI zugreifen will, kann man dies über

myTaschenrechner -> UI Objekt -> Methode des UI Objekts

erreichen. Der Trick ist also nicht das UI Objekt direkt anzusprechen, sondern den "Umweg" über eine Instanz der Klassen Taschenrechner zu gehen, diese Instanz heißt hier myTaschenrechner.

Der Taschenrechner sollte sich nun übersetzen lassen und das Addieren und Subtrahieren unterstützen.