QMLドキュメントの構造

QMLドキュメントは、3つの部分からなるQMLソースコードです:

  • オプションのプラグマリスト
  • インポート
  • ルートオブジェクト宣言

慣例として、インポート文とオブジェクト階層の定義は空行1行で区切られます。

QMLドキュメントは常にUTF-8でエンコードされます。

プラグマ

プラグマはQMLエンジンに対する命令であり、現在のファイル中のオブジェクトの特性を指定したり、エンジンがコードを解釈する方法を変更したりするために使用します。以下のプラグマについて詳しく説明します。

  • Singleton
  • ListPropertyAssignBehavior
  • ComponentBehavior
  • FunctionSignatureBehavior
  • NativeMethodBehavior
  • ValueTypeBehavior
  • Translator

シングルトン

pragma Singleton はQMLドキュメントで定義されたコンポーネントをシングルトンとして宣言します。シングルトンは1つのQMLエンジンにつき一度だけ生成されます。QMLで宣言されたシングルトンを使うには、そのモジュールに登録する必要があります。CMake での登録方法はqt_target_qml_sourcesを参照してください。

ListPropertyAssignBehavior

このプラグマにより、QMLドキュメントで定義されたコンポーネントにおいて、リストプロパティへの割り当てをどのように扱うかを定義することができます。デフォルトでは、リストプロパティへの代入はリストに追加されます。この動作を明示的に指定するには、Append を指定します。また、リスト・プロパティの内容を常に置換するように要求するには、Replace を、デフォルト・プロパティでない場合に置換するように要求するには、ReplaceIfNotDefault を使用します。例えば

pragma ListPropertyAssignBehavior: ReplaceIfNotDefault

注: QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPENDQML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACEQML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT マクロをクラス宣言に追加することで、C++定義型でも同じ宣言を行うことができます。

コンポーネント動作

同じQMLファイル内に複数のコンポーネントを定義することができます。QMLファイルのルートスコープはコンポーネントであり、さらに、QQmlComponent 型の要素や、プロパティとして明示的または暗黙的に作成された要素、インラインコンポーネントを持つことができます。これらのコンポーネントは入れ子構造になっています。各内部コンポーネントは特定の外部コンポーネントの中にあります。たいていの場合、外側コンポーネントで定義されたIDは、入れ子になったすべての内側コンポーネントにアクセスできます。しかし、あるコンポーネントから別のコンテキストで要素を作成し、別のIDを使用することができます。そうすることで、外側のIDが利用可能であるという前提が崩れます。そのため、エンジンやQMLツールは、そのようなIDが実行時にどのような型に解決されるかを事前に知ることはできません。

ComponentBehaviorプラグマを使用すると、ファイル内で定義されたすべての内部コンポーネントが、元のコンテキスト内でのみオブジェクトを生成するように制限することができます。コンポーネントがそのコンテキストにバインドされている場合、同じファイル内の外部コンポーネントのIDをコンポーネント内で安全に使用することができます。QMLツールは、外側のIDを特定の型とともに使用できるものとみなします。

コンポーネントをコンテキストにバインドするには、Bound を指定します:

pragma ComponentBehavior: Bound

これは、名前が衝突した場合、バインドされたコンポーネントの外部で定義されたIDが、コンポーネントから生成されたオブジェクトのローカルプロパティを上書きすることを意味します。そうでなければ、モジュールの後のバージョンでコンポーネントにさらにプロパティが追加される可能性があるため、実際にIDを使用するのは安全ではない。コンポーネントがバインドされていない場合、ローカルプロパティはコンポーネント外部で定義されたIDをオーバーライドしますが、コンポーネント内部で定義されたIDはオーバーライドしません。

以下の例では、矩形の色のrプロパティではなく、ListView オブジェクトのrプロパティをcolor という ID で表示しています。

pragma ComponentBehavior: Bound
import QtQuick

ListView {
 id: color
 property int r: 12
 model: 1

 delegate: Rectangle {
  Component.onCompleted: console.log(color.r)
 }
}

ComponentBehavior のデフォルト値はUnbound です。明示的に指定することもできます。Qt の将来のバージョンでは、デフォルトはBound に変更される予定です。

