C++からQML型を定義する
C++のコードでQMLを拡張する場合、C++のクラスをQMLの型システムに登録することで、 QMLのコード内でそのクラスをデータ型として利用することができます。C++型の属性をQMLに公開する」で説明したように、QObject から派生したクラスのプロパティやメソッド、シグナルにはQMLからアクセスできますが、そのようなクラスは型システムに登録されない限り、QMLからデータ型として利用することはできません。さらに、登録することにより、QML からインスタンス化可能なQML オブジェクト型として利用できたり、QML からクラスのシングルトンインスタンスをインポートして利用できたりといった機能を提供することができます。
さらに、Qt Qmlモジュールは、アタッチド・プロパティや デフォルト・プロパティといったQML 固有の機能を C++ で実装するための仕組みを提供しています。
(なお、本書で扱う重要な概念の多くは、チュートリアル「C++でQML拡張を書く」で解説しています)。
注:QMLの型を宣言するヘッダはすべて、プロジェクトのインクルードパスから接頭辞なしでアクセスできる必要があります。
C++とQMLの統合方法についての詳細は、C++とQMLの統合の概要のページを参照してください。
QML型システムへのC++型の登録
QObject から派生したクラスを QML の型システムに登録することで、QML のコード内からデータ型として利用できるようになります。
QML ではインスタンス化可能な型とインスタンス化不可能な型の両方を登録することが できます。インスタンス化可能な型を登録することで、C++のクラスをQMLのオブジェクト型の定義として利用することができるようになります。また、型メタデータを登録することで、QML と C++ の間でやり取りされる プロパティ値、メソッドのパラメータや戻り値、シグナルパラメータのデータ型として、 その型(およびクラスで宣言された列挙型)を使用することができるようになります。
インスタンス化不可能な型を登録すると、そのクラスもデータ型として登録され ますが、QMLからQMLオブジェクト型としてインスタンス化することはできません。これは例えば、ある型がQMLに公開すべき列挙型を持つが、その型自体はインスタンス化できない場合などに便利です。
C++の型をQMLに公開するための正しい方法の選択については、「C++とQMLの正しい統合方法の選択」を参照してください。
事前条件
以下のマクロはすべてqqmlregistration.h
のヘッダから利用可能です。マクロを利用できるようにするためには、それらを使用するファイルに以下のコードを追加する必要があります:
#include <QtQml/qqmlregistration.h>
さらに、クラス宣言は、プロジェクトのインクルード・パスを通して到達可能なヘッダーに存在しなければなりません。宣言はコンパイル時に登録コードを生成するために使用され、登録コードは宣言を含むヘッダーをインクルードする必要があります。
インスタンス化可能なオブジェクト・タイプの登録
QObject から派生したC++ クラスは、QML オブジェクト型の定義として登録することが できます。一度 QML の型システムに登録されたクラスは、他のオブジェクト型と同様に QML のコードから宣言し、インスタンス化することができます。C++ 型の属性を QML に公開する(Exposing Attributes of C++ Types to QML)と説明されているように、QObject から派生したクラスのプロパティ、メソッド、シグナルに QML コードからアクセスすることができます。
QObject から派生したクラスをQMLのオブジェクト型として登録するには、 クラス宣言にQML_ELEMENT
またはQML_NAMED_ELEMENT(<name>)
を追加します。また、ビルドシステムの調整も必要です。qmakeの場合は、CONFIG += qmltypes
、QML_IMPORT_NAME
、QML_IMPORT_MAJOR_VERSION
をプロジェクトファイルに追加してください。CMakeの場合は、クラスを含むファイルをqt_add_qml_module()でセットアップしたターゲットの一部にしてください。これにより、指定されたメジャーバージョンの型名前空間にクラスが登録され、QML の型名としてクラス名または明示的に指定された名前が使用されます。マイナーバージョンは、プロパティやメソッド、シグナルに付けられたリビジョンから導かれます。デフォルトのマイナーバージョンは0
です。クラス宣言にQML_ADDED_IN_VERSION()
マクロを追加することで、特定のマイナーバージョンからしか利用できないように明示的に制限することができます。クライアントは、その型を使用するために、名前空間の適切なバージョンをインポートすることができます。
例えば、author
とcreationDate
プロパティを持つMessage
クラスがあるとします:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
この型は、適切な型ネームスペースとバージョン番号をプロジェクト・ファイルに追加することで登録できます。例えば、この型をバージョン1.0のcom.mycompany.messaging
名前空間で利用できるようにするには、次のようにします:
qt_add_qml_module(messaging URI com.mycompany.messaging VERSION 1.0 SOURCES message.cpp message.h )
CONFIG += qmltypes QML_IMPORT_NAME = com.mycompany.messaging QML_IMPORT_MAJOR_VERSION = 1
クラスが宣言されているヘッダーがプロジェクトのインクルードパスからアクセスできない場合、生成された登録コードをコンパイルできるようにインクルードパスを修正する必要があるかもしれません。
INCLUDEPATH += com/mycompany/messaging
この型はQMLのオブジェクト宣言で使用することができ、以下の例のようにプロパティを読み書きすることができます:
import com.mycompany.messaging Message { author: "Amelie" creationDate: new Date() }
値型の登録
Q_GADGET マクロを持つ型はすべてQML の値型として登録することができます。一度QMLの型システムに登録された型は、QMLのコードの中でプロパティ型と して使用することができます。C++型の属性をQMLに公開する(Exposing Attributes of C++ Types to QML)」が説明するように、QMLのコードからはどのような値型のプロパティやメソッドにもアクセスすることができます。
オブジェクト型とは対照的に、値型には小文字の名前が必要です。値型の登録にはQML_VALUE_TYPE またはQML_ANONYMOUS マクロを使用します。C++のクラスは通常大文字の名前なので、QML_ELEMENT に相当するものはありません。それ以外の登録方法は、オブジェクト型の登録方法とよく似ています。
例えば、姓と名の2つの文字列からなる値型person
を登録したいとします:
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: // ... };
値型でできることには、さらにいくつかの制限があります:
- 値型をシングルトーンにすることはできません。
- 値型はシングルトンであってはならない。値型はdefault-constructibleかつcopy-constructibleでなければならない。
- 値型のメンバとしてQProperty 。値型はコピーされ、その時点でQProperty のバインディングをどうするかを決める必要があります。値型にQProperty を使うべきではありません。
- 値型はアタッチされたプロパティを提供できません。
- 値型の拡張(QML_EXTENDED )を定義するAPIは公開されておらず、将来変更される可能性があります。
列挙型を持つ値型
値型から列挙型をQMLに公開するには、いくつかの特別な手順が必要です。
また、小文字の名前を持つ型は、(pragma ValueTypeBehavior: Addressableを指定しない限り)JavaScriptのコードでは一般的にアドレス指定ができません。もしC++の値型に列挙型を持たせてQMLに公開したい場合は、列挙型を別途公開する必要があります。
これはQML_FOREIGN_NAMESPACE を使うことで解決できます。まず、値型から派生して別の C++ 型を作成します:
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: enum TheEnum { A, B, C }; Q_ENUM(TheEnum) //... }; class PersonDerived: public Person { Q_GADGET };
次に、その派生型を外部名前空間として公開します:
namespace PersonDerivedForeign
{
Q_NAMESPACE
QML_NAMED_ELEMENT(Person)
QML_FOREIGN_NAMESPACE(PersonDerived)
}
これにより、TheEnum
という列挙と、A
、B
、C
という値を持つ、Person
(大文字)というQML名前空間が生成されます。そして、QMLで次のように書くことができます:
someProperty: Person.A
同時に、person
(小文字)という値型も以前とまったく同じように使うことができます。
インスタンス化不可能な型の登録
時々、QObject から派生したクラスを QML の型システム に登録する必要がありますが、インスタンス化可能な型としては登録されません。例えば、C++のクラスがそうです:
- インスタンス化不可能なインターフェース型である。
- QML に公開する必要のない基底クラスの型である。
- QML からアクセス可能であるが、インスタンス化できない列挙型を宣言している。
- シングルトン・インスタンスとして QML に提供され、QML からはインスタンス化できない型。
Qt Qmlモジュールにはインスタンス化不可能な型を登録するためのマクロがいくつか用意されています:
- QML_ANONYMOUS インスタンス化不可能で QML から参照できない C++ 型を登録します。これにより、QMLからインスタンス化可能な継承された型をエンジンに強制することができます。
- QML_INTERFACE 既存の Qt インタフェース型を登録します。この型は QML からはインスタンス化できず、QML のプロパティを宣言することもできません。QML からこの型の C++ プロパティを使用すると、期待通りのインターフェイスキャストが行われます。
- QML_UNCREATABLE(reason)をQML_ELEMENT またはQML_NAMED_ELEMENT と組み合わせることで、インスタンス化可能ではないが、QMLの型システムで型として識別可能な名前付きC++型が登録されます。これは型の列挙型や付属プロパティはQMLからアクセス可能であるが、型自体はインスタンス化可能でない場合に便利です。パラメータには、その型のインスタンスを生成しようとした場合に表示されるエラーメッセージを指定します。
- QML_SINGLETON を または と組み合わせることで、後述するようにQMLからインポート可能なシングルトン型が登録されます。QML_ELEMENT QML_NAMED_ELEMENT
QMLの型システムに登録されるC++の型は、たとえそれがインスタンス化不可能なものであっても、すべてQObject-derived でなければならないことに注意してください。
シングルトン型によるシングルトンオブジェクトの登録
シングルトン型は、クライアントが手動でオブジェクトのインスタンスを生成することなく、 プロパティやシグナル、メソッドを名前空間に公開することを可能にします。 特にQObject シングルトン型は、機能やグローバルなプロパティ値を提供する効率的で便利な方法です。
シングルトン型は、エンジン内のすべてのコンテキストで共有されるため、関連するQQmlContext を持たないことに注意してください。QObject シングルトン型のインスタンスは、QQmlEngine によって構築され所有され、エンジンが破棄されると破棄されます。
QObject シングルトン型は、他のQObject 型やインスタンス化された型と同様の方法で対話することができます。ただし、(エンジンが構築し所有する)インスタンスは 1 つしか存在せず、id ではなく型名で参照する必要があります。QObject シングルトン型のQ_PROPERTYはバインドすることができ、QObject モジュールAPIのQ_INVOKABLE 関数はシグナル・ハンドラ式で使用することができます。また、".pragma library "スクリプトのインポートの代わりに使用して、グローバルな状態を保存したり、グローバルな機能を提供することもできます。
QObject QObject また、".pragma library "スクリプトのインポートの代わりに使用することで、グローバルな状態を保存したり、グローバルな機能を提供することができます。次の例では、QObject のシングルトンタイプがバージョン1.0で "MyThemeModule "名前空間に登録され、QObject がQColor "color"Q_PROPERTY を持っていると仮定しています:
import MyThemeModule 1.0 as Theme Rectangle { color: Theme.color // binding. }
QJSValue もシングルトン型として公開することができますが、クライアントはそのようなシングルトン型のプロパティにはバインドできないことに注意する必要があります。
新しいシングルトン型の実装や登録方法、既存のシングルトン型の使用方法についてはQML_SINGLETON を参照してください。シングルトンについてのより詳細な情報についてはQMLのシングルトンを参照してください。
注意: QMLで登録された型の列挙値は大文字で始まります。
最終プロパティ
Q_PROPERTY のFINAL
修飾子を使って宣言されたプロパティはオーバーライドできません。つまり、QMLやC++の派生型で宣言された同名のプロパティや関数は、 QMLエンジンによって無視されます。偶発的なオーバーライドを避けるため、可能な限りFINAL
でプロパティを宣言してください。プロパティのオーバーライドは派生クラスだけでなく、基底クラスのコンテキストを実行する QML コードからも見ることができます。このようなQMLコードは、通常、元のプロパティを期待します。これはよくある間違いの原因です。
また、FINAL
で宣言されたプロパティは QML の関数や C++ のQ_INVOKABLE メソッドではオーバーライドできません。
型の改訂とバージョン
型登録関数の多くは、登録された型に対してバージョンの指定を要求します。型のリビジョンとバージョンは、以前のバージョンとの互換性を保ちつつ、新しいプロパティやメソッドを新しいバージョンに存在させることができます。
2つのQMLファイルを考えてみましょう:
// main.qml import QtQuick 1.0 Item { id: root MyType {} }
// MyType.qml import MyTypes 1.0 CppType { value: root.x }
CppType
は C++ のクラスCppType
に対応しています。
CppTypeの作者が、新しいバージョンの型定義でCppTypeにroot
プロパティを追加した場合、root
はトップレベル・コンポーネントのid
でもあるため、root.x
は異なる値に解決されます。作成者は、新しいroot
プロパティが特定のマイナーバージョンから利用できるように指定することができます。これにより、既存のプログラムを壊すことなく、既存の型に新しいプロパティや機能を追加することができる。
REVISIONタグは、root
プロパティが型のリビジョン1で追加されたことを示すために使用されます。Q_INVOKABLE'、シグナル、スロットなどのメソッドも、Q_REVISION(x)
マクロを使用してリビジョンのタグを付けることができます:
class CppType : public BaseType { Q_OBJECT Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1) QML_ELEMENT signals: Q_REVISION(1) void rootChanged(); };
この方法で指定されたリビジョンは、プロジェクトファイルで指定されたメジャーバージョンに対するマイナーバージョンとして自動的に解釈されます。この場合、root
は、MyTypes
バージョン1.1以降がインポートされたときにのみ使用できます。MyTypes
バージョン1.0のインポートは影響を受けません。
同じ理由で、それ以降のバージョンで導入された新しい型は、QML_ADDED_IN_VERSION マクロでタグ付けする必要があります。
この言語の特徴により、既存のアプリケーションを壊すことなく動作を変更することができます。従って、QMLモジュールの作者は、マイナーバージョン間で何が変更されたかを常に文書化することを忘れないようにし、QMLモジュールの利用者は、更新されたimport文を配置する前に、アプリケーションが正しく動作するかどうかを確認する必要があります。
型が依存する基底クラスの改訂は、型自身を登録する際に自動的に登録されます。これは、他の作者が提供する基底クラスから派生する場合、例えば Qt Quick モジュールのクラスを拡張する場合などに便利です。
注意: QMLエンジンは、グループ化されたプロパティオブジェクトやアタッチされたプロパティオブジェクトのプロパティやシグナルのリビジョンには対応していません。
拡張オブジェクトの登録
既存のクラスや技術をQMLに統合する場合、APIをより宣言的な環境に適合させるため に手を加えなければならないことがよくあります。最良の結果を得るためには、元のクラスを直接修正するのが一般的ですが、 それが不可能であったり、他の理由により複雑であったりする場合には、 拡張オブジェクトを利用することで、直接修正することなく限定的な拡張を行うことが できます。
拡張オブジェクトは、既存の型に追加のプロパティを追加します。拡張型の定義によって、プログラマはクラスの登録時に拡張型として知られる追加の型を提供することができます。拡張型のメンバは、QMLの中で使用される際に、元のターゲットクラスに透過的にマージされます。例えば
QLineEdit { leftMargin: 20 }
leftMargin
プロパティは、既存の C++ 型であるQLineEdit に、ソースコードを変更することなく追加された新しいプロパティです。
QML_EXTENDED (拡張)マクロは、拡張型を登録するためのマクロです。引数は、拡張として使用する別のクラスの名前です。
また、QML_EXTENDED_NAMESPACE (namespace)を使用して、名前空間、特にその中で宣言された列挙を型の拡張として登録することもできます。拡張する型自体が名前空間である場合は、代わりに QML_NAMESPACE_EXTENDED(namespace) を使う必要があります。
QObjectコンストラクタはQObject ポインタを受け取ります。ただし、拡張クラスの作成は最初の拡張プロパティにアクセスするまで延期されます。拡張クラスが作成され、ターゲット・オブジェクトが親として渡されます。元のオブジェクトのプロパティにアクセスすると、代わりに拡張オブジェクトの対応するプロパティが使用されます。
外部型の登録
上記のマクロを保持するために変更できないC++型があるかもしれません。それらは、サードパーティ・ライブラリの型であったり、それらのマクロの存在と矛盾する何らかの契約を満たす必要がある型であったりします。しかし、QML_FOREIGN マクロを使用することで、それらの型をQMLに公開することができます。そのためには、以下のように登録マクロのみからなる構造体を別途作成してください:
// Contains class Immutable3rdParty #include <3rdpartyheader.h> struct Foreign { Q_GADGET QML_FOREIGN(Immutable3rdParty) QML_NAMED_ELEMENT(Accessible3rdParty) QML_ADDED_IN_VERSION(2, 4) // QML_EXTENDED, QML_SINGLETON ... };
このコードから、Immutable3rdPartyのメソッドとプロパティを持ち、 Foreignで指定されたQMLの特性(例:singleton、extended)を持つQML型が得られます。
QML固有の型と属性の定義
付属プロパティの提供
QMLの言語構文には、アタッチド・プロパティや アタッチド・シグナル・ハンドラ といった、オブジェクトに付加される属性の概念があります。基本的に、このような属性はアタッチ型によって実装され提供され、これらの属性は別の型のオブジェクトにアタッチすることができます。これは、オブジェクト型自体(またはオブジェクトの継承型)によって提供される通常のオブジェクト・プロパティとは対照的です。
例えば、以下のItem は、アタッチド・プロパティとアタッチド・ハンドラを使用しています:
import QtQuick 2.0 Item { width: 100; height: 100 focus: true Keys.enabled: false Keys.onReturnPressed: console.log("Return key was pressed") }
ここで、Item オブジェクトは、Keys.enabled
とKeys.onReturnPressed
の値にアクセスし、設定することができます。これにより、Item オブジェクトは、自身の既存の属性の拡張として、これらの追加属性にアクセスすることができます。
付属オブジェクトの実装手順
上記の例を考えるとき、いくつかの関係者がいます:
enabled
、returnPressed
シグナルを持つ無名のアタッチド・オブジェクト・タイプのインスタンスがあり、Item オブジェクトにアタッチされて、これらの属性にアクセスしたり設定したりできるようになっている。- Item オブジェクトは、アタッチされたオブジェクトタイプのインスタンスがアタッチされたアタッシェ(attachee)である。
- Keys はアタッチ型であり、アタッチされた オブジェクト型の属性に アクセスするための名前付き修飾子 "Keys "をアタッチ型に提供する。
QML エンジンはこのコードを処理するとき、アタッチされたオブジェクト型のインスタンスを1つ生成し、このインスタンスをItem オブジェクトにアタッチします。これにより、インスタンスのenabled
とreturnPressed
属性にアクセスできるようになります。
アタッチド・オブジェクトを提供するメカニズムは、アタッチド・オブジェクト・タイプと アタッチド・タイプのクラスを提供することによって、C++から実装することができる。アタッチド・オブジェクト型には、アタッシェ・オブジェクトにアクセス可能な属性を定義するQObject 派生クラスを提供する。アタッチ型には、QObject-derived クラスを用意します:
- 以下のシグネチャを持つ static qmlAttachedProperties() を実装する:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);
このメソッドは、アタッチされたオブジェクト・タイプのインスタンスを返す。
QMLエンジンは、
object
パラメータで指定されたattacheeにアタッチされたオブジェクト型のインスタンスをアタッチするために、このメソッドを呼び出します。厳密には必須ではありませんが、このメソッドの実装では、メモリリークを防ぐため、返されたインスタンスをobject
の親とするのが通例です。このメソッドは、各 attachee オブジェクトのインスタンスに対してエンジンによって最大 1 回呼び出される。その結果、attachee
object
が破棄されるまで、アタッチメント・オブジェクトは削除されない。 - をアタッチ型として宣言するには、クラス宣言にQML_ATTACHED(attached) マクロを追加します。引数はアタッチオブジェクト型の名前です。
アタッチド・オブジェクトの実装:例
例えば、先の例で説明したMessage
:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
例えば、Message
がメッセージボードに公開されたときにシグナルをトリガーし、メッセージがメッセージボード上でいつ期限切れになったかを追跡する必要があるとします。これらの属性をMessage
に直接追加するのは意味がないので、"MessageBoard "修飾子を通して提供されるMessage
オブジェクトのアタッチされた属性として実装することができます。先に説明した概念に照らし合わせると、ここで関係する当事者は以下の通りです:
published
シグナルと期限切れプロパティを提供する匿名アタッチド・オブジェクト型のインスタンス。この型はMessageBoardAttachedType
で実装されている。- アタッシェとなる
Message
オブジェクト。 Message
オブジェクトがアタッチされた属性にアクセスするために使用するアタッチ型となるMessageBoard
型。
以下は実装例である。まず、アタッシェにアクセスするために必要なプロパティとシグナルを持つアタッシェ・オブジェクト・タイプが必要です:
class MessageBoardAttachedType : public QObject { Q_OBJECT Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged) QML_ANONYMOUS public: MessageBoardAttachedType(QObject *parent); bool expired() const; void setExpired(bool expired); signals: void published(); void expiredChanged(); };
次に、アタッチ型であるMessageBoard
は、MessageBoardAttachedType によって実装されるように、アタッチされたオブジェクト型のインスタンスを返すqmlAttachedProperties()
メソッドを宣言しなければならない。さらに、MessageBoard
をQML_ATTACHED() マクロでアタッチ型として宣言しなければならない:
class MessageBoard : public QObject { Q_OBJECT QML_ATTACHED(MessageBoardAttachedType) QML_ELEMENT public: static MessageBoardAttachedType *qmlAttachedProperties(QObject *object) { return new MessageBoardAttachedType(object); } };
これで、Message
型は、アタッチされたオブジェクト型のプロパティとシグナルにアクセスできるようになります:
Message { author: "Amelie" creationDate: new Date() MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00") MessageBoard.onPublished: console.log("Message by", author, "has been published!") }
さらに、C++の実装では、qmlAttachedPropertiesObject ()関数を呼び出すことで、任意のオブジェクトにアタッチされたアタッチド・オブジェクト・インスタンスにアクセスできる。
たとえば、次のようになります:
Message *msg = someMessageInstance(); MessageBoardAttachedType *attached = qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg)); qDebug() << "Value of MessageBoard.expired:" << attached->expired();
アタッチされたプロパティの伝播
QQuickAttachedPropertyPropagator をサブクラス化すると、 や と同様に、アタッチされたプロパティを親オブジェクトから子オブジェクトに伝搬することができます。これは、 、 、 を介したプロパゲーションに対応しています。font palette items popups windows
プロパティ修飾子型
プロパティ修飾型は、QMLオブジェクト型の特別な一種です。プロパティ修飾型のインスタンスは(QMLオブジェクトインスタンスの)プロパティに影響します。プロパティ修飾タイプには2つの種類があります:
- プロパティ値書き込みインターセプター
- プロパティ値ソース
プロパティ値書き込みインターセプターは、プロパティに書き込まれる値をフィルタしたり、変更したりするために使用することができます。現在、唯一サポートされているプロパティ値書き込みインターセプターは、QtQuick
インポートによって提供されるBehavior タイプです。
プロパティ値ソースは、時間の経過とともにプロパティの値を自動的に更新するために使用できます。クライアントは、独自のプロパティ値ソース・タイプを定義することができます。QtQuick
インポートによって提供される様々なプロパティ・アニメーション型は、プロパティ値ソースの例である。
プロパティ修飾タイプのインスタンスは、"<ModifierType> on <propertyName>"構文によってQMLオブジェクトのプロパティに適用することができます:
import QtQuick 2.0 Item { width: 400 height: 50 Rectangle { width: 50 height: 50 color: "red" NumberAnimation on x { from: 0 to: 350 loops: Animation.Infinite duration: 2000 } } }
これは一般に "on "構文と呼ばれています。
クライアントは独自のプロパティ値ソースタイプを登録することができますが、現在のところプロパティ値書き込みインターセプターは登録できません。
プロパティ値ソース
プロパティ値ソースは、<PropertyValueSource> on <property>
の構文を使って、プロパティの値を自動的に更新することができるQML型です。例えば、QtQuick
モジュールが提供する様々なプロパティ・アニメーション・タイプがプロパティ値ソースの例です。
プロパティ値ソースは、QQmlPropertyValueSource をサブクラス化し、時間の経過とともにプロパティに異なる値を書き込む実装を提供することで、C++で実装することができます。プロパティ値ソースがQMLの<PropertyValueSource> on <property>
構文を使ってプロパティに適用されると、エンジンによってこのプロパティへの参照が与えられ、プロパティ値を更新できるようになります。
例えば、RandomNumberGenerator
クラスをプロパティ値ソースとして利用できるようにし、QML プロパティに適用すると、500 ミリ秒ごとにプロパティ値が異なる乱数に更新されるようにしたとします。さらに、この乱数生成器には最大値を与えることができます。このクラスは次のように実装します:
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged); QML_ELEMENT public: RandomNumberGenerator(QObject *parent) : QObject(parent), m_maxValue(100) { QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty())); m_timer.start(500); } int maxValue() const; void setMaxValue(int maxValue); virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; } signals: void maxValueChanged(); private slots: void updateProperty() { m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue)); } private: QQmlProperty m_targetProperty; QTimer m_timer; int m_maxValue; };
QMLエンジンは、RandomNumberGenerator
をプロパティの値ソースとして使用する場合、RandomNumberGenerator::setTarget()
を呼び出し、値ソースが適用されたプロパティの型を提供します。RandomNumberGenerator
の内部タイマーが500ミリ秒ごとにトリガーされると、指定されたプロパティに新しい数値が書き込まれます。
RandomNumberGenerator
クラスが QML の型システムに登録されると、QML からプロパティ値のソースと して利用できるようになります。以下では、Rectangle の幅を500ミリ秒ごとに変更するために使用しています:
import QtQuick 2.0 Item { width: 300; height: 300 Rectangle { RandomNumberGenerator on width { maxValue: 300 } height: 100 color: "red" } }
それ以外の点では、プロパティ値ソースはプロパティやシグナル・メソッドなどを持つ通常のQML型ですが、<PropertyValueSource> on <property>
の構文を用いてプロパティ値を変更することができるという機能が追加されています。
プロパティ値ソースオブジェクトがプロパティに代入されるとき、QMLはまず 通常のQML型と同様に代入を試みます。割り当てに失敗した場合のみ、エンジンはsetTarget() メソッドを呼び出します。これにより、この型は値のソースとしてだけでなく、他のコンテキストでも使用することができるようになります。
QMLオブジェクト型のデフォルトプロパティと親プロパティの指定
インスタンス化可能な QML オブジェクト型として登録されたQObject 由来の型には、デフォルトプロパティを指定することができます。デフォルトプロパティとは、オブジェクトの子プロパティが特定のプロパティに代入されていない場合に、自動的に代入されるプロパティのことです。
デフォルトプロパティは、"DefaultProperty "の値を指定して、Q_CLASSINFO()マクロを呼び出すことで設定することができます。例えば、以下のMessageBoard
クラスは、そのmessages
プロパティをクラスのデフォルト・プロパティとして指定します:
class MessageBoard : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) Q_CLASSINFO("DefaultProperty", "messages") QML_ELEMENT public: QQmlListProperty<Message> messages(); private: QList<Message *> m_messages; };
これにより、MessageBoard
オブジェクトの子プロパティが、特定のプロパティに割り当てられていない場合、自動的にそのmessages
プロパティに割り当てられるようになります。例えば
MessageBoard { Message { author: "Naomi" } Message { author: "Clancy" } }
もしmessages
がデフォルト・プロパティとして設定されていなければ、Message
オブジェクトはすべて、以下のように、代わりにmessages
プロパティに明示的に割り当てられなければなりません:
MessageBoard { messages: [ Message { author: "Naomi" }, Message { author: "Clancy" } ] }
(ちなみに、Item::data プロパティはデフォルト・プロパティです。このdata
プロパティに追加されたItem オブジェクトはすべてItem::children のリストにも追加されます。したがって、デフォルト・プロパティを使用することで、children プロパティに明示的に割り当てることなく、アイテムのビジュアル・チルドレンを宣言することができます)。
さらに、"ParentProperty"Q_CLASSINFO() を宣言することで、QML 階層における親オブジェクトを表すプロパティを QML エンジンに通知することができます。例えば、Message 型を次のように宣言します:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QObject* board READ board BINDABLE boardBindable) Q_PROPERTY(QString author READ author BINDABLE authorBindable) Q_CLASSINFO("ParentProperty", "board") QML_ELEMENT public: Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; } QObject *board() const { return m_board.value(); } QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); } QString author() const { return m_author.value(); } QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); } private: QProperty<QObject *> m_board; QProperty<QString> m_author; };
親プロパティを定義することで、qmllintなどのツールはコードの意図をより的確に把握することができ、プロパティアクセスによる誤警告を回避することができます。
Qt Quick モジュールによるビジュアル項目の定義
Qt Quickモジュールでユーザーインターフェースを構築する場合、視覚的に描画されるすべての QML オブジェクトはItem 型から派生する必要があります。このItem 型は、Qt Quickモジュールが提供するQQuickItem C++ クラスによって実装されています。したがって、QMLベースのユーザーインターフェースに統合できるビジュアル型をC++で実装する必要がある場合は、このクラスをサブクラス化する必要があります。
詳しくはQQuickItem のドキュメントを参照してください。また、チュートリアル「Writing QML Extensions with C++」では、QQuickItem ベースのビジュアルアイテムを C++ で実装し、Qt Quick ベースのユーザーインターフェースに組み込む方法を紹介しています。
オブジェクトの初期化通知を受け取る
QMLのカスタムオブジェクトの中には、オブジェクトが作成され、そのプロパティがすべて設定されるまで、特定のデータの初期化を遅らせた方がよいものがあります。例えば、初期化にコストがかかる場合や、すべてのプロパティ値が初期化されるまで初期化を行うべきではない場合などです。
Qt Qmlモジュールは、このような目的のためにサブクラス化されるQQmlParserStatus 。Qt Qmlモジュールは、コンポーネントのインスタンス化の様々な段階で呼び出される仮想メソッドを定義しています。これらの通知を受け取るには、C++クラスはQQmlParserStatus を継承し、Q_INTERFACES() マクロを使用してQtメタシステムに通知する必要があります。
例えば
class MyQmlType : public QObject, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) QML_ELEMENT public: virtual void componentComplete() { // Perform some initialization here now that the object is fully created } };
ここに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。