Empfehlungen für Qt QML

Für die weitere Entwicklung von Apps sollten Sie sich mit einigen Details vertraut machen, die zur Verbesserung der Performance Ihrer Apps und Reduzierung von Problemen beitragen. In diesem Thema werden wichtige Details erläutert, die Sie beachten sollten.

Kartenansicht

ArcGIS Runtime SDK for Qt bietet drei Muster für das Anzeigen einer Karte in einer Kartenansicht. In AppStudio schreiben Sie unter Verwendung von QML eine eigene App, weshalb Sie den Kartentyp MapView verwenden.

Die QML-API von The Qt Company gibt Ihnen die Möglichkeit, mit QML – einer deklarativen, gut lesbaren Syntax für den Entwurf und die Erstellung responsiver und flüssiger Benutzeroberflächen für native Desktop- und mobile Anwendungen – plattformübergreifende Apps zu schreiben. ArcGIS Runtime SDK for Qt erweitert QML um QML-Typen mit ArcGIS Runtime-Funktionalität. Objekte werden hierarchisch deklariert und verfügen über bindbare Eigenschaften, um automatisch ein dynamisches Verhalten zu realisieren. JavaScript-Funktionen werden verwendet, um bei Bedarf prozeduralen Code bereitzustellen. Dies ist eine wichtige Funktion für Entwickler, die bereits mit der Webentwicklung vertraut sind und native Apps entwickeln möchten.

Deklarieren Sie ein Rectangle mit einer MapView. Deklarieren Sie innerhalb von MapView eine anzuzeigende Map und einen ersten Viewpoint.

Rectangle {
    width: 800
    height: 600
     property real scaleFactor: System.displayScaleFactor
     // Map view UI presentation at top
    MapView {
        id: mv
         anchors.fill: parent
        wrapAroundMode: Enums.WrapAroundModeDisabled
         Map {
            BasemapTopographic {}
            initialViewpoint: viewPoint
             FeatureLayer {
                id: featureLayer
                 ServiceFeatureTable {
                    id: featureTable
                    url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/SF311/FeatureServer/0"
                }
            }
        }
         ViewpointCenter {
            id: viewPoint
            center: Point {
                x: -13630484
                y: 4545415
                spatialReference: SpatialReference {
                    wkid: 102100
                }
            }
            targetScale: 300000
        }
    }

Stark typisierte Variablen

Beim Deklarieren von Eigenschaften in QML stellt die Verwendung des generischen Variablentyps var einen einfachen und bequemen Weg dar, jedoch ist die Verwendung von stark typisierten Variablen stets vorzuziehen. Eine stark typisierte Variable gibt explizit an, welcher Typ von Daten in der Variable gespeichert werden kann, z. B. ganze Zahlen, Zeichenfolgen oder Dezimalwerte. Durch stark typisierte Variablen kann die Zuweisung falscher Werte verhindert werden. Zudem sind sie im Vergleich zu Variablen des Typs "var" besser lesbar und lassen sich leichter debuggen. Bei einer Variablen des Typs "var" wird beispielsweise nicht verhindert, dass ein Dezimalwert zugewiesen wird, selbst wenn der Variablenwert nur für ganze Zahlen konzipiert ist.

Im folgenden Beispiel wird der ganzzahligen Variable "intValue" zunächst der Wert 10 zugewiesen. Zu einem späteren Zeitpunkt im Code wird dieselbe Variable mit einem Zeichenfolgenwert aktualisiert.

property int intValue = 10
...
intValue = "A string value"

Beim Ausführen des Codes wird in der Konsole ein Fehler angezeigt, der explizit auf die Zeile verweist, in der der Zeichenfolgenwert festgelegt wurde. Die Meldung lautet Error: Cannot assign QString to int, sodass die Problembehandlung des Codes leichter durchgeführt werden kann.

Wenn einer "var"-Variablen ein Wert des falschen Typs zugewiesen wird, wird kein Fehler gemeldet.

Skalierbare Benutzeroberflächen

Für das Design der App-Benutzeroberfläche ist es wichtig, dass Sie die Vielzahl an sich stetig weiterentwickelnden Bildschirmgrößen und -auflösungen berücksichtigen, mit denen die App verwendet werden kann. Zu diesem Zweck können Sie Symbole, Schaltflächen und Hintergründe für die einzelnen Anzeigeauflösungen einbinden oder Dateien im Format "Scalable Vector Graphic" (SVG) verwenden.

Wenn Sie einen Bildsatz mit verschiedenen Auflösungen einbinden, sollten Sie für die Dateien eine Namenskonvention wählen, mit der sich die einzelnen Bildgrößen klar voneinander unterscheiden lassen. Beispiel: Die Namen "Bildname1x.png", "Bildname1.5x.png" und "Bildname2x.png" geben an, dass es sich jeweils um ein Bild mit 1-fachem, 1,5-fachem und 2-fachem DPI-Basiswert handelt.

SVG-Dateien eignen sich optimal für kleine Bilder und werden in der Regel für Symbole verwendet. Große SVG-Dateien werden u. U. langsam gerendert, sodass für Hintergründe die Verwendung von Bildern zu erwägen ist.

Weitere Informationen finden Sie in der Qt-Dokumentation unter "Scalable User Interfaces".

Debuggen der App

AppStudio baut auf der Qt-Umgebung auf. Sie erhalten also Zugriff auf alle Debugging-Dienstprogramme, die auch für Qt verwendet werden. Im Folgenden finden Sie einige hilfreiche Tipps und Tricks für das Debuggen Ihrer Qt-Apps.

Verwenden eines Web-Proxy

Beim Debuggen von AppStudio-Apps ist ein Web-Debugging-Dienstprogramm oft nützlich, um die gesendeten HTTP-Anforderungen und die zurückgegebenen Antworten zu erfassen und zu analysieren. Fiddler und Charles sind zwei Beispiele für Web-Debugging-Dienstprogramme, die hierfür verwendet werden. Sie können diese Werkzeuge zum Debuggen einer App verwenden, indem Sie den folgenden C++-Code in die App einfügen: QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8888));. Dieser Code kann zur Laufzeit gesetzt werden. Er kann sich entweder in main.cpp oder in einer anderen C++-Klasse befinden. Über das Verwenden eines Proxys für das Debuggen hinaus kann mit QNetworkProxy::setApplicationProxy() auch der Proxyserver Ihrer Organisation angegeben werden. Geben Sie dazu die URL und den Port des Proxy-Servers Ihrer Organisation an. Weitere Informationen finden Sie in der AppFramework-Dokumentation zu NetworkProxy.

