Suchen

Komponenten Multiplattform-Entwicklung mit dem Qt-Framework

| Autor / Redakteur: Hendrik Sattler * / Franz Graser

Plattformunabhängigkeit wird in zunehmendem Maße von Anwendungen gefordert. Das C++-Framework Qt erlaubt es, Applikationen zu entwickeln, die gleichermaßen unter Windows und Linux laufen.

Firmen zum Thema

Multiplattform-Entwicklung: Viele Entwickler programmieren ihre Embedded-Applikationen für verschiedene Zielsysteme. Mit dem Qt-Framework lässt sich der Aufwand hier deutlch reduzieren.
Multiplattform-Entwicklung: Viele Entwickler programmieren ihre Embedded-Applikationen für verschiedene Zielsysteme. Mit dem Qt-Framework lässt sich der Aufwand hier deutlch reduzieren.
(Bild: negativespace.co / CC0 )

Das Framework Qt erlaubt es, Programme zu erstellen, die gleichermaßen unter Windows und Linux laufen können. Dies bedeutet Plattformunabhängigkeit im weitesten Sinne. Szenarien für den Bedarf und Einsatz dieser Möglichkeit gibt es viele, etwa die grafische Benutzeroberfläche (User Interface, UI) eines unter Linux laufenden Geräts, die als Windows-Programm simuliert wird und so auch Windowsusern zur Verfügung steht, oder Kollaborationswerkzeuge, die ebenfalls nicht auf Windows-Nutzer als Zielgruppe beschränkt bleiben sollen. Zur Sicherstellung dieser Plattformunabhängigkeit werden verschiedene Ansätze zur Anwendung gebracht. Während für die UI-Simulation ein möglichst gleiches Aussehen sinnvoll erscheint, sollen Kollaborationswerkzeuge sich gut in die Desktop-Umgebung einpassen.

Startet man in der Softwareentwicklung mit einem neuen Projekt oder portiert ein vorhandenes Programm auf andere Compiler oder Plattformen, dann stehen am Anfang oft schon die Compiler selbst im Weg.

Compiler und andere Entwicklungswerkzeuge

Der Compiler von Visual Studio C++ beispielsweise ist nur unter Windows verfügbar, während unter Linux eher der GNU-Compiler (gcc) oder alternativ LLVM/clang verwendet wird. Bei allen diesen Compilern kann es vorkommen, dass im zu portierenden Code Fehler an bestimmten Stellen angemerkt werden, die bei anderen Compilern anstandslos durchlaufen.

Weitere Komplikationen können durch die Verwendung compilereigener Features im zu portierenden Code entstehen. Für solche Probleme bietet Qt in gewissem Umfang Hilfsmakros an, da Qt selbst den Anspruch hat, mit einer Vielzahl unterschiedlichster Compiler arbeiten zu können. Der Weg von Qt5 hin zur Nutzung von modernem C++11 ändert diese Ausrichtung ein wenig, was die erforderlichen Compiler-Features angeht, somit auch die unterstützten Versionen.

Für einen komplexen Build-Prozess zur Generierung von Programmen muss auch dessen Verwaltung betrachtet werden. Mit dem unter Windows gebräuchlichen Visual Studio werden MSBuild-Projekt-Dateien verwendet, während das unter Linux übliche GNU make mit Makefile-Dateien arbeitet. Hier bieten sich als Abstraktionsschicht die Meta-Build-Tools qmake oder CMake an, da sie beide sowohl Makefiles als auch Visual-Studio-Projekte generieren können. Während qmake mit Qt direkt mitgeliefert wird, wird CMake seit Qt5 explizit unterstützt und wird in vielen Qt-basierten Projekten verwendet.

Ergänzendes zum Thema
Erst prüfen, dann implementieren

Möchte man Plattformunabhängigkeit in einer Anwendung erreichen, so stellt Qt sehr viele Funktionen bereit, die das Erreichen dieses Ziels vereinfachen. Dennoch ist das Prüfen aller eigenen Anforderungen auf allen gewünschten Plattformen unabdingbar.

Es gibt keinen Königsweg

