Open Source im professionellen Einsatz
Linux-Magazin 04/2011
1342

Ans Eingemachte: Der Code

Die Methoden der Beispiel-App rufen eine C++-Klasse auf. Die Hilfsklasse bedarf keiner Bildschirmausgabe, daher ist sie kein sichtbares »QDeclarative«-Item: Sie leitet sich schlicht von »QObject« ab.

Tipparbeit erspart dem Programmierer der »Class Wizard« (Abbildung 2), er wählt Dateinamen nach dem Klassennamen, was sich auf Wunsch auch ändern lässt. »Next« führt zur »Summary«-Seite, ein letztes »Finish« erzeugt die Dateien. Im Editor erscheint jetzt die Datei »picturelabeler.cpp«.

Abbildung 2: Der C++-Class-Wizard erzeugt Header und Implementierungsdatei einer neuen Klasse und trägt sie in das Projekt ein.

Um die Deklaration anzupassen, erhält die vom Wizard erzeugte Klassendeklaration »picturelabeler.h« im Zweig »Headers« des Projektbaums das Makro »Q_INVOKABLE«, das QML später aufrufen wird. Der Abschnitt »public« sollte jetzt so aussehen:

public:
    explicit PictureLabeler(
        QObject *parent = 0 );
    // returns the timestamp
    // or an empty string on error
    Q_INVOKABLE QString addTimestamp(
        const QString& pictureFilename );

Zurück in der Implementierungsdatei klickt der Qt-Entwickler im Bereich »Open Documents« auf »picturelabeler.cpp« (Listing 1). Nach den Includes am Textbeginn bleibt der Konstruktor leer (Zeile 8). Ab Zeile 13 implementiert er die neue Methode »addTimestamp()«.

Listing 1:
»picturelabeler.cpp«

01 #include <QDateTime>
02 #include <QFont>
03 #include <QImage>
04 #include <QPainter>
05
06 #include "picturelabeler.h"
07 
08 PictureLabeler::PictureLabeler( 
09   QObject *parent) : QObject(parent)
10 {
11 }
12 
13 QString PictureLabeler::addTimestamp(
14   const QString& pictureFilename )
15 {
16   const QString dt =
17     QDateTime::currentDateTime().toString();
18   QImage img( pictureFilename );
19 
20   if( ! img.isNull() ) {
21     QPainter painter( &img );
22     painter.setPen( Qt::blue );
23     painter.setFont(QFont( "Arial", 32 ) );
24     painter.drawText(16,img.height()-18, dt );
25 
26     if( img.save( pictureFilename ) )
27         return dt;
28   }
29   return QString("");
30 }

Um die Klasse QML zur Verfügung zu stellen, muss die »main.cpp« des Projekts ihren Typ bei der QML-Engine registrieren. Dafür erhält sie zu Beginn die zwei benötigten Header und nach Instanzierung der »QApplication« die Template-Methode »qmlRegisterType()«:

#include "picturelabeler.h"
#include <qdeclarative.h>
[...]
  QApplication app(argc, argv);
  qmlRegisterType<PictureLabeler>(
      "com.basyskom.qmlcomponents",
      1, 0, "PictureLabeler" );[...]

Nun darf der QML-Code auf die C++-Hilfsklasse zugreifen. Die App entsteht im Weiteren allein durch Editieren der QML-Hauptdatei.

Das Beispiel spricht Qt Mobility nur von QML aus an, daher benötigt es keine Anpassungen in der Datei »TimePic.pro«. Wer auch von C++ aus auf Qt Mobility zugreifen möchte, beachtet die Hinweise im Kasten "Tipps und Tricks". Die vollständige QML-Hauptdatei liegt mit allen anderen Sourcen auf dem Listing-Server bereit [2]. Weitere QML-Beispiele sammelt die Doku-Seite zu Qt Quick [3]. Im Folgenden zeigt ein kurzes Codebeispiel die Konzepte, die in der Beispiel-App Anwendung finden.

Tipps und Tricks

