スコープと命名解決
QML のプロパティバインディング、インライン関数、インポートされた JavaScript ファイルは、すべて JavaScript のスコープ内で実行されます。スコープは式がどの変数にアクセスできるかを制御し、2つ以上の名前が衝突した場合にどの変数が優先されるかを制御します。
JavaScript 組み込みのスコープ機構は非常に単純であるため、QML では言語拡張に 適合するようにスコープ機構を拡張しています。
JavaScript のスコープ
QML のスコープ拡張は、JavaScript の自然なスコープを妨げるものではありません。JavaScriptプログラマは、関数やプロパティバインディング、インポートしたJavaScriptファイルをQMLでプログラミングする際に、既存の知識を再利用することができます。
次の例では、addConstant()
メソッドは、QML オブジェクトのa
やb
プロパティの値に関係なく、プログラマーが期待するように、渡されたパラメータに 13 を追加します。
QtObject { property int a: 3 property int b: 9 function addConstant(b) { var a = 13; return b + a; } }
QMLはJavaScriptの通常のスコープルールを尊重し、バインディングにも適用します。この全く邪悪で忌まわしいバインディングは QML オブジェクトのa
プロパティに 12 を代入します。
QtObject { property int a a: { var a = 12; a; } }
QMLのすべてのJavaScript式、関数、ファイルは独自の変数オブジェクトを持ちます。あるファイルで宣言されたローカル変数が、別のファイルで宣言された ローカル変数と衝突することはありません。
型名とインポートされたJavaScriptファイル
QMLドキュメントには、そのドキュメントから見える型名やJavaScriptファイルを定義するimport文が含まれています。QMLの宣言自体で使用されるだけでなく、JavaScriptのコードでも、付属のプロパティや列挙値にアクセスする際に型名が使用されます。
インポートの効果は、QML文書内のすべてのプロパティバインディングやJavaScript関数に適用されます。次の例は、いくつかの列挙値にアクセスし、インポートされたJavaScript関数を呼び出す単純なQMLファイルです。
import QtQuick 2.0 import "code.js" as Code ListView { snapMode: ListView.SnapToItem delegate: Component { Text { elide: Text.ElideMiddle text: "A really, really long string that will require eliding." color: Code.defaultColor() } } }
バインディングスコープオブジェクト
プロパティバインディングを持つオブジェクトは、バインディングのスコープオブジェクトとして知られています。次の例では、Item オブジェクトがバインディングのスコープオブジェクトです。
Item { anchors.left: parent.left }
バ イ ンデ ィ ン グは、 ス コ ープオブジ ェ ク ト のプ ロ パテ ィ に無条件でア ク セ ス で き ます。先ほどの例では、バインディングはItem のparent
プロパティに直接アクセスしています。QMLはJavaScriptに対してより構造化されたオブジェクト指向のアプローチを導入しているため、JavaScriptのthis
プロパティを使用する必要はありません。
バインディングからアタッチドプロパティにアクセスする際には、スコープオブジェクトとの相互作用のために注意が必要です。概念的に、 添付プ ロ パテ ィ はすべてのオブジ ェ ク ト 上に存在 し ます。その結果、修飾されていないアタッチド・プロパティを読み込むと、常にスコープ・オブジェクト上のアタッチド・プロパティに解決されますが、これは必ずしもプログラマーの意図したものではありません。
たとえば、PathView 型は、そのデリゲートに対して、パス内の位置に応じて補間された値のプロパティをアタッチします。PathView は、これらのプロパティをデリゲート内のルート・オブジェクトにのみ意味のある形で付加するため、これらにアクセスするサブ・オブジェクトは、以下に示すように、ルート・オブジェクトを明示的に修飾しなければなりません。
PathView { delegate: Component { Rectangle { id: root Image { scale: root.PathView.scale } } } }
Image オブジェクトがroot
接頭辞を省略した場合、設定されていないPathView.scale
アタッチ・プロパティに誤ってアクセスしてしまいます。
コンポーネントのスコープ
QML文書内の各QMLコンポーネントは論理的なスコープを定義します。各文書は少なくとも1つのルートコンポーネントを持ちますが、他のインラインサブコンポーネントを持つこともできます。コンポーネントのスコープは、コンポーネント内のオブジェクトIDと、 コンポーネントのルートオブジェクトのプロパティの和です。
Item { property string title Text { id: titletype text: "<b>" + title + "</b>" font.pixelSize: 22 anchors.top: parent.top } Text { text: titletype.text font.pixelSize: 18 anchors.bottom: parent.bottom } }
上の例では、上部にリッチテキストのタイトル文字列を表示し、下部に同じテキストの小さなコピーを表示するシンプルなQMLコンポーネントを示しています。最初のText
タイプは、表示するテキストを形成する際に、コンポーネントのtitle
プロパティに直接アクセスします。ルート型のプロパティに直接アクセスできることで、コンポーネント全体にデータを配布することが簡単になります。
2番目のText
型は、idを使用して1番目のテキストに直接アクセスします。idはQMLプログラマによって明示的に指定されるため、常に他のプロパティ名よりも優先されます(JavaScriptスコープ内のものを除く)。例えば、先ほどの例でバインディングのスコープオブジェクトに titletype
というプロパティがあったとしても、titletype
のidが優先されます。
コンポーネントインスタンスの階層
QMLでは、コンポーネントインスタンスはそのコンポーネントスコープを連結してスコープ階層を形成します。コンポーネントインスタンスは、先祖のコンポーネントスコープに直接アクセスすることができます。
これを示す最も簡単な方法は、インラインサブコンポーネントのコンポーネントスコープが暗黙のうちに外側コンポーネントの子としてスコープされていることです。
Item {
property color defaultColor: "blue"
ListView {
delegate: Component {
Rectangle {
color: defaultColor
}
}
}
}
コンポーネントのインスタンス階層によって、デリゲート・コンポーネントのインスタンスはItem
タイプのdefaultColor
プロパティにアクセスすることができます。もちろん、デリゲート・コンポーネントにdefaultColor
というプロパティがあれば、そちらが優先される。
コンポーネントのインスタンス・スコープの階層は、アウトオブライン・コンポーネン トにも及びます。次の例では、TitlePage.qml
コンポーネントが2つのTitleText
インスタンスを作成しています。TitleText
title
TitlePage
QML は動的スコープ言語であり、使用する場所によってtitle
プロパティの解決方法が異なります。
// TitlePage.qml import QtQuick 2.0 Item { property string title TitleText { size: 22 anchors.top: parent.top } TitleText { size: 18 anchors.bottom: parent.bottom } } // TitleText.qml import QtQuick 2.0 Text { property int size text: "<b>" + title + "</b>" font.pixelSize: size }
動的スコープは非常に強力ですが、QMLコードの挙動が予測しづらくなるのを防ぐため、 慎重に使用する必要があります。一般的には、2つのコンポーネントがすでに別の方法で緊密に結合されている場合にのみ使用すべきです。再利用可能なコンポーネントを構築する場合には、このようなプロパティ・インターフェースを使用することが望ましいでしょう:
// TitlePage.qml import QtQuick 2.0 Item { id: root property string title TitleText { title: root.title size: 22 anchors.top: parent.top } TitleText { title: root.title size: 18 anchors.bottom: parent.bottom } } // TitleText.qml import QtQuick 2.0 Text { property string title property int size text: "<b>" + title + "</b>" font.pixelSize: size }
オーバーライドされたプロパティ
QMLでは、あるオブジェクト宣言で定義されたプロパティ名を、それを継承する別のオブジェクト宣言で宣言されたプロパティでオーバーライドすることができます。例えば
// Displayable.qml import QtQuick 2.0 Item { property string title property string detail Text { text: "<b>" + title + "</b><br>" + detail } function getTitle() { return title } function setTitle(newTitle) { title = newTitle } } // Person.qml import QtQuick 2.0 Displayable { property string title property string firstName property string lastName function fullName() { return title + " " + firstName + " " + lastName } }
ここでは、title
という名前が Displayable の出力テキストの見出しと Person オブジェクトの敬称の両方に与えられています。
オーバーライドされたプロパティは、それが参照されるスコープに従って解決されます。Person コンポーネントのスコープ内、または Person コンポーネントのインスタンスを参照する外部スコープからは、title
が Person.qml 内で宣言されたプロパティに解決されます。fullName
関数は、Person 内部で宣言されたtitle
プロパティを参照します。
一方、Displayable コンポーネントの内部では、title
は Displayable.qml 内で宣言されたプロパティを参照します。getTitle() 関数と setTitle() 関数、および Text オブジェクトのtext
プロパティのバインディングはすべて、Displayable コンポーネントで宣言されたtitle
プロパティを参照します。
同じ名前を共有しているにもかかわらず、2つのプロパティは完全に分離しています。一方のプロパティのonChangedシグナルハンドラは、同名のもう一方のプロパティの変更によってトリガされることはありません。どちらかのプロパティのエイリアスは、どちらか一方を参照しますが、両方を参照することはできません。
JavaScript グローバルオブジェクト
QML では、混乱を避けるため、グローバルオブジェクトのプロパティと衝突する型、id、 プロパティ名を禁止しています。プログラマはMath.min(10, 9)
が常に期待通りに動作することを確信することができます!
詳細はJavaScript ホスト環境を参照してください。
©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。