Auf dieser Seite

Interaktion mit QML-Objekten aus C++

Alle QML-Objekttypen sind vom Typ „ QObject “ abgeleitete Typen, unabhängig davon, ob sie intern von der Engine implementiert oder von Drittanbietern definiert wurden. Das bedeutet, dass die QML-Engine das Qt-Meta-Objekt-System nutzen kann, um jeden beliebigen QML-Objekttyp dynamisch zu instanziieren und die erstellten Objekte zu untersuchen.

Dies ist nützlich, um QML-Objekte aus C++-Code zu erstellen – sei es, um ein visuell darstellbares QML-Objekt anzuzeigen, oder um Daten von nicht-visuellen QML-Objekten in eine C++-Anwendung zu integrieren. Sobald ein QML-Objekt erstellt wurde, kann es von C++ aus untersucht werden, um Eigenschaften zu lesen und zu schreiben, Methoden aufzurufen und Signalbenachrichtigungen zu empfangen.

Weitere Informationen zu C++ und den verschiedenen Methoden zur QML-Integration finden Sie auf der Übersichtsseite zur C++- und QML-Integration.

Laden von QML-Objekten aus C++

Ein QML-Dokument kann mit ` QQmlComponent ` oder ` QQuickView` geladen werden. ` QQmlComponent ` lädt ein QML-Dokument als C++-Objekt, das anschließend über C++-Code geändert werden kann. ` QQuickView ` tut dies ebenfalls, doch da ` QQuickView ` eine von ` QWindow` abgeleitete Klasse ist, wird das geladene Objekt zusätzlich visuell dargestellt; ` QQuickView ` wird in der Regel verwendet, um ein darstellbares QML-Objekt in die Benutzeroberfläche einer Anwendung zu integrieren.

Nehmen wir zum Beispiel an, es gibt eine Datei „ MyItem.qml “, die wie folgt aussieht:

import QtQuick

Item {
    width: 100; height: 100
}

Dieses QML-Dokument kann mit QQmlComponent oder QQuickView mithilfe des folgenden C++-Codes geladen werden. Bei Verwendung eines ` QQmlComponent ` muss ` QQmlComponent::create()` aufgerufen werden, um eine neue Instanz der Komponente zu erstellen, während ein ` QQuickView ` automatisch eine Instanz der Komponente erstellt, auf die über ` QQuickView::rootObject()` zugegriffen werden kann:

// 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();

Diese ` object ` ist die Instanz der Komponente ` MyItem.qml `, die erstellt wurde. Sie können nun die Eigenschaften des Elements mithilfe von ` QObject::setProperty()` oder ` QQmlProperty::write()` ändern:

object->setProperty("width", 500);
QQmlProperty(object, "width").write(500);

Der Unterschied zwischen QObject::setProperty() und QQmlProperty::write() besteht darin, dass Letzteres nicht nur den Eigenschaftswert festlegt, sondern auch die Bindung entfernt. Angenommen, die obige Zuweisung width wäre eine Bindung zu height gewesen:

width: height

Wenn sich der Wert von „ height “ unter „ Item “ nach dem Aufruf von „ object->setProperty("width", 500) “ ändern würde, würde „ width “ erneut aktualisiert werden, da die Bindung weiterhin aktiv ist. Ändert sich jedoch der Wert von „ height “ nach dem Aufruf von „ QQmlProperty(object, "width").write(500) “, wird „ width “ nicht geändert, da die Bindung nicht mehr existiert.

Alternativ können Sie das Objekt in seinen tatsächlichen Typ umwandeln und Methoden mit Kompilierungssicherheit aufrufen. In diesem Fall ist das Basisobjekt von ` MyItem.qml ` ein ` Item`, das durch die Klasse ` QQuickItem ` definiert ist:

QQuickItem *item = qobject_cast<QQuickItem*>(object);
item->setWidth(500);

Sie können sich auch über ` QMetaObject::invokeMethod()` und ` QObject::connect()` mit beliebigen Signalen verbinden oder Methoden aufrufen, die in der Komponente definiert sind. Weitere Details finden Sie unten unter „Aufrufen von QML-Methoden und Verbinden mit QML-Signalen “.

Zugriff auf QML-Objekte über klar definierte C++-Schnittstellen

Der beste Weg, um von C++ aus mit QML zu interagieren, besteht darin, eine entsprechende Schnittstelle in C++ zu definieren und in QML selbst darauf zuzugreifen. Bei anderen Methoden kann eine Umgestaltung Ihres QML-Codes leicht dazu führen, dass die Interaktion zwischen QML und C++ nicht mehr funktioniert. Dies hilft auch dabei, die Interaktion zwischen QML- und C++-Code nachzuvollziehen, da eine Steuerung über QML sowohl für Benutzer als auch für Tools wie qmllint leichter nachvollziehbar ist. Der Zugriff auf QML aus C++ führt zu QML-Code, der nicht nachvollziehbar ist, ohne manuell zu überprüfen, ob kein externer C++-Code eine bestimmte QML-Komponente verändert; und selbst dann kann sich der Umfang des Zugriffs im Laufe der Zeit ändern, was die fortgesetzte Nutzung dieser Strategie zu einer Wartungsbelastung macht.