Während der Artikel entstand, haben die Autoren eine Reihe Erfahrungen gesammelt. Mögen sie anderen Entwicklern beim Umschiffen derselben Probleme helfen!

Qt Mobility und C++

Wenn die Anwendung von C++ aus auf Qt Mobility zugreifen soll, ist der erste Schritt ein Doppelklick im Projektpanel von Qt Creator auf die betreffende Datei. Würde die Beispielanwendung nicht von QML, sondern von C++ auf das Qt-Mobility-Camera-API zugreifen, so wäre in der ».pro«-Datei das Qt-Mobility-Modul »multimedia« anzumelden, wie es der blau hinterlegte Text in Abbildung 4 zeigt.

Abbildung 4: Wenn der Entwickler statt über QML in C++ auf das Kamera-API zugreifen möchte, fügt er in der Projektdatei des Qt-Build-Tools Qmake die Auswahl des Qt-Mobility-Moduls »multimedia« hinzu. Das Programmierbeispiel wählt stattdessen den Weg über QML.

Fehler beim Kopieren auf das Target

Beim Kopieren der App auf das N900 kann es im Output-Fenster des Creator zu Fehlermeldungen kommen, weil ein anderes Programm die Systemdatenbank belegt. Abhilfe schafft, auf dem Gerät alle anderen Programme außer dem Fenster »Developer Passwort« des Mad-Developer zu schließen.

Möglicherweise erscheint im Output-Fenster nun diese Fehlermeldung:

Starting remote process ...
file:///opt/usr/share/TimePic/qml/TimePic/U main.qml:1:1: module "QtQuick" U 
is not installed
     import QtQuick 1.0
     ^
Terminated

Sie bedeutet, dass statt Qt 4.7.1 das schwächere 4.7.0 installiert ist. Falls die Systemintegrität nicht durch ein isoliertes Qt-Update in Gefahr geraten soll, ist eine geänderte erste Zeile in »main.qml« die Alternative: Statt »import QtQuick 1.0« muss sie »import Qt 4.7« lauten.

Möglich ist auch ein ELF-Error beim Ausführen auf dem Gerät. Erscheint beim Run mit Target »Maemo« und »release« die Fehlermeldung »:-1: error: main.o: Relocations in generic ELF (EM: 3)«, so hilft es, mit den Menü-Optionen »Build | Rebuild all« einen neuen Versuch zu starten.

Fehler beim Ausführen der App im Target»Desktop«

Der beim Entstehen des Artikels verwendeten Technology Preview des Qt SDK fehlte das QML-Modul von Qt Mobility für den Desktop. Das führte dazu, dass das QML-Programm beim Befehl »import QtMultimedia-Kit 1.1« unverrichteter Dinge abbrach. Es vermisste offenbar die Datei »QTSDK//Desktop/Qt/471/gcc/lib/libQtMultimediaKit.so.1.0.2«. Ein Suchlauf könnte sich lohnen - eventuell kursieren dazu inzwischen Tipps in Internetforen.

Hallo QML

Das in Listing 2 gezeigte QML-Programm erzeugt der Projekt-Wizard für jede neue Qt-Quick-Anwendung. Der Wizard deklariert per Default im »Hello World«-Hauptelement die »MouseArea« parallel zu einem »Text«-Element. Damit deckt sie die gesamte Fläche des umgebenden Hauptrechtecks ab: Ein Mausklick irgendwo im Fenster schließt die App. Wandert die »MouseArea« aber wie in Listing 3 in die Deklaration des »Text«-Elements, beenden nur Klicks auf den Text die App, sie ignoriert Mausereignisse in der leeren Fläche um den Text herum.

Listing 2: Mausklick im
Hauptfeld

01 Rectangle {
02   width: 360
03   height: 360
04   Text {
05     text: "Hello World"
06     anchors.centerIn: parent
07   }
08   MouseArea {
09     anchors.fill: parent
10     onClicked: {
11       Qt.quit();
12     }
13   }
14 }

