C++ を使用した高度な QML 拡張機能の作成
BirthdayParty ベースプロジェクト
extending-qml-advanced/advanced1-Base-project
このチュートリアルでは、誕生日パーティーを例に挙げて、QMLのいくつかの機能について解説します。以下で説明する各種機能のコードは、この誕生日パーティー・プロジェクトに基づいており、QML拡張機能に関する最初のチュートリアルで扱われた内容の一部を活用しています。このシンプルな例を基に、以下で説明する様々なQML拡張機能を具体的に示していきます。 コードに追加される各新しい拡張機能の完全なコードは、各セクションのタイトルの下に指定された場所にあるチュートリアル、またはこのページの最後にあるコードへのリンクから確認できます。
ベースプロジェクトでは、Person クラスとBirthdayParty クラスが定義されており、これらはそれぞれ参加者とパーティー自体をモデル化しています。
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
QML_ELEMENT
...
QString m_name;
int m_shoeSize = 0;
};
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
QML_ELEMENT
...
Person *m_host = nullptr;
QList<Person *> m_guests;
};パーティーに関するすべての情報は、対応する QML ファイルに保存できます。
BirthdayParty {
host: Person {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Person { name: "Leo Hodges" },
Person { name: "Jack Smith" },
Person { name: "Anne Brown" }
]
}main.cpp ファイルは、誰の誕生日か、そのパーティーに誰が招待されているかを表示するシンプルなシェルアプリケーションを作成します。
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadFromModule("People", "Main");
std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };このアプリは、パーティに関する以下の概要を出力します。
"Bob Jones" is having a birthday!
They are inviting:
"Leo Hodges"
"Jack Smith"
"Anne Brown"以下のセクションでは、継承と型変換を使用して、Person だけでなく、Boy およびGirl の参加者をサポートする方法、デフォルトプロパティを使用してパーティの参加者を暗黙的にゲストとして割り当てる方法、プロパティを1つずつではなくグループとして割り当てる方法、 添付オブジェクトを使用して招待客の応答を追跡する方法、プロパティ値ソースを使用して「ハッピーバースデー」の歌詞を時間経過とともに表示する方法、およびサードパーティのオブジェクトをQMLに公開する方法について解説します。
継承と型変換
extending-qml-advanced/advanced2-Inheritance-and-coercion
現在、各参加者は「人」としてモデル化されています。これは少し汎用的すぎるため、参加者についてより詳しい情報を得られると良いでしょう。参加者を「男の子」と「女の子」に細分化することで、誰が来るのかをより具体的に把握できるようになります。
これを実現するために、Boy クラスとGirl クラスが導入され、どちらもPerson を継承しています。
class Boy : public Person
{
Q_OBJECT
QML_ELEMENT
public:
using Person::Person;
};
class Girl : public Person
{
Q_OBJECT
QML_ELEMENT
public:
using Person::Person;
};Person クラスは変更されておらず、Boy およびGirl のC++クラスは、これを基にした単純な拡張です。型とそのQML名は、QML_ELEMENT を使用してQMLエンジンに登録されます。
なお、BirthdayParty 内のhost およびguests プロパティは、依然としてPerson のインスタンスを受け取ります。
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
QML_ELEMENT
...
};Person クラス自体の実装に変更はありません。しかし、Person クラスがBoy およびGirl の共通の基底クラスとして再利用されたため、Person は QML から直接インスタンス化できなくなりました。代わりに、明示的にBoy またはGirl をインスタンス化する必要があります。
class Person : public QObject
{
...
QML_ELEMENT
QML_UNCREATABLE("Person is an abstract base class.")
...
};QML内からのPerson のインスタンス化は禁止したいものの、プロパティ型として使用したり、他の型をこの型に強制変換したりできるようにするため、QMLエンジンへの登録は依然として必要です。これがQML_UNCREATABLE マクロの役割です。Person 、Boy 、Girl の 3 つの型はすべて QML システムに登録されているため、代入時に QML はBoy およびGirl オブジェクトを、自動的に(かつ型安全に)Person に変換します。
これらの変更により、出席者に関する追加情報を用いて、次のように誕生日パーティーを指定できるようになりました。
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Boy { name: "Leo Hodges" },
Boy { name: "Jack Smith" },
Girl { name: "Anne Brown" }
]
}デフォルトのプロパティ
extending-qml-advanced/advanced3-Default-properties
現在、QML ファイルでは、各プロパティが明示的に割り当てられています。たとえば、host プロパティにはBoy が、guests プロパティにはBoy またはGirl のリストが割り当てられています。これは簡単ですが、この特定のユースケースではもう少し簡略化できます。guests プロパティを明示的に割り当てる代わりに、partyオブジェクトの内部に直接Boy およびGirl オブジェクトを追加し、それらを暗黙的にguests に割り当てることができます。指定した参加者の中で、ホストではない全員がゲストであるというのは理にかなっています。この変更は純粋に構文上のものですが、多くの状況においてより自然な感覚をもたらすことができます。
guests プロパティを、BirthdayParty のデフォルトプロパティとして指定できます。つまり、BirthdayParty 内で作成された各オブジェクトは、暗黙的にデフォルトプロパティguests に追加されます。その結果、QMLは以下のようになります。
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
Boy { name: "Leo Hodges" }
Boy { name: "Jack Smith" }
Girl { name: "Anne Brown" }
}この動作を有効にするために必要な変更は、`BirthdayParty ` に `DefaultProperty ` クラス情報アノテーションを追加し、`guests ` をそのデフォルトプロパティとして指定することだけです。
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
Q_CLASSINFO("DefaultProperty", "guests")
QML_ELEMENT
...
};この仕組みについては、すでにご存じの方も多いかもしれません。QMLにおいて、Item のすべての子孫要素のデフォルトプロパティはdata プロパティです。Item のプロパティに明示的に追加されていない要素はすべて、data に追加されます。これにより、構造が明確になり、コード内の不要なノイズが軽減されます。
グループ化されたプロパティ
extending-qml-advanced/advanced4-Grouped-properties
ゲストの靴に関する詳細情報が必要です。サイズに加え、靴の色、ブランド、価格も保存したいと考えています。この情報はShoeDescription クラスに保存されます。
class ShoeDescription : public QObject
{
Q_OBJECT
Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
...
};各人物には、name と靴の説明(shoe )という 2 つのプロパティが設定されるようになりました。
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
...
};shoe descriptionの各要素の値を個別に指定することも可能ですが、少々手間がかかります。
Girl {
name: "Anne Brown"
shoe.size: 7
shoe.color: "red"
shoe.brand: "Job Macobs"
shoe.price: 99.99
}グループ化されたプロパティを使用すると、これらのプロパティをより洗練された方法で割り当てることができます。各プロパティに値を1つずつ割り当てる代わりに、個々の値をグループとしてshoe プロパティに渡すことができるため、コードの可読性が向上します。この機能はQML全体でデフォルトで利用可能であるため、有効にするための変更は不要です。
host: Boy {
name: "Bob Jones"
shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 }
}添付プロパティ
extending-qml-advanced/advanced5-Attached-properties
ホストが招待状を送る時期がやってきました。どのゲストがいつ招待状に返信したかを追跡するために、その情報を保存する場所が必要です。BirthdayParty オブジェクト自体に保存するのはあまり適していません。より良い方法は、返信をpartyオブジェクトの添付オブジェクトとして保存することです。
まず、ゲストの返信を保持するBirthdayPartyAttached クラスを宣言します。
class BirthdayPartyAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL)
QML_ANONYMOUS
...
};そして、これをBirthdayParty クラスにアタッチし、qmlAttachedProperties() を定義して、そのアタッチされたオブジェクトを返すようにします。
class BirthdayParty : public QObject
{
...
QML_ATTACHED(BirthdayPartyAttached)
...
static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
};これで、QML内で添付オブジェクトを使用して、招待客のRSVP情報を保持できるようになります。
BirthdayParty {
Boy {
name: "Robert Campbell"
BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd")
}
Boy {
name: "Leo Hodges"
shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd")
}
host: Boy {
name: "Jack Smith"
shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
}
}最後に、以下の方法でその情報にアクセスできます。
QDate rsvpDate;
QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
if (attached)
rsvpDate = attached->property("rsvp").toDate();プログラムは、今後のパーティーに関する以下の概要を出力します。
"Jack Smith" is having a birthday!
He is inviting:
"Robert Campbell" RSVP date: "Wed Mar 1 2023"
"Leo Hodges" RSVP date: "Mon Mar 6 2023"プロパティ値ソース
extending-qml-advanced/advanced6-Property-value-source
パーティーの間、ゲストはホストのために歌わなければなりません。ゲストを助けるために、その場に合わせた歌詞をプログラムが表示できれば便利です。この目的のために、プロパティ値ソースを使用して、時間の経過とともに歌の歌詞を生成します。
class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_INTERFACES(QQmlPropertyValueSource)
...
void setTarget(const QQmlProperty &) override;
};クラス `HappyBirthdaySong ` が値ソースとして追加されます。このクラスは `QQmlPropertyValueSource ` を継承し、`Q_INTERFACES ` マクロを使用して `QQmlPropertyValueSource ` インターフェースを実装する必要があります。関数 `setTarget() ` を使用して、このソースがどのプロパティに対して作用するかを定義します。 この場合、値ソースはBirthdayParty のannouncement プロパティに書き込みを行い、時間の経過とともに歌詞を表示します。この値ソースには内部タイマーが備わっており、partyのannouncement プロパティを繰り返し歌詞の次の行に設定します。
QMLでは、BirthdayParty 内でHappyBirthdaySong がインスタンス化されます。そのシグネチャにあるon キーワードは、値ソースが対象とするプロパティを指定するために使用され、この場合はannouncement です。また、HappyBirthdaySong オブジェクトのname プロパティは、パーティのホスト名にもバインドされています。
BirthdayParty {
id: party
HappyBirthdaySong on announcement {
name: party.host.name
}
...
}このプログラムは、partyStarted シグナルを使用してパーティーが始まった時刻を表示し、その後、以下の「ハッピーバースデー」の詩を繰り返し出力します。
Happy birthday to you,
Happy birthday to you,
Happy birthday dear Bob Jones,
Happy birthday to you!外部オブジェクトの統合
extending-qml-advanced/advanced7-Foreign-objects-integration
参加者は、歌詞を単にコンソールに出力するだけでなく、色に対応したより凝った表示を使いたいと考えています。彼らはこれをプロジェクトに統合したいと考えていますが、サードパーティのライブラリに由来するため、現時点では QML から画面を設定することはできません。 この問題を解決するには、必要な型をQMLエンジンに公開し、そのプロパティをQML内で直接変更できるようにする必要があります。
ディスプレイは、ThirdPartyDisplay クラスによって制御できます。このクラスには、表示するコンテンツや、テキストの前景色および背景色を定義するためのプロパティがあります。
class Q_DECL_EXPORT ThirdPartyDisplay : public QObject
{
Q_OBJECT
Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged FINAL)
Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor NOTIFY colorsChanged FINAL)
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY colorsChanged FINAL)
...
};この型をQMLに公開するには、QML_ELEMENT を使用してエンジンに登録します。ただし、このクラスは変更のためにアクセスできないため、単にQML_ELEMENT を追加することはできません。 この型をエンジンに登録するには、外部から登録する必要があります。これが `QML_FOREIGN ` の役割です。型内で他のQMLマクロと組み合わせて使用する場合、それらのマクロは自身が属する型ではなく、`QML_FOREIGN` で指定された外部型に適用されます。
class ForeignDisplay : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(ThirdPartyDisplay)
QML_FOREIGN(ThirdPartyDisplay)
};これにより、BirthdayParty には display という新しいプロパティが追加されました。
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL)
Q_PROPERTY(ThirdPartyDisplay *display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
...
};また、QML では、3 つ目の派手なディスプレイ上のテキストの色を明示的に設定できます。
BirthdayParty {
display: ThirdPartyDisplay {
foregroundColor: "black"
backgroundColor: "white"
}
...
}BirthdayParty のannouncement プロパティを設定すると、それ自体で出力するのではなく、そのメッセージがファンシーなディスプレイに送信されるようになりました。
void BirthdayParty::setAnnouncement(const QString &announcement)
{
if (m_announcement != announcement) {
m_announcement = announcement;
emit announcementChanged();
}
m_display->setContent(announcement);
}その結果、前のセクションと同様に、次のような出力が繰り返し表示されます。
[Fancy ThirdPartyDisplay] Happy birthday to you,
[Fancy ThirdPartyDisplay] Happy birthday to you,
[Fancy ThirdPartyDisplay] Happy birthday dear Bob Jones,
[Fancy ThirdPartyDisplay] Happy birthday to you!「QML オブジェクト型のデフォルトプロパティおよび親プロパティの指定」、「グループ化されたプロパティ」、「アタッチドプロパティの提供」、「プロパティ値のソース」、および「外部型の登録」も参照してください 。
© 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.