Qt QML 最佳做法

如果继续开发应用程序,您应当了解一些详细信息,这些详细信息能够使您的应用程序更好地运行并且遇到更少的问题。 本主题介绍了您应当记住的重要详细信息。

地图视图

ArcGIS Runtime SDK for Qt 提供了三种在地图视图中显示地图的模式。 在 AppStudio 中,您将使用 QML 编写应用程序,因此您将使用 MapView 地图类型。

Qt 公司的 QML API 允许您使用 QML 编写跨平台应用程序;QML 是一种声明性的高度可读的语法,用于为本机桌面和移动应用程序设计和构建响应迅速且非常流畅的用户界面。 ArcGIS Runtime SDK for Qt 通过提供 ArcGIS Runtime 功能的 QML 类型来扩展 QML。 对象将按等级进行声明,并具有可绑定属性以自动提供动态行为。 JavaScript 函数用于根据需要提供程序代码。 对于已经熟悉 Web 开发并想要开发原生应用程序的开发人员来说,这是一项非常重要的功能。

声明一个 Rectangle,其中包含 MapView。 在 MapView 内,声明要显示的 Map 和初始 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
        }
    }

强制输入变量

在 QML 中声明属性时,使用通用变量类型 var 既简单又方便,但是使用强制输入变量的效果一定会更好一些。 强制输入变量将明确指出变量中可以存储什么类型的数据,例如整数、字符串或小数值。 强制输入变量可以防止分配错误的值,且与 var 类型的变量相比更容易读取和调试。 var 类型的变量不会阻止系统将小数值分配给该变量,即使该值只应该包含整数。

在下面的示例中,整数变量 intValue 最初被分配值 10。稍后在代码中,相同的变量将被更新为字符串值。

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

运行此代码时,控制台中将显示错误,该错误将明确引用设置字符串值处的行号,并指示错误:无法将 QString 分配至 int,从而更方便用户对代码进行故障排除。

如果将错误类型的值分配至 var 类型的变量,则不会报告错误。

可扩展用户界面

在设计应用程序的用户界面时,必须要考虑您的应用程序可能面对的不断变化的屏幕尺寸和分辨率范围。 这一点可以通过包含每个显示分辨率的图标、按钮和背景,或使用可伸缩矢量图形 (SVG) 来实现。

当包含一组不同分辨率的图像时,请选择能够清楚区分图像大小的文件命名约定。 例如:image-name1x.png、image-name1.5x.png、image-name2x.png 分别代表 1 倍基础 DPI、1.5 倍基础 DPI 和 3 倍基础 DPI 的图像。

SVG 文件非常适合较小图像(通常是图标)。 大型 SVG 文件的渲染速度可能会很慢,因此请考虑将图像用于背景。

有关详细信息,请参阅 Qt 可扩展用户界面

调试应用程序

AppStudio 以 Qt 框架为基础,这意味着您有权访问与 Qt 配合使用的所有相同的调试实用程序。 以下是调试 Qt 应用程序的一些有用的提示和技巧。

使用 web 代理

在调试 AppStudio 应用程序时,web 调试实用程序通常对于捕获和分析已发送的 HTTP 请求以及返回的响应非常有用。 Fiddler 和 Charles 是用于执行此任务的 web 调试实用程序的两个示例。 借助这些工具,您可以通过在应用程序中添加以下 C++ 代码来调试应用程序:QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8888));。 此代码可以在运行时设置,并且可以位于 main.cpp 或一些其他 C++ 类中。 除了使用代理进行调试之外,QNetworkProxy::setApplicationProxy() 也可以用于指定组织的代理服务器。 为此,请指定组织代理服务器的 URL 和端口。 有关详细信息,请参阅 AppFramework NetworkProxy 文档

使用 JSON.stringify()

使用 JSON.stringify() 方法时,请避免在对象参考中对其进行传递。 请使用对象的 json 属性作为参数,例如:JSON.stringify(graphic.geometry.json)

QML API 内存模型

适用于 QML 应用程序的内存模型基于垃圾收集。 将由 QML Engine 垃圾收集器来清理未引用的变量。 因此,必须显式引用所有对象,才能避免当其仍处于使用状态时的垃圾收集。

使用 JavaScript 创建对象