コンテキストにバインドされたデリゲート・コンポーネントは、インスタンス化時に自身のプライベート・コンテキストを受け取りません。つまり、この場合、モデル・データは必須プロパティを介してのみ渡すことができます。コンテキスト・プロパティを介してモデル・データを渡すことはできません。これは、InstantiatorRepeaterListViewTableViewGridViewTreeView などのデリゲートと、DelegateModel を内部的に使用するもの全般に関することです。

例えば、以下のようにすると動作しません

pragma ComponentBehavior: Bound
import QtQuick

ListView {
 delegate: Rectangle {
     color: model.myColor
 }
}

ListViewdelegate プロパティはコンポーネントです。したがって、Rectangle の周りには暗黙的にComponent が作成されます。そのコンポーネントは、そのコンテキストにバインドされています。このコンポーネントは、ListView が提供するコンテキスト・プロパティmodel を受け取りません。これを動作させるには、このように書く必要があります:

pragma ComponentBehavior: Bound
import QtQuick

ListView {
 delegate: Rectangle {
     required property color myColor
     color: myColor
 }
}

QMLファイルではコンポーネントを入れ子にすることができます。このプラグマは、どんなに深くネストされていても、ファイル内のすべてのコンポーネントに適用されます。

FunctionSignatureBehavior

このプラグマで、関数に対する型アノテーションの処理方法を変更することができます。Qt 6.7 以降では、関数を呼び出すときに型アノテーションが強制されます。以前は、QMLスクリプトコンパイラのみが型注釈を強制していました。インタプリタやJITコンパイラは、型注釈を無視します。型注釈が常に強制されるようになったのは、以前のバージョンと比べると、引数が不一致でも関数を呼び出すことができるようになったためです。

Ignored をvalueに指定することで、QMLエンジンとQMLスクリプトコンパイラは型アノテーションを無視するようになり、インタープリタとJITの6.7以前の挙動が復元されます。その結果、先にC++にコンパイルされるコードが少なくなり、インタープリタや JITコンパイルされるコードが多くなります。

値としてEnforced を指定すると、デフォルトが明示的に指定されます:型注釈は常に強制されます。

NativeMethodBehavior

C++ メソッドを、取得元オブジェクトとは異なるthis オブジェクトで呼び出すことは、歴史的な理由により壊れています。元のオブジェクトはthis オブジェクトとして使用されます。pragma NativeMethodBehavior: AcceptThisObject を設定することで、指定したthis オブジェクトの使用を許可できます。RejectThisObject を指定すると、過去の動作が保持されます。

この例は、C++メソッドと'this'オブジェクトで見ることができる。

ValueTypeBehavior

このプラグマで、値型とシーケンスの扱い方を変更できます。

通常、JavaScriptのコードでは小文字の名前を型名にすることはできません。これは、値型名が小文字であるために起こる問題です。このプラグマの値としてAddressable を指定することで、これを変更することができます。Addressable を指定すると、JavaScript の値を特定の名前付き値型に明示的に強制することができます。これは、as 演算子を使用し、オブジェクト型で行うように行います。さらに、instanceof 演算子を使用して値型をチェックすることもできます:

pragma ValueTypeBehavior: Addressable
import QtQml

QtObject {
 property var a
 property real b: (a as rect).x
 property bool c: a instanceof rect

 property var rect // inaccessible. "rect" is a type name.
}

上の例のrect は型名になっているので、rect というプロパティをシャドウすることになります。

目的の型に明示的にキャストすることは、ツールの作成に役立ちます。Qt Quick Compilerが効率的なコードを生成することができます。qmllintを使用すると、このような発生を見つけることができます。

また、デフォルトの動作を明示的に指定するために、Inaddressable

Qt 6.8 で導入されたAssertable は、ValueTypeBehavior プラグマの別の属性です。Qt 6.6と6.7での間違いのため、上記のa as rect は、arect であるかどうかをチェックするだけでなく、a が互換性のある型であれば、rect を構築します。これは明らかに型アサーションが行うべきことではありません。Assertable を指定することで、このような挙動を防ぐことができます。また、値型に対する型アサーションは、その型のチェックのみに制限されます。値型をas で使用する場合は、必ずこれを指定する必要があります。 いずれにせよ、値型に対する型アサーションが失敗した場合、undefined.

instanceof は継承のチェックだけで、すべての可能な型 保持をチェックするわけではないので、この問題はありません。