In manchen Situationen hängt die Plattformunabhängigkeit nicht nur von Qt selbst ab, sondern wird von weiteren Komponenten maßgeblich bestimmt. In anderen Fällen bedienen verschiedene, plattformspezifische Implementationen die eigenen Anforderungen unter Umständen besser oder erweitern die vorhandene Implementation in Qt um weitere Spezialitäten oder Plattformen.

Mit diesen Meta-Build-Tools ist es möglich, die Build-Regeln für verschiedene Plattformen nur an einer Stelle zu verwalten. Die Kommandozeilen-Frontends der Compiler, Linker und anderer SDK-Tools unterscheiden sich in der Regel deutlich. Die Programme und ihre Parameter zur Erstellung statischer oder dynamisch gelinkter Bibliotheken, zur Laufzeit geladener Module oder Programmdateien können stark variieren. Auch hier sind solche Meta-Build-Tools hilfreich, indem sie die Nutzung der SDK-Tools abstrahieren und damit vereinheitlichen

Zusätzlich werden oft eigene Tools verwendet, um z.B. Code zu generieren. Bei diesen Tools muss man sich während der Portierung ebenfalls um die Plattformunabhängigkeit kümmern, kann dafür aber durchaus zu denselben Mitteln greifen.

Aushängeschild von Qt: Grafische Elemente

Die Bibliotheken für die graphischen Elemente sind das buchstäblich augenfälligste und wohl bekannteste Beispiel für die Problematik der Plattformunabhängigkeit. Lautet die Aufgabe, in C++ eine native GUI für Windows zu erstellen, war lange Zeit die MFC die einzige Möglichkeit, während es unter Linux mehrere Bibliotheken gab. Mit neueren Windows-Versionen kamen auch hier neue Möglichkeiten hinzu, aber das Grundproblem blieb bestehen: Diese GUI-APIs gibt es nur unter Windows. Möchte man dieselbe GUI auch unter Linux anbieten können, kommt man um eine für beide Plattformen verwendbare Bibliothek nicht herum, möchte man nicht zwei GUIs pflegen. Qt ist eine dieser Möglichkeiten.

Die grafischen Elemente sind der wohl bekannteste Teil von Qt. Die klassische Form sind die mit C++ programmierbaren QWidgets für Dialoge und Anwendungsfenster. Sie können alternativ auch deklarativ über XML angesprochen werden, sodass etwa Dialoge mit einem mitgelieferten grafischen Designer erstellt werden können. Die neueste Form der Anwendungsgestaltung, QML mit QtQuick, lehnt sich dagegen eher an Web-Technologien an. Es gibt jedoch verknüpfende Elemente zur C++-Welt von Qt.

Die Grafikoberflächen unter Linux gestalten sich indes recht unterschiedlich. Während auf dem Desktop aktuell ein X-Server von X.org genutzt wird, wird es zukünftig wohl eher Wayland sein. Embedded-Geräte nutzen dagegen oft direkt auf einem Framebuffer aufbauende Techniken. Die meisten dieser Möglichkeiten werden auch von Qt unterstützt, die Reife der Umsetzung ist jedoch recht unterschiedlich und damit im Einzelfall zu prüfen.

Auch die Verwendung von 3-D-Elementen ist unter Windows und Linux möglich, Qt-intern wird dazu jedoch immer auf OpenGL zurückgegriffen. Während unter Linux OpenGL der Systemstandard für 3D ist, ist die Unterstützung von OpenGL unter Windows nicht immer gegeben. Daher wird bei Qt5 die ANGLE-Bibliothek mitgeliefert, die automatisch nach DirectX 9 oder 11 konvertieren kann. Alternativ, wenn etwa eine Grafikkarte kein DirectX unterstützt, wird alles in Software gerendert, was aber sehr zeitaufwändig ist. Die Unterstützung für OpenGL wird bei Programmstart geprüft oder durch den User vorgegeben.