ArccGISRuntimeEnvironment.createObject() 方法将通过 QML 引擎来创建并返回对象的新实例。 这些实例的生存时间取决于实例的父对象。 如果将父实例作为垃圾回收,则其子实例也不例外。 可以通过多种方法来保留使用 ArccGISRuntimeEnvironment.createObject() 创建的实例,具体如下:

  • 可以在 ArccGISRuntimeEnvironment.createObject() 的第三个可选参数中指定父对象。
  • 在您的 QML 声明性代码中,将该对象声明为另一个对象的属性。 然后在 JavaScript 代码中,使用 ArccGISRuntimeEnvironment.createObject() 来实例化该对象。 包含该属性的类将成为新实例的父对象。 确保将该属性声明为正在实例化的相同 ArcGIS Runtime QML 类型。

如果未指定父对象,则需将 JavaScript 设置为对象实例的父对象。 如果不存在该实例的其余 JavaScript 引用(即,当实例超出范围时),QML 将追踪该实例并将其删除。

某些 ArcGIS Runtime QML 类型包含 clone() 方法,该方法可用于创建该类型的对象实例。 如果使用 clone() 创建实例,则 JavaScript 将成为其父对象。 可以使用 parent 属性来更改父对象。

使用模型

显式建模访问对象实例。 使用模型时,需要提供保存的数据以供模型访问。 将新创建的对象实例直接置于 QML ListModel 中不足以维持这些实例的生存时间,原因在于这样做不会引用计数这些实例。 (请参阅上一部分中有关使用 JavaScript 创建对象的信息。)避免垃圾收集的一种方法是创建一个作为列表的属性,然后将实例添加到保留的列表中。 列表属性的生存时间为其父对象的生存时间。

// 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);

此概念也适用于基于 QQmlListProperty 的列表对象实例。 虽然可以直接将此类列表分配为模型的输入,但应避免这样做。 该模型将创建列表项目的非显式引用,因此将对这些项目进行垃圾收集。 要获得正确结果,需要先将所有列表元素分配给本地列表。 这些显式参考将保留并避免早期垃圾收集。 以下示例将构建 list 组件并从要素表插入所有字段。 然后,将列表设置为 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;
}

对象唯一性

即使对于相同的 getter 或函数调用,也不能保证将返回相同的对象。 同样,应避免直接比较对象,例如:

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)

有关 QML 类型的自定义属性

自定义(用户定义)属性是在声明 QML 类型时添加至 QML 类型实例的属性。 该属性不是类型的一部分,但是作为特定实例上的自定义属性存在。 QML 语言允许您在声明实例时向实例添加自定义属性。 虽然可以将自定义属性添加到 ArcGIS Runtime QML 类型实例,但是,由于在 API 内维护实例的方式,可能不会在所有情况下都保留该属性。 因此,在不支持自定义属性的类型的实例上,请避免使用自定义属性。

以下 QML 类型以及从这些类型派生的类型均支持自定义属性。 其他 ArcGIS Runtime QML 类型不支持自定义属性。

  • ArcGISMapServiceInfo
  • 底图
  • Credential
  • FeatureTable
  • 地理数据库
  • GraphicsOverlay
  • LayerContent
  • 地图
  • 门户
  • PortalTask
  • VectorTileSourceInfo

使用 JSON

许多 QML 类型都派生自 JsonSerializable,由此能够在 JSON 中序列化类型或填充其内容。 在根据 JSON 创建对象时,请先创建一个实例,然后将该实例作为输入传递到此方法。

此 JavaScript 代码将根据 JSON 来创建并填充 PictureMarkerSymbol,然后将其用于新图形中。

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

使用已翻译字符串

应用程序 UI 中显示的所有字符串都应使用 qsTr() 格式,以确保您可以全球化应用程序

在定义应用程序逻辑时,考虑已翻译的字符串也很重要。 如果用户将根据 UI 中的字符串选择选项(例如从下拉菜单中选择),则 switch 语句中的每个 case 都必须使用已翻译的字符串。

在以下示例中,无论应用程序显示哪种语言,系统都将在选择“关于”菜单项时显示关于面板:

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

Windows 上的 OpenGLANGLE 渲染

Qt 将为 Windows 平台提供两个不同的渲染引擎:OpenGL 和通过 ANGLEDirectX。 默认情况下,Qt 将尝试使用 OpenGL 进行渲染。 如果没有合适的驱动程序,它将回退以使用 ANGLE 进行渲染。 之前版本的 ArcGIS Runtime 仅支持 ANGLE。 自 ArcGIS Runtime 100.3.0 起,您的应用程序可以完全支持 OpenGLANGLE,因此,如果无法在给定系统上使用 OpenGL,您还可以利用渲染引擎和 Qt 的回退机制。 有关详细信息,请参阅 Qt 文档

要在 OpenGLANGLE 渲染之间选择一项以将其作为默认值,请使用 AppStudio 中的设置 > 平台 > Windows > 图形渲染引擎选项。 无需更改代码。