Verwenden von JSON.stringify()

Vermeiden Sie es, bei Verwendung der Methode JSON.stringify() die Objektreferenz selbst zu übergeben. Verwenden Sie die Eigenschaft json des Objekts als Argument. Beispiel: JSON.stringify(graphic.geometry.json):

QML-API-Speichermodell

Das für QML-Apps verwendete Speichermodell basiert auf automatischer Speicherbereinigung. Nicht referenzierte Variablen werden mit der automatischen Speicherbereinigung der QML Engine bereinigt. Daher müssen alle Objekte ausdrücklich referenziert werden, um die automatische Speicherbereinigung zu verhindern, wenn sie weiterhin verwendet werden.

Objekterstellung mit JavaScript

Mit der Methode ArccGISRuntimeEnvironment.createObject() werden neue Instanzen eines Objekts erstellt und über die QML-Engine zurückgegeben. Die Lebensdauer dieser Instanzen wird durch das Parent-Objekt der Instanz bestimmt. Bei der automatischen Speicherbereinigung für das Parent-Objekt werden auch die Child-Instanzen bereinigt. Es gibt verschiedene Möglichkeiten, eine mit ArccGISRuntimeEnvironment.createObject() erstellte Instanz beizubehalten. Sie können u. a. folgendermaßen vorgehen:

  • Sie können das Parent-Objekt im dritten, optionalen Parameter von ArccGISRuntimeEnvironment.createObject() angeben.
  • Deklarieren Sie das Objekt in Ihrem deklarativen QML-Code als Eigenschaft eines anderen Objekts. Instanziieren Sie das Objekt anschließend im JavaScript-Code mit ArccGISRuntimeEnvironment.createObject(). Die Klasse, die die Eigenschaft enthält, wird zum Parent der neuen Instanz. Achten Sie darauf, die Eigenschaft mit dem gleichen ArcGIS Runtime-QML-Typ zu deklarieren, den Sie instanziieren.