Je nach Anforderung kann der Darstellungsstil der integrierten Elemente angepasst werden, so dass sie etwa zur Desktop-Oberfläche passen. Dabei werden nicht die nativen Widgets der Plattform verwendet, so dass eine einfache Oberfläche auch unter Linux einer nativen Windowsanwendung zumindest sehr ähnlich sehen kann, etwa für den oben genannten Fall der UI-Simulation. Alternativ wählt man je nach Plattform einen anderen Stil zur besseren Desktop-Integration.

Mit dieser Vielseitigkeit kann sich Qt zwar vielen Umgebungen anpassen, doch gehen mit dieser Flexibilität auch Probleme einher. Auch wenn man auf jeder Plattform denselben Stil verwendet, sind die Elemente in ihrer Größe leicht variabel, da beispielsweise das zugrunde liegende Font-Rendering unterschiedlich ist oder die verwendeten Fonts eben doch nicht komplett gleich sind. Diese Probleme sind jedoch lösbar, da die in Qt enthaltenen Widgets ihre benötigte Größe meist selbst berechnen können. Dies sollte man auch bei eigenen Widgets beachten, um Darstellungsfehler zu vermeiden.

Diese Größeninformationen werden von Layouts in Kombination mit weiteren Layout-Regeln genutzt, um Widgets in Dialogen und Anwendungen möglichst optimal anzuordnen. Solche Regeln beinhalten den Platz zwischen Widgets und die Größenänderung der Widgets, einzeln und relativ zueinander. Widgets, die nicht zu einem Layout gehören wie etwa Dialoge, bestimmen ihre Größe selbst und damit auch den verfügbaren Platz für alle ihnen untergeordneten Widgets. Dies kann leicht zu Widgets mit abgeschnittenem Text führen.

Multimedia-Inhalte und -Hardware

Verschiedene Qt-Stile: Die Fenster-Inhalte sind auf unterschiedliche Desktop-Oberflächen anpassbar
Verschiedene Qt-Stile: Die Fenster-Inhalte sind auf unterschiedliche Desktop-Oberflächen anpassbar
(Bild: Mixed Mode)

In einer Gegenüberstellung der QWidgets und der Unterstützung von Multimedia-Inhalten in Qt zeigt sich deutlich, wie unterschiedlich die Plattformunabhängigkeit umgesetzt werden kann. Während die grafischen Dialogelemente dies durch komplettes eigenes Zeichnen erreichen, werden nur Multimedia-Inhalte unterstützt, die durch die Plattform verarbeitet werden können. Unter Linux wird das Gstreamer-Backend verwendet, unter Windows gibt es das DirectShow-Backend. Sollen Inhalte auf beiden Plattformen laufen, müssen die verwendeten Codecs entsprechend gewählt oder zusätzlich installiert werden. Die Abhängigkeit von externen Implementationen dürfte in diesem Fall die Möglichkeiten bezüglich unterstützter Formate eher verbessert haben.

Natürlich kann man auch separate Bibliotheken zum Dekodieren auf beiden Plattformen nutzen. Für die Tonausgabe als rohen PCM-Stream stehen in der QtMultimedia-Bibliothek die passenden Klassen bereit. Leider variiert hier die Qualität der Implementation stark. Insbesondere unter Linux ist sie nicht ganz unproblematisch, wenn ALSA und PulseAudio gleichermaßen gut unterstützt werden sollen. Auch auf die Erkennung der Hardware-Möglichkeiten sollte man hier nicht explizit setzen, da etwa die Samplerate nur eine Auswahl üblicher Werte testet und nicht den kompletten Umfang der Hardware abdeckt. Dies schränkt jedoch die generelle Funktionalität nicht ein.

Möchte man nicht Medien abspielen, sondern Audio- oder Videodaten aufzeichnen, so ist auch das einfach machbar. Vorhandene Mikrofoneingänge oder Kameras können enumeriert werden und entsprechende Daten ausgelesen werden, um sie entweder direkt wieder auszugeben oder passend kodiert abzuspeichern. Wie beim Dekodieren von Inhalten ist man beim Encodieren von den verfügbaren Codecs im System abhängig oder nutzt zum Encodieren die passenden Bibliotheken.

