C++ から QML オブジェクトを操作する
QML のオブジェクト型は、エンジン内部で実装されたものであれ、サードパーティが 定義したものであれ、すべてQObject から派生したものです。つまり、QML エンジンは QtMeta Object System を使ってQML オブジェクトを動的にインスタンス化し、生成されたオブジェクトを調べることができます。
これは、C++のコードからQMLオブジェクトを生成する際に便利で、視覚的にレンダリングできるQMLオブジェクトを表示したり、視覚的でないQMLオブジェクトのデータをC++アプリケーションに統合したりすることができます。一旦QMLオブジェクトが作成されると、C++からそのオブジェクトを検査し、 プロパティの読み書きを行ったり、メソッドを呼び出したり、シグナル通知を受け取ったりすることができます。
C++と様々なQML統合方法についての詳細は、C++とQML統合の概要ページをご参照ください。
C++ からの QML オブジェクトの読み込み
QML 文書は、QQmlComponent またはQQuickView を使って読み込むことができます。QQmlComponent は QML 文書を C++ オブジェクトとして読み込み、C++ コードから変更することができます。QQuickView も同様に読み込みますが、QQuickView はQWindow 由来のクラスであるため、読み込まれたオブジェクトはビジュアル表示されます。QQuickView は一般的に、表示可能な QML オブジェクトをアプリケーションのユーザーインターフェースに統合するために使用されます。
例えば、次のようなMyItem.qml
ファイルがあるとします:
import QtQuick Item { width: 100; height: 100 }
このQML文書は、QQmlComponent またはQQuickView を使って、次のようなC++コードで読み込むことができます。QQmlComponent を使用するには、QQmlComponent::create() を呼び出してコンポーネントの新しいインスタンスを作成する必要があります。一方、QQuickView を使用すると、自動的にコンポーネントのインスタンスが作成され、QQuickView::rootObject() からアクセスできるようになります:
// Using QQmlComponent QQmlEngine engine; QQmlComponent component(&engine, QUrl::fromLocalFile("MyItem.qml")); QObject *object = component.create(); ... delete object; | // Using QQuickView QQuickView view; view.setSource(QUrl::fromLocalFile("MyItem.qml")); view.show(); QObject *object = view.rootObject(); |
このobject
は、作成されたMyItem.qml
コンポーネントのインスタンスです。QObject::setProperty() またはQQmlProperty::write() を使用して、アイテムのプロパティを変更できます:
object->setProperty("width", 500); QQmlProperty(object, "width").write(500);
QObject::setProperty()
とQQmlProperty::write()
の違いは、後者がプロパティ値の設定に加えてバインディングの削除も行うことです。例えば、上記のwidth
の代入がheight
へのバインディングであったとします:
width: height
object->setProperty("width", 500)
呼び出しの後にItem
のheight
が変更された場合、バインディングは有効なままなので、width
は再度更新されます。しかし、QQmlProperty(object, "width").write(500)
の呼び出し後にheight
が変更された場合、バインディングはもう存在しないので、width
は変更されません。
別の方法として、オブジェクトを実際の型にキャストし、コンパイル時に安全なメソッドを呼び出すこともできます。この場合、MyItem.qml
のベース・オブジェクトはItem であり、QQuickItem クラスによって定義されます:
QQuickItem *item = qobject_cast<QQuickItem*>(object); item->setWidth(500);
また、QMetaObject::invokeMethod()やQObject::connect()を使って、コンポーネント内で定義されたシグナルや呼び出しメソッドに接続することもできます。詳しくは後述のQMLメソッドの呼び出しと QMLシグナルへの接続を参照してください。
よく定義された C++ インターフェースによる QML オブジェクトへのアクセス
C++ から QML にアクセスするための最良の方法は、C++ でインターフェースを定義し、 QML からそのインターフェースにアクセスすることです。他の方法では、QMLのコードをリファクタリングすると、QMLとC++の相互作用が簡単に壊れてしまいます。また、QMLとC++の相互作用は、QMLを経由することで、ユーザやqmllintのようなツールの両方から、より簡単に推論することができます。C++からQMLにアクセスすると、外部のC++コードがあるQMLコンポーネントを変更していないことを手作業で確認しなければ理解できないQMLコードになります。
QMLにやりとりをさせるには、まずC++のインターフェースを定義する必要があります:
class CppInterface : public QObject { Q_OBJECT QML_ELEMENT // ... };
QML主導のアプローチを用いると、このインターフェースは2つの方法で操作することができます:
シングルトン
1つの方法は、インターフェイスにQML_SINGLETON マクロを追加することで、 インターフェイスをシングルトンとして登録し、すべてのコンポーネントに公開することです。その後、単純なimport文によってインターフェースを利用できるようになります:
import my.company.module Item { Component.onCompleted: { CppInterface.foo(); } }
単純にオブジェクトを渡すだけでは、プロパティを介して他のコンポーネントに明示的に渡したり、非限定アクセスを使用するという遅くて推奨されない方法を利用したりする必要があるからです。
初期プロパティ
もうひとつの方法は、QML_UNCREATABLE を使ってインターフェースを作成不可能なものとしてマークし、QQmlComponent::createWithInitialProperties()を使ってルートQMLコンポーネントに供給し、QML側で必須プロパティを指定する方法です。
ルートコンポーネントは次のようになります:
import QtQuick Item { required property CppInterface interface Component.onCompleted: { interface.foo(); } }
ここで必須プロパティとしてマークすることで、インターフェイスプロパティが設定されずにコンポーネントが作成されることを防ぎます。
createWithInitialProperties()
以外はC++ から QML オブジェクトを読み込むで説明したのと同じ方法でコンポーネントを初期化することができます:
component.createWithInitialProperties(QVariantMap{{u"interface"_s, QVariant::fromValue<CppInterface *>(new CppInterface)}});
この方法は、インターフェースをルートコンポーネントだけが使用できるようにする必要がある場合に推奨されます。また、C++側でインターフェイスのシグナルやスロットに接続しやすくなります。
どちらの方法もニーズに合わない場合は、代わりにC++モデルの使い方を調べるとよいでしょう。
ロードされたQMLオブジェクトへのオブジェクト名によるアクセス
QML コンポーネントは基本的にオブジェクトツリーであり、その子オブジェクトには 兄弟オブジェクトとそれ自身の子オブジェクトがあります。QML コンポーネントの子オブジェクトは、QObject::objectName プロパティとQObject::findChild() を使って探すことができます。例えば、MyItem.qml
のルート・アイテムに、Rectangle の子アイテムがあったとします:
import QtQuick Item { width: 100; height: 100 Rectangle { anchors.fill: parent objectName: "rect" } }
その子の位置は次のようになります:
オブジェクトは、同じobjectName
を持つ複数の子を持つ可能性があることに注意。たとえば、ListView はそのデリゲートのインスタンスを複数作成するので、そのデリゲートが特定の objectName で宣言されている場合、ListView は同じobjectName
を持つ複数の子を持つことになります。この場合、QObject::findChildren() を使用して、objectName
に一致するすべての子を見つけることができます。
警告 C++からQMLオブジェクトにアクセスし、それらを操作することは可能ですが、 テストやプロトタイピングを目的とする場合を除き、推奨される方法ではありません。QMLとC++の統合の強みの一つは、C++のロジックやデータセットのバックエンドとは別に、QMLでUIを実装できることです。このようなアプローチでは、QMLのUIをC++のUIに影響を与えずに変更することも難しくなります。
C++からQMLオブジェクト型のメンバへのアクセス
プロパティ
QML オブジェクトの中で宣言されたプロパティは、自動的に C++ からアクセスできるようになります。次のような QML アイテムがあるとします:
someNumber
プロパティの値はQQmlProperty 、あるいはQObject::setProperty() やQObject::property() を使って設定したり読み込んだりすることができます:
QQmlEngineengine.コンポーネント(&engine, "MyItem.qml")QQmlComponentcomponent(&engine, "MyItem.qml");QObject*オブジェクト =component.create(); qDebug() << "Property value:" << QQmlProperty::read(object, "someNumber").toInt(); QQmlProperty::write(object, "someNumber", 5000); qDebug() << "Property value:" << object->property("someNumber").toInt(); object->setProperty("someNumber", 100);
QML プロパティの値を変更する際には、常にQObject::setProperty(),QQmlProperty (),QMetaProperty::write() を使用するようにしましょう。例えば、m_buttonText
のメンバ変数の値を内部的に反映したbuttonText
プロパティを持つカスタム型PushButton
があるとします。このようにメンバ変数を直接変更することは良いアイデアではありません:
//bad code QQmlComponent component(engine, "MyButton.qml"); PushButton *button = qobject_cast<PushButton*>(component.create()); button->m_buttonText = "Click me";
値が直接変更されるため、Qtのメタオブジェクトシステムをバイパスすることになり、QMLエンジンはプロパティの変更を認識することができません。つまり、buttonText
へのプロパティのバインディングは更新されず、onButtonTextChanged
ハンドラも呼び出されません。
QMLメソッドの呼び出し
すべてのQMLメソッドはメタオブジェクトシステムに公開されており、C++からQMetaObject::invokeMethod() を使って呼び出すことができます。以下のコードにあるように、コロンの後にパラメータや戻り値の型を指定することが できます。これは例えば、C++のあるシグネチャを持つシグナルをQMLで定義されたメソッドに接続したい場合などに便利です。型を省略した場合、C++のシグネチャはQVariant を使用します。
以下はQMetaObject::invokeMethod() を使って QML メソッドを呼び出す C++ アプリケーションです:
QML | // MyItem.qml import QtQuick Item { function myQmlFunction(msg: string) : string { console.log("Got message:", msg) return "some return value" } } |
C++ | // main.cppQQmlEngineengine;QQmlComponentcomponent(&engine, "MyItem.qml");QObject*object =component.create();QString戻り値QStringmsg= "Hello from C++";QMetaObject::invokeMethod(object, "myQmlFunction",Q_RETURN_ARG(QString戻り値),Q_ARG(QStringmsg)); qDebug() << "QML function returned:" << returnedValue; deleteオブジェクト; |
コロンの後に指定されたパラメータと戻り値の型に注目してください。型名には値型と オブジェクト型を使うことができます。
もし型が省略されたり、QMLでvar
と指定された場合は、QMetaObject::invokeMethod を呼び出す際に、Q_RETURN_ARG()やQ_ARG()で型としてQVariant を渡す必要があります。
QMLシグナルへの接続
すべての QML シグナルは自動的に C++ で利用可能となり、通常の Qt C++ シグナルと同様にQObject::connect() を使って接続することができます。その代わり、C++のシグナルはシグナルハンドラを用いてQMLオブジェクトに受信させることができます。
以下は、qmlSignal
という名前のシグナルを持つ QML コンポーネントです。 は文字列型のパラメータで発信されます。このシグナルはQObject::connect() を使って C++ オブジェクトのスロットに接続され、qmlSignal
が発信されるたびにcppSlot()
メソッドが呼び出されるようになっています:
クラスMyClass :publicQObject {Q_OBJECTpublic slots:voidcppSlot(constQString&msg) { スロット qDebug() << "Called the C++ slot with message:" << msg; } };intmain(intargc, char *argv[]) { { app(argc, argv) QGuiApplicationapp(argc,argv); QQuickViewビュー(QUrl::fromLocalFile("MyItem.qml")); QObject*MyClass myClass; QObject::connect(item,SIGNAL(qmlSignal(QString)), &myClass,SLOT(cppSlot(QString))); view.show();returnapp.exec(); } |
シグナルパラメータのQMLオブジェクト型は、C++ではクラスへのポインタに変換されます:
classMyClass :publicQObject { Q_OBJECTpublic slots:voidcppSlot(QQuickItem*アイテム qDebug() << "Called the C++ slot with item:" << item; qDebug() << "Item dimensions:" << item->width() << item->height(]); } };intmain(intargc, char *argv[]) { { app(argc, argv) QGuiApplicationapp(argc,argv); QQuickViewビュー(QUrl::fromLocalFile("MyItem.qml")); QObject*MyClass myClass; QObject::connect(item,SIGNAL(qmlSignal(QVariant)), &myClass,SLOT(cppSlot(QVariant))); view.show();returnapp.exec(); } |
© 2025 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.