Programmentwicklung mit Qt auf Windows 7

Nokias Qt Framework deckt sämtliche Bereiche moderner Desktop- und Systemprogrammierung ab. Es ist eine quelloffene Alternative zu Microsofts Foundation Classes, die sich besonders dann empfiehlt, wenn Portabilität auf Linux oder Mac OS X gewünscht ist. Das dreiteilige Tutorial gibt eine Einführung und wird auch auf die Parallelprogrammierung eingehen.

Lesezeit: 18 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 29 Beiträge
Von
  • Matthias Nagorni
Inhaltsverzeichnis

Nokias Qt Framework deckt sämtliche Bereiche moderner Desktop- und Systemprogrammierung ab. Es ist eine quelloffene Alternative zu Microsofts Foundation Classes, die sich besonders dann empfiehlt, wenn Portabilität auf Linux oder Mac OS X gewünscht ist. Das dreiteilige Tutorial gibt eine Einführung und wird auch auf die Parallelprogrammierung eingehen.

Dank seiner Konzepte und des großen Funktionsumfangs eignet sich Qt sowohl für den Gelegenheitsprogrammierer, der seine Anwendung mal eben schnell mit einer kleinen grafischen Oberfläche versehen möchte, als auch für den Entwickler anspruchsvoller kommerzieller Applikationen. Der kommerzielle Einsatz von Qt ist inzwischen kostenfrei möglich, da das Framework seit der im März 2009 veröffentlichten Version 4.5 unter der LGPL 2.1 erhältlich ist.

Mehr Infos

Plattformunabhängige Parallelprogrammierung mit C++ und Qt

In dieser Reihe bisher erschienen:

Die Klassenbibliotheken von Qt bieten portable Abstraktionen für nahezu alle Bereiche moderner C++-Programmierung, von SQL, XML und Netzwerkprogrammierung bis hin zu OpenGL. Seit mehr als zehn Jahren finden sich zudem Klassen für die Parallelprogrammierung im Funktionsumfang, wobei der betreffende Teil 2008 mit Qt 4.4 deutlich erweitert wurde und inzwischen sogar MapReduce implementiert.

Neben den Bibliotheken enthält das Qt SDK die Entwicklungsumgebung Qt Creator und eine Reihe weiterer Werkzeuge. Unter Windows liefert es MinGW, die minimalistische Version der GNU-Compiler-Werkzeuge, mit. Wer lieber Microsofts IDE Visual Studio verwendet, kann das mit Qt ebenfalls tun. Auf Windows Vista und Windows 7 ist das sogar die einzige offiziell unterstützte Variante. Für das Tutorial genügen zunächst Qt Creator und MinGW. Im Folgenden sei am Beispiel einer einfachen Bildbetrachter-Applikation eine kurze Einführung in die Programmierung mit Qt gegeben. Anschließend wird gezeigt, wie man das Programm mit einem Icon verzieren und einen Installer dafür bauen kann.

Mehr Infos

Quellcode zum Artikel

  • QHelloWorld (.zip, 8 KByte)
  • QSimpleViewer (.zip, 23 KByte)

[Update] Die Beispiele wurden gegenüber der ursprünglichen Version noch ein mal leicht überarbeitet. In main.cpp werden QApplication und QMainWindow nicht mehr dynamisch erzeugt, wodurch ihr Destruktor bei Programmende automatisch aufgerufen wird. [/Update]

Im ersten Schritt ist dazu das aktuelle Qt SDK zu installieren. Der Online-Installer ist praktisch, weil sich mit ihm nur die Komponenten herunterladen lassen, die man wirklich installieren möchte. Dazu wählt man im Installer die Option Custom und kann anschließend nicht benötigte Komponenten wie die Symbian-Unterstützung und die Simulatoren deselektieren. Unter Linux ist es empfehlenswert, auf die Qt-Pakete der jeweiligen Linux-Distribution zurückzugreifen.

Zunächst ist mit einem "Hello World"-Programm, das einen einfachen Bildbetrachter in nur 30 Zeilen implementiert, die Umgebung zu testen. Dazu muss der Leser ein Verzeichnis QHelloWorld anlegen und die Dateien main.cpp und QHelloWorld.pro sowie QHelloWorld.rc und QHelloWorld.ico dorthin kopieren. Die Dateien finden Sie hier zum Download.

QHelloWorld, ein einfacher Bildbetrachter in 30 Zeilen Qt-Code (Abb. 1)

