Meilleures pratiques Qt QML

Il est important de connaître certains éléments pouvant optimiser les performances de vos applications et réduire les problèmes éventuels lorsque vous développez des applications. Cette rubrique aborde les détails importants que vous devez garder à l'esprit.

Vue cartographique

ArcGIS Runtime SDK for Qt offre trois façons d’afficher une carte dans une vue cartographique. Dans AppStudio, vous écrivez votre application en QML, donc vous utilisez le type de carte MapView.

L’API QML de Qt Company vous permet d’écrire des applications multiplateformes avec QML, une syntaxe déclarative d’une grande lisibilité pour la conception et la création d’interfaces utilisateur réactives et fluides destinées aux applications bureautiques et mobiles natives. ArcGIS Runtime SDK for Qt prolonge le QML via des types QML qui offrent des fonctions ArcGIS Runtime. Les objets sont déclarés hiérarchiquement et ont des propriétés pouvant être liées afin de fournir automatiquement un comportement dynamique. Les fonctions JavaScript permettent de fournir un code procédural lorsque cela est nécessaire. Il s’agit d’une fonctionnalité importante pour les développeurs déjà familiarisés avec le développement Web et qui souhaitent développer des applications natives.

Déclarer un Rectangle contenant une MapView. Dans MapView, déclarez une Map à afficher et un Viewpoint initial.

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
        }
    }

Variables fortement typées

Lorsque vous déclarez des propriétés dans QML, même s’il est simple et pratique d’utiliser le type de variable générique var, il est toujours préférable d’utiliser des variables fortement typées. Une variable fortement typée déclare explicitement quel type de données peut être stocké dans la variable : un entier, une chaîne ou une valeur décimale par exemple. Les variables fortement typées peuvent empêcher l’attribution de valeurs incorrectes, et elles sont plus faciles à lire et à déboguer que les variables de type var. Une variable de type var n’empêche pas une valeur décimale de lui être attribuée, même si la valeur n’était censée accueillir que des entiers.

Dans l’exemple suivant, la valeur 10 est initialement attribuée à la variable entière intValue. Plus loin dans le code, la même variable est mise à jour avec une valeur de chaîne.

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

Lorsque ce code est exécuté, une erreur s’affiche dans la console. Cette erreur désigne explicitement le numéro de ligne où la valeur de chaîne a été définie et indique Error: Cannot assign QString to int (Erreur : impossible d’attribuer QString sur un entier), ce qui facilite la résolution du code.

Si une valeur avec un type incorrect est attribuée à une variable de type var, aucune erreur n’est signalée.

Interfaces utilisateur évolutives

Lorsque vous concevez l’interface utilisateur de votre application, il est important de tenir compte de la plage toujours évolutive des résolutions et tailles d’écran sur lesquelles votre application risque d’être utilisée. Pour ce faire, vous pouvez ajouter des icônes, des boutons et des arrière-plans pour chaque résolution d’affichage ou recourir aux graphiques vectoriels évolutifs (SVG).

Lorsque vous incluez un ensemble d’images à différentes résolutions, choisissez une convention de nom de fichier qui fait clairement la différence entre les tailles d’image. Par exemple : image-nom1x.png, image-nom1.5x.png, image-nom2x.png représentent les images qui sont égales à 1 fois le PPP de base, 1,5 fois le PPP de base et 2 fois le PPP de base.

Les fichiers SVG sont parfaits pour les petites images, par exemple les icônes. Le rendu des fichiers SVG volumineux pouvant être lent, utilisez les images pour les arrière-plans.

Pour plus d’informations, consultez la rubrique sur les interfaces utilisateur évolutives de Qt.

Déboguer vos applications

AppStudio s’appuie sur la structure Qt, ce qui vous permet d’utiliser les mêmes utilitaires de débogage que ceux disponibles avec Qt. Voici quelques conseils et astuces utiles pour déboguer vos applications Qt.

Utilisation d’un proxy Web

