Einstieg in die Programmierung mit Qt: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
(veraltetes Bild geaendert)
 
(14 intermediate revisions by 7 users not shown)
Line 1: Line 1:
[[Category:Learning]] [toc align_right="yes" depth="3"]
[[Category:Learning]]


= Einstieg in die Programmierung mit Qt =
= Einstieg in die Programmierung mit Qt =


Willkommen in der Welt von Qt — dem Cross-Plattform-Werkzeug für graphische Benutzeroberflächen (Graphical User Interface = GUI). In dieser Einführung vermitteln wir grundlegende Kenntnisse zu Qt und entwicklen dazu eine einfache Notizzettel-Anwendung. Nach dem Studium dieser Anleitung sollten Sie in der Lage sein, sich eingehend mit unseren Übersichten und der Dokumentation unserer Programmierschnittstellen (APIs) zu beschäftigen, um die benötigten Informationen für die Entwicklung Ihrer Anwendung zu finden.
Willkommen in der Welt von Qt — dem Cross-Plattform-Werkzeug für graphische Benutzeroberflächen (Graphical User Interface = GUI). In dieser Einführung vermitteln wir grundlegende Kenntnisse zu Qt und entwickeln dazu eine einfache Notizzettel-Anwendung. Nach dem Studium dieser Anleitung sollten Sie in der Lage sein, sich eingehend mit unseren Übersichten und der Dokumentation unserer Programmierschnittstellen (APIs) zu beschäftigen, um die benötigten Informationen für die Entwicklung Ihrer Anwendung zu finden.
 
Den englischen Originaltext zu dieser Übersetzung finden Sie unter der URL http://doc.qt.io/qt-4.8/gettingstartedqt.html bzw. [http://doc.qt.io/qt-5/gettingstartedqt.html http://doc.qt.io/gettingstartedqt.html].
 
Diese Anleitung setzt voraus, dass Sie schon ein bisschen mit der Programmiersprache C++ vertraut sind. Für Leser ohne diese Vorkenntnisse gibt es alternativ diese Anleitung: http://stefanfrings.de/qt_lernen/index.html
 
=== Für einen aktuellen Zugang zu C++17 und Qt6 empfehlen sich folgende Seiten: ===
# Für Qt: https://doc.qt.io/qt-6/qtwidgets-tutorials-notepad-example.html (englisch)
# Für C++: https://de.wikipedia.org/wiki/C%2B%2B


Den englischen Originaltext zu dieser Übersetzung finden Sie unter der URL "http://doc.qt.nokia.com/gettingstartedqt.html":http://doc.qt.nokia.com/gettingstartedqt.html.


== Hallo Notizzettel ==
== Hallo Notizzettel ==
Line 11: Line 18:
In diesem ersten Beispiel werden wir einen einfachen Notizzettel (Notepad) erzeugen und als Fenster auf dem Schreibtisch anzeigen. Das stellt das einfachste Qt-Programm mit grafischer Oberfläche dar.
In diesem ersten Beispiel werden wir einen einfachen Notizzettel (Notepad) erzeugen und als Fenster auf dem Schreibtisch anzeigen. Das stellt das einfachste Qt-Programm mit grafischer Oberfläche dar.