Lädt man die Projektdatei QHelloWorld.pro in Qt Creator, öffnet sich ein Dialog mit Einstellungen für das Projekt. Hier übernimmt man das standardmäßig ausgewählte Ziel Desktop. Unter Details lässt sich definieren, ob Pfade sowohl für die Debug- als auch für die Release-Versionen anzulegen sind. Falls mehrere Versionen eines Programms unabhängig voneinander entwickelt werden sollen, empfiehlt es sich, die Option Shadow Build zu deaktivieren. Ein Klick auf den grünen Play-Button im Hauptfenster des Qt Creator oder STRG + R startet zunächst das Kompilieren und bei Erfolg das Beispielprogramm. Dieses erfragt per Filebrowser den Namen einer Bilddatei und stellt das Bild im Maßstab 1:1 dar.

Hier ein Blick auf den Quellcode der main.cpp:

#include <QtGui/QApplication>
#include <QtGui/QMainWindow>
#include <QtGui/QMenu>
#include <QtGui/QMenuBar>
#include <QtGui/QScrollArea>
#include <QtGui/QLabel>
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtGui/QFileDialog>

int main(int argc, char *argv[])
{
QApplication *myApp = new QApplication(argc, argv);
QMainWindow *top = new QMainWindow();
top->setWindowTitle("QHelloWorld (c)2011 by Matthias Nagorni");
QScrollArea *scroll = new QScrollArea;
QLabel *label = new QLabel;
scroll->setWidget(label);
QMenu *fileMenu = top->menuBar()->addMenu("&Datei");
fileMenu->addAction("&Beenden", myApp, SLOT(quit()));
top->setCentralWidget(scroll);
top->setGeometry(100, 100, 800, 600);
top->show();
QImage image;
if (argc > 1) image = QImage(argv[1]);
else image = QImage(QFileDialog::getOpenFileName());
label->setPixmap(QPixmap::fromImage(image));
label->setMinimumSize(image.width(), image.height());
return myApp->exec();
}

Das QApplication-Objekt übernimmt die Verwaltung der Applikation und enthält insbesondere die Event-Loop. Diese wird in der vorletzten Zeile mit myApp->exec() gestartet. Für das Hauptfenster der Anwendung erzeugt das Programm ein QMainWindow-Objekt und legt mit der Funktion setCentralWidget das Fenster für die Programmoberfläche unterhalb der Menüleiste fest. Im Beispiel ist dies ein mit Scroll-Leisten verziertes QLabel-Objekt. Solche Objekte kommen meist bei der Darstellung von Text zum Einsatz, sie können aber auch als Container für Bilder dienen. In der Menüleiste erzeugt der Aufruf von top->menuBar()->addMenu("&Datei") den Menüpunkt "Datei". Die Funktion addAction fügt einen Menüpunkt zum Beenden des Programms hinzu.

Über die Klasse QImage implementiert das Programm umfangreiche Funktionen zum Laden, Speichern und Manipulieren von Bildern in fast allen verbreiteten Formaten. Im Beispiel lässt sich der Name der Bilddatei entweder als Kommandozeilenparameter übergeben oder in einem File-Dialog auswählen, wobei die Dateiendung das Bildformat bestimmt. Die benötigten Bibliotheken bindet Qt automatisch ein. Unter Windows muss der Entwickler allerdings sicherstellen, dass das Programm die betreffenden .dll-Dateien findet – dazu später mehr.

Das Hauptfenster der Qt Creator IDE mit dem Quellcode von QHelloWorld (Abb. 2)

Auch wenn sich ein File-Browser einfach per statischer Funktion aus der QFileDialog-Klasse aufrufen lässt, ist man doch ohne eigene Klassen recht eingeschränkt. QSimpleViewer erweitert daher unseren Bildbetrachter um die Klassen Gui für die Programmoberfläche, Data für die Bilddaten und View zum Anzeigen des Bildes (alle Dateien hier zum Download). Um die für Echtzeitszenarien wichtige Klasse QTimer zu demonstrieren, ist in dem Programm eine einfache Diashow umgesetzt.

Zunächst sei die Klasse Gui betrachtet, die von QWidget, der Basisklasse aller Widgets, abgeleitet ist. Der Konstruktor generiert die Programmoberfläche mit einem View-Widget, das mit Scroll-Leisten ausgestattet ist. Hier ist ein wichtiges Konzept von Qt zu erkennen: Widgets erscheinen bei Qt in der Regel nur auf dem Bildschirm, wenn sie mit einem Layout-Objekt assoziiert sind. Im Beispiel dient dazu ein QVBoxLayout-Objekt, in das der Aufruf addWidget(scroll) das mit der Scroll-Leiste verzierte View-Widget platziert.