Zu sehen war bereits, dass der Entwickler C++-Methoden für QML verfügbar macht, indem er sie in der betreffenden Headerdatei mit dem Makro »Q_INVOKABLE« kennzeichnet. Auch in Listing 3 ruft Zeile 7 eine C++-Funktion auf, um die App zu beenden: »Qt.quit()« schließt die Hauptanwendung einschließlich Viewer mit der laufenden QML-App. Dies funktioniert nur, wenn man im Wizard durch die Wahl des Typs »Qt Quick Application« eine hybride App erzeugt. Eine solche App startet im C++-Hauptprogramm eine »QApplication« und verwendet die Klasse »QmlApplicationViewer«, um den QML-Code zu laden, zu parsen und auszuführen.

Listing 3: Mausklick im
Textfeld

01 Text {
02   text: "Hello World"
03   anchors.centerIn: parent
04   MouseArea {
05     anchors.fill: parent
06     onClicked: {
07       Qt.quit();
08     }
09   }
10 }

Im Folgenden kommt außerdem das Konzept zum Tragen, dass QML-Elemente zumeist rechteckige Bereiche des Programmfensters sind. Als oberste Ebene dient oft das Element »Rectangle«, möglich wären auch andere deklarative Objekte. Die Referenz [4] führt alle QML-Elemente auf. Unsichtbare Elemente wie die »MouseArea« leisten Hilfsdienste: Ebenso wie die sichtbaren Elemente enthalten sie Deklarationen und Steuercode, der meist in kleinen Funktionen steckt (hier die »onClicked()«-Methode). Schließlich stellen Eigenschaften einen Großteil des üblichen QML-Code. Werte für »width« und »text« setzen die in »anchors« zusammengefassten Properties in Bezug zueinander, etwa »fill« oder »centerIn«. Das »Item«-Element dient als Basis aller sichtbaren QML-Elemente, ein Blick in die Dokumentation lohnt [5].

Die hybride Beispiel-App »TimePic« deklariert zu Beginn im Hauptelement zunächst drei Properties vom Typ »int«, um den in QML nicht vorhandenen Aufzählungstyp Enum zu simulieren (Listing 4, Zeilen 1 bis 3). Der symbolische Name »pageModeCommandView« in Zeile 3 ist sicherer, als wenn dort »pageMode = 2« stünde: Mögliche Tippfehler erkennt der QML-Parser bei Namen, nicht jedoch bei Zahlen.

Listing 4: Der
Labeler

01 property int pageModeCommandView: 1;
02 property int pageModeImageView:   2;
03 property int pageMode: pageModeCommandView;
04 [...]
05 PictureLabeler {
06   id: labeler
07 }
08 
09 Camera {
10   id: cameraObject
11   anchors.centerIn: parent
12   width: 100
13   height: 100
14 
15   onImageCaptured: {
16     console.log("Image captured!" )
17   }
18 
19   onImageSaved: {
20     console.log("Image saved!" )
21     imageFilename.color = "blue"
22     imageFilename.text = "images files " + capturedImagePath
23 
24     var stamp = labeler.addTimestamp(capturedImagePath) 

Aus den importierten C++-Modulen instanziert die App als Nächstes die Klassen »PictureLabeler« als »labeler« (Zeile 5) und »Camera« als »cameraObject« (Zeile 9). Der Labeler erhält nur eine ID, das Kamera-Objekt auch Eigenschaften. Das Namensschema der Methoden »onImageCaptured()« und »onImageSaved()« (Zeilen 15 und 19) folgt dem Präfix »on« gefolgt von einem Signal der C++-Klasse, das einen großen Anfangsbuchstaben erhält. Darum verknüpft die QML-Engine die Methode automatisch als Slot mit dem Signal.

Bei Redaktionsschluss dieses Artikels war das QML-Element »Camera« zwar in Qt Mobility 1.1 enthalten, sein API aber noch nicht dokumentiert. Die Sourcen auf Gitorious [6] stellten sich als wertvolle Informationsquellen heraus.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 7 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

comments powered by Disqus

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.