Über den Tellerrand geschaut

Softwareentwicklung für Mobil und Multiplattform: Qt statt Xamarin

Jörg Preiß
Jörg Preiß
28. Februar 2017

Microsoft eröffnet sich mit Xamarin Forms das Feld der Plattformunabhängigkeit für WPF. Dabei existiert bereits seit Langem ein Framework, das auch ohne erneute Implementierung unter den Betriebssystemen Windows, MacOS, Linux und seit Version 5 auch unter iOS, Android, Sailfish OS und anderen läuft. Die Rede ist von Qt. Ausgesprochen werden die beiden Buchstaben wie das englische Wort für niedlich: cute [kju:t].

LISTING 1: MAIN.CPP

 

Qt existiert bereits seit 1992. Die spätere Firma Trolltech vertrieb das Framework lange Zeit kommerziell. Für den Linux- Desktop KDE gab es eine freie Version, die später auch unter die GPL gestellt wurde. Beide Versionen unterschieden sich durch die Verfügbarkeit bestimmter Module. Ab Version 4.5 im Jahre 2009 wurde die LPGL hinzugefügt. Auch die aktuelle Version 5.x liegt in einer kommerziellen und einer freien Version vor. Mittlerweile liegen die Rechte bei The QT Company.

Das Besondere an der Qt-Entwicklung war das Signal/Slot- Prinzip. Während viele andere Frameworks noch auf Events setzten, wurde hier schon das spätere Publish-Subscribe-Pattern eingesetzt. Ein Button stellt das Signal clicked() bereit, eine View-Komponente kann dieses Signal an einen eigenen Slot onClicked() binden.

Mit der Version 4.7 wurde die Qt Markup Language QML eingeführt. Generierte der Designer vorher noch den fertigen Quellcode, kann man nun die Oberfläche in einer JSON-artigen Sprache beschreiben. Die Bildschirmelemente können durch JavaScript manipuliert, Werte und Listen gebunden werden. Man erreicht insgesamt eine Architektur, die Model- View-Delegate genannt wird. Für den vorliegenden Artikel wurde Qt in Version 5.6 verwendet.

Erstes Fenster

Die Aufteilung zwischen der Logik und der Ansicht erfährt, ähnlich wie bei WPF, eine klare Trennung durch die Verwendung von unterschiedlichen Sprachen. Die Applikation wird durch C++-Code initialisiert, wie in Listing 1 zu sehen. Innerhalb der QQmlEngine können in einem QQmlContext Werte registriert werden, an die gebunden werden kann. Prinzipiell lässt sich alles registrieren, was von QObject abgeleitet ist. So lässt sich der von WPF bekannte DataContext realisieren, hinter dem ein Controller stecken kann wie in Listing 2.

Deklarierte Properties können innerhalb des QML-Codes gelesen werden. Damit dort auch Änderungen sichtbar werden, sind noch Signale beim Setzen der Werte zu schicken. Listing 3 verdeutlicht die Verwendung der Properties: Die Text-Elemente bieten lesenden Zugriff, TextInput erlaubt auch Änderungen. Das Binding in der ersten Variante leitet den neuen Text an das zweite TextInput weiter. In der zweiten Variante wird die Änderung per Funktionsaufruf durchgeführt und bedarf keines weiteren Bindings.

LISTING 2: MAINCONTROLLER.H

 

LISTING 3: MAIN.QML

 

Listen, Filter, Sortierung

Wie sieht der Umgang mit Listen aus? In der WPF-Welt füllt man eine ListBox mit einer ItemsSource und verändert den Style der Liste sowie die Templates der ListItems. Unter QML füllt man die Liste anhand eines Models und beschreibt das Aussehen der Elemente mithilfe eines Delegates.

Es gibt verschiedene Möglichkeiten der Präsentation der Elemente. Im Beispielprojekt Highrise wird eine GridView für die mehrspaltige Anzeige sowie eine ListView für die Übersicht benutzt. Eine weitere Möglichkeit wäre die Auflistung mithilfe eines Repeaters, auf die der Artikel nicht eingeht.

Im vorliegenden Beispiel sind die Delegates der Listen statisch. Es spricht allerdings nichts dagegen, sie durch eine JavaScript- Funktion berechnen zu lassen. Dadurch ist zum Beispiel eine Kachelview möglich, die in Abhängigkeit der Fenstergröße zwischen verschiedenen Ansichten umschaltet.

In Listing 4 wird die Liste aller Räume mit ihrem derzeitigen Zustand visualisiert. Die Liste ist zweispaltig, weshalb eine GridView zum Einsatz kommt. Es wird auch weder Sortierung noch Filterung benötigt, sodass direkt auf ein Model zugegriffen werden kann. Bei dem Model handelt es sich um das BuildingModel, eine Klasse, die von QAbstractListModel abgeleitet wird (Listing 5).