Für den Vollbild-Modus empfiehlt es sich, ein weiteres View-Widget als rahmenloses Fenster zu erzeugen. Das Window-Flag Qt::Window signalisiert, dass ein Toplevel-Fenster erzeugt werden soll, während Qt::FramelessWindowHint den Rahmen verschwinden lässt.

Eines der fundamentalen Konzepte von Qt ist die Verknüpfung von Events mit entsprechenden Handler-Funktionen anhand von Signalen und Slots. Die Signale werden einfach in den Header-Dateien unter dem Schlüsselwort signals: deklariert und mit dem Befehl emit an beliebiger Stelle innerhalb des Programmcodes der Klasse ausgelöst. Die Handler-Funktionen deklariert man in der Header-Datei unter dem Schlüsselwort slots:. Verwendet der Entwickler eines der Schlüsselwörter, muss er das Makro Q_OBJECT in der Header-Datei aufrufen. Zum Verbinden von Signalen und Slots dient die statische Funktion connect aus der Basisklasse QObject.

Der Konstruktor der Gui-Klasse verbindet über QObject::connect die beiden Buttons mit den entsprechenden Funktionen. Das Verlassen des Vollbild-Modus geschieht per Esc-Taste in der View-Klasse. Hier propagiert ein eigenes Signal leaveFullScreen das Event in die Gui-Klasse, wo dann die Funktion normalScreen() die benötigten Einstellungen setzt.

Eine Klasse mit vielfältigen Einsatzmöglichkeiten ist QTimer. Solange das Timing nicht allzu exakt sein muss, lässt sie sich für Echtzeitanwendungen verwenden, um beispielsweise eine Funktion in regelmäßigen Abständen auszuführen. Das Beispiel verwendet QTimer für das Laden des nächsten Bilds in der Diashow. Dazu erzeugt der Konstruktor von Gui ein QTimer-Objekt, setzt das Timer-Intervall auf 3000 ms und verbindet per QObject::connect das Signal timeout() mit der Slot-Funktion nextSlide(). Nun sei ein Blick auf einige der übrigen Funktionen der Klasse Gui geworfen. Die Slot-Funktion loadImages() ruft die statische Funktion QFileDialog::getOpenFileNames() auf, die ein Dialogfenster erzeugt, in dem man die zu ladenden Bilddateien auswählt. Die Funktion Gui::saveImage() verwendet eine statische Funktion der Klasse QFileDialog zur Eingabe eines einzelnen Dateinamens.

Solange diese statischen Funktionen verwendet werden, greift Qt auf die File-Dialoge des jeweiligen Betriebssystems zurück. Unter Windows 7 erscheinen also die Standarddialoge mit entsprechender Lokalisierung. Das ist nicht der Fall, wenn man QFileDialog-Objekte erzeugt und mit der Funktion exec() ausführt.

Zunächst maximiert die Funktion Gui::fullScreen() das im Konstruktor rahmenlos erzeugte fullView-Toplevel-Fenster. Der Aufruf von raise() holt es in den Vordergrund, sodass es vor allen anderen Fenstern steht. Nun ist noch sicherzustellen, dass das Fenster auf Tastatureingaben reagiert, damit man den Vollbild-Modus per Esc-Taste verlassen kann. Mit setFocusPolicy(Qt::StrongFocus) legt der Entwickler fest, dass sich der Fokus sowohl per Maus als auch per Tabulator-Taste selektieren lässt. Zusätzlich kann man den Fokus explizit mit setFocus() setzen. Die Slot-Funktion normalScreen() versteckt das Vollbild-Fenster, sodass wieder das Hauptfenster des Programms erscheint.

Die Klasse Data speichert das Bild in einem QImage-Objekt und implementiert Funktionen zum Laden und Speichern sowie Skalieren und Drehen. Der Konstruktor erzeugt zunächst ein QImage im Format RGB32. In diesem Truecolor-Format sind für den roten, grünen und blauen Farbkanal je 8 Bit reserviert. Mit dem Aufruf von fill(0x808080) füllt man das Bild mit einem grauen Hintergrund.

Zum Laden und Speichern bietet QImage die Funktionen load(FileName) und save(FileName), wobei das Bildformat durch die Dateiendung bestimmt wird. Da QImage-Objekte von Qt per "Implicit Data Sharing" mit "Copy on Write" verwaltet werden, lassen sie sich in den Funktionen Data::scaleImage und Data::rotateImage einfach als Wert zuweisen.

Wie man ein eigenes Widget mit Bildschirmausgabe erzeugt, das auf Maus- und Tastatureingaben reagiert, demonstriert die Klasse View (Sie finden sie unter den Dateien zum QSimpleViewer).