Wenn Sie kein Parent-Objekt angeben, wird JavaScript als Parent der Objektinstanz festgelegt. QML verfolgt die Instanz und löscht sie, wenn keine JavaScript-Verweise auf die Instanz mehr vorhanden sind (d. h. wenn die Instanz nicht mehr im Geltungsbereich liegt).

Einige ArcGIS Runtime-QML-Typen enthalten eine clone()-Methode zum Erstellen einer Objektinstanz dieses Typs. Wenn Sie clone() zum Erstellen einer Instanz verwenden, ist JavaScript das Parent. Mithilfe der Eigenschaft parent können Sie das Parent ändern.

Arbeiten mit Modellen

Modelle greifen explizit auf Objektinstanzen zu. Beim Arbeiten mit Modellen müssen persistente Daten bereitgestellt werden, damit das Modell darauf zugreifen kann. Das direkte Platzieren neu erstellter Objektinstanzen in ein QML-ListModel reicht nicht aus, um die Lebensdauer dieser Instanzen zu erhalten, da dabei keine Verweisanzahl für die Instanzen ermittelt wird. (Informationen zur Objekterstellung mit JavaScript finden Sie im vorherigen Abschnitt.) Eine Möglichkeit zur Vermeidung der automatischen Speicherbereinigung besteht darin, eine Eigenschaft zu erstellen, die eine Liste ist, und der persistent gespeicherten Liste die Instanzen hinzuzufügen. Die Lebensdauer der Listeneigenschaft entspricht der des Parent.

// In a QML declaration:
ListModel {
  id: myListModel
  property var geoms: []
}

//...
// In a JavaScript function:
var newPoint = point.clone();

// avoid the following, because newPoint is not reference counted 
// myListModel.append(
//    {"geometry", newPoint
//    });  

// do this instead to give newPoint the same lifetime as myListModel
myListModel.geoms.push(newPoint);

Dieses Konzept gilt auch für Listenobjektinstanzen auf der Basis von QQmlListProperty. Eine solche Liste kann zwar direkt als Eingabe für ein Modell festgelegt werden, Sie sollten dies jedoch vermeiden. Das Modell erstellt nicht explizite Verweise auf die Listenelemente, sodass für diese eine automatische Speicherbereinigung erfolgt. Um die richtigen Ergebnisse zu erzielen, weisen Sie zuerst alle Listenelemente einer lokalen Liste hinzu. Diese expliziten Referenzen werden beibehalten und verhindern die vorzeitige automatische Speicherbereinigung. Im folgenden Beispiel wird eine list-Komponente erstellt, und es werden alle Felder aus der Feature-Tabelle eingefügt. Anschließend wird die Liste als Modell von ComboBox festgelegt.

GeodatabaseFeatureTable {
    id: featureTable
}

ComboBox {
    id: comboBox
//    model: featureTable.fields // avoid this, because the model will create non-explicit references
    textRole: "name"
}

property var fieldList: []

function initFieldListModel() {
    for (var i = 0; i < featureTable.fields.length; ++i) {
        fieldList.push(featureTable.fields[i]);
    }
    comboBox.model = fieldList;
}

Eindeutigkeit von Objekten

Es gibt keine Garantie dafür, dass Sie dasselbe Objekt für denselben Getter- oder Funktionsaufruf zurück erhalten. Deshalb sollten direkte Objektvergleiche wie im folgenden Beispiel vermieden werden:

Map { id: map }
...
var viewpoint = map.initialViewpoint;
// Avoid comparisons like the following code.
// Although the objects' contents are identical, 
// the objects may be different objects.
if (viewpoint === map.initialViewpoint)

Benutzerdefinierte Eigenschaften bei QML-Typen