Damit QML die Interaktion steuern kann, müssen Sie zunächst eine C++-Schnittstelle definieren:

class CppInterface : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    // ...
};

Bei einem QML-gesteuerten Ansatz kann auf diese Schnittstelle auf zwei Arten zugegriffen werden:

Singletons

Eine Möglichkeit besteht darin, die Schnittstelle als Singleton zu registrieren, indem das Makro `QML_SINGLETON` zur Schnittstelle hinzugefügt wird, wodurch sie für alle Komponenten verfügbar wird. Anschließend kann die Schnittstelle über eine einfache Importanweisung genutzt werden:

import my.company.module

Item {
    Component.onCompleted: {
        CppInterface.foo();
    }
}

Verwenden Sie diesen Ansatz, wenn Sie Ihre Schnittstelle an mehr Stellen als nur in der Stammkomponente benötigen, da die einfache Weitergabe eines Objekts erfordern würde, es explizit über eine Eigenschaft an andere Komponenten weiterzugeben oder die langsame und nicht empfohlene Methode des unqualifizierten Zugriffs zu nutzen.

Anfängliche Eigenschaften

Eine weitere Möglichkeit besteht darin, die Schnittstelle über ` QML_UNCREATABLE ` als nicht erstellbar zu kennzeichnen und sie der QML-Stammkomponente mithilfe von ` QQmlComponent::createWithInitialProperties()` sowie einer erforderlichen Eigenschaft auf der QML-Seite zu übergeben.

Ihre Stammkomponente könnte etwa wie folgt aussehen:

import QtQuick

Item {
    required property CppInterface interface
    Component.onCompleted: {
        interface.foo();
    }
}

Indem die Eigenschaft hier als erforderlich gekennzeichnet wird, wird verhindert, dass die Komponente erstellt wird, ohne dass die Schnittstelleneigenschaft festgelegt ist.

Sie können Ihre Komponente dann auf dieselbe Weise initialisieren, wie unter „Laden von QML-Objekten aus C++“ beschrieben, allerdings unter Verwendung von „ createWithInitialProperties() “:

component.createWithInitialProperties(QVariantMap{{u"interface"_s, QVariant::fromValue<CppInterface *>(new CppInterface)}});

Diese Methode ist vorzuziehen, wenn Sie wissen, dass Ihre Schnittstelle nur für die Stammkomponente verfügbar sein muss. Außerdem ermöglicht sie eine einfachere Anbindung an Signale und Slots der Schnittstelle auf der C++-Seite.

Sollte keine dieser Methoden Ihren Anforderungen entsprechen, sollten Sie stattdessen die Verwendung von C++-Modellen in Betracht ziehen.

Zugriff auf geladene QML-Objekte über den Objektnamen

QML-Komponenten sind im Wesentlichen Objektbäume mit untergeordneten Elementen, die wiederum gleichrangige Elemente und eigene untergeordnete Elemente haben. Untergeordnete Objekte von QML-Komponenten können mithilfe der Eigenschaft „ QObject::objectName “ mit der Funktion „ QObject::findChild()“ ermittelt werden. Wenn beispielsweise das Stammelement in „ MyItem.qml “ ein untergeordnetes Element „ Rectangle “ hätte:

import QtQuick

Item {
    width: 100; height: 100

    Rectangle {
        anchors.fill: parent
        objectName: "rect"
    }
}

Das untergeordnete Element könnte wie folgt gefunden werden:

QObject *rect = object->findChild<QObject*>("rect");
if (rect)
    rect->setProperty("color", "red");

Beachten Sie, dass ein Objekt mehrere untergeordnete Elemente mit demselben „ objectName “ haben kann. Beispielsweise erstellt „ ListView “ mehrere Instanzen seines Delegaten. Wenn sein Delegat also mit einem bestimmten „objectName“ deklariert ist, hat „ ListView “ mehrere untergeordnete Elemente mit demselben „ objectName “. In diesem Fall kann „ QObject::findChildren()“ verwendet werden, um alle untergeordneten Elemente mit einem passenden „ objectName “ zu finden.

Warnung: Obwohl es möglich ist, von C++ aus auf QML-Objekte zuzugreifen und diese zu bearbeiten, ist dies – außer zu Test- und Prototyping-Zwecken – nicht der empfohlene Ansatz. Eine der Stärken der QML- und C++-Integration ist die Möglichkeit, Benutzeroberflächen in QML getrennt von der C++-Logik und dem Daten-Backend zu implementieren; dies scheitert jedoch, wenn die C++-Seite beginnt, QML direkt zu manipulieren. Ein solcher Ansatz erschwert zudem die Änderung der QML-Benutzeroberfläche, ohne dass dies Auswirkungen auf das entsprechende C++-Gegenstück hat.