Un utilitaire de débogage Web est souvent utile pour déboguer des applications AppStudio afin de capturer et d’analyser les requêtes HTTP envoyées et les réponses reçues. Fiddler et Charles sont deux exemples d’utilitaires de débogage Web utilisés pour effectuer cette tâche. Ces outils peuvent être utilisés pour déboguer une application en ajoutant le code C++ suivant à votre application : QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8888));. Ce code peut être défini au moment de l’exécution et se trouver dans votre classe main.cpp ou dans une autre classe C++. Outre l’utilisation d’un proxy pour le débogage, QNetworkProxy::setApplicationProxy() permet également de spécifier le serveur proxy de votre organisation. Pour cela, spécifiez l’URL et le port du serveur proxy de votre organisation. Pour plus d’informations consultez la documentation NetworkProxy d’AppFramework.

Utilisation de JSON.stringify()

Lorsque vous utilisez la méthode JSON.stringify(), évitez de transmettre la référence d’objet elle-même. Utilisez la propriété json de l’objet comme argument, par exemple : JSON.stringify(graphic.geometry.json).

Modèle de mémoire de l’API QML

Le modèle de mémoire utilisé pour les applications QML repose sur le nettoyage de la mémoire. Les variables non référencées sont nettoyées par le récupérateur de mémoire du moteur QML. Par conséquent, tous les objets doivent être explicitement référencés pour ne pas être nettoyés lorsqu'ils ne sont pas utilisés.

Création d'objets avec JavaScript

La méthode ArccGISRuntimeEnvironment.createObject() crée et renvoie de nouvelles instances d’un objet via le moteur QML. La durée de vie de ces instances est déterminée par l’objet parent de l’instance. Lorsque le parent est nettoyé de la mémoire, ses instances enfant le sont également. Il existe plusieurs façons de prolonger une instance créée avec ArccGISRuntimeEnvironment.createObject(), notamment les suivantes :

  • Vous pouvez spécifier l’objet parent dans le troisième paramètre facultatif de ArccGISRuntimeEnvironment.createObject().
  • Dans votre code déclaratif QML, déclarez l’objet en tant que propriété d’un autre objet. Puis dans le code JavaScript, instanciez l’objet avec ArccGISRuntimeEnvironment.createObject(). La classe contenant la propriété devient le parent de la nouvelle instance. Veillez à ce que la propriété que vous déclarez soit du même type QML ArcGIS Runtime que celui que vous instanciez.

Si aucun parent n’est spécifié, JavaScript est défini comme parent de l’instance de l’objet. QML suit l’instance et la supprime lorsqu’il ne reste plus de référence JavaScript à l’instance (c’est-à-dire, lorsque l’instance quitte son champ d’action.)

Certains types QML ArcGIS Runtime intègrent une méthode clone() permettant de créer une instance d’objet de ce type. Lorsque vous utilisez clone() pour créer une instance, JavaScript est son parent. Vous pouvez changer le parent à l’aide de la propriété parent.

Utilisation des modèles

Les modèles accèdent explicitement aux instances d’objets. Lorsque vous utilisez des modèles, il est nécessaire de fournir des données conservées auxquelles le modèle peut accéder. Placer des instances d’objets nouvellement créées directement dans un ListModel QML ne suffit pas à conserver la durée de vie de ces instances car ceci ne permet pas de référencer ces instances. (Reportez-vous à la section précédente concernant la création d’objets avec JavaScript.) Une façon d’éviter le nettoyage par la mémoire est de créer une propriété qui soit une liste et d’ajouter les instances à la liste conservée. La durée de vie de la propriété de liste est celle de son 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);

Ce concept s’applique également pour lister les instances d’objets basées sur QQmlListProperty. Bien qu’il soit possible d’assigner directement une telle liste en tant qu’entrée de modèle, ceci devrait être évité. Ce modèle crée des références non-explicites aux éléments de la liste ; les éléments sont ainsi nettoyés de la mémoire. Pour obtenir les résultats corrects, assignez tout d’abord tous les éléments de la liste à une liste locale. Ces références explicites sont conservées et ne sont pas nettoyées prématurément de la mémoire. L’exemple suivant génère un composant list et insère tous les champs de la table d’entités. Ensuite, il définit la liste comme modèle de ComboBox.

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;
}