[[Image:http://doc.qt.nokia.com/4.7/images/gs1.png|Notepad]]
[[File:Screenshot_Notepad_Example.png|center]]


Der Quelltext dazu:
Der Quelltext dazu:


<code><br />#include &lt;QApplication&amp;gt;<br />#include &lt;QTextEdit&amp;gt;
<syntaxhighlight lang="cpp">
#include <QApplication>
#include <QTextEdit>
 
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
 
QTextEdit textEdit;
textEdit.show();
 
return app.exec();
}
</syntaxhighlight>
 
Wir werden den Code Zeile für Zeile analysieren. In den ersten zwei Zeilen binden wir die Header-Dateien für [http://doc.qt.io/qt-5/qapplication.html QApplication] und [http://doc.qt.io/qt-5/qtextedit.html QTextEdit] ein, die zwei für dieses Beispiel benötigten Klassen. Für alle Qt-Klassen gibt es Header-Dateien, die genauso benannt sind wie die Klasse selbst.
 
Zeile 6 erzeugt ein [http://doc.qt.io/qt-5/qapplication.html QApplication]-Objekt. Es verwaltet die Ressourcen für die gesamte Anwendung und wird immer für eine Qt-Anwendung mit GUI benötigt. Das Objekt benötigt argc und argv, weil Qt-Anwendungen einige Kommandozeilen-Optionen auswerten können.
 
Zeile 8 erzeugt ein [http://doc.qt.io/qt-5/qtextedit.html QTextEdit]-Objekt. Ein Texteditor ist ein sichtbares Element der GUI. In Qt werden diese Elemente "Widgets" genannt (ein Kunstwort aus Window (Fenster) und Gadget (Vorrichtung)). Beispiele für weitere Widgets sind "scroll bars" (Rollbalken), "Labels" (Beschriftungen/Markierungen) und "radio buttons" (eine Auswahl von Optionen, von denen nur eine ausgewählt werden kann). Ein Widget kann als Container für andere Widgets benutzt werden, beispielsweise ein Dialog oder das Hauptfenster einer Anwendung.
 
Zeile 9 zeigt den Texteditor als eigenständiges Fenster auf dem Bildschirm an. Da Widgets auch als Container fungieren können (z.B. ein [http://doc.qt.io/qt-5/qmainwindow.html QMainWindow], das Werkzeugleisten, Menüs, eine Statuszeile und einige andere Widgets enthält), kann ein einzelnes Widget auch als einzelnes Fenster auf dem Bildschirm dargestellt werden. Ein Widget ist von Haus aus nicht sichtbar, erst die Funktion [http://doc.qt.io/qt-5/qwidget.html#show show()] macht es sichtbar.


int main(int argv, char **args)<br />{<br /> QApplication app(argv, args);
In Zeile 11 startet [http://doc.qt.io/qt-5/qapplication.html QApplication] seine Ereignis-Schleife ("event loop"). Wenn eine Qt-Anwendung läuft, werden verschiedene Ereignisse ("events") erzeugt und an die Widgets der Anwendung weitergereicht. Dabei handelt es sich z.B. um das Drücken einer Maustaste oder einen Tastaturanschlag. Wenn Sie in einem Texteditor Text eingeben, erhält er verschiedene "Taste gedrückt"-Ereignisse und reagiert darauf, indem er den eingegebenen Text auf dem Bildschirm darstellt.


QTextEdit textEdit;<br /> textEdit.show();
Um die Anwendung auszuführen, öffnen Sie eine Kommandozeilen-Umgebung (shell) und wechseln Sie in das Verzeichnis, in dem sich die .cpp-Datei ihres Programms befindet. Folgende Befehle erzeugen das Programm:


return app.exec&amp;amp;#40;&amp;#41;;<br />}<br /></code>
<syntaxhighlight lang="bash">
qmake -project
qmake
make
</syntaxhighlight>


Wir werden den Code Zeile für Zeile analysieren. In den ersten zwei Zeilen binden wir die Header-Dateien für &quot;QApplication&amp;quot;:http://doc.qt.nokia.com/qapplication.html und &quot;QTextEdit&amp;quot;:http://doc.qt.nokia.com/qtextedit.html ein, die zwei für dieses Beispiel benötigten Klassen. Für alle Qt-Klassen gibt es Header-Dateien, die genauso benannt sind wie die Klasse selbst.
Damit wird eine ausführbare Datei im Verzeichnis teil1 erzeugt (bitte beachten Sie: unter Windows müssen Sie ggf. nmake anstelle von make verwenden. Außerdem wird die ausführbare Datei in teil1\debug oder teil1\release erzeugt). qmake ist das Erstellungs-Werkzeug von Qt und benötigt eine Konfigurationsdatei. qmake erzeugt diese Datei für uns, wenn wir es mit dem Argument -project aufrufen. Zusammen mit der Konfigurationsdatei (mit der Endung .pro) generiert qmake ein Makefile, mit dem das Programm erstellt werden kann. Wir sehen uns später an, wie man .pro-Dateien selbst schreibt.


Zeile 6 erzeugt ein &quot;QApplication&amp;quot;:http://doc.qt.nokia.com/qapplication.html-Objekt. Es verwaltet die Ressourcen für die gesamte Anwendung und wird immer für eine Qt-Anwendung mit GUI benötigt. Das Objekt benötigt argv und args, weil Qt-Anwendungen einige Kommandozeilen-Optionen auswerten können.
=== Weitere Informationen ===
|''. Über… |''. Hier… |
|Widgets und Fenster-Geometrie | [http://doc.qt.io/qt-5/application-windows.html Window and Dialog Widget] |
|Ereignisse und ihre Behandlung | [http://doc.qt.io/qt-5/eventsandfilters.html The Event System] |


Zeile 8 erzeugt ein &quot;QTextEdit&amp;quot;:http://doc.qt.nokia.com/qtextedit.html-Objekt. Ein Texteditor ist ein sichtbares Element der GUI. In Qt werden diese Elemente &quot;Widgets&amp;quot; genannt (ein Kunstwort aus Window (Fenster) und Gadget (Vorrichtung)). Beispiele für weitere Widgets sind &quot;scroll bars&amp;quot; (Rollbalken), &quot;Labels&amp;quot; (Beschriftungen/Markierungen) und &quot;radio buttons&amp;quot; (eine Auswahl von Optionen, von denen nur eine ausgewählt werden kann). Ein Widget kann als Container für andere Widgets benutzt werden, beispielsweise ein Dialog oder das Hauptfenster einer Anwendung.
== Einen Knopf zum Beenden hinzufügen ==
Eine richtige Anwendung benötigt üblicherweise mehr als ein Widget. Wir werden jetzt einen [http://doc.qt.io/qt-5/qpushbutton.html QPushButton] unterhalb des Notizzettels einfügen. Die Anwendung wird beendet, wenn der Knopf gedrückt wird, d.h. mit der Maus auf ihn geklickt wird.


Zeile 9 zeigt den Texteditor als eigenständiges Fenster auf dem Bildschirm an. Da Widgets auch als Container fungieren können (z.B. ein &quot;QMainWindow&amp;quot;:http://doc.qt.nokia.com/qmainwindow.html, das Werkzeugleisten, Menüs, eine Statuszeile und einige andere Widgets enthält), kann ein einzelnes Widget auch als einzelnes Fenster auf dem Bildschirm dargestellt werden. Ein Widget ist von Haus aus nicht sichtbar, erst die Funktion &quot;show()&quot;:http://doc.qt.nokia.com/qwidget.html#show macht es sichtbar.
<gallery>
File:/qt-4.8/images/gs2.png|http://doc.qt.io/qt-4.8/images/gs2.png
</gallery>


In Zeile 11 startet &quot;QApplication&amp;quot;:http://doc.qt.nokia.com/qapplication.html seine Ereignis-Schleife (&quot;event loop&amp;quot;). Wenn eine Qt-Anwendung läuft, werden verschiedene Ereignisse (&quot;events&amp;quot;) erzeugt und an die Widgets der Anwendung weitergereicht. Dabei handelt es sich z.B. um das Drücken einer Maustaste oder einen Tastaturanschlag. Wenn Sie in einem Texteditor Text eingeben, erhält er verschiedene &quot;Taste gedrückt&amp;quot;-Ereignisse und reagiert darauf, indem er den eingegebenen Text auf dem Bildschirm darstellt.
Sehen wir uns den Code dazu an:


Um die Anwendung auszuführen, öffnen Sie eine Kommanndozeilen-Umgebung (shell) und wechseln Sie in das Verzeichnis, in dem sich die .cpp-Datei ihres Programms befindet. Folgende Befehle erzeugen das Programm:
<syntaxhighlight lang="cpp">
#include <QApplication>
#include <QLayout>
#include <QTextEdit>
#include <QPushButton>


<code><br />qmake -project<br />qmake<br />make<br /></code>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);


Damit wird eine ausführbare Datei im Verzeichnis teil1 erzeugt (bitte beachten Sie: unter Windows müssen Sie ggf. nmake anstelle von make verwenden. Außerdem wird die ausführbare Datei in teil1\debug oder teil1\release erzeugt). qmake ist das Erstellungs-Werkzeug von Qt und benötigt eine Konfigurationsdatei. qmake erzeugt diese Datei für uns, wenn wir es mit dem Argument <s>project aufrufen. Zusammen mit der Konfigurationsdatei (mit der Endung .pro) generiert qmake ein Makefile, mit dem das Programm erstellt werden kann. Wir sehen uns später an, wie man .pro-Dateien selbst schreibt.
QTextEdit *textEdit = new QTextEdit;
<br />h3. Weitere Informationen
QPushButton *quitButton = new QPushButton("&Quit");
<br />|''. Über… |''. Hier… |<br />|Widgets und Fenster-Geometrie | &quot;Window and Dialog Widget&amp;quot;:http://doc.qt.nokia.com/application-windows.html |<br />|Ereignisse und ihre Behandlung | &quot;The Event System&amp;quot;:http://doc.qt.nokia.com/eventsandfilters.html |
 
<br />h2. Einen Knopf zum Beenden hinzufügen
QObject::connect(quitButton, SIGNAL (clicked()), qApp, SLOT (quit()));
<br />Eine richtige Anwendung benötigt üblicherweise mehr als ein Widget. Wir werden jetzt einen &quot;QPushButton&amp;quot;:http://doc.qt.nokia.com/qpushbutton.html unterhalb des Notizzettels einfügen. Die Anwendung wird beendet, wenn der Knopf gedrückt wird, d.h. mit der Maus auf ihn gecklickt wird.
 
<br />[[Image:http://doc.qt.nokia.com/4.7/images/gs2.png|http://doc.qt.nokia.com/4.7/images/gs2.png]]
QVBoxLayout *layout = new QVBoxLayout;
<br />Sehen wir uns den Code dazu an:
layout->addWidget(textEdit);
<br /><code><br />#include &lt;QtGui&amp;gt;
layout->addWidget(quitButton);
<br />int main(int argv, char **args)<br />{<br /> QApplication app(argv, args);
 
<br /> QTextEdit textEdit;<br /> QPushButton quitButton(&quot;Quit&amp;quot;);
QWidget window;
<br /> QObject::connect(&amp;quitButton, SIGNAL (clicked()), qApp, SLOT (quit()));
window.setLayout(layout);
<br /> QVBoxLayout layout;<br /> layout.addWidget(&amp;textEdit);<br /> layout.addWidget(&amp;quitButton);
 
<br /> QWidget window;<br /> window.setLayout(&amp;layout);
window.show();
<br /> window.show();
 
<br /> return app.exec&amp;amp;#40;&amp;#41;;<br />}<br /></code>
return app.exec();
<br />Zeile 1 bindet &quot;QtGui&amp;quot;:http://doc.qt.nokia.com/qtgui.html ein. Diese Header-Datei enthält alle GUI-Klassen von Qt.
}
<br />In Zeile 10 verwenden wir den Signal-Slot-Mechanismus von Qt, um die Anwendung zu beenden sobald der Beenden-Knopf gedrückt wurde. Ein &quot;Slot&amp;quot; ist eine spezielle Funktion, die zur Laufzeit über ihren Namen (als reine Zeichenkette) aufgerufen werden kann. Ein &quot;Signal&amp;quot; ist ebenfalls eine spezielle Funktion. Wird sie aufgerufen, werden wiederum die bei ihr registrierten Slots ausgeführt. Wir nennen das &quot;den Slot mit dem Signal verbinden&amp;quot; (&quot;connect&amp;quot;) bzw. das Signal aussenden (&quot;emit&amp;quot;).
</syntaxhighlight>
<br />&quot;quit()&quot;:http://doc.qt.nokia.com/qcoreapplication.html#quit ist ein Slot in &quot;QApplication&amp;quot;:http://doc.qt.nokia.com/qapplication.html und beendet die Anwendung. &quot;clicked()&quot;:http://doc.qt.nokia.com/qabstractbutton.html#clicked ist ein Signal, das von &quot;QPushButton&amp;quot;:http://doc.qt.nokia.com/qpushbutton.html ausgesendet wird, wenn er gedrückt wird. Die statische Funktion &quot;QObject::connect()&quot;:http://doc.qt.nokia.com/qobject.html#connect dient dazu, den Slot mit dem Signal zu verbinden. SIGNAL () und SLOT() sind zwei Makros, die die Funktions-Signaturen für die zu verbindenden Signale und Slots erhalten. Außerdem müssen Zeiger auf die Objekte angegeben werden, die das Signal aussenden und empfangen.  
 
<br />Zeile 12 erzeugt ein &quot;QVBoxLayout&amp;quot;:http://doc.qt.nokia.com/qvboxlayout.html. Wie bereits erwähnt, können Widgets andere Widgets enthalten. Es ist möglich, die Geometrie (Größe und Position) eines Unter-Widgets (Kind-Widget) direkt zu setzen. Üblicherweise ist es aber einfacher, eine Layout-Klasse zu verwenden, die die Geometrie der Kinder eines Widgets verwaltet. Ein &quot;QVBoxLayout&amp;quot;:http://doc.qt.nokia.com/qvboxlayout.html beispielsweise ordnet die Kinder in einer Zeile nebeneinander an.
In Zeile 13 verwenden wir den Signal-Slot-Mechanismus von Qt, um die Anwendung zu beenden sobald der Beenden-Knopf gedrückt wurde. Ein "Slot" ist eine spezielle Funktion, die zur Laufzeit über ihren Namen (als reine Zeichenkette) aufgerufen werden kann. Ein "Signal" ist ebenfalls eine spezielle Funktion. Wird sie aufgerufen, werden wiederum die bei ihr registrierten Slots ausgeführt. Wir nennen das "den Slot mit dem Signal verbinden" ("connect") bzw. das Signal aussenden ("emit").
<br />Zeile 13 und 14 fügen den Editor und den Knopf in das Layout ein. In Zeile 17 weisen wir das Layout einem Widget zu.
 
<br />h3. Weitere Informationen
[http://doc.qt.io/qt-5/qcoreapplication.html#quit quit()] ist ein Slot in [http://doc.qt.io/qt-5/qapplication.html QApplication] und beendet die Anwendung. [http://doc.qt.io/qt-5/qabstractbutton.html#clicked clicked()] ist ein Signal, das von [http://doc.qt.io/qt-5/qpushbutton.html QPushButton] ausgesendet wird, wenn er gedrückt wird. Die statische Funktion [http://doc.qt.io/qt-5/qobject.html#connect QObject::connect()] dient dazu, den Slot mit dem Signal zu verbinden. SIGNAL () und SLOT() sind zwei Makros, die die Funktions-Signaturen für die zu verbindendenden Signals und Slots erhalten. Außerdem müssen Zeiger auf die Objekte angegeben werden, die das Signal aussenden und empfangen.  
<br />|''. Über… |''. Hier… |<br />|Signale und Slots | &quot;Signals &amp; Slots&amp;quot;:http://doc.qt.nokia.com/signalsandslots.html|<br />|Layouts | &quot;Layout Management&amp;quot;:http://doc.qt.nokia.com/layout.html, &quot;Widgets and Layouts&amp;quot;:http://doc.qt.nokia.com/widgets-and-layouts.html, &quot;Layout Examples&amp;quot;:http://doc.qt.nokia.com/examples-layouts.html |<br />|Die von Qt mitgelieferten Widgets | &quot;Qt Widget Gallery&amp;quot;:http://doc.qt.nokia.com/gallery.html, &quot;Widget Examples&amp;quot;:http://doc.qt.nokia.com/examples-widgets.html |
 
<br />h2. Von QWidget abgeleitete Klassen
In Zeile 15 wird ein [http://doc.qt.io/qt-5/qvboxlayout.html QVBoxLayout] erzeugt. Wie bereits erwähnt, können Widgets andere Widgets enthalten. Es ist möglich, die Geometrie (Größe und Position) eines Unter-Widgets (Kind-Widget) direkt zu setzen. Üblicherweise ist es aber einfacher, eine Layout-Klasse zu verwenden, die die Geometrie der Kinder eines Widgets verwaltet. Ein [http://doc.qt.io/qt-5/qvboxlayout.html QVBoxLayout] beispielsweise ordnet die Kinder in einer Zeile nebeneinander an.
<br />Wenn der Benutzer die Anwendung beenden möchte, wollen Sie ihn möglicherweise vorher fragen, ob er/sie das wirklich will. In diesem Beispiel erstellen wir eine von &quot;QWidget&amp;quot;:http://doc.qt.nokia.com/qwidget.html abgeleitete Klasse und fügen einen Slot hinzu, der mit dem Beenden-Knopf verbunden wird.
 
<br />[[Image:http://doc.qt.nokia.com/4.7/images/gs3.png|http://doc.qt.nokia.com/4.7/images/gs3.png]]
In Zeile 16 und 17 wird der Editor und der Knopf in das Layout eingefügt. In Zeile 20 weisen wir das Layout einem Widget zu.
<br />Sehen wir uns den Quelltext an:
 
<br /><code><br />class Notepad : public QWidget<br />{<br /> Q_OBJECT
=== Weitere Informationen ===
<br />public:<br /> Notepad();
|''. Über… |''. Hier… |
<br />private slots:<br /> void quit();
|Signale und Slots | [http://doc.qt.io/qt-5/signalsandslots.html| Signals & Slots]
<br />private:<br /> QTextEdit *textEdit;<br /> QPushButton *quitButton;<br />};<br /></code>
|Layouts | [http://doc.qt.io/qt-5/layout.html Layout Management], [http://doc.qt.io/qt-5/widgets-and-layouts.html Widgets and Layouts], [http://doc.qt.io/qt-5/examples-layouts.html Layout Examples] |
<br />Das Makro &quot;Q_OBJECT&amp;quot;:http://doc.qt.nokia.com/qobject.html#Q_OBJECT muss als erstes in der Definition der Klasse auftauchen. Es markiert unsere Klasse als &quot;QObject&amp;quot;:http://doc.qt.nokia.com/qobject.html</s> sie muss natürlich auch von &quot;QObject&amp;quot;:http://doc.qt.nokia.com/qobject.html (oder einer davon abgeleiteten Klasse) erben. &quot;QObject&amp;quot;:http://doc.qt.nokia.com/qobject.html fügt einer normalen C++-Klasse noch einige weitere Fähigkeiten hinzu. Insbesondere können die Namen der Klasse und der Slots zur Laufzeit abgefragt werden. Außerdem ist es möglich, die Parameter eines Slots abzufragen und die dazugehörige Funktion aufzurufen.
|Die von Qt mitgelieferten Widgets | [http://doc.qt.io/qt-5/gallery.html Qt Widget Gallery], [http://doc.qt.io/qt-5/examples-widgets.html Widget Examples] |
 
== Von QWidget abgeleitete Klassen ==
Wenn der Benutzer die Anwendung beenden möchte, wollen Sie ihn möglicherweise vorher fragen, ob er/sie das wirklich will. In diesem Beispiel erstellen wir eine von [http://doc.qt.io/qt-5/qwidget.html QWidget] abgeleitete Klasse und fügen einen Slot hinzu, der mit dem Beenden-Knopf verbunden wird.
 
<gallery>
File:/qt-4.8/images/gs3.png|http://doc.qt.io/qt-4.8/images/gs3.png
</gallery>
 
Sehen wir uns den Quelltext an:
 
<syntaxhighlight lang="cpp">
#include <QWidget>
#include <QTextEdit>
#include <QPushButton>
 
class Notepad : public QWidget
{
Q_OBJECT
 
public:
Notepad();
 
private slots:
void quit();
 
private:
QTextEdit   *textEdit;
QPushButton *quitButton;
};
</syntaxhighlight>
 
Das Makro [http://doc.qt.io/qt-5/qobject.html#Q_OBJECT Q_OBJECT] muss als erstes in der Definition der Klasse auftauchen. Es markiert unsere Klasse als [http://doc.qt.io/qt-5/qobject.html QObject]- sie muss natürlich auch von [http://doc.qt.io/qt-5/qobject.html QObject] (oder einer davon abgeleiteten Klasse) erben. [http://doc.qt.io/qt-5/qobject.html QObject] fügt einer normalen C++-Klasse noch einige weitere Fähigkeiten hinzu. Insbesondere können die Namen der Klasse und der Slots zur Laufzeit abgefragt werden. Außerdem ist es möglich, die Parameter eines Slots abzufragen und die dazugehörige Funktion aufzurufen.


In Zeile 13 wird der Slot quit() deklariert. Das geht mit dem Makro slots ganz einfach. Der Slot quit() kann nun mit einem Signal mit passender Signatur (jedes Signal, das keinen Parameter hat) verbunden werden.
In Zeile 13 wird der Slot quit() deklariert. Das geht mit dem Makro slots ganz einfach. Der Slot quit() kann nun mit einem Signal mit passender Signatur (jedes Signal, das keinen Parameter hat) verbunden werden.
Line 73: Line 149:
Die Benutzeroberfläche erzeugen wir nicht mehr in der Funktion main(), sondern im Konstruktor von Notepad. Dort verbinden wir auch den Slot.
Die Benutzeroberfläche erzeugen wir nicht mehr in der Funktion main(), sondern im Konstruktor von Notepad. Dort verbinden wir auch den Slot.


<code><br />Notepad::Notepad()<br />{<br /> textEdit = new QTextEdit;<br /> quitButton = new QPushButton(tr(&quot;Quit&amp;quot;));
<syntaxhighlight lang="cpp">
#include "notepad.h"
#include <QApplication>
 
Notepad::Notepad()
{
textEdit   = new QTextEdit;
quitButton = new QPushButton(tr("Quit"));
 
connect(quitButton, SIGNAL (clicked()), this, SLOT (quit()));
 
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(textEdit);
layout->addWidget(quitButton);
 
setLayout(layout);
 
setWindowTitle(tr("Notepad"));
}
 
void Notepad::quit()
{
qApp->quit();
}
 
</syntaxhighlight>
 
Wie sie in der Definition der Klasse gesehen haben, benutzen wir Zeiger auf unsere [http://doc.qt.io/qt-5/qobject.html-Objekte QObject] (textEdit und quitButton). Grundsätzlich gilt, dass Sie Instanzen von [http://doc.qt.io/qt-5/qobject.html QObject] (und davon abgeleiteten Klassen) auf dem Heap erzeugen und sie niemals kopieren sollten.
 
Von jetzt an kapseln wir die für den Benutzer sichtbaren Zeichenketten in der Funktion [http://doc.qt.io/qt-5/qobject.html#tr tr()]. Das ist notwendig, wenn Sie die Anwendung in mehreren Sprachen (z.B. Deutsch und Englisch) zur Verfügung stellen möchten. Auf Einzelheiten gehen wir an dieser Stelle nicht ein. Mehr erfahren Sie über den Link zu Qt Linguist in der Tabelle mit weiteren Informationen.
 
=== Weitere Informationen ===
|''. Über… |''. Hier… |
|tr() und "Internationalisierung" |[http://doc.qt.io/qt-5/linguist-manual.html Qt Linguist Manual], [http://doc.qt.io/qt-5/i18n-source-translation.html Writing Source Code for Translation], [http://doc.qt.io/qt-5/linguist-hellotr.html Hello tr() Example], [http://doc.qt.io/qt-5/internationalization.html Internationalization with Qt] |
| und das Objekt-Model von Qt |[hhttp://doc.qt.io/qt-5//object.html Object Model] |
|qmake und das Erstellungs-System von Qt|[http://doc.qt.io/qt-5/qmake-manual.html qmake Manual] |
 
=== Eine .pro-Datei erzeugen ===
Für dieses Beispiel werden wir eine eigene .pro-Datei erstellen, und nicht die Option -project von qmake.
 
<syntaxhighlight lang="make">
HEADERS = notepad.h
SOURCES = notepad.cpp  main.cpp
</syntaxhighlight>
 
Folgende Befehle erzeugen das Programm
 
<syntaxhighlight lang="bash">
qmake
make
</syntaxhighlight>
 
== Ein QMainWindow verwenden ==
Viele Anwendungen werden vom Einsatz eines [http://doc.qt.io/qt-5/qmainwindow.html QMainWindow] (Hauptfenster) profitieren. Es hat sein eigenes Layout, dem Sie eine Menüzeile, Dock Widgets (es gibt keine vernünftige deutsche Übersetzung hierfür), Werkzeugleisten und eine Status-Zeile hinzufügen können. [http://doc.qt.io/qt-5/qmainwindow.html QMainWindow] hat einen zentralen Bereich, der von jeder Art von Widget ausgefüllt werden kann. In unserem Beispiel werden wir hier den Texteditor platzieren.
 
<gallery>
File:/qt-4.8/images/gs4.png|http://doc.qt.io/qt-4.8/images/gs4.png
</gallery>
 
Sehen wir uns die neue Definition der Klasse Notepad an.
 
<syntaxhighlight lang="cpp">
#include <QMainWindow>
#include <QTextEdit>
#include <QAction>
#include <QMenu>
 
class Notepad : public QMainWindow
{
Q_OBJECT
 
public:
Notepad();
 
private slots:
void open();
void save();
void quit();
 
private:
QTextEdit *textEdit;
 
QAction *openAction;
QAction *saveAction;
QAction *exitAction;


connect(quitButton, SIGNAL (clicked()), this, SLOT (quit()));
QMenu *fileMenu;
};
</syntaxhighlight>


QVBoxLayout '''layout = new QVBoxLayout;<br /> layout-&gt;addWidget(textEdit);<br /> layout-&gt;addWidget(quitButton);
Wir fügen zwei weitere Slots hinzu, mit denen ein Dokument gelesen und gespeichert werden kann. Wir werden sie im folgenden Abschnitt implementieren.
<br /> setLayout(layout);
<br /> setWindowTitle(tr(&quot;Notepad&amp;quot;));<br />}<br /></code>
<br />Wie sie in der Definition der Klasse gesehen haben, benutzen wir Zeiger auf unsere &quot;QObject&amp;quot;:http://doc.qt.nokia.com/qobject.html-Objekte (textEdit und quitButton). Grundsätzlich gilt, dass Sie Instanzen von &quot;QObject&amp;quot;:http://doc.qt.nokia.com/qobject.html (und davon abgeleiteten Klassen) auf dem Heap erzeugen und sie niemals kopieren sollten.
<br />Von jetzt an kapseln wir die für den Benutzer sichtbaren Zeichenketten in der Funktion &quot;tr()&quot;:http://doc.qt.nokia.com/qobject.html#tr. Das ist notwendig, wenn Sie die Anwendung in mehreren Sprachen (z.B. Deutsch und Englisch) zur Verfügung stellen möchten. Auf Einzelheiten gehen wir an dieser Stelle nicht ein. Mehr erfahren Sie über den Link zu Qt Linguist in der Tabelle mit weiteren Informationen.
<br />h3. Weitere Informationen
<br />|''. Über… |''. Hier… |<br />|tr() und &quot;Internationalisierung&amp;quot; |&quot;Qt Linguist Manual&amp;quot;:http://doc.qt.nokia.com/linguist-manual.html, &quot;Writing Source Code for Translation&amp;quot;:http://doc.qt.nokia.com/i18n-source-translation.html, &quot;Hello tr() Example&amp;quot;:http://doc.qt.nokia.com/linguist-hellotr.html, &quot;Internationalization with Qt&amp;quot;:http://doc.qt.nokia.com/internationalization.html |<br />|&quot;QObjects&amp;quot;:http://doc.qt.nokia.com/qtwebkit-bridge.html#qobjects und das Objekt-Model von Qt |&quot;Object Model&amp;quot;:http://doc.qt.nokia.com/object.html |<br />|qmake und das Erstellungs-System von Qt|&quot;qmake Manual&amp;quot;:http://doc.qt.nokia.com/qmake-manual.html |
<br />h3. Eine .pro-Datei erzeugen
<br />Für dieses Beispiel werden wir eine eigene .pro-Datei erstellen, und nicht die Option <s>project von qmake.
<br /><code><br />HEADERS = notepad.h<br />SOURCES = notepad.cpp  main.cpp<br /></code>
<br />Folgende Befehle erzeugen das Programm
<br /><code><br />qmake<br />make<br /></code>
<br />h2. Ein QMainWindow verwenden
<br />Viele Anwendungen werden vom Einsatz eines &quot;QMainWindow&amp;quot;:http://doc.qt.nokia.com/qmainwindow.html (Hauptfenster) profitieren. Es hat sein eigenes Layout, dem Sie eine Menüzeile, Dock Widgets (es gibt keine vernünftige deutsche Übersetzung hierfür), Werkzeugleisten und eine Status-Zeile hinzufügen können. &quot;QMainWindow&amp;quot;:http://doc.qt.nokia.com/qmainwindow.html hat einen zentralen Bereich, der von jeder Art von Widget ausgefüllt werden kann. In unserem Beispiel werden wir hier den Texteditor platzieren.
<br />[[Image:http://doc.qt.nokia.com/4.7/images/gs4.png|http://doc.qt.nokia.com/4.7/images/gs4.png]]
<br />Sehen wir uns die neue Definition der Klasse Notepad an.
<br /><code><br />#include &lt;QtGui&amp;gt;
<br />class Notepad : public QMainWindow<br />{<br /> Q_OBJECT
<br />public:<br /> Notepad();
<br />private slots:<br /> void open();<br /> void save();<br /> void quit();
<br />private:<br /> QTextEdit *textEdit;
<br /> QAction *openAction;<br /> QAction *saveAction;<br /> QAction *exitAction;
<br /> QMenu '''fileMenu;<br />};<br /></code>
<br />Wir fügen zwei weitere Slots hinzu, mit denen ein Dokument gelesen und gespeichert werden kann. Wir werden sie im folgenden Abschnitt implementieren.
<br />In einem Hauptfenster muss der selbe Slot oftmals von mehreren verschiedenen Widgets aus aufgerufen werden. Dazu gehören beispielsweise Menüpunkte und Knöpfe in einer Werkzeugleiste. Um diese Aufgabe zu erleichtern, gibt es in Qt die Klasse &quot;QAction&amp;quot;:http://doc.qt.nokia.com/qaction.html, die mit mehreren Widgets verknüpft und mit einem Slot verbunden werden kann. So erzeugen beispielsweise sowohl &quot;QMenu&amp;quot;:http://doc.qt.nokia.com/qmenu.html als auch &quot;QToolBar&amp;quot;:http://doc.qt.nokia.com/qtoolbar.html Menüpunkte und Werkzeug-Knöpfe aus den selben &quot;QActions&amp;quot;:http://doc.qt.nokia.com/qaction.html. Wie das funktioniert werden wir in Kürze sehen.
<br />Wie vorhin bauen wir die Benutzeroberfläche im Konstruktor von Notepad zusammen.
<br /><code><br />Notepad::Notepad()<br />{<br /> saveAction = new QAction(tr(&quot;&amp;Open&amp;quot;), this);<br /> saveAction = new QAction(tr(&quot;&amp;Save&amp;quot;), this);<br /> exitAction = new QAction(tr(&quot;E&amp;amp;xit&amp;quot;), this);
<br /> connect(openAction, SIGNAL (triggered()), this, SLOT (open()));<br /> connect(saveAction, SIGNAL (triggered()), this, SLOT (save()));<br /> connect(exitAction, SIGNAL (triggered()), qApp, SLOT (quit()));
<br /> fileMenu = menuBar()<s>&gt;addMenu(tr(&quot;&amp;File&amp;quot;));<br /> fileMenu</s>&gt;addAction(openAction);<br /> fileMenu-&gt;addAction(saveAction);<br /> fileMenu-&gt;addSeparator();<br /> fileMenu-&gt;addAction(exitAction);
<br /> textEdit = new QTextEdit;<br /> setCentralWidget(textEdit);
<br /> setWindowTitle(tr(&quot;Notepad&amp;quot;));<br />}<br /></code>
<br />&quot;QActions&amp;quot;:http://doc.qt.nokia.com/qaction.html werden mit einem Text erzeugt, der auf/mit den Widgets dargestellt werden soll, denen wir sie zuordnen (in unserem Fall Menüpunkte). Wenn wir sie auch in einer Werkzeugleiste verwenden wollen, könnten wir ihnen noch &quot;Icons&amp;quot;:http://doc.qt.nokia.com/qicon.html (kleine Bilder) zuweisen.
<br />Wenn jetzt ein Menüpunkt aufgerufen wird, so wird die dazugehörige Aktion getriggert und der mit ihr verbundene Slot wird aufgerufen.
<br />h3. Weitere Informationen
<br />|''. Über… |''. Hier… |<br />| Hauptfenster und die zugehörigen Klassen | &quot;Application Main Window&amp;quot;:http://doc.qt.nokia.com/mainwindow.html, &quot;Main Window Examples&amp;quot;:http://doc.qt.nokia.com/examples-mainwindow.html |<br />| MDI-Anwendungen | &quot;QMdiArea&amp;quot;:http://doc.qt.nokia.com/qmdiarea.html, &quot;MDI Example&amp;quot;:http://doc.qt.nokia.com/mainwindows-mdi.html |


<br />h2. Speichern und einlesen
In einem Hauptfenster muss derselbe Slot oftmals von mehreren verschiedenen Widgets aus aufgerufen werden. Dazu gehören beispielsweise Menüpunkte und Knöpfe in einer Werkzeugleiste. Um diese Aufgabe zu erleichtern, gibt es in Qt die Klasse [http://doc.qt.io/qt-5/qaction.html QAction], die mit mehreren Widgets verknüpft und mit einem Slot verbunden werden kann. So erzeugen beispielsweise sowohl [http://doc.qt.io/qt-5/qmenu.html QMenu] als auch [http://doc.qt.io/qt-5/qtoolbar.html QToolBar] Menüpunkte und Werkzeug-Knöpfe aus denselben [http://doc.qt.io/qt-5/qaction.html QActions]. Wie das funktioniert werden wir in Kürze sehen.
<br />In diesem Beispiel implementieren wir die beiden Slots open() und save(), die wir zuvor ergänzt haben.
 
<br />[[Image:http://doc.qt.nokia.com/4.7/images/gs5.png|http://doc.qt.nokia.com/4.7/images/gs5.png]]
Wie vorhin bauen wir die Benutzeroberfläche im Konstruktor von Notepad zusammen.
<br />Wir kümmern uns zuerst um den Slot open() zum öffnen eines Dokuments:
 
<br /><code><br />void Notepad::open()<br />{<br /> QString fileName = QFileDialog::getOpenFileName(this, tr(&quot;Open File&amp;quot;), &quot;&quot;,<br /> tr(&quot;Text Files ('''.txt);;C++ Files ('''.cpp'''.h)&quot;));
<syntaxhighlight lang="cpp">
<br /> if (fileName != &quot;&quot;) {<br /> QFile file&amp;amp;#40;fileName&amp;amp;#41;;<br /> if (!file.open(QIODevice::ReadOnly)) {<br /> QMessageBox::critical(this, tr(&quot;Error&amp;quot;),<br /> tr(&quot;Could not open file&amp;quot;));<br /> return;<br /> }<br /> QString contents = file.readAll().constData();<br /> textEdit</s>&gt;setPlainText(contents);<br /> file.close();<br /> }<br />}<br /></code>
#include "notepad.h"
<br />Als erstes müssen wir den Benutzer nach der zu öffnenden Datei fragen. Qt stellt einen &quot;QFileDialog&amp;quot;:http://doc.qt.nokia.com/qfiledialog.html zur Verfügung, mit dem ein Benutzer eine Datei auswählen kann. Die Abbildung oben zeigt den Dialog wie er unter Kubuntu aussieht. Die statische Funktion &quot;getOpenFileName()&quot;:http://doc.qt.nokia.com/qfiledialog.html#getOpenFileName zeigt einen modalen Dialog zur Dateiauswahl und kehrt erst zurück, wenn der Benutzer eine Datei ausgewählt oder die Aktion abgebrochen hat. Sie liefert den Pfad zur ausgwählten Datei zurück, oder eine leere Zeichenkette, falls der Benutzer die Auswahl abgebrochen hat.
#include <QMenuBar>
<br />Haben wir einen Dateinamen erhalten, versuchen wir die Datei mit &quot;open()&quot;:http://doc.qt.nokia.com/qiodevice.html#open zu öffnen. Die Funktion liefert true zurück, wenn das erfolgreich war. Die Fehlerbehandlung wollen wir an dieser Stelle nicht weiter vertiefen, mehr erfahren Sie in den Links im Kasten &quot;Weitere Informationen&amp;quot;. Falls die Datei nicht geöffnet werden kann, benutzen wir eine &quot;QMessageBox&amp;quot;:http://doc.qt.nokia.com/qmessagebox.html um einen Dialog mit Fehlermeldung anzuzeigen (Weitere Details finden Sie in der Klassenbeschreibung von &quot;QMessageBox&amp;quot;:http://doc.qt.nokia.com/qmessagebox.html).
 
<br />Das eigentliche Einlesen der Daten ist einfach, wir benutzen die Funktion &quot;readAll()&quot;:http://doc.qt.nokia.com/qiodevice.html#readAll, die den gesamten Inhalt der Datei in einem &quot;QByteArray&amp;quot;:http://doc.qt.nokia.com/qbytearray.html zurückliefert. Die Funktion &quot;constData()&quot;:http://doc.qt.nokia.com/qbytearray.html#constData liefert die Daten des Arrays als const char''' zurück, was wiederum zur Initialisierung im Konstruktor eines &quot;QString&amp;quot;:http://doc.qt.nokia.com/qstring.html verwendet werden kann. Der Inhalt kann damit im Texteditor dargestellt werden. Wir schließen die Datei mit &quot;close()&quot;:http://doc.qt.nokia.com/qiodevice.html#close, um die belegten Ressourcen (z.B. den File-Descriptor) an das Betriebssystem zurückzugeben.
Notepad::Notepad()
{
openAction = new QAction(tr("&Open"), this);
saveAction = new QAction(tr("&Save"), this);
exitAction = new QAction(tr("E&xit"), this);
 
connect(openAction, SIGNAL (triggered()), this, SLOT (open()));
connect(saveAction, SIGNAL (triggered()), this, SLOT (save()));
connect(exitAction, SIGNAL (triggered()), this, SLOT (quit()));
 
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAction);
fileMenu->addAction(saveAction);
fileMenu->addSeparator();
fileMenu->addAction(exitAction);
 
textEdit = new QTextEdit;
setCentralWidget(textEdit);
 
setWindowTitle(tr("Notepad"));
}
</syntaxhighlight>
 
[http://doc.qt.io/qt-5/qaction.html QActions] werden mit einem Text erzeugt, der auf/mit den Widgets dargestellt werden soll, denen wir sie zuordnen (in unserem Fall Menüpunkte). Wenn wir sie auch in einer Werkzeugleiste verwenden wollen, könnten wir ihnen noch [http://doc.qt.io/qt-5/qicon.html Icons] (kleine Bilder) zuweisen.
 
Wenn jetzt ein Menüpunkt aufgerufen wird, so wird die dazugehörige Aktion getriggert und der mit ihr verbundene Slot wird aufgerufen.
 
=== Weitere Informationen ===
|''. Über… |''. Hier… |
| Hauptfenster und die zugehörigen Klassen | [http://doc.qt.io/qt-5/mainwindow.html Application Main Window], [http://doc.qt.io/qt-5/examples-mainwindow.html Main Window Examples] |
| MDI-Anwendungen | [http://doc.qt.io/qt-5/qmdiarea.html QMdiArea], [http://doc.qt.io/qt-5/mainwindows-mdi.html MDI Example] |
 
== Speichern und einlesen ==
In diesem Beispiel implementieren wir die beiden Slots open() und save(), die wir zuvor ergänzt haben.
 
<gallery>
File:/qt-4.8/images/gs5.png|http://doc.qt.io/qt-4.8/images/gs5.png
</gallery>
 
Wir kümmern uns zuerst um den Slot open() zum Öffnen eines Dokuments:
 
<syntaxhighlight lang="cpp">
#include <QFileDialog>
#include <QMessageBox>
 
void Notepad::open()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
  tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
 
if (fileName != "") {
  QFile file(fileName);
 
  if (!file.open(QIODevice::ReadOnly)) {
  QMessageBox::critical(this, tr("Error"),
  tr("Could not open file"));
  return;
  }
 
  QString contents = file.readAll().constData();
  textEdit->setPlainText(contents);
  file.close();
}
}
</syntaxhighlight>
 
Als erstes müssen wir den Benutzer nach der zu öffnenden Datei fragen. Qt stellt einen [http://doc.qt.io/qt-5/qfiledialog.html QFileDialog] zur Verfügung, mit dem ein Benutzer eine Datei auswählen kann. Die Abbildung oben zeigt den Dialog wie er unter Kubuntu aussieht. Die statische Funktion [http://doc.qt.io/qt-5/qfiledialog.html#getOpenFileName getOpenFileName()] zeigt einen modalen Dialog zur Dateiauswahl und kehrt erst zurück, wenn der Benutzer eine Datei ausgewählt oder die Aktion abgebrochen hat. Sie liefert den Pfad zur ausgwählten Datei zurück, oder eine leere Zeichenkette, falls der Benutzer die Auswahl abgebrochen hat.
 
Haben wir einen Dateinamen erhalten, versuchen wir die Datei mit [http://doc.qt.io/qt-5/qiodevice.html#open open()] zu öffnen. Die Funktion liefert true zurück, wenn das erfolgreich war. Die Fehlerbehandlung wollen wir an dieser Stelle nicht weiter vertiefen, mehr erfahren Sie in den Links im Kasten "Weitere Informationen". Falls die Datei nicht geöffnet werden kann, benutzen wir eine [http://doc.qt.io/qt-5/qmessagebox.html QMessageBox] um einen Dialog mit Fehlermeldung anzuzeigen (Weitere Details finden Sie in der Klassenbeschreibung von [http://doc.qt.io/qt-5/qmessagebox.html QMessageBox]).
 
Das eigentliche Einlesen der Daten ist einfach, wir benutzen die Funktion [http://doc.qt.io/qt-5/qiodevice.html#readAll readAll()], die den gesamten Inhalt der Datei in einem [http://doc.qt.io/qt-5/qbytearray.html QByteArray] zurückliefert. Die Funktion [http://doc.qt.io/qt-5/qbytearray.html#constData constData()] liefert die Daten des Arrays als const char''' zurück, was wiederum zur Initialisierung im Konstruktor eines [http://doc.qt.io/qt-5/qstring.html QString] verwendet werden kann. Der Inhalt kann damit im Texteditor dargestellt werden. Wir schließen die Datei mit [http://doc.qt.io/qt-5/qiodevice.html#close close()], um die belegten Ressourcen (z.B. den File-Descriptor) an das Betriebssystem zurückzugeben.'''


Sehen wir uns nun den Slot save() zum abspeichern des Textes an:
Sehen wir uns nun den Slot save() zum abspeichern des Textes an:


<code><br />void Notepad::open()<br />{<br /> QString fileName = QFileDialog::getSaveFileName(this, tr(&quot;Save File&amp;quot;), &quot;&quot;,<br /> tr(&quot;Text Files ('''.txt);;C++ Files ('''.cpp '''.h)&quot;));
<syntaxhighlight lang="cpp">
<br /> if (fileName != &quot;&quot;) {<br /> QFile file&amp;amp;#40;fileName&amp;amp;#41;;<br /> if (!file.open(QIODevice::WriteOnly)) {<br /> // error message<br /> } else {<br /> QTextStream stream(&amp;file);<br /> stream &lt;&lt; textEdit-&gt;toPlainText();<br /> stream.flush();<br /> file.close();<br /> }<br /> }<br />}<br /></code>
#include <QFileDialog>
<br />Zum Schreiben des Textes in eine Datei benutzen wir die Klasse &quot;QTextStream&amp;quot;:http://doc.qt.nokia.com/qtextstream.html, ein Aufsatz (Wrapper) für &quot;QFile&amp;quot;:http://doc.qt.nokia.com/qfile.html-Objekte. Der &quot;QTextStream&amp;quot;:http://doc.qt.nokia.com/qtextstream.html kann QStrings direkt in eine Datei schreiben; &quot;QFile&amp;quot;:http://doc.qt.nokia.com/qfile.html hingegen kann nur einfache Zeichenketten (char''') über die Funktion &quot;write()&quot;:http://doc.qt.nokia.com/qiodevice.html#write von &quot;QIODevice&amp;quot;:http://doc.qt.nokia.com/qiodevice.html schreiben.
#include <QTextStream>
 
void Notepad::save()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "",
  tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));
 
if (fileName != "") {
  QFile file(fileName);
  if (!file.open(QIODevice::WriteOnly)) {
  // error message
  } else {
  QTextStream stream(&file);
  stream << textEdit->toPlainText();
  stream.flush();
  file.close();
  }
}
}
</syntaxhighlight>
 
Zum Schreiben des Textes in eine Datei benutzen wir die Klasse [http://doc.qt.io/qt-5/qtextstream.html QTextStream], ein Aufsatz (Wrapper) für [http://doc.qt.io/qt-5/qfile.html-Objekte QFile]. Der [http://doc.qt.io/qt-5/qtextstream.html QTextStream] kann QStrings direkt in eine Datei schreiben; [http://doc.qt.io/qt-5/qfile.html QFile] hingegen kann nur einfache Zeichenketten (char''') über die Funktion [http://doc.qt.io/qt-5/qiodevice.html#write write()] von [http://doc.qt.io/qt-5/qiodevice.html QIODevice] schreiben.'''
 
<syntaxhighlight lang="cpp">
#include <QApplication>
#include <QMessageBox>
 
void Notepad::quit()
{
QMessageBox messageBox;
messageBox.setWindowTitle(tr("Notepad"));
messageBox.setText(tr("Do you really want to quit?"));
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
messageBox.setDefaultButton(QMessageBox::No);
 
if (messageBox.exec() == QMessageBox::Yes)
  qApp->quit();
}
</syntaxhighlight>
 
 
main.cpp
<syntaxhighlight lang="cpp">
#include "notepad.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
 
Notepad notepad;
notepad.show();
 
return app.exec();
}
</syntaxhighlight>
 


=== Weitere Informationen ===
=== Weitere Informationen ===
 
|''. Über… |''. Hier… |
{|
!Über…
!Hier…
|-
|Dateien und Ein-Ausgabe-Geräte
|Dateien und Ein-Ausgabe-Geräte
|&quot;QFile&amp;quot;:http://doc.qt.nokia.com/qfile.html, &quot;QIODevice&amp;quot;:http://doc.qt.nokia.com/qiodevice.html
|[http://doc.qt.io/qt-5/qfile.html QFile], [http://doc.qt.io/qt-5/qiodevice.html QIODevice]
|}
|

Latest revision as of 11:26, 5 November 2023


Einstieg in die Programmierung mit Qt

Willkommen in der Welt von Qt — dem Cross-Plattform-Werkzeug für graphische Benutzeroberflächen (Graphical User Interface = GUI). In dieser Einführung vermitteln wir grundlegende Kenntnisse zu Qt und entwickeln dazu eine einfache Notizzettel-Anwendung. Nach dem Studium dieser Anleitung sollten Sie in der Lage sein, sich eingehend mit unseren Übersichten und der Dokumentation unserer Programmierschnittstellen (APIs) zu beschäftigen, um die benötigten Informationen für die Entwicklung Ihrer Anwendung zu finden.

Den englischen Originaltext zu dieser Übersetzung finden Sie unter der URL http://doc.qt.io/qt-4.8/gettingstartedqt.html bzw. http://doc.qt.io/gettingstartedqt.html.

Diese Anleitung setzt voraus, dass Sie schon ein bisschen mit der Programmiersprache C++ vertraut sind. Für Leser ohne diese Vorkenntnisse gibt es alternativ diese Anleitung: http://stefanfrings.de/qt_lernen/index.html

Für einen aktuellen Zugang zu C++17 und Qt6 empfehlen sich folgende Seiten:

  1. Für Qt: https://doc.qt.io/qt-6/qtwidgets-tutorials-notepad-example.html (englisch)
  2. Für C++: https://de.wikipedia.org/wiki/C%2B%2B


Hallo Notizzettel

In diesem ersten Beispiel werden wir einen einfachen Notizzettel (Notepad) erzeugen und als Fenster auf dem Schreibtisch anzeigen. Das stellt das einfachste Qt-Programm mit grafischer Oberfläche dar.

Screenshot Notepad Example.png

Der Quelltext dazu:

#include <QApplication>
#include <QTextEdit>

int main(int argc, char *argv[])
{
 QApplication app(argc, argv);

 QTextEdit textEdit;
 textEdit.show();

 return app.exec();
}

Wir werden den Code Zeile für Zeile analysieren. In den ersten zwei Zeilen binden wir die Header-Dateien für QApplication und QTextEdit ein, die zwei für dieses Beispiel benötigten Klassen. Für alle Qt-Klassen gibt es Header-Dateien, die genauso benannt sind wie die Klasse selbst.

Zeile 6 erzeugt ein QApplication-Objekt. Es verwaltet die Ressourcen für die gesamte Anwendung und wird immer für eine Qt-Anwendung mit GUI benötigt. Das Objekt benötigt argc und argv, weil Qt-Anwendungen einige Kommandozeilen-Optionen auswerten können.

Zeile 8 erzeugt ein QTextEdit-Objekt. Ein Texteditor ist ein sichtbares Element der GUI. In Qt werden diese Elemente "Widgets" genannt (ein Kunstwort aus Window (Fenster) und Gadget (Vorrichtung)). Beispiele für weitere Widgets sind "scroll bars" (Rollbalken), "Labels" (Beschriftungen/Markierungen) und "radio buttons" (eine Auswahl von Optionen, von denen nur eine ausgewählt werden kann). Ein Widget kann als Container für andere Widgets benutzt werden, beispielsweise ein Dialog oder das Hauptfenster einer Anwendung.

Zeile 9 zeigt den Texteditor als eigenständiges Fenster auf dem Bildschirm an. Da Widgets auch als Container fungieren können (z.B. ein QMainWindow, das Werkzeugleisten, Menüs, eine Statuszeile und einige andere Widgets enthält), kann ein einzelnes Widget auch als einzelnes Fenster auf dem Bildschirm dargestellt werden. Ein Widget ist von Haus aus nicht sichtbar, erst die Funktion show() macht es sichtbar.

In Zeile 11 startet QApplication seine Ereignis-Schleife ("event loop"). Wenn eine Qt-Anwendung läuft, werden verschiedene Ereignisse ("events") erzeugt und an die Widgets der Anwendung weitergereicht. Dabei handelt es sich z.B. um das Drücken einer Maustaste oder einen Tastaturanschlag. Wenn Sie in einem Texteditor Text eingeben, erhält er verschiedene "Taste gedrückt"-Ereignisse und reagiert darauf, indem er den eingegebenen Text auf dem Bildschirm darstellt.

Um die Anwendung auszuführen, öffnen Sie eine Kommandozeilen-Umgebung (shell) und wechseln Sie in das Verzeichnis, in dem sich die .cpp-Datei ihres Programms befindet. Folgende Befehle erzeugen das Programm:

qmake -project
qmake
make

Damit wird eine ausführbare Datei im Verzeichnis teil1 erzeugt (bitte beachten Sie: unter Windows müssen Sie ggf. nmake anstelle von make verwenden. Außerdem wird die ausführbare Datei in teil1\debug oder teil1\release erzeugt). qmake ist das Erstellungs-Werkzeug von Qt und benötigt eine Konfigurationsdatei. qmake erzeugt diese Datei für uns, wenn wir es mit dem Argument -project aufrufen. Zusammen mit der Konfigurationsdatei (mit der Endung .pro) generiert qmake ein Makefile, mit dem das Programm erstellt werden kann. Wir sehen uns später an, wie man .pro-Dateien selbst schreibt.

Weitere Informationen

|. Über… |. Hier… | |Widgets und Fenster-Geometrie | Window and Dialog Widget | |Ereignisse und ihre Behandlung | The Event System |

Einen Knopf zum Beenden hinzufügen

Eine richtige Anwendung benötigt üblicherweise mehr als ein Widget. Wir werden jetzt einen QPushButton unterhalb des Notizzettels einfügen. Die Anwendung wird beendet, wenn der Knopf gedrückt wird, d.h. mit der Maus auf ihn geklickt wird.

Sehen wir uns den Code dazu an:

#include <QApplication>
#include <QLayout>
#include <QTextEdit>
#include <QPushButton>

int main(int argc, char *argv[])
{
 QApplication app(argc, argv);

 QTextEdit *textEdit = new QTextEdit;
 QPushButton *quitButton = new QPushButton("&Quit");

 QObject::connect(quitButton, SIGNAL (clicked()), qApp, SLOT (quit()));

 QVBoxLayout *layout = new QVBoxLayout;
 layout->addWidget(textEdit);
 layout->addWidget(quitButton);

 QWidget window;
 window.setLayout(layout);

 window.show();

 return app.exec();
}

In Zeile 13 verwenden wir den Signal-Slot-Mechanismus von Qt, um die Anwendung zu beenden sobald der Beenden-Knopf gedrückt wurde. Ein "Slot" ist eine spezielle Funktion, die zur Laufzeit über ihren Namen (als reine Zeichenkette) aufgerufen werden kann. Ein "Signal" ist ebenfalls eine spezielle Funktion. Wird sie aufgerufen, werden wiederum die bei ihr registrierten Slots ausgeführt. Wir nennen das "den Slot mit dem Signal verbinden" ("connect") bzw. das Signal aussenden ("emit").

quit() ist ein Slot in QApplication und beendet die Anwendung. clicked() ist ein Signal, das von QPushButton ausgesendet wird, wenn er gedrückt wird. Die statische Funktion QObject::connect() dient dazu, den Slot mit dem Signal zu verbinden. SIGNAL () und SLOT() sind zwei Makros, die die Funktions-Signaturen für die zu verbindendenden Signals und Slots erhalten. Außerdem müssen Zeiger auf die Objekte angegeben werden, die das Signal aussenden und empfangen.

In Zeile 15 wird ein QVBoxLayout erzeugt. Wie bereits erwähnt, können Widgets andere Widgets enthalten. Es ist möglich, die Geometrie (Größe und Position) eines Unter-Widgets (Kind-Widget) direkt zu setzen. Üblicherweise ist es aber einfacher, eine Layout-Klasse zu verwenden, die die Geometrie der Kinder eines Widgets verwaltet. Ein QVBoxLayout beispielsweise ordnet die Kinder in einer Zeile nebeneinander an.

In Zeile 16 und 17 wird der Editor und der Knopf in das Layout eingefügt. In Zeile 20 weisen wir das Layout einem Widget zu.

Weitere Informationen

|. Über… |. Hier… | |Signale und Slots | Signals & Slots |Layouts | Layout Management, Widgets and Layouts, Layout Examples | |Die von Qt mitgelieferten Widgets | Qt Widget Gallery, Widget Examples |

Von QWidget abgeleitete Klassen

Wenn der Benutzer die Anwendung beenden möchte, wollen Sie ihn möglicherweise vorher fragen, ob er/sie das wirklich will. In diesem Beispiel erstellen wir eine von QWidget abgeleitete Klasse und fügen einen Slot hinzu, der mit dem Beenden-Knopf verbunden wird.

Sehen wir uns den Quelltext an:

#include <QWidget>
#include <QTextEdit>
#include <QPushButton>

class Notepad : public QWidget
{
 Q_OBJECT

public:
 Notepad();

private slots:
 void quit();

private:
 QTextEdit   *textEdit;
 QPushButton *quitButton;
};

Das Makro Q_OBJECT muss als erstes in der Definition der Klasse auftauchen. Es markiert unsere Klasse als QObject- sie muss natürlich auch von QObject (oder einer davon abgeleiteten Klasse) erben. QObject fügt einer normalen C++-Klasse noch einige weitere Fähigkeiten hinzu. Insbesondere können die Namen der Klasse und der Slots zur Laufzeit abgefragt werden. Außerdem ist es möglich, die Parameter eines Slots abzufragen und die dazugehörige Funktion aufzurufen.

In Zeile 13 wird der Slot quit() deklariert. Das geht mit dem Makro slots ganz einfach. Der Slot quit() kann nun mit einem Signal mit passender Signatur (jedes Signal, das keinen Parameter hat) verbunden werden.

Die Benutzeroberfläche erzeugen wir nicht mehr in der Funktion main(), sondern im Konstruktor von Notepad. Dort verbinden wir auch den Slot.

#include "notepad.h"
#include <QApplication>

Notepad::Notepad()
{
 textEdit   = new QTextEdit;
 quitButton = new QPushButton(tr("Quit"));

 connect(quitButton, SIGNAL (clicked()), this, SLOT (quit()));

 QVBoxLayout *layout = new QVBoxLayout;
 layout->addWidget(textEdit);
 layout->addWidget(quitButton);

 setLayout(layout);

 setWindowTitle(tr("Notepad"));
}

void Notepad::quit()
{
 qApp->quit();
}

Wie sie in der Definition der Klasse gesehen haben, benutzen wir Zeiger auf unsere QObject (textEdit und quitButton). Grundsätzlich gilt, dass Sie Instanzen von QObject (und davon abgeleiteten Klassen) auf dem Heap erzeugen und sie niemals kopieren sollten.

Von jetzt an kapseln wir die für den Benutzer sichtbaren Zeichenketten in der Funktion tr(). Das ist notwendig, wenn Sie die Anwendung in mehreren Sprachen (z.B. Deutsch und Englisch) zur Verfügung stellen möchten. Auf Einzelheiten gehen wir an dieser Stelle nicht ein. Mehr erfahren Sie über den Link zu Qt Linguist in der Tabelle mit weiteren Informationen.

Weitere Informationen

|. Über… |. Hier… | |tr() und "Internationalisierung" |Qt Linguist Manual, Writing Source Code for Translation, Hello tr() Example, Internationalization with Qt | | und das Objekt-Model von Qt |[hhttp://doc.qt.io/qt-5//object.html Object Model] | |qmake und das Erstellungs-System von Qt|qmake Manual |

Eine .pro-Datei erzeugen

Für dieses Beispiel werden wir eine eigene .pro-Datei erstellen, und nicht die Option -project von qmake.

HEADERS = notepad.h
SOURCES = notepad.cpp  main.cpp

Folgende Befehle erzeugen das Programm

qmake
make

Ein QMainWindow verwenden

Viele Anwendungen werden vom Einsatz eines QMainWindow (Hauptfenster) profitieren. Es hat sein eigenes Layout, dem Sie eine Menüzeile, Dock Widgets (es gibt keine vernünftige deutsche Übersetzung hierfür), Werkzeugleisten und eine Status-Zeile hinzufügen können. QMainWindow hat einen zentralen Bereich, der von jeder Art von Widget ausgefüllt werden kann. In unserem Beispiel werden wir hier den Texteditor platzieren.

Sehen wir uns die neue Definition der Klasse Notepad an.

#include <QMainWindow>
#include <QTextEdit>
#include <QAction>
#include <QMenu>

class Notepad : public QMainWindow
{
 Q_OBJECT

public:
 Notepad();

private slots:
 void open();
 void save();
 void quit();

private:
 QTextEdit *textEdit;

 QAction *openAction;
 QAction *saveAction;
 QAction *exitAction;

 QMenu *fileMenu;
};

Wir fügen zwei weitere Slots hinzu, mit denen ein Dokument gelesen und gespeichert werden kann. Wir werden sie im folgenden Abschnitt implementieren.

In einem Hauptfenster muss derselbe Slot oftmals von mehreren verschiedenen Widgets aus aufgerufen werden. Dazu gehören beispielsweise Menüpunkte und Knöpfe in einer Werkzeugleiste. Um diese Aufgabe zu erleichtern, gibt es in Qt die Klasse QAction, die mit mehreren Widgets verknüpft und mit einem Slot verbunden werden kann. So erzeugen beispielsweise sowohl QMenu als auch QToolBar Menüpunkte und Werkzeug-Knöpfe aus denselben QActions. Wie das funktioniert werden wir in Kürze sehen.

Wie vorhin bauen wir die Benutzeroberfläche im Konstruktor von Notepad zusammen.

#include "notepad.h"
#include <QMenuBar>

Notepad::Notepad()
{
 openAction = new QAction(tr("&Open"), this);
 saveAction = new QAction(tr("&Save"), this);
 exitAction = new QAction(tr("E&xit"), this);

 connect(openAction, SIGNAL (triggered()), this, SLOT (open()));
 connect(saveAction, SIGNAL (triggered()), this, SLOT (save()));
 connect(exitAction, SIGNAL (triggered()), this, SLOT (quit()));

 fileMenu = menuBar()->addMenu(tr("&File"));
 fileMenu->addAction(openAction);
 fileMenu->addAction(saveAction);
 fileMenu->addSeparator();
 fileMenu->addAction(exitAction);

 textEdit = new QTextEdit;
 setCentralWidget(textEdit);

 setWindowTitle(tr("Notepad"));
}

QActions werden mit einem Text erzeugt, der auf/mit den Widgets dargestellt werden soll, denen wir sie zuordnen (in unserem Fall Menüpunkte). Wenn wir sie auch in einer Werkzeugleiste verwenden wollen, könnten wir ihnen noch Icons (kleine Bilder) zuweisen.

Wenn jetzt ein Menüpunkt aufgerufen wird, so wird die dazugehörige Aktion getriggert und der mit ihr verbundene Slot wird aufgerufen.

Weitere Informationen

|. Über… |. Hier… | | Hauptfenster und die zugehörigen Klassen | Application Main Window, Main Window Examples | | MDI-Anwendungen | QMdiArea, MDI Example |

Speichern und einlesen

In diesem Beispiel implementieren wir die beiden Slots open() und save(), die wir zuvor ergänzt haben.

Wir kümmern uns zuerst um den Slot open() zum Öffnen eines Dokuments:

#include <QFileDialog>
#include <QMessageBox>

void Notepad::open()
{
 QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
  tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));

 if (fileName != "") {
  QFile file(fileName);

  if (!file.open(QIODevice::ReadOnly)) {
   QMessageBox::critical(this, tr("Error"),
   tr("Could not open file"));
   return;
  }

  QString contents = file.readAll().constData();
  textEdit->setPlainText(contents);
  file.close();
 }
}

Als erstes müssen wir den Benutzer nach der zu öffnenden Datei fragen. Qt stellt einen QFileDialog zur Verfügung, mit dem ein Benutzer eine Datei auswählen kann. Die Abbildung oben zeigt den Dialog wie er unter Kubuntu aussieht. Die statische Funktion getOpenFileName() zeigt einen modalen Dialog zur Dateiauswahl und kehrt erst zurück, wenn der Benutzer eine Datei ausgewählt oder die Aktion abgebrochen hat. Sie liefert den Pfad zur ausgwählten Datei zurück, oder eine leere Zeichenkette, falls der Benutzer die Auswahl abgebrochen hat.

Haben wir einen Dateinamen erhalten, versuchen wir die Datei mit open() zu öffnen. Die Funktion liefert true zurück, wenn das erfolgreich war. Die Fehlerbehandlung wollen wir an dieser Stelle nicht weiter vertiefen, mehr erfahren Sie in den Links im Kasten "Weitere Informationen". Falls die Datei nicht geöffnet werden kann, benutzen wir eine QMessageBox um einen Dialog mit Fehlermeldung anzuzeigen (Weitere Details finden Sie in der Klassenbeschreibung von QMessageBox).

Das eigentliche Einlesen der Daten ist einfach, wir benutzen die Funktion readAll(), die den gesamten Inhalt der Datei in einem QByteArray zurückliefert. Die Funktion constData() liefert die Daten des Arrays als const char zurück, was wiederum zur Initialisierung im Konstruktor eines QString verwendet werden kann. Der Inhalt kann damit im Texteditor dargestellt werden. Wir schließen die Datei mit close(), um die belegten Ressourcen (z.B. den File-Descriptor) an das Betriebssystem zurückzugeben.

Sehen wir uns nun den Slot save() zum abspeichern des Textes an:

#include <QFileDialog>
#include <QTextStream>

void Notepad::save()
{
 QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "",
  tr("Text Files (*.txt);;C++ Files (*.cpp *.h)"));

 if (fileName != "") {
  QFile file(fileName);
 
  if (!file.open(QIODevice::WriteOnly)) {
   // error message
  } else {
   QTextStream stream(&file);
   stream << textEdit->toPlainText();
   stream.flush();
   file.close();
  }
 }
}

Zum Schreiben des Textes in eine Datei benutzen wir die Klasse QTextStream, ein Aufsatz (Wrapper) für QFile. Der QTextStream kann QStrings direkt in eine Datei schreiben; QFile hingegen kann nur einfache Zeichenketten (char) über die Funktion write() von QIODevice schreiben.

#include <QApplication>
#include <QMessageBox>

void Notepad::quit()
{
 QMessageBox messageBox;
 messageBox.setWindowTitle(tr("Notepad"));
 messageBox.setText(tr("Do you really want to quit?"));
 messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
 messageBox.setDefaultButton(QMessageBox::No);

 if (messageBox.exec() == QMessageBox::Yes)
  qApp->quit();
}


main.cpp

#include "notepad.h"
#include <QApplication>

int main(int argc, char *argv[])
{
 QApplication app(argc, argv);

 Notepad notepad;
 notepad.show();

 return app.exec();
}


Weitere Informationen

|. Über… |. Hier… | |Dateien und Ein-Ausgabe-Geräte |QFile, QIODevice |