Auf die verschiedenen Attribute eines ListItems greift die Software durch Rollen zu. Es müssen wenigstens die Funktionen data(), rowCount() und roleNames() überschrieben werden, um die Liste lesend darzustellen. Im vorliegenden Model werden auch die Zustände der Räume veränderbar sein, sodass auch die Funktion setData() überschrieben ist. Hier ist besonders auf das Emittieren des Signals dataChanged am Ende der Funktion hinzuweisen, ohne die keine Änderung propagiert wird.

Anstatt die Zugriffe auf Attribute per Rollen zu spezifizieren, gibt es auch die Möglichkeit, die Werte auf Spalten zu verteilen. In diesem Fall leitet man vom QAbstractTableModel ab und verteilt die Attribute auf Columns. Entsprechende Beispiele lassen sich der Dokumentation entnehmen.

Die Raumliste auf der rechten Seite wird also komplett ausgegeben. Bei der Übersicht links soll dagegen eine Filterung möglich sein. Da die Datenquelle gleich bleibt – alle Räume – wird lediglich ein QSortFilterProxyModel benötigt, das in diesem Fall das Herausfiltern aller nicht gewünschten Elemente übernimmt. Hierfür wird die Funktion filterAcceptsRow() überschrieben. Darüber hinaus könnte es eine Sortierung geben, indem lessThan() überschrieben wird.

Listing 6 zeigt den Vorteil registrierter Elemente innerhalb des QmlContexts: Der BuildingProxy muss im QmlContext bekannt gemacht werden, damit die ListView darauf zugreifen kann. Ist er aber einmal registriert, kann man von überall darauf zugreifen. Die Automatismen innerhalb von Qt veranlassen das Update der betroffenen Fenster.

LISTING 4: ROOMGRIDVIEW.QML

 

Tastatursteuerung

Ein immer wieder stark missverstandenes Konzept ist der Fokus – egal unter welchem Framework. Befinde ich mich in einer Eingabezeile und drücke [Tab], möchte ich in der nächsten Eingabezeile landen. So weit der einfache Fall. Aber das simple Highrise-Beispiel weist schon drei verschiedene und eigentlich gleichzeitig aktive Fokusse auf: die EditBox stellt die gerade genannte Eingabemöglichkeit dar. Es gibt noch ein fokussiertes Element, das beim Drücken von [Return] den Lichtstatus des Raums ändert – analog zu einem Doppelklick. Hierfür ist Keys.onReturnPressed verantwortlich.

 

LISTING 5: BUILDINGMODEL.H

 

Ebenso gibt es ein fokussiertes Element, das hier allerdings nur der Navigation dient. Jeweils ein Fokusbereich wird in QML durch einen FocusScope eingeschlossen, wie in den oben genannten Dateien zu sehen ist. Innerhalb Highrise/BuildingView.qml werden alle drei Bereiche durch activeFocusOnTab als erreichbar markiert.

 

Eigene Komponenten

Streng genommen wurden in Highrise bereits mehrere eigene Komponenten benutzt: Jede QML-Datei kann als Typ in einer anderen QML-Datei verwendet werden, so wie in Listing 4 und Listing 6 für die beiden Ansichten links und rechts.

Aber auch alle Dateien unter Highrise/components wurden als eigenständige Controls konzipiert, sei es die EditBox mit eingebettetem Icon oder die Scrollbar. Je mehr man mit Qt und Qml arbeitet, desto eher verzichtet man auf den Einsatz vorgefertigter Komponenten wie einen Button.

Einen Button kann man mithilfe der style-Property in begrenztem Umfang in seinem Aussehen beeinflussen. Aber durch den Einsatz von Rectangles, MouseAreas und sonstigen Basiselementen lässt sich das Einsatzgebiet einer Komponente noch genauer spezifizieren.

Als konkretes Beispiel sei ein RangeSlider genannt. also ein Slider mit einem unteren und einem oberen Wert. Es hilft nicht, zwei Slider übereinanderzulegen. Stattdessen nimmt man ein Rechteck als Zuglinie des Sliders, zwei Rectangles für die Thumbs, darin jeweils eine MouseArea, um die Thumbs anfassbar zu machen, und übernimmt die Werte mittels JavaScript-Code. Natürlich besteht auch die Möglichkeit, eigene Controls in C++ zu implementieren und sie innerhalb von Qml zur Verfügung zu stellen.

LISTING 6: BUILDINGVIEW.QML

 

Animationen

Zugegebenermaßen sind die Möglichkeiten von WPF durch den Einsatz von Storyboards gewaltig, allerdings nicht immer leicht einzusetzen. Es gibt Entwickler, die Blend nur verwenden, um Optionen der Storyboards zu konfigurieren.