Es überlädt die benötigten virtuellen Funktionen der Basisklasse QWidget. Das Erzeugen eigener Bildschirminhalte bewerkstelligt die Funktion paintEvent, die Qt immer dann aufruft, wenn das Widget neu zu zeichnen ist. Zum Zeichnen des Bildes dient die Funktion drawImage eines QPainter-Objekts. Im Vollbildmodus skaliert View::paintEvent das Bild zunächst auf die volle Bildschirmgröße und stellt es zentriert dar.

Das Implementieren eigener Event-Handler für Maus- und Tastatureingaben geschieht durch ein Überlagern der entsprechenden virtuellen Funktion. QSimpleViewer verwandelt die Maus schließlich in eine Fernbedienung für den Diabetrachter: Die Funktion View::mouseReleaseEvent wertet Mausklicks aus und lädt das nächste oder vorhergehende Bild. In View::keyReleaseEvent reagiert das Programm auf die Esc-Taste mit dem Verlassen des Vollbild-Modus. Da nicht jede Maus über einen mittleren Button verfügt, lässt sich das Bild alternativ mit der Taste T drehen.

QSimpleViewer erweitert den Bildbetrachter um Diashow- und Vollbild-Funktionen. Zusätzlich lassen sich Bilder nun drehen und skalieren. (Abb. 3)

Der Code in main.cpp ist QHelloWorld recht ähnlich, allerdings verwendet das Beispiel nun eigene Klassen zur Darstellung des Bildes und greift dafür nicht mehr auf QLabel zurück. Es ist weiterhin möglich, QSimpleViewer eine Bilddatei als Parameter zu übergeben. Unter Windows lässt sich das Programm daher als Standardanwendung für Bilddateien wählen.

Ebenfalls weitgehend plattformunabhängig ist der Build-Prozess, sieht man von Ausnahmen wie dem Einbinden von Programm-Icons ab. Ausgangspunkt sind die Projektdateien mit der Endung .pro, aus denen das Tool qmake die Makefiles generiert. qmake ist direkt in den Qt Creator integriert, der das Tool zu Beginn des Build-Prozesses automatisch ausführt.

Qt-Projektdateien haben einen denkbar einfachen Aufbau. Unter dem Schlüsselwort QT listet der Entwickler die benötigten Qt-Bibliotheken. Unter TARGET spezifiziert er den Namen der ausführbaren Datei. Dazu kommen noch die Listen der Quelldateien und Header, mehr nicht. Die Projektdatei QSimpleViewer.pro bindet zusätzlich noch ein eigenes Icon ein. Hier ist das Vorgehen ausnahmsweise plattformspezifisch und funktioniert unter Windows mit der unter OTHER_FILES gelisteten Datei QHelloWorld.rc. (Das Icon hat der Autor mit der freien Bildbearbeitssoftware Gimp erzeugt, indem er ein Bild der Apollo-17-Mission auf 48 x 48 Pixel verkleinert und mit der Endung .ico als Icon-Datei gespeichert hat.)

Qt Creator kompiliert Programme wahlweise als Debug- oder Release-Versionen, wobei das Werkzeug dafür die beim Öffnen der Projektdatei unter Details spezifizierten Pfade verwendet. Voreingestellt ist der Debug-Modus. Für die fertige Beispielanwendung ändert man diesen auf Release und startet dann den Build durch Klick auf den grünen Play-Button. Nach dem Kompilieren enthält das release-Verzeichnis neben der ausführbaren .exe-Datei und den .o-Dateien mit dem Objektcode mehrere Files, deren Name mit moc_ beginnt. Diese Dateien erzeugt der Qt Meta-Object Compiler moc für jede Klasse, die das Makro QT_OBJECT aufruft. Sie dienen der Implementierung des Signal-Slot-Mechanismus sowie der Verwaltung dynamischer Objekteigenschaften und Runtime-Typ-Informationen.

Wenn der Leser nun versucht, QSimpleViewer.exe direkt zu starten, wird er eine Fehlermeldung wegen fehlender Bibliotheken erhalten. Abhilfe schafft das Setzen der entsprechenden Pfade oder das Kopieren der benötigten .dll-Dateien in das release-Verzeichnis. Es handelt sich dabei um die Dateien libgcc_s_dw2-1.dll und mingwm10.dll sowie den Qt-Bibliotheken QtCore4.dll und QtGui4.dll. Etwas versteckt findet man die Dateien im Verzeichnis QtSDK\Desktop\Qt\4.7.4\mingw\bin. Für die Unterstützung der Bildformate muss das Programm auf die .dll-Dateien aus dem Verzeichnis QtSDK\Desktop\Qt\4.7.4\mingw\plugins\imageformats zugreifen können. Daher muss der Leser im release-Verzeichnis ein Unterverzeichnis plugins und darin ein Unterverzeichnis imageformats anlegen und dorthin sämtliche .dll-Dateien aus QtSDK\Desktop\Qt\4.7.4\mingw\plugins\imageformats kopieren.