Bilddateien sind deutlich einfacher zu integrieren. Alle wichtigen Formate wie BMP, JPG und PNG werden ohnehin direkt unterstützt. HTML-Inhalte können durch den integrierten Browser direkt verarbeitet werden. Die Technik dazu lieferte bis vor kurzem noch QtWebKit, während bei den neueren Qt5-Versionen QtWebKit von QtWebEngine abgelöst wurde. Es basiert auf Chromium, der OpenSource-Variante von Chrome. Beide Möglichkeiten unterscheiden sich leicht in der Handhabung, sollten aber die essentiellen Features bieten, insbesondere durch Manipulation von Elementattributen und Ausführung von JavaScript.

Auch die Ausgabe auf Druckern ist einfach. Vorhandene Drucker sind enumerierbar und die Ausgabe erfolgt wie auf anderen Widgets. Die Unterstützung für das Drucken (oder Erstellen) in eine PDF-Datei ist direkt integriert und kann auch programmintern wie ein Drucker angesprochen werden.

Oft möchte man in Programmen auch auf Dateien zugreifen. Auch dafür hält Qt Klassen bereit. Sie reichen von einfacher Verwaltung von Dateien und Verzeichnissen über ausgefeilte Abstraktionen als Input- oder Output-Stream und nutzen die auf der jeweiligen Plattform verfügbaren APIs. Man muss jedoch beachten, dass diese Implementationen in Qt teilweise stark abstrahiert sind und nicht immer alle Informationen der Plattform zur Verfügung stellen. So sind die Fehlercodes und Fehlertexte beim Zugriff auf Dateien nicht zugänglich und nur stark vereinfacht etwa durch QFile::FileErrors-Enum dargestellt. Es ist jedoch nicht schwierig, Alternativen in Form eigener Klassen zu entwickeln, die auf die eigenen Bedürfnisse besser zutreffen und trotzdem die Qt-I/O-Streams unterstützen, somit letztlich auch ins übrige Qt voll integrierbar sind.

Dateien und Input-/Output-Streams mit Qt nutzen

Qt stellt auch ein QFileSystem-Modell für die Ansicht von Dateibäumen bereit, das viele Prozessschritte in einen separaten Thread auslagert und es somit vermeidet, den GUI-Thread zu blockieren. Das ist für viele Anwendungszwecke ausreichend schnell. Bei Einsatzszenarien mit vielen Tausend Verzeichniseinträgen kann aber eine Alternative wünschenswert sein. Aktuell lässt es auch noch die Handhabung von Hotplugging-Unterstützung unter Windows vermissen, während dies unter Linux implizit durch das Beobachten von Verzeichnisänderungen gegeben ist.

Die Nutzung von Qt-I/O-Streams ist auch mit Netzwerkprotokollen möglich. Direkt unterstützt werden HTTP und FTP als High-Level-Protokolle. Hier ist jedoch nur die Funktionalität zur Handhabung des Inhalts von URLs enthalten. Insbesondere die Verwaltung von FTP-Verzeichnissen gibt es seit Qt5 nicht mehr direkt. Die Erweiterung um weitere Protokolle ist jedoch leicht möglich. Dazu kann man die vorhandenen Klassen für die Low-Level-Protokolle TCP und UDP für Server- und Client-Anwendungen verwenden, auch ohne den aktuellen Thread zu blockieren. Erfordert das Protokoll verschlüsselte Kommunikation mit TLS, so ist auch dies integrierbar.

Hendrik Sattler
Hendrik Sattler
(Bild: Mixed Mode)

Wenn man abseits von Netzwerkkommunikation schaut, so gibt es in Qt5 auch Klassen, mit denen serielle Ports angesprochen werden können oder der Bluetooth-Stack der Plattform genutzt werden kann. Der Windows-Bluetooth-Stack von Microsoft wird jedoch nicht unterstützt und die generelle Ausrichtung der Implementierung scheint in Richtung Mobile Devices zu gehen.

Dieser Beitrag ist ursprünglich auf unserem Schwesterportal Elektronikpraxis.de erschienen.

* Hendrik Sattler arbeitet bei Mixed Mode in Gräfelfing bei München. Er befasst sich mit Buildsystemen, Qt, Cross-Platform sowie Embedded-Themen.

(ID:44418564)