Einstieg in die Programmierung mit Qt: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
(Remove non-functioning "toc" command)
(veraltetes Bild geaendert)
 
(6 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{Cleanup | reason=Auto-imported from ExpressionEngine.}}
[[Category:Learning]]
[[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 13: 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>
<syntaxhighlight lang="cpp">
#include <QApplication>
#include <QApplication>
#include <QTextEdit>
#include <QTextEdit>


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


  QTextEdit textEdit;
  QTextEdit textEdit;
Line 30: Line 35:
  return app.exec();
  return app.exec();
}
}
</code>
</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.nokia.com/qapplication.html QApplication] und [http://doc.qt.nokia.com/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.
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.nokia.com/qapplication.html-Objekt QApplication]. 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.
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.nokia.com/qtextedit.html-Objekt QTextEdit]. 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 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.nokia.com/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.nokia.com/qwidget.html#show show()] macht es sichtbar.
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.


In Zeile 11 startet [http://doc.qt.nokia.com/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.
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.


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:
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:


<code>
<syntaxhighlight lang="bash">
qmake -project
qmake -project
qmake
qmake
make
make
</code>
</syntaxhighlight>


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.
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.
Line 54: Line 59:
=== Weitere Informationen ===
=== Weitere Informationen ===
|''. Über… |''. Hier… |
|''. Über… |''. Hier… |
|Widgets und Fenster-Geometrie | [http://doc.qt.nokia.com/application-windows.html Window and Dialog Widget] |
|Widgets und Fenster-Geometrie | [http://doc.qt.io/qt-5/application-windows.html Window and Dialog Widget] |
|Ereignisse und ihre Behandlung | [http://doc.qt.nokia.com/eventsandfilters.html The Event System] |
|Ereignisse und ihre Behandlung | [http://doc.qt.io/qt-5/eventsandfilters.html The Event System] |


== Einen Knopf zum Beenden hinzufügen ==
== Einen Knopf zum Beenden hinzufügen ==
Eine richtige Anwendung benötigt üblicherweise mehr als ein Widget. Wir werden jetzt einen [http://doc.qt.nokia.com/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 gecklickt wird.
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.


[[Image:http://doc.qt.nokia.com/4.7/images/gs2.png|http://doc.qt.nokia.com/4.7/images/gs2.png]]
<gallery>
File:/qt-4.8/images/gs2.png|http://doc.qt.io/qt-4.8/images/gs2.png
</gallery>


Sehen wir uns den Code dazu an:
Sehen wir uns den Code dazu an:


<code>
<syntaxhighlight lang="cpp">
#include <QtGui>
#include <QApplication>
#include <QLayout>
#include <QTextEdit>
#include <QPushButton>


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


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


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


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


  QWidget window;
  QWidget window;
  window.setLayout(&layout);
  window.setLayout(layout);


  window.show();
  window.show();
Line 87: Line 97:
  return app.exec();
  return app.exec();
}
}
</code>
</syntaxhighlight>


Zeile 1 bindet [http://doc.qt.nokia.com/qtgui.html QtGui] ein. Diese Header-Datei enthält alle GUI-Klassen von Qt.
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").


In Zeile 10 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").
[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.  


[http://doc.qt.nokia.com/qcoreapplication.html#quit quit()] ist ein Slot in [http://doc.qt.nokia.com/qapplication.html QApplication] und beendet die Anwendung. [http://doc.qt.nokia.com/qabstractbutton.html#clicked clicked()] ist ein Signal, das von [http://doc.qt.nokia.com/qpushbutton.html QPushButton] ausgesendet wird, wenn er gedrückt wird. Die statische Funktion [http://doc.qt.nokia.com/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 verbindenden Signale und Slots erhalten. Außerdem müssen Zeiger auf die Objekte angegeben werden, die das Signal aussenden und empfangen.  
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.


Zeile 12 erzeugt ein [http://doc.qt.nokia.com/qvboxlayout.html QVBoxLayout]. 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.nokia.com/qvboxlayout.html 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.
 
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.


=== Weitere Informationen ===
=== Weitere Informationen ===
|''. Über… |''. Hier… |
|''. Über… |''. Hier… |
|Signale und Slots | [http://doc.qt.nokia.com/signalsandslots.html| Signals & Slots]
|Signale und Slots | [http://doc.qt.io/qt-5/signalsandslots.html| Signals & Slots]
|Layouts | [http://doc.qt.nokia.com/layout.html Layout Management], [http://doc.qt.nokia.com/widgets-and-layouts.html Widgets and Layouts], [http://doc.qt.nokia.com/examples-layouts.html Layout Examples] |
|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] |
|Die von Qt mitgelieferten Widgets | [http://doc.qt.nokia.com/gallery.html Qt Widget Gallery], [http://doc.qt.nokia.com/examples-widgets.html Widget Examples] |
|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 ==
== 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.nokia.com/qwidget.html QWidget] abgeleitete Klasse und fügen einen Slot hinzu, der mit dem Beenden-Knopf verbunden wird.
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.


[[Image:http://doc.qt.nokia.com/4.7/images/gs3.png|http://doc.qt.nokia.com/4.7/images/gs3.png]]
<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:
Sehen wir uns den Quelltext an:


<code>
<syntaxhighlight lang="cpp">
#include <QWidget>
#include <QTextEdit>
#include <QPushButton>
 
class Notepad : public QWidget
class Notepad : public QWidget
{
{
Line 127: Line 141:
  QPushButton *quitButton;
  QPushButton *quitButton;
};
};
</code>
</syntaxhighlight>


Das Makro [http://doc.qt.nokia.com/qobject.html#Q_OBJECT Q_OBJECT] muss als erstes in der Definition der Klasse auftauchen. Es markiert unsere Klasse als [http://doc.qt.nokia.com/qobject.html QObject]- sie muss natürlich auch von [http://doc.qt.nokia.com/qobject.html QObject] (oder einer davon abgeleiteten Klasse) erben. [http://doc.qt.nokia.com/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.
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 135: 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>
<syntaxhighlight lang="cpp">
#include "notepad.h"
#include <QApplication>
 
Notepad::Notepad()
Notepad::Notepad()
{
{
Line 143: Line 160:
  connect(quitButton, SIGNAL (clicked()), this, SLOT (quit()));
  connect(quitButton, SIGNAL (clicked()), this, SLOT (quit()));


  QVBoxLayout layout = new QVBoxLayout;
  QVBoxLayout *layout = new QVBoxLayout;
  layout->addWidget(textEdit);
  layout->addWidget(textEdit);
  layout->addWidget(quitButton);
  layout->addWidget(quitButton);
Line 151: Line 168:
  setWindowTitle(tr("Notepad"));
  setWindowTitle(tr("Notepad"));
}
}
</code>


Wie sie in der Definition der Klasse gesehen haben, benutzen wir Zeiger auf unsere [http://doc.qt.nokia.com/qobject.html-Objekte QObject] (textEdit und quitButton). Grundsätzlich gilt, dass Sie Instanzen von [http://doc.qt.nokia.com/qobject.html QObject] (und davon abgeleiteten Klassen) auf dem Heap erzeugen und sie niemals kopieren sollten.
void Notepad::quit()
{
qApp->quit();
}
 
</syntaxhighlight>


Von jetzt an kapseln wir die für den Benutzer sichtbaren Zeichenketten in der Funktion [http://doc.qt.nokia.com/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.
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 ===
=== Weitere Informationen ===
|''. Über… |''. Hier… |
|''. Über… |''. Hier… |
|tr() und "Internationalisierung" |[http://doc.qt.nokia.com/linguist-manual.html Qt Linguist Manual], [http://doc.qt.nokia.com/i18n-source-translation.html Writing Source Code for Translation], [http://doc.qt.nokia.com/linguist-hellotr.html Hello tr() Example], [http://doc.qt.nokia.com/internationalization.html Internationalization with Qt] |
|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] |
|[http://doc.qt.nokia.com/qtwebkit-bridge.html#qobjects QObjects] und das Objekt-Model von Qt |[http://doc.qt.nokia.com/object.html Object Model] |
| 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.nokia.com/qmake-manual.html qmake Manual] |
|qmake und das Erstellungs-System von Qt|[http://doc.qt.io/qt-5/qmake-manual.html qmake Manual] |


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


<code>
<syntaxhighlight lang="make">
HEADERS = notepad.h
HEADERS = notepad.h
SOURCES = notepad.cpp  main.cpp
SOURCES = notepad.cpp  main.cpp
</code>
</syntaxhighlight>


Folgende Befehle erzeugen das Programm
Folgende Befehle erzeugen das Programm


<code>
<syntaxhighlight lang="bash">
qmake
qmake
make
make
</code>
</syntaxhighlight>


== Ein QMainWindow verwenden ==
== Ein QMainWindow verwenden ==
Viele Anwendungen werden vom Einsatz eines [http://doc.qt.nokia.com/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.nokia.com/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.
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.


[[Image:http://doc.qt.nokia.com/4.7/images/gs4.png|http://doc.qt.nokia.com/4.7/images/gs4.png]]
<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.
Sehen wir uns die neue Definition der Klasse Notepad an.


<code>
<syntaxhighlight lang="cpp">
#include <QtGui>
#include <QMainWindow>
#include <QTextEdit>
#include <QAction>
#include <QMenu>


class Notepad : public QMainWindow
class Notepad : public QMainWindow
Line 209: Line 237:
  QMenu *fileMenu;
  QMenu *fileMenu;
};
};
</code>
</syntaxhighlight>


Wir fügen zwei weitere Slots hinzu, mit denen ein Dokument gelesen und gespeichert werden kann. Wir werden sie im folgenden Abschnitt implementieren.
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 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 [http://doc.qt.nokia.com/qaction.html QAction], die mit mehreren Widgets verknüpft und mit einem Slot verbunden werden kann. So erzeugen beispielsweise sowohl [http://doc.qt.nokia.com/qmenu.html QMenu] als auch [http://doc.qt.nokia.com/qtoolbar.html QToolBar] Menüpunkte und Werkzeug-Knöpfe aus den selben [http://doc.qt.nokia.com/qaction.html QActions]. Wie das funktioniert werden wir in Kürze sehen.
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.


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


<code>
<syntaxhighlight lang="cpp">
#include "notepad.h"
#include <QMenuBar>
 
Notepad::Notepad()
Notepad::Notepad()
{
{
Line 226: Line 257:
  connect(openAction, SIGNAL (triggered()), this, SLOT (open()));
  connect(openAction, SIGNAL (triggered()), this, SLOT (open()));
  connect(saveAction, SIGNAL (triggered()), this, SLOT (save()));
  connect(saveAction, SIGNAL (triggered()), this, SLOT (save()));
  connect(exitAction, SIGNAL (triggered()), qApp, SLOT (quit()));
  connect(exitAction, SIGNAL (triggered()), this, SLOT (quit()));


  fileMenu = menuBar()->addMenu(tr("&File"));
  fileMenu = menuBar()->addMenu(tr("&File"));
Line 239: Line 270:
  setWindowTitle(tr("Notepad"));
  setWindowTitle(tr("Notepad"));
}
}
</code>
</syntaxhighlight>


[http://doc.qt.nokia.com/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.nokia.com/qicon.html Icons] (kleine Bilder) zuweisen.
[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.
Wenn jetzt ein Menüpunkt aufgerufen wird, so wird die dazugehörige Aktion getriggert und der mit ihr verbundene Slot wird aufgerufen.
Line 247: Line 278:
=== Weitere Informationen ===
=== Weitere Informationen ===
|''. Über… |''. Hier… |
|''. Über… |''. Hier… |
| Hauptfenster und die zugehörigen Klassen | [http://doc.qt.nokia.com/mainwindow.html Application Main Window], [http://doc.qt.nokia.com/examples-mainwindow.html Main Window Examples] |
| 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.nokia.com/qmdiarea.html QMdiArea], [http://doc.qt.nokia.com/mainwindows-mdi.html MDI Example] |
| 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 ==
== Speichern und einlesen ==
In diesem Beispiel implementieren wir die beiden Slots open() und save(), die wir zuvor ergänzt haben.
In diesem Beispiel implementieren wir die beiden Slots open() und save(), die wir zuvor ergänzt haben.


[[Image:http://doc.qt.nokia.com/4.7/images/gs5.png|http://doc.qt.nokia.com/4.7/images/gs5.png]]
<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:


Wir kümmern uns zuerst um den Slot open() zum öffnen eines Dokuments:
<syntaxhighlight lang="cpp">
#include <QFileDialog>
#include <QMessageBox>


<code>
void Notepad::open()
void Notepad::open()
{
{
Line 278: Line 313:
  }
  }
}
}
</code>
</syntaxhighlight>


Als erstes müssen wir den Benutzer nach der zu öffnenden Datei fragen. Qt stellt einen [http://doc.qt.nokia.com/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.nokia.com/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.
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.nokia.com/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.nokia.com/qmessagebox.html QMessageBox] um einen Dialog mit Fehlermeldung anzuzeigen (Weitere Details finden Sie in der Klassenbeschreibung von [http://doc.qt.nokia.com/qmessagebox.html QMessageBox]).
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.nokia.com/qiodevice.html#readAll readAll()], die den gesamten Inhalt der Datei in einem [http://doc.qt.nokia.com/qbytearray.html QByteArray] zurückliefert. Die Funktion [http://doc.qt.nokia.com/qbytearray.html#constData constData()] liefert die Daten des Arrays als const char''' zurück, was wiederum zur Initialisierung im Konstruktor eines [http://doc.qt.nokia.com/qstring.html QString] verwendet werden kann. Der Inhalt kann damit im Texteditor dargestellt werden. Wir schließen die Datei mit [http://doc.qt.nokia.com/qiodevice.html#close close()], um die belegten Ressourcen (z.B. den File-Descriptor) an das Betriebssystem zurückzugeben.
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>
<syntaxhighlight lang="cpp">
void Notepad::open()
#include <QFileDialog>
#include <QTextStream>
 
void Notepad::save()
{
{
  QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "",
  QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "",
Line 307: Line 345:
  }
  }
}
}
</code>
</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>


Zum Schreiben des Textes in eine Datei benutzen wir die Klasse [http://doc.qt.nokia.com/qtextstream.html QTextStream], ein Aufsatz (Wrapper) für [http://doc.qt.nokia.com/qfile.html-Objekte QFile]. Der [http://doc.qt.nokia.com/qtextstream.html QTextStream] kann QStrings direkt in eine Datei schreiben; [http://doc.qt.nokia.com/qfile.html QFile] hingegen kann nur einfache Zeichenketten (char''') über die Funktion [http://doc.qt.nokia.com/qiodevice.html#write write()] von [http://doc.qt.nokia.com/qiodevice.html QIODevice] schreiben.


=== Weitere Informationen ===
=== Weitere Informationen ===
 
|''. Über… |''. Hier… |
{|
!Über…
!Hier…
|-
|Dateien und Ein-Ausgabe-Geräte
|Dateien und Ein-Ausgabe-Geräte
|[http://doc.qt.nokia.com/qfile.html QFile], [http://doc.qt.nokia.com/qiodevice.html QIODevice]
|[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 |