Im letzten Schritt sei nun das Programm zusammen mit Bibliotheken und dem Icon in einen Installer verpackt. Das lässt sich mit dem Open-Source-Tool Inno Setup von Jordan Russell in wenigen Minuten bewerkstelligen. Am bequemsten ist es, dafür den "Script Wizard" des Inno Setup Compilers zu verwenden. Im Welcome-Dialog des Programms ist das die voreingestellte Option. Es folgt eine Reihe von Dialogen, in denen der Wizard Informationen zum Programm, den benötigten Dateien und Verzeichnissen sowie zu den unterstützten Sprachen abfragt. Im Dialog "Application Information" genügt es, Namen und Version, optional auch den Programmautor anzugeben. Die Voreinstellungen im Dialog "Application Folder" wird man in der Regel unverändert lassen. Unter "Application Files" gibt es dagegen mehr zu tun: Hier sind die für die Anwendung benötigten Dateien aufzulisten. Im Beispiel sind das QSimpleViewer.exe aus dem release-Verzeichnis als "Application main executable file" sowie die Dateien libgcc_s_dw2-1.dll, mingwm10.dll, QtCore4.dll, QtGui4.dll, QSimpleViewer.ico und LICENSE.txt und das Verzeichnis plugins. Für letzteres klickt man auf Add Folder und beantwortet die Frage, ob Unterverzeichnisse mit einbezogen werden sollen, mit "Ja".

Der Dialog "Application Icons" enthält Optionen zu Startmenü und Desktop-Icon. Die Voreinstellungen orientieren sich an üblichen Konventionen und können daher unverändert bleiben. Die Lizenz LICENSE.txt spezifiziert man im Dialog "Application Documentation". Inno Setup unterstützt eine Reihe von Sprachen, die unter "Setup Languages" zu aktivieren sind. Der Dialog "Compiler Settings" erlaubt im Feld Compiler output base file name die Wahl eines Dateinamen für den Installer. Im Feld Custom Setup icon file lässt sich die Datei QSimpleViewer.ico als Icon wählen. Nach Überspringen des Dialogs "Inno Setup Preprocessor" startet ein Klick auf Finish die Erzeugung des Skripts. Anschließend fragt das Programm, ob das Skript kompiliert und gespeichert werden soll. Im Beispiel empfiehlt es sich, das Skript unter QSimpleViewer-Inno im Verzeichnis des QSimpleViewer-Projekts zu speichern. Inno Setup erzeugt hier ein Unterverzeichnis Output mit dem fertigen Installer QSimpleViewer-Setup.exe. Dieser enthält nun in gepackter Form die vollständige Applikation samt Bibliotheken.

Mit dem Inno Setup Script Wizard ist der Installer in wenigen Minuten gebaut (Abb. 4).

Auch unter Windows stehen mit dem Qt SDK und MinGW kraftvolle Open-Source-Werkzeuge für die Entwicklung nahezu beliebig komplexer Anwendungen auf Basis von C++ zur Verfügung. Beim Bauen des Installers mit dem ebenfalls kostenlos erhältlichen Open-Source-Tool Inno Setup muss man achtgeben, dass man wirklich sämtliche für das Programm benötigten Bibliotheken mit einpackt. Solange man keine plattformspezifischen Bibliotheken verwendet, lassen sich mit Qt entwickelte Applikationen unverändert auf Linux und Mac OS X kompilieren.

Nachdem dieser erste Teil des Tutorials einen ersten Einblick in die Programmierung mit Qt und die Werkzeuge zum Bauen und Verpacken von Anwendungen gegeben hat wird der zweite Teil die grundlegenden Klassen für die Parallelprogrammierung vorstellen. In einfachen Beispielen werden deren Verwendung sowie mögliche Fallstricke demonstriert.

Wer sich ausführlicher mit Qt beschäftigen möchte, findet auf der Nokia-Webseite umfangreiche Dokumentation zu allen Bereichen des Frameworks. Die Online Reference Documentation gibt einen Überblick über die Entwicklungs-Tools. Hier findet sich auch die vollständige Beschreibung sämtlicher Qt-Klassen.

Dr. Matthias Nagorni
schreibt seit mehr als 10 Jahren Open-Source-Software in Qt und arbeitet zurzeit freiberuflich als Consultant.
(rl)