Qt QML best practices

As you continue developing apps, you should know a few details that can make your apps perform better and encounter fewer problems. This topic discusses important details that you should keep in mind.

Map view

The ArcGIS Runtime SDK for Qt offers three patterns for displaying a map in a map view. In AppStudio, you will write your app in QML, so you will use the MapView map type.

The QML API from The Qt Company, allows you to quickly produce apps with QML. QML offers a declarative, highly readable syntax for designing and building responsive and fluid user interfaces for native desktop and mobile applications. ArcGIS Runtime SDK for Qt extends QML with QML types that provide ArcGIS Runtime functionality. Objects are declared hierarchically and have bindable properties to provide automatically dynamic behavior. JavaScript functions are used to provide procedural code when needed. This is a powerful feature for developers who are familiar with web development and want to develop native apps.

Declare a Rectangle containing a MapView . In the MapView , declare a Map to display and an initial Viewpoint .

                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
        }
    }

Debugging your app

AppStudio is built on top of the Qt Framework, which means you have access to all of the same debugging utilities used with Qt. Below are some helpful tips and tricks for debugging your Qt apps.

Using a web proxy

When debugging AppStudio apps, a web debugging utility is often useful to capture and analyze the HTTP requests that are sent and responses that are returned. Fiddler and Charles are two examples of web debugging utilities used to perform this task. You can use these tools to debug an app by adding the following C++ code in your app: QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8888)); . This code can be set at runtime and can be either in your main.cpp or in some other C++ class. Beyond using a proxy for debugging, QNetworkProxy::setApplicationProxy() can also be used to specify your organization's proxy server. To do this, specify the URL and port of your organization's proxy server. Further information can be found in the AppFramework NetworkProxy documentation.

Using JSON.stringify()

When using the JSON.stringify() method, avoid passing in the object reference itself. Use the json property of the object as the argument, for example: JSON.stringify(graphic.geometry.json) .

QML API memory model

The memory model used for QML apps is based on garbage collection. Unreferenced variables will be cleaned up by the QML Engine garbage collector. Therefore, all objects must be explicitly referenced to avoid garbage collection when they are still in use.

Object creation using JavaScript

The ArccGISRuntimeEnvironment.createObject() method creates and returns new instances of an object through the QML engine. The lifetime of these instances is determined by the instance's parent object. When the parent has garbage collected, so will its child instances. There are various ways to persist an instance created with ArccGISRuntimeEnvironment.createObject() , including the following:

  • You can specify the parent object in the third, optional parameter of ArccGISRuntimeEnvironment.createObject() .
  • In your QML declarative code, declare the object as a property of another object. Then in JavaScript code, instantiate the object with ArccGISRuntimeEnvironment.createObject() . The class containing the property becomes the parent of the new instance. Be sure to declare the property to be of the same ArcGIS Runtime QML type as you are instantiating.

If you do not specify a parent, JavaScript is set as the object instance's parent. QML tracks the instance and deletes it when there are no remaining JavaScript references to the instance (that is, when the instance goes out of scope.)

Some ArcGIS Runtime QML types include a clone() method used to create a new object instance of that type. When you use clone() to create a new instance, JavaScript is its parent. You can change the parent using the parent property.

Working with models

Models access object instances explicitly. When working with models, it is necessary to provide persisted data for the model to access. Placing newly created object instances directly into a QML ListModel is not sufficient to maintain the lifetime of those instances, because doing so does not reference count those instances. (See the previous section about object creation using JavaScript.) One way to avoid garbage collection is to create a property that is a list, and add the instances to the persisted list. The lifetime of the list property is that of its parent.

                 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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);

This concept also applies to list object instances based on QQmlListProperty . Although you can directly assign such a list as input for a model, you should avoid doing this. The model will create nonexplicit references to the list items, so the items will be garbage collected. To get the correct results, first assign all the list elements to a local list. These explicit references will persist and avoid early garbage collection. The following example builds a list component and inserts all the fields from the feature table. Then, it sets the list as the model of the ComboBox .

                  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}

Object uniqueness

There is no guarantee that you will get the same object back even for the same getter or function call. As such, direct object comparison should be avoided, for example:

       
1
2
3
4
5
6
7
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)

Custom properties on QML types

A custom (user-defined) property is one that you add to an instance of a QML type when you declare it. The property is not part of the type but exists as a custom property on the specific instance. The QML language allows you to add custom properties to instances when you declare them. While you can add custom properties to ArcGIS Runtime QML type instances, that property may not be preserved in all cases due to the way instances are maintained in the API. Therefore, avoid using custom properties on instances of types that do not support them.

The following QML types, and types derived from these types, support custom properties. Other ArcGIS Runtime QML types do not.

  • ArcGISMapServiceInfo
  • Basemap
  • Credential
  • FeatureTable
  • Geodatabase
  • GraphicsOverlay
  • LayerContent
  • Map
  • Portal
  • PortalTask
  • VectorTileSourceInfo

Working with JSON

Many QML types inherit from JsonSerializable , giving you the ability to serialize the type, or populate its contents, in JSON. When creating a new object from JSON, create an instance first, and then pass that instance as input to the method.

             
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
  }
}

OpenGL or ANGLE rendering on Windows

Qt has two different rendering engines for the Windows platform: OpenGL and DirectX via ANGLE. By default, Qt will try to render using OpenGL. If the proper drivers are not available, it will fall back to using ANGLE for rendering. Previous releases of ArcGIS Runtime only supported ANGLE. Starting with ArcGIS Runtime 100.3.0, your apps can fully support both OpenGL and ANGLE so that you can use either rendering engine and Qt's fallback mechanism when OpenGL cannot be used on a given system. For more information, see Qt's documentation.

To choose between OpenGL and ANGLE rendering as a default, use the Settings > Platforms > Windows > Graphics Rendering Engine options in AppStudio. No code changes are needed.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.