Qt QML のベスト プラクティス

アプリの開発を続けているうちに、アプリのパフォーマンスを改善できるいくつかの詳細に気づいたり、少し問題が発生したりすることがあるはずです。 このトピックでは、留意すべき重要な詳細について説明します。

マップ ビュー

ArcGIS Runtime SDK for Qt には、マップ ビューにマップを表示するためのパターンが 3 つあります。 AppStudio では、QML でアプリを記述するため、MapView マップ タイプを使用します。

Qt 社の QML API を利用すると、デスクトップおよびモバイルのネイティブ アプリケーション向けに応答性がよく流動的なユーザー インターフェイスを設計して構築するための読みやすい宣言型の構文である QML でクロスプラットフォーム アプリを作成できます。 ArcGIS Runtime SDK for Qt は、ArcGIS Runtime の機能を提供する QML タイプを使用して QML を拡張します。 オブジェクトは階層的に宣言され、そのバインド可能なプロパティにより動的な挙動を自動的に指定できます。 JavaScript 関数を使用して、必要に応じてプロシージャル コードを提供します。 これは、Web 開発にすでに精通し、ネイティブ アプリを開発する開発者向けの重要な機能です。

MapView を含む Rectangle を宣言します。 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"

このコードを実行すると、文字列が設定された行番号を明示的に参照する、[Error: Cannot assign QString to int] を示すエラーがコンソールに表示され、コードのトラブルシューティングしやすくなります。

間違ったタイプの値が var タイプの変数に割り当てられた場合、エラーはレポートされません。

拡張性に優れたユーザー インターフェイス

アプリのユーザー インターフェイスを設計するとき、アプリが使用される画面のサイズと解像度の今後の発展について検討することが重要です。 これを行うには、表示解像度ごとにアイコン、ボタン、および背景を含めるか、SVG (Scalable Vector Graphics) を使用します。

さまざまな解像度の画像セットを含めるとき、画像サイズを明確に区別できるファイル命名規則を選択します。 たとえば、image-name1x.png、image-name1.5x.png、image-name2x.png は、それぞれ基本 DPI の 1 倍、1.5 倍、3 倍の画像を表します。

SVG ファイルは、一般的にアイコンなどの小さい画像に最も適しています。 大きい SVG ファイルは、レンダリングに時間がかかる可能性があるため、背景には画像を使用することを検討してください。

詳細については、Qt の Scalable User Interfaces をご参照ください。

アプリのデバッグ

AppStudio は Qt Framework に基づいて構築されています。つまり、Qt で使用されるデバッグ ユーティリティと同じデバッグ ユーティリティのすべてにアクセスできます。 Qt アプリのデバッグについての有益なヒントとコツをいくつか紹介します。

Web プロキシの使用

AppStudio アプリのデバッグ時に、Web デバッグ ユーティリティは、送信される HTTP リクエストと返される応答のキャプチャおよび解析に役立ちます。 Fiddler と Charles は、このタスクの実行に使用される Web デバッグ ユーティリティの 2 つの例です。 これらのツールを使用して、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 エンジンのガーベッジ コレクターによってクリーンアップされます。 そのため、使用中のすべてのオブジェクトを明示的に参照して、ガーベッジ コレクションを避ける必要があります。

JavaScript を使用したオブジェクト作成

ArccGISRuntimeEnvironment.createObject() メソッドを指定すると、QML エンジンを通してオブジェクトの新しいインスタンスが作成され返されます。 これらのインスタンスのライフタイムは、インスタンスの親オブジェクトによって決まります。 親がガベージ コレクションされると、その子インスタンスもガベージ コレクションされます。 ArccGISRuntimeEnvironment.createObject() を使用して作成されたインスタンスを次のようにさまざまな方法で保持できます。

  • ArccGISRuntimeEnvironment.createObject() の 3 番目のオプション パラメーターに親オブジェクトを指定できます。
  • QML 宣言コード内で、オブジェクトを別のオブジェクトのプロパティとして宣言します。 次に、JavaScript コード内で、ArccGISRuntimeEnvironment.createObject() を使用してオブジェクトをインスタンス化します。 プロパティを含むクラスが新しいインスタンスの親になります。 インスタンス化している ArcGIS Runtime QML タイプと同じになるようにプロパティを宣言してください。

親を指定しない場合、JavaScript がオブジェクト インスタンスの親として設定されます。 QML はインスタンスを追跡して、インスタンスへの JavaScript 参照がなくなる (つまり、インスタンスが範囲外になる) とそのインスタンスを削除します。

いくつかの ArcGIS Runtime QML タイプには、そのタイプのオブジェクト インスタンスを作成するために使用される clone() メソッドが含まれます。 clone() を使用してインスタンスを作成すると、JavaScript がその親になります。 parent プロパティを使用して親を変更できます。

モデルの操作

モデルはオブジェクト インスタンスに明示的にアクセスします。 モデルを操作する場合は、モデルがアクセスする保存済みデータを指定する必要があります。 新たに作成されたオブジェクト インスタンスを QML ListModel に直接配置しても、それらのインスタンスの数を参照しないため、それらのインスタンスのライフタイムを維持するためには不十分です (JavaScript を使用したオブジェクト作成に関する前のセクションを参照)。ガベージ コレクションを避ける方法の 1 つは、リストであるプロパティを作成して、そのインスタンスを持続的リストに追加します。 リスト プロパティのライフタイムは、その親のライフタイムです。

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

オブジェクトの一意性

同じゲッターまたは関数呼び出しでも、同じオブジェクトを取得できるという保証はありません。 そのため、次に示すように、直接のオブジェクト比較を避ける必要があります。

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 言語を使用すると、宣言時にカスタム プロパティをインスタンスに追加できます。 カスタム プロパティを ArcGIS Runtime QML タイプ インスタンスに追加できますが、インスタンスを API 内で維持する方法によっては、どのような場合でもそのプロパティは保持されないことがあります。 したがって、カスタム プロパティをサポートしていないタイプのインスタンス上ではカスタム プロパティを使用しないでください。

次の QML タイプおよびそのタイプから派生するタイプはカスタム プロパティをサポートします。 その他の ArcGIS Runtime QML タイプはサポートしていません。

  • ArcGISMapServiceInfo
  • ベースマップ
  • 資格情報
  • 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 上での OpenGL または ANGLE レンダリング

Qt には、Windows プラットフォーム向けに OpenGL および ANGLE を介した DirectX という 2 つの異なるレンダリング エンジンがあります。 デフォルトでは、Qt は OpenGL を使用してレンダリングしようとします。 適切なドライバーがない場合、ANGLE を使用するレンダリングにフォールバックします。 以前のリリースの ArcGIS RuntimeANGLE のみをサポートしていました。 ArcGIS Runtime 100.3.0 から、アプリは OpenGLANGLE の両方を完全にサポートできるため、どちらのレンダリング エンジンも利用でき、所定のシステムで OpenGL を使用できないときは Qt のフォールバック メカニズムを利用できます。 詳細については、Qt のドキュメントをご参照ください。

デフォルトのレンダリングとして OpenGLANGLE のどちらかを選択する場合は、AppStudio[設定] > [プラットフォーム] > [Windows] > [グラフィックス レンダリング エンジン] オプションを使用します。 コードの変更は必要ありません。