注: int およびdouble 型でas を使用することはお勧めしません。JavaScript のルールでは、計算結果はすべて浮動小数点数であり、たとえそれがたまたま整数と同じ値を保持していたとしてもです。逆に、JavaScript で宣言する整数定数は QML の型マッピングルールでは double ではありません。さらに、intdouble は予約語です。これらの型は型名前空間を通してのみ扱うことができます。

値型とシーケンスは一般に参照として扱われます。つまり、あるプロパティから値型のインスタンスをローカル値に取り込み、そのローカル値を変更した場合、元のプロパティも変更されます。さらに、元のプロパティを明示的に書き込むと、ローカル値も更新されます。この動作は多くの場所で直感的ではありません。ValueTypeBehavior プラグマに対するCopyReference の値は、この動作を変更するための実験的なオプションです。これらを使うべきではありません。Copy を指定すると、すべての値型が実際のコピーとして扱われます。Reference を指定すると、デフォルトの動作が明示的に指定されます。

値型やシーケンスが副作用の影響を受ける可能性がある場合は、Copy を使用するのではなく、明示的に値型やシーケンスの参照を再ロードする必要があります。副作用は関数を呼び出したり、プロパティを強制的に設定したりするときに発生します。例えば、次のコードではwidth を書いた後、変数f が副作用の影響を受けています。これは、width が変更されたときに、font を更新するバインディングが派生型やBinding 要素にあるかもしれないからです。

import QtQuick
Text {
 function a() : real {
     var f = font;
     width = f.pixelSize;
     return f.pointSize;
 }
}

この問題に対処するには、width に対する書き込み操作の間、f を保持しないようにすればよい:

import QtQuick
Text {
 function a() : real {
     var f = font;
     width = f.pixelSize;
     f = font;
     return f.pointSize;
 }
}

これは、次のように短縮できます:

import QtQuick
Text {
 function a() : real {
     width = font.pixelSize;
     return font.pointSize;
 }
}

font プロパティの再取得はコストがかかると思われるかもしれませんが、実はQMLエンジンは値型参照を読み出すたびに自動的にリフレッシュします。ですから、これは最初のバージョンよりもコストがかかるわけではなく、同じ操作をより明確に表現する方法なのです。

トランスレータ

このプラグマにより、ファイル中の翻訳のコンテキストを設定することができます。

pragma Translator: myTranslationContext
pragma Translator: "myTranslationContext"

QMLの国際化についての詳細はUse qsTrを参照してください。

インポート

ドキュメント内で参照される QML オブジェクトタイプをエンジンに読み込ませるために、必要なモジュールや型名前空間をインポートする必要があります。デフォルトでは、ドキュメントは同じディレクトリにある.qml ファイルを通して定義された QML オブジェクトタイプにアクセスすることができます。ドキュメントがそれ以外のオブジェクトタイプを参照する必要がある場合は、それらのタイプが登録されている型名前空間をインポートする必要があります。

QMLには、CやC++とは異なり、QML engine に表示する前に文書を修正するプリプロセッサはありませんimport の記述は文書中のコードをコピーして前置するのではなく、文書中の型参照をどのように解決するかをQMLエンジンに指示します。RectangleListView のように、QML文書中に存在する型参照は、JavaScriptブロックや プロパティバインディング内を含め、すべてimport文のみに基づいて解決されます。また、import QtQuick 2.0 のように、import のような記述が少なくとも1つ存在しなければなりません。

QMLのインポートに関する詳しい情報は、QML Syntax - Import Statementsを参照してください。

ルートオブジェクトの宣言

QML文書では、インスタンス化可能なオブジェクトの階層構造を記述します。各オブジェクトの定義は、型、id、オブジェクト名、プロパティ、メソッド、 シグナル、シグナルハンドラなど、一定の構造を持っています。

QMLファイルには、1つのルートオブジェクト定義しか記述してはいけません。以下は無効であり、エラーとなります:

// MyQmlFile.qml
import QtQuick 2.0

Rectangle { width: 200; height: 200; color: "red" }
Rectangle { width: 200; height: 200; color: "blue" }    // invalid!

これは、.qmlファイルが自動的にQML型を定義し、その型が1つのQMLオブジェクト定義をカプセル化するためです。これについては「QMLオブジェクトの型定義としてのドキュメント」で詳しく説明します。

Type annotations and assertionsType annotations and assertionsも参照してください

©2024 The Qt Company Ltd. 本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。