Qml bietet an dieser Stelle ebenfalls eine Statemachine. In EditBox wird nur ein State deklariert: Wenn ein Text eingegeben wurde, verschwindet das Watermark-Icon. Wird der neue State betreten, werden die angegebenen Properties gesetzt. Wird er andererseits wieder verlassen, werden die Änderungen zurückgenommen. Hier knüpft sich der State an eine Bedingung. Der State kann aber auch durch eine Zuweisung state = “hasText” gewechselt werden.

Da das Ausblenden des Icons zu abrupt wäre, soll es animiert werden. Hier kommen die Transitions ins Spiel. Sie beschreiben die Übergänge im Allgemeinen oder von einem State in einen anderen. In den Transitions beschreiben die verschiedenen Animation-Elemente die Art der Animation. Es gibt beispielsweise einen OpacityAnimator, einen RotationAnimator oder allgemein eine NumberAnimation. Die Animationen können durch ParallelAnimation parallel ablaufen oder durch SequentialAnimation nacheinander, und beide Typen lassen sich schachteln.

Highrise

Das Beispielprojekt Highrise mit einer GridView und einer ListView

Eigene Ressourcen

Was man als WPF-Entwickler sofort vermisst, ist die Möglichkeit, seine Ressourcen in ResourceDictionaries auszulagern. Es ist einfach schlechter Stil, die Farbdefinitionen in allen Controls zu wiederholen. Aber es gibt Abhilfe: die Singleton- Module. Die erste Zeile der entsprechenden Qml-Datei lautet pragma Singleton, darin wird ein QtObject erzeugt mit allen Properties. Für ein Farb-Singleton lautet also beispielsweise eine Colors.qml

oder aber für Fonts.qml entsprechend

Beide Dateien werden als Singleton-Module registriert und können in jeder anderen QML-Datei benutzt werden:

Debugging

Werden im XAML die wesentlichen Probleme – wenn überhaupt – in der Console ausgegeben, greift man in QML auf console.debug() innerhalb des JavaScript-Codes zurück. Schwierig wird es, wenn Bindungen zu den Modellen nicht greifen oder Update-Signale nicht emittiert werden.

An dieser Stelle kann vielleicht dies helfen. Ähnlich Snoop stellt es den aktuellen Baum dar, listet Signale und verbundene Slots auf, visualisiert den Zustand der StateMachine und dergleichen mehr.

Fazit

Es gibt viele Gemeinsamkeiten zwischen QML und XAML. Was bei der einen Sprache ein Signal darstellt, übernimmt bei der anderen ein INotifyPropertyChanged. Es existieren Bindungen und Attached Properties. In beiden Welten entspricht es schlechtem Stil, zu viel Funktionalität innerhalb des Markups zu realisieren.

Aber es gibt auch große Unterschiede. Da Techniken wie Templating und ResourceDictionaries in der von WPF gewohnten Weise fehlen, ist die Herangehensweise bei Qt eine andere. Dafür erscheint die Lernkurve insgesamt etwas weniger steil. Angenehm fällt auch auf, dass die Anreicherung der Oberflächenfunktionalität mittels JavaScript direkter ist. Techniken wie Behaviors werden nicht gebraucht. Neben den genannten Plattformen fehlt das nahende Internet of Things. Es besteht die Möglichkeit, einen Qt-optimierten Software-Stack auf Geräten zu booten. Ob sich dieser Ansatz gegenüber Windows 10 behaupten wird, muss sich zeigen.

Dieser Artikel erschien erstmalig in der dotnetpro

Möchten Sie mehr zu unseren Leistungen, Produkten oder zu unserem UX-Prozess erfahren?
Wir sind gespannt auf Ihre Anfrage.

Luzie Seeliger

Projektkoordination und Kommunikation

+49 681 959 3110

Kontaktformular

  • Saarbrücken

    Science Park Saar, Saarbrücken

    Standort Südwest

    Hauptsitz Saarbrücken
    Centigrade GmbH
    Science Park 2
    66123 Saarbrücken
    Deutschland
    Saarland
    Auf der Karte

    +49 681 959 3110

    +49 681 959 3119

  • Mülheim an der Ruhr

    Games Factory Mühlheim an der Ruhr

    Standort Nordwest

    Geschäftsstelle Mülheim
    Centigrade GmbH
    Kreuzstraße 1-3
    45468 Mülheim an der Ruhr
    Deutschland
    Nordrhein-Westfalen
    Auf der Karte

    +49 208 883 672 89

    +49 681 959 3119

  • Haar · München

    Haar / München

    Standort Süd

    Geschäftsstelle München
    Centigrade GmbH
    Bahnhofstraße 18
    85540 Haar · München
    Deutschland
    Bayern
    Auf der Karte

    +49 89 20 96 95 94

    +49 681 959 3119

  • Frankfurt am Main

    Frankfurt am Main

    Standort Mitte

    Geschäftsstelle Frankfurt
    Centigrade GmbH
    Kaiserstraße 61
    60329 Frankfurt am Main
    Deutschland
    Hessen
    Auf der Karte

    +49 69 241 827 91

    +49 681 959 3119