C++ からの QML 型の定義
C++ コードで QML を拡張する場合、C++ クラスを QML 型システムに登録することで、そのクラスを QML コード内のデータ型として使用できるようになります。 「C++ 型の属性を QML に公開する」で説明したように、QObject 派生クラスのプロパティ、メソッド、シグナルは QML からアクセス可能ですが、そのようなクラスは、型システムに登録されるまでは、QML からデータ型として使用することはできません。 さらに、登録を行うことで、そのクラスをQMLからインスタンス化可能なQMLオブジェクト型として使用できるようにしたり、そのクラスのシングルトンインスタンスをQMLからインポートして使用できるようにしたりするなど、その他の機能も利用できるようになります。
さらに、 Qt Qml このモジュールは、C++ において、アタッチドプロパティやデフォルトプロパティといった QML 固有の機能を実装するためのメカニズムも提供します。
(なお、このドキュメントで取り上げられている重要な概念の多くは、「C++ を使用した QML 拡張機能の作成」チュートリアルで実例が紹介されています。
注:QML 型を宣言するすべてのヘッダーは、プロジェクトのインクルードパスからプレフィックスなしでアクセスできる必要があります。
C++ およびさまざまな QML 統合方法の詳細については、「C++ と QML の統合の概要」ページを参照してください。
QML タイプシステムへの C++ タイプの登録
QObject を継承したクラスを QML タイプシステムに登録することで、その型を QML コード内からデータ型として使用できるようになります。
エンジンでは、インスタンス化可能な型とインスタンス化不可能な型の両方の登録が可能です。インスタンス化可能な型を登録すると、C++ クラスを QML オブジェクト型の定義として使用できるようになり、QML コードからのオブジェクト宣言でその型を使用し、この型のオブジェクトを作成できるようになります。 また、登録によってエンジンに追加の型メタデータが提供され、その型(およびそのクラスで宣言された列挙型)を、QMLとC++の間でやり取りされるプロパティ値、メソッドの引数および戻り値、シグナルの引数としてのデータ型として使用できるようになります。
インスタンス化不可能な型を登録しても、同様にそのクラスはデータ型として登録されますが、その型をQMLからQMLオブジェクト型としてインスタンス化して使用することはできません。これは、例えば、型にQMLに公開すべき列挙型が含まれているものの、型自体はインスタンス化可能にしてはいけない場合などに役立ちます。
C++型をQMLに公開するための適切なアプローチを選択するためのクイックガイドについては、「C++とQML間の適切な統合方法の選択」を参照してください。
前提条件
以下で言及するすべてのマクロは、QtQmlIntegrationモジュールのqqmlintegration.h ヘッダーファイルから利用可能です。
マクロを利用可能にするには、それらを使用するファイルに以下のコードを追加する必要があります:
#include <QtQmlIntegration/qqmlintegration.h>すでにQtQml モジュールをリンクしている場合は、代わりにqqmlregistration.h ヘッダーファイルを使用できます。これにより、qqmlintegration.h が次のようにインクルードされます:
#include <QtQml/qqmlregistration.h>さらに、クラスの宣言は、プロジェクトのインクルードパス経由でアクセス可能なヘッダーファイル内に記述する必要があります。これらの宣言はコンパイル時に登録コードを生成するために使用され、登録コードには宣言を含むヘッダーファイルをインクルードする必要があります。
インスタンス化可能なオブジェクト型の登録
QObject を継承したC++クラスは、 QMLオブジェクト型の定義として登録できます。クラスがQML型システムに登録されると、そのクラスはQMLコードから他のオブジェクト型と同様に宣言およびインスタンス化できるようになります。 クラスインスタンスが作成されると、QML から操作できるようになります。「C++ 型の属性を 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:
// ...
};この型は、プロジェクト・ファイルに適切な型ネームスペースとバージョン番号を追加することで登録できます。たとえば、com.mycompany.messaging ネームスペースでバージョン 1.0 のこの型を利用可能にするには、次のようにします:
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コード内でプロパティ型として使用できるようになります。そのインスタンスはQMLから操作可能です。「C++型の属性を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:
// ...
};値型で実行できることには、さらにいくつかの制限があります:
- 値型はシングルトンにすることはできません。
- 値型は、デフォルトコンストラクタとコピーコンストラクタを持つ必要があります。
- 値型でQProperty をメンバとして使用することは問題を引き起こします。値型はコピーされるため、その時点でQProperty へのバインディングをどう扱うかを決定する必要があります。値型ではQProperty を使用すべきではありません。
- 値型はアタッチドプロパティを提供できません。
- 値型への拡張を定義するための API(QML_EXTENDED )は公開されておらず、将来変更される可能性があります。
列挙型を持つ値型
値型からQMLに対して列挙型を公開するには、いくつかの追加の手順が必要です。
QMLでは値型の名前の先頭文字は小文字で表記されますが、小文字で始まる型は、一般的にJavaScriptコードから参照することができません(`pragma ValueTypeBehavior: Addressable`を指定した場合を除く)。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)
}これにより、Person (大文字)という名前のQMLネームスペースが生成され、TheEnum という列挙型と、A 、B 、C という値が定義されます。その後、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_ELEMENT または と組み合わせて使用すると、後述するように、QMLからインポート可能なシングルトン型を登録します。QML_NAMED_ELEMENT
QML タイプシステムに登録されるすべての C++ タイプは、たとえインスタンス化できないものであっても、QObject から派生している必要があることに注意してください。
シングルトン型によるシングルトンオブジェクトの登録
シングルトン型を使用すると、クライアントがオブジェクトのインスタンスを手動で生成する必要なく、名前空間内でプロパティ、シグナル、およびメソッドを公開することができます。特にQObject のシングルトン型は、機能やグローバルなプロパティ値を提供するための効率的で便利な手段です。
シングルトン型は、エンジン内のすべてのコンテキストで共有されるため、関連付けられたQQmlContext を持たない点に注意してください。QObject のシングルトン型のインスタンスは、QQmlEngine によって生成・管理され、エンジンが破棄されると破棄されます。
QObject のシングルトン型は、他のQObject やインスタンス化された型と同様に操作できますが、1つのインスタンス(エンジンによって生成・所有される)しか存在せず、IDではなく型名で参照する必要がある点が異なります。QObject のシングルトン型のQ_PROPERTYにはバインドが可能であり、QObject モジュールAPIのQ_INVOKABLE 関数はシグナルハンドラ式内で使用できます。これにより、シングルトン型はスタイリングやテーマ設定を実装するための理想的な手段となり、また「.pragma library」スクリプトインポートの代わりに、グローバル状態の保存やグローバル機能の提供にも使用できます。
一度登録されると、QObject のシングルトン型は、QMLに公開されている他のQObject インスタンスと同様にインポートして使用できます。以下の例では、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 で登録された型の列挙 値は、大文字で始まる必要があります。
final プロパティ
Q_PROPERTY に対してFINAL 修飾子を使用して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 マクロを使用してリビジョンにタグ付けすることができます。
class CppType : public BaseType
{
Q_OBJECT
Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION(1, 0))
QML_ELEMENT
signals:
Q_REVISION(1, 0) void rootChanged();
};この方法で指定されたリビジョンは、プロジェクトファイルで指定されたメジャーバージョンに対するマイナーバージョンとして自動的に解釈されます。この場合、root は、MyTypes バージョン1.1以降がインポートされた場合にのみ利用可能です。MyTypes バージョン1.0のインポートには影響ありません。
同様の理由から、新しいバージョンで導入された新しい型には、QML_ADDED_IN_VERSION マクロを付与する必要があります。
この言語機能により、既存のアプリケーションの互換性を損なうことなく動作の変更を行うことが可能になります。したがって、QMLモジュールの作成者は、マイナーバージョン間の変更点を必ず文書化するようにし、QMLモジュールの利用者は、更新されたインポート文を適用する前に、アプリケーションが正常に動作し続けることを確認する必要があります。
その型が依存している基底クラスの改訂版は、型自体を登録する際に自動的に登録されます。これは、他の作成者が提供する基底クラスを継承する場合、例えばQt Quick モジュールのクラスを拡張する場合などに役立ちます。
注:QMLエンジンは 、グループ化されたプロパティオブジェクトやアタッチされたプロパティオブジェクトのプロパティやシグナルのリビジョンをサポートしていません。
拡張オブジェクトの登録
既存のクラスや技術を QML に統合する際、宣言型環境により適応させるために API の微調整が必要になることがよくあります。通常は元のクラスを直接変更することで最良の結果が得られますが、それが不可能な場合や他の要因によって複雑になる場合には、拡張オブジェクトを使用することで、直接変更することなく、自身が管理する型に対して限定的な拡張を行うことができます。 Qt 独自の型を拡張することはサポートされていません。
拡張オブジェクトは、既存の型に追加のプロパティを追加します。拡張された型定義により、プログラマはクラスを登録する際に、「拡張型」と呼ばれる追加の型を指定できるようになります。そのメンバは、QML内から使用される際、元のターゲットクラスと透過的に統合されます。例えば:
QLineEdit {
leftMargin: 20
}leftMargin プロパティは、ソースコードを変更することなく、既存のC++型であるQLineEdit に追加された新しいプロパティです。
QML_EXTENDED (拡張)マクロは、拡張型を登録するために使用されます。引数には、拡張として使用する別のクラスの名前を指定します。
また、QML_EXTENDED_NAMESPACE (名前空間)を使用することで、名前空間、特にその中に宣言された列挙型を、型の拡張として登録することもできます。拡張対象の型自体が名前空間である場合は、代わりにQML_NAMESPACE_EXTENDED (名前空間)を使用する必要があります。
拡張クラスは、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 オブジェクトは「アタッシェ」であり、アタッチされたオブジェクト型のインスタンスがこれにアタッチされています。
- Keys はアタッチ元タイプであり、アタッチ先に対して「Keys」という名前の修飾子を提供し、アタッチ先はこの修飾子を通じてアタッチされたオブジェクトタイプの属性にアクセスすることができます。
QML エンジンがこのコードを処理する際、アタッチされたオブジェクト型の単一のインスタンスを作成し、このインスタンスを `Item ` オブジェクトにアタッチすることで、そのインスタンスの `enabled ` および `returnPressed ` 属性へのアクセスを可能にします。
アタッチされたオブジェクトを提供する仕組みは、アタッチされたオブジェクト型および アタッチ元型用のクラスを用意することで、C++から実装できます。アタッチされたオブジェクト型については、QObject を継承したクラスを用意し、アタッチ先オブジェクトからアクセス可能にする属性を定義します。アタッチ元型については、QObject を継承したクラスを用意し、以下の条件を満たすようにします:
- 以下のシグネチャを持つ静的関数 `qmlAttachedProperties()` を実装すること:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);このメソッドは、アタッチされたオブジェクト型のインスタンスを返す必要があります。
QMLエンジンは、
objectパラメータで指定されたアタッチ先に対して、アタッチ対象オブジェクト型のインスタンスをアタッチするために、このメソッドを呼び出します。厳密には必須ではありませんが、メモリリークを防ぐために、このメソッドの実装では、返されるインスタンスの親をobjectに設定するのが慣例となっています。エンジンは、以降の添付プロパティへのアクセス用に返されたインスタンスポインタをキャッシュするため、このメソッドは各アタッチイーオブジェクトインスタンスに対して最大1回のみ呼び出されます。その結果、アタッチイーである
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シグナルとexpiredプロパティを提供する、匿名の添付オブジェクト型のインスタンス。この型は、以下のMessageBoardAttachedTypeによって実装されますMessageオブジェクト。これがアタッチ先となります- `
MessageBoard` 型。これは、`Message` オブジェクトがアタッチされた属性にアクセスするために使用するアタッチ元となる型です
以下に実装例を示します。まず、アタッチ先からアクセス可能な必要なプロパティとシグナルを持つ、アタッチされたオブジェクト型が必要です:
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 ミリ秒ごとにプロパティの値が異なる乱数に更新されます。さらに、この乱数生成器には `maxValue` を指定することもできます。このクラスは次のように実装できます:
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 オブジェクト型のデフォルトプロパティおよび親プロパティの指定
QObject から派生し、インスタンス化可能な QML オブジェクト型として登録された型は、オプションでその型のデフォルトプロパティを指定できます。デフォルトプロパティとは、オブジェクトの子要素が特定のプロパティに割り当てられていない場合に、自動的に割り当てられるプロパティのことです。
デフォルトプロパティは、クラスに対してQ_CLASSINFO()マクロを呼び出し、特定の「DefaultProperty」値を指定することで設定できます。たとえば、以下の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 モジュールを使用したビジュアルアイテムの定義
`xml-ph-0000@deepl.internal` モジュールを使用してユーザーインターフェースを構築する場合、 Qt Quick モジュールを使用してユーザーインターフェースを構築する場合、視覚的にレンダリングされるすべてのQMLオブジェクトは、Item 型から派生している必要があります。これは、 が Qt Quick。この `Item ` 型は、QQuickItem というC++クラスによって実装されており、このクラスは Qt Quick モジュールによって提供されるxml-ph-0001@deepl.internalというC++クラスによって実装されています。したがって、QMLベースのユーザーインターフェースに統合可能な視覚的タイプをC++で実装する必要がある場合は、このクラスをサブクラス化する必要があります。
詳細については、QQuickItem のドキュメントを参照してください。また、「C++ を使用した QML 拡張機能の作成」チュートリアルでは、QQuickItem ベースのビジュアルアイテムを C++ で実装し、Qt Quick ベースのユーザーインターフェースに統合する方法が示されています。
オブジェクトの初期化に関する通知の受信
一部のカスタム QML オブジェクト型では、オブジェクトが作成され、そのすべてのプロパティが設定されるまで、特定のデータの初期化を遅らせる方が有益な場合があります。たとえば、初期化に時間がかかる場合や、すべてのプロパティ値が初期化されるまで初期化を実行すべきでない場合などがこれに該当します。
Qt Qml モジュールは、これらの目的のためにサブクラス化できるQQmlParserStatus を提供しています。このクラスは、コンポーネントのインスタンス化中のさまざまな段階で呼び出される複数の仮想メソッドを定義しています。これらの通知を受け取るには、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
}
};「 QML Type Registration Macros 」 および「概要 - QML と C++ の統合」も参照してください 。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.