Zugriff auf Mitglieder eines QML-Objekttyps aus C++

Eigenschaften

Alle in einem QML-Objekt deklarierten Eigenschaften sind automatisch von C++ aus zugänglich. Angenommen, es gibt ein QML-Element wie dieses:

// MyItem.qml
import QtQuick

Item {
    property int someNumber: 100
}

Der Wert der Eigenschaft „ someNumber “ kann mithilfe von „ QQmlProperty “ oder „ QObject::setProperty()“ und „ QObject::property()“ gesetzt und ausgelesen werden:

QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject*object = 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);

Sie sollten zum Ändern eines QML-Eigenschaftswerts immer „ QObject::setProperty()“, „ QQmlProperty “ oder „ QMetaProperty::write()“ verwenden, um sicherzustellen, dass die QML-Engine über die Änderung der Eigenschaft informiert wird. Angenommen, Sie haben einen benutzerdefinierten Typ „ PushButton “ mit einer Eigenschaft „ buttonText “, die intern den Wert einer Member-Variablen „ m_buttonText “ widerspiegelt. Eine direkte Änderung der Member-Variablen wie in diesem Beispiel ist keine gute Idee:

//bad code
QQmlComponent component(engine, "MyButton.qml");
PushButton *button = qobject_cast<PushButton*>(component.create());
button->m_buttonText = "Click me";

Da der Wert direkt geändert wird, wird das Metaobjektsystem von Qt umgangen, und die Qml-Engine wird nicht über die Änderung der Eigenschaft informiert. Das bedeutet, dass Eigenschaftsbindungen zu ` buttonText ` nicht aktualisiert würden und etwaige ` onButtonTextChanged `-Handler nicht aufgerufen würden.

Aufruf von QML-Methoden

Alle QML-Methoden sind für das Metaobjektsystem zugänglich und können von C++ aus mit ` QMetaObject::invokeMethod()` aufgerufen werden. Sie können die Typen für die Parameter und den Rückgabewert nach dem Doppelpunkt angeben, wie im folgenden Codeausschnitt gezeigt. Dies kann beispielsweise nützlich sein, wenn Sie ein Signal in C++ mit einer bestimmten Signatur an eine in QML definierte Methode binden möchten. Wenn Sie die Typen weglassen, verwendet die C++-Signatur ` QVariant`.

Hier ist eine C++-Anwendung, die eine QML-Methode mit ` QMetaObject::invokeMethod()` aufruft:

QML
// MyItem.qml
import QtQuick

Item {
    function myQmlFunction(msg: string) : string {
        console.log("Got message:", msg)
        return "some return value"
    }
}
C++
// main.cpp
QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject*object = component.create();

QString returnedValue;
QString msg = "Hallo aus C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
        Q_RETURN_ARG(QString, returnedValue),
        Q_ARG(QString, msg));

qDebug() << "QML function returned:" << returnedValue;
Objekt löschen;

Beachten Sie den nach dem Doppelpunkt angegebenen Parameter- und Rückgabetyp. Sie können Werttypen und Objekttypen als Typnamen verwenden.

Wenn der Typ in QML weggelassen oder als „ var “ angegeben wird, müssen Sie beim Aufruf von „ QMetaObject::invokeMethod “ „ QVariant “ als Typ mit „ Q_RETURN_ARG()“ und „ Q_ARG()“ übergeben.

Verbindung zu QML-Signalen

Alle QML-Signale stehen C++ automatisch zur Verfügung und können wie jedes gewöhnliche Qt-C++-Signal über ` QObject::connect()` verbunden werden. Umgekehrt kann jedes C++-Signal von einem QML-Objekt mithilfe von Signal-Handlern empfangen werden.

Hier ist eine QML-Komponente mit einem Signal namens „ qmlSignal “, das mit einem Parameter vom Typ „String“ ausgesendet wird. Dieses Signal wird über „ QObject::connect()“ mit dem Slot eines C++-Objekts verbunden, sodass die Methode „ cppSlot() “ aufgerufen wird, sobald „ qmlSignal “ ausgesendet wird:

// MyItem.qml
import QtQuick

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(msg: string)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal("Hello from QML")
    }
}
class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(const QString&msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject*item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QString)),
                     &myClass, SLOT(cppSlot(QString)));

    view.show();
    return app.exec();
}

Ein QML-Objekttyp in einem Signalparameter wird in einen Zeiger auf die Klasse in C++ übersetzt:

// MyItem.qml
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(anObject: Item)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal(item)
    }
}
class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(QQuickItem*item) {
       qDebug() << "Called the C++ slot with item:" << item;

       qDebug() << "Item dimensions:" << item->width()
               << item->height();
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject*item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
                     &myClass, SLOT(cppSlot(QVariant)));

    view.show();
    return app.exec();
}

© 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.