Eine benutzerdefinierte Eigenschaft ist eine Eigenschaft, die einer Instanz eines QML-Typs beim Deklarieren hinzugefügt wird. Die Eigenschaft ist nicht Teil des Typs, sondern liegt als benutzerdefinierte Eigenschaft der spezifischen Instanz vor. Mit QML können Sie Instanzen bei der Deklaration benutzerdefinierte Eigenschaften hinzuzufügen. Sie können ArcGIS Runtime-QML-Instanzen zwar benutzerdefinierte Eigenschaften hinzufügen, eine solche Eigenschaft wird jedoch aufgrund der Art der Verwaltung von Instanzen innerhalb der API möglicherweise nicht in allen Fällen beibehalten. Vermeiden Sie daher die Verwendung benutzerdefinierter Eigenschaften für Instanzen von Typen, die diese nicht unterstützen.

Die folgenden QML-Typen sowie von diesen abgeleiteten Typen bieten Unterstützung für benutzerdefinierte Eigenschaften. Für andere QML-Typen in ArcGIS Runtime gilt dies nicht.

  • ArcGISMapServiceInfo
  • Grundkarte
  • Credential
  • FeatureTable
  • Geodatabase
  • GraphicsOverlay
  • LayerContent
  • Karte
  • Portal
  • PortalTask
  • VectorTileSourceInfo

Arbeiten mit JSON

Für viele QML-Typen erfolgt eine Vererbung aus JsonSerializable, wodurch Sie die Möglichkeit haben, den Typ in JSON zu serialisieren oder mit Inhalt zu füllen. Wenn Sie ein Objekt aus JSON erstellen, erstellen Sie zuerst eine Instanz und übergeben diese Instanz dann als Eingabe an die Methode.

Mit diesem JavaScript-Code wird ein PictureMarkerSymbol-Objekt aus JSON erstellt, befüllt und in einer neuen Grafik verwendet.

Graphic {
  id: myGraphic


  property var pmsJson: {"type": "esriPMS", "url": "http://myurl.com/folder/my-icon.png","width": 60,"height": 60, "angle":20}


  Component.onCompleted: {
    var pmsFromJson = ArcGISRuntimeEnvironment.createObject("PictureMarkerSymbol", {json: pmsJson});
    pmsFromJson.errorChanged.connect(function(error){ verify(false, error.message + " (" + error.additionalMessage + ")"); });
    myGraphic.symbol = pmsFromJson;
  }
}

Arbeiten mit übersetzten Zeichenfolgen

Alle Zeichenfolgen, die in der App-Benutzeroberfläche angezeigt werden, sollten die Formatierung "qsTr()" aufweisen, damit die App globalisiert werden kann.

Es ist zudem wichtig, übersetzte Zeichenfolgen bereits beim Definieren der App-Logik zu berücksichtigen. Wenn der Benutzer basierend auf Zeichenfolgen in der Benutzeroberfläche eine Auswahl trifft, beispielsweise bei der Auswahl aus einem Dropdown-Menü, müssen alle case-Ausdrücke in der switch-Anweisung die übersetzte Zeichenfolge verwenden.

Im folgenden Beispiel wird unabhängig von der Anzeigesprache der App der Bereich "Info" angezeigt, wenn das Menüelement "Info" ausgewählt wird:

case qsTr("About"):
    pageView.hideSearchItem()
    panelDockItem.addDock("aboutPanel")
    break

Rendering mit OpenGL oder ANGLE unter Windows

Qt verfügt über zwei verschiedene Rendering-Engines für die Windows-Plattform: OpenGL und DirectX über ANGLE. Standardmäßig versucht Qt, mit OpenGL zu rendern. Wenn die benötigten Treiber nicht verfügbar sind, wird zum Rendern auf ANGLE zurückgegriffen. In früheren Versionen von ArcGIS Runtime wurde nur ANGLE unterstützt. Ab ArcGIS Runtime 100.3.0 können Ihre Apps sowohl OpenGL als auch ANGLE vollständig unterstützen, sodass Sie beide Rendering-Engines sowie den Fallback-Mechanismus von Qt nutzen können, wenn OpenGL auf einem bestimmten System nicht verwendet werden kann. Weitere Informationen finden Sie in der Qt-Dokumentation.

In den AppStudio-Optionen unter Einstellungen > Plattformen > Windows > Grafik-Rendering-Engine legen Sie fest, ob das Rendering standardmäßig mit OpenGL oder ANGLE erfolgt. Es sind keine Codeänderungen notwendig.