Unicité de l'objet

Il n'est pas certain que vous receviez le même objet, même pour le même appel de fonction ou la même méthode getter. C’est pourquoi la comparaison directe d’objets doit être évitée, par exemple : 

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)

Propriétés personnalisées sur les types QML

Une propriété personnalisée (définie par l’utilisateur) est une propriété que vous ajoutez à une instance d’un type QML lors de sa déclaration. La propriété ne fait pas partie du type mais existe en tant que propriété personnalisée sur l’instance spécifique. Le langage QML vos permet d’ajouter des propriétés personnalisées aux instances lorsque vous les déclarez. S’il est possible d’ajouter des propriétés personnalisées aux instances de type QML ArcGIS Runtime, ces propriétés peuvent ne pas être préservées dans certains cas en raison de la façon dont les instances sont gérées dans l’API. Il faut par conséquent éviter d’utiliser des propriétés personnalisées sur les instances lorsque le type ne permet pas de les utiliser.

Les types QML suivants, et les types qui en sont dérivés, permettent d’utiliser les propriétés personnalisées. Les autres types QML ArcGIS Runtime ne le permettent pas.

  • ArcGISMapServiceInfo
  • Fond de carte
  • Informations d’identification
  • FeatureTable
  • Géodatabase
  • GraphicsOverlay
  • LayerContent
  • Carte
  • Portail
  • PortalTask
  • VectorTileSourceInfo

Utilisation de JSON

De nombreux types QML héritent de JsonSerializable, ce qui vous permet de sérialiser le type, ou de renseigner ses contenus, dans JSON. Pour créer un objet dans JSON, créez tout d’abord une instance, puis transmettez cette instance comme entrée pour la méthode.

Ce code JavaScript crée et renseigne un PictureMarkerSymbol à partir du format JSON et l’utilise dans un nouveau graphique.

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;
  }
}

Utilisation des chaînes traduites

Toutes les chaînes qui apparaissent dans l’interface utilisateur d’une application doivent utiliser le format qsTr() pour s’assurer que vous pouvez globaliser votre application.

Il est également important de tenir compte des chaînes traduites lorsque vous définissez la logique de votre application. Si un utilisateur sélectionne une option en fonction des chaînes de l’interface utilisateur, par exemple une sélection dans un menu déroulant, chaque cas à l’intérieur de la déclaration de bascule doit utiliser la chaîne traduite.

Dans l’exemple suivant, le volet About (À propos) apparaît lorsque l’élément de menu About (À propos) est sélectionné, quelle que soit la langue dans laquelle l’application s’affiche.

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

Rendu OpenGL ou ANGLE sur Windows

Qt possède deux moteurs de rendu différents pour la plateforme Windows : OpenGL et DirectX via ANGLE. Par défaut, Qt tente d’utiliser OpenGL pour le rendu. Si les pilotes corrects ne sont pas disponibles, Qt utilise ANGLE pour le rendu. Les versions précédentes de ArcGIS Runtime prenaient uniquement en charge ANGLE. Depuis ArcGIS Runtime 100.3.0, vos applications peuvent prendre totalement en charge OpenGL et ANGLE, vous permettant ainsi d’utiliser l’un des deux moteurs de rendu et le mécanisme de secours de Qt lorsque OpenGL ne peut pas être utilisé sur un système donné. Pour plus d’informations, reportez-vous à la Documentation de Qt.

Pour choisir entre le rendu par défaut OpenGL et ANGLE, utilisez les options Settings (Paramètres) > Platforms (Plateformes) > Windows > Graphics Rendering Engine (Moteur de rendu graphique) dans AppStudio. Il n’est pas nécessaire de modifier le code.