Attribute von C++-Typen für QML verfügbar machen

QML kann leicht um Funktionen erweitert werden, die in C++-Code definiert sind. Aufgrund der engen Integration der QML-Engine mit dem Qt-Metaobjektsystem ist jede Funktionalität, die durch eine von QObject abgeleitete Klasse oder einen Q_GADGET -Typ angemessen dargestellt wird, von QML-Code aus zugänglich. Dies ermöglicht den direkten Zugriff auf C++-Daten und -Funktionen von QML aus, oft mit geringen oder gar keinen Änderungen.

Die QML-Engine ist in der Lage, QObject -Instanzen über das Meta-Objektsystem zu introspektieren. Das bedeutet, dass jeder QML-Code auf die folgenden Member einer Instanz einer von QObject abgeleiteten Klasse zugreifen kann:

  • Eigenschaften
  • Methoden (sofern sie öffentliche Slots sind oder mit Q_INVOKABLE gekennzeichnet sind)
  • Signale

(Zusätzlich sind Enums verfügbar, wenn sie mit Q_ENUM deklariert wurden. Siehe Datentypkonvertierung zwischen QML und C++ für weitere Details).

Im Allgemeinen sind diese von QML aus zugänglich, unabhängig davon, ob eine von QObject abgeleitete Klasse im QML-Typsystem registriert wurde. Wenn eine Klasse jedoch in einer Weise verwendet werden soll, die es erforderlich macht, dass die Engine auf zusätzliche Typinformationen zugreift - zum Beispiel, wenn die Klasse selbst als Methodenparameter oder Eigenschaft verwendet werden soll, oder wenn einer ihrer Enum-Typen auf diese Weise verwendet werden soll - dann muss die Klasse möglicherweise registriert werden. Die Registrierung wird für alle Typen empfohlen, die Sie in QML verwenden, da nur registrierte Typen zur Kompilierzeit analysiert werden können.

Die Registrierung ist für Q_GADGET Typen erforderlich, da sie nicht von einer bekannten gemeinsamen Basis abgeleitet sind und nicht automatisch verfügbar gemacht werden können. Ohne Registrierung sind ihre Eigenschaften und Methoden unzugänglich.

Sie können C++-Typen aus einem anderen Modul in Ihrem eigenen Modul verfügbar machen, indem Sie mit der Option DEPENDENCIES eine Abhängigkeit zu Ihrem qt_add_qml_module-Aufruf hinzufügen. Sie können zum Beispiel eine Abhängigkeit von QtQuick einrichten, damit Ihre in QML exponierten C++-Typen QColor als Methodenargumente und Rückgabewerte verwenden können. QtQuick exponiert QColor als Werttyp Farbe. Solche Abhängigkeiten können zur Laufzeit automatisch abgeleitet werden, aber Sie sollten sich nicht darauf verlassen.

Beachten Sie auch, dass einige der wichtigen Konzepte, die in diesem Dokument behandelt werden, im Tutorial Writing QML Extensions with C++ demonstriert werden.

Weitere Informationen über C++ und die verschiedenen QML-Integrationsmethoden finden Sie auf der Übersichtsseite zu C++ und QML-Integration.

Datentypbehandlung und -eigentum

Alle Daten, die von C++ an QML übertragen werden, ob als Eigenschaftswert, als Methodenparameter oder Rückgabewert oder als Signalparameterwert, müssen von einem Typ sein, der von der QML-Engine unterstützt wird.

Standardmäßig unterstützt die Engine eine Reihe von Qt C++-Typen und kann sie bei der Verwendung von QML automatisch entsprechend konvertieren. Zusätzlich können C++-Klassen, die im QML-Typsystem registriert sind, als Datentypen verwendet werden, ebenso wie ihre Enums, wenn sie entsprechend registriert sind. Siehe Datentypkonvertierung zwischen QML und C++ für weitere Informationen.

Außerdem werden bei der Übertragung von Daten von C++ nach QML die Regeln des Dateneigentums berücksichtigt. Siehe Dateneigentum für weitere Details.

Offenlegung von Eigenschaften

Eine Eigenschaft kann für jede von QObject abgeleitete Klasse mit dem Makro Q_PROPERTY() angegeben werden. Eine Eigenschaft ist ein Datenelement der Klasse mit einer zugehörigen Lesefunktion und einer optionalen Schreibfunktion.

Alle Eigenschaften einer QObject-abgeleiteten oder Q_GADGET -Klasse sind von QML aus zugänglich.

Unten sehen Sie zum Beispiel eine Klasse Message mit einer Eigenschaft author. Wie durch den Q_PROPERTY Makroaufruf angegeben, ist diese Eigenschaft durch die author() Methode lesbar und durch die setAuthor() Methode schreibbar:

Hinweis: Verwenden Sie nicht typedef oder using für Q_PROPERTY Typen, da dies moc verwirren wird. Dies kann dazu führen, dass bestimmte Typvergleiche fehlschlagen.

Anstelle von:

using FooEnum = Foo::Enum;

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
};

Verweisen Sie direkt auf den Typ:

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged)
};

Um Message verfügbar zu machen, müssen Sie QML_ELEMENT in C++ und qt_add_qml_module in CMake verwenden.

class Message : public QObject
{
    Q_OBJECT
    QML_ELEMENT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
    void setAuthor(const QString &a)
    {
        if (a != m_author) {
            m_author = a;
            emit authorChanged();
        }
    }

    QString author() const
    {
        return m_author;
    }

signals:
    void authorChanged();

private:
    QString m_author;
};

Eine Instanz von Message kann als erforderliche Eigenschaft an eine Datei namens MyItem.qml übergeben werden, um sie verfügbar zu machen:

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

    QQuickView view;
    Message msg;
    view.setInitialProperties({{"msg", &msg}});
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}

Dann könnte die Eigenschaft author aus MyItem.qml gelesen werden:

// MyItem.qml
import QtQuick

Text {
    required property Message msg

    width: 100; height: 100
    text: msg.author    // invokes Message::author() to get this value

    Component.onCompleted: {
        msg.author = "Jonah"  // invokes Message::setAuthor()
    }
}

Für eine maximale Interoperabilität mit QML sollte jede Eigenschaft, die beschreibbar ist, ein zugehöriges NOTIFY-Signal haben, das immer dann ausgegeben wird, wenn sich der Wert der Eigenschaft geändert hat. Dies ermöglicht die Verwendung der Eigenschaft mit Property Binding, einer wesentlichen Funktion von QML, die Beziehungen zwischen Eigenschaften erzwingt, indem eine Eigenschaft automatisch aktualisiert wird, sobald sich der Wert einer ihrer Abhängigkeiten ändert.

Im obigen Beispiel ist das zugehörige NOTIFY-Signal für die Eigenschaft author authorChanged , wie im Makroaufruf Q_PROPERTY() angegeben. Das bedeutet, dass jedes Mal, wenn das Signal ausgegeben wird - wie bei der Änderung des Autors in Message::setAuthor() - die QML-Engine benachrichtigt wird, dass alle Bindungen, die die Eigenschaft author betreffen, aktualisiert werden müssen, und im Gegenzug aktualisiert die Engine die Eigenschaft text durch erneuten Aufruf von Message::author().

Wenn die Eigenschaft author beschreibbar wäre, aber kein zugehöriges NOTIFY-Signal hätte, würde der Wert text mit dem von Message::author() zurückgegebenen Anfangswert initialisiert, aber bei späteren Änderungen dieser Eigenschaft nicht mehr aktualisiert werden. Darüber hinaus führt jeder Versuch, die Eigenschaft von QML aus zu binden, zu einer Laufzeitwarnung der Engine.

Hinweis: Es wird empfohlen, das NOTIFY-Signal <Eigenschaft>geändert zu nennen, wobei <property> der Name der Eigenschaft ist. Der zugehörige, von der QML-Engine erzeugte Signalhandler für Eigenschaftsänderungen hat immer die Form on<Property>Changed, unabhängig vom Namen des zugehörigen C++-Signals. Es wird daher empfohlen, dass der Signalname dieser Konvention folgt, um Verwechslungen zu vermeiden.

Hinweise zur Verwendung von Notify-Signalen

Um Schleifen oder eine übermäßige Auswertung zu vermeiden, sollten Entwickler sicherstellen, dass das Eigenschaftsänderungssignal nur ausgegeben wird, wenn sich der Eigenschaftswert tatsächlich geändert hat. Wenn eine Eigenschaft oder eine Gruppe von Eigenschaften nur selten verwendet wird, ist es auch erlaubt, dasselbe NOTIFY-Signal für mehrere Eigenschaften zu verwenden. Dies sollte mit Bedacht geschehen, um sicherzustellen, dass die Leistung nicht leidet.

Das Vorhandensein eines NOTIFY-Signals ist mit einem geringen Overhead verbunden. Es gibt Fälle, in denen der Wert einer Eigenschaft zum Zeitpunkt der Objekterstellung festgelegt wird und sich anschließend nicht mehr ändert. Dies ist am häufigsten der Fall, wenn ein Typ gruppierte Eigenschaften verwendet und das gruppierte Eigenschaftsobjekt einmal zugewiesen und erst beim Löschen des Objekts wieder freigegeben wird. In diesen Fällen kann das CONSTANT-Attribut anstelle eines NOTIFY-Signals zur Eigenschaftsdeklaration hinzugefügt werden.

Das CONSTANT-Attribut sollte nur für Eigenschaften verwendet werden, deren Wert erst im Klassenkonstruktor festgelegt und finalisiert wird. Alle anderen Eigenschaften, die in Bindungen verwendet werden sollen, sollten stattdessen ein NOTIFY-Signal haben.

Eigenschaften mit Objekttypen

Eigenschaften von Objekttypen sind von QML aus zugänglich, vorausgesetzt, der Objekttyp wurde entsprechend im QML-Typsystem registriert.

Zum Beispiel könnte der Typ Message eine Eigenschaft body vom Typ MessageBody* haben:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
public:
    MessageBody* body() const;
    void setBody(MessageBody* body);
};

class MessageBody : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
// ...
}

Angenommen, der Typ Message wurde im QML-Typsystem registriert, so dass er von QML-Code aus als Objekttyp verwendet werden kann:

Message {
    // ...
}

Wenn der Typ MessageBody ebenfalls im Typsystem registriert wäre, wäre es möglich, MessageBody der Eigenschaft body eines Message zuzuweisen, und zwar direkt im QML-Code:

Message {
    body: MessageBody {
        text: "Hello, world!"
    }
}

Eigenschaften mit Objekt-Listen-Typen

Eigenschaften, die Listen von QObject-abgeleiteten Typen enthalten, können ebenfalls in QML exponiert werden. Zu diesem Zweck sollte man jedoch QQmlListProperty und nicht QList<T> als Eigenschaftstyp verwenden. Der Grund dafür ist, dass QList kein von QObject abgeleiteter Typ ist und daher nicht die notwendigen QML-Eigenschaftsmerkmale über das Qt-Meta-Objektsystem bereitstellen kann, wie z. B. Signalbenachrichtigungen, wenn eine Liste geändert wird.

Zum Beispiel hat die MessageBoard Klasse unten eine messages Eigenschaft vom Typ QQmlListProperty, die eine Liste von Message Instanzen speichert:

class MessageBoard : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
public:
    QQmlListProperty<Message> messages();

private:
    static void append_message(QQmlListProperty<Message> *list, Message *msg);

    QList<Message *> m_messages;
};

Die Funktion MessageBoard::messages() erstellt einfach eine QQmlListProperty und gibt sie von ihrem QList<T> m_messages Member zurück, wobei die entsprechenden Funktionen zur Listenänderung wie vom QQmlListProperty Konstruktor gefordert übergeben werden:

QQmlListProperty<Message> MessageBoard::messages()
{
    return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message);
}

void MessageBoard::append_message(QQmlListProperty<Message> *list, Message *msg)
{
    MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
    if (msg)
        msgBoard->m_messages.append(msg);
}

Beachten Sie, dass der Typ der Vorlagenklasse für QQmlListProperty - in diesem Fall Message - im QML-Typsystem registriert sein muss.

Gruppierte Eigenschaften

Jede schreibgeschützte Eigenschaft eines Objekts ist vom QML-Code aus als gruppierte Eigenschaft zugänglich. Dies kann verwendet werden, um eine Gruppe von zusammenhängenden Eigenschaften darzustellen, die einen Satz von Attributen für einen Typ beschreiben.

Nehmen wir zum Beispiel an, die Eigenschaft Message::author sei vom Typ MessageAuthor und nicht nur eine einfache Zeichenkette, mit den Untereigenschaften name und email:

class MessageAuthor : public QObject
{
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString email READ email WRITE setEmail)
public:
    ...
};

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageAuthor* author READ author)
public:
    Message(QObject *parent)
        : QObject(parent), m_author(new MessageAuthor(this))
    {
    }
    MessageAuthor *author() const {
        return m_author;
    }
private:
    MessageAuthor *m_author;
};

Die Eigenschaft author könnte mit der Syntax für gruppierte Eigenschaften in QML wie folgt beschrieben werden:

Message {
    author.name: "Alexandra"
    author.email: "alexandra@mail.com"
}

Ein Typ, der als gruppierte Eigenschaft dargestellt wird, unterscheidet sich von einer objektartigen Eigenschaft dadurch, dass die gruppierte Eigenschaft schreibgeschützt ist und bei der Erstellung vom übergeordneten Objekt mit einem gültigen Wert initialisiert wird. Die Untereigenschaften der gruppierten Eigenschaft können von QML aus geändert werden, aber das Objekt der gruppierten Eigenschaft selbst wird sich nie ändern, während einer objektartigen Eigenschaft jederzeit ein neuer Objektwert von QML aus zugewiesen werden kann. Somit wird die Lebensdauer eines gruppierten Eigenschaftsobjekts streng von der übergeordneten C++-Implementierung kontrolliert, während eine objektartige Eigenschaft durch QML-Code frei erstellt und zerstört werden kann.

Offenlegung von Methoden (einschließlich Qt-Slots)

Jede Methode eines QObject-abgeleiteten Typs ist von QML-Code aus zugänglich, wenn sie es ist:

  • eine öffentliche Methode, die mit dem Makro Q_INVOKABLE() gekennzeichnet ist
  • Eine Methode, die ein öffentlicher Qt-Slot ist

Zum Beispiel hat die MessageBoard Klasse unten eine postMessage() Methode, die mit dem Q_INVOKABLE Makro gekennzeichnet wurde, sowie eine refresh() Methode, die ein öffentlicher Slot ist:

class MessageBoard : public QObject
{ Q_OBJECT QML_ELEMENTpublic: Q_INVOKABLE bool postMessage(const QString &msg) {        qDebug() << "Called the C++ method with" << msg;
       return true; }public slots: void refresh() {        qDebug() << "Called the C++ slot";
    } };

Wenn eine Instanz von MessageBoard als erforderliche Eigenschaft für eine Datei MyItem.qml festgelegt wurde, könnte MyItem.qml die beiden Methoden aufrufen, wie in den folgenden Beispielen gezeigt:

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

    MessageBoard msgBoard;
    QQuickView view;
    view.setInitialProperties({{"msgBoard", &msgBoard}});
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}
QML
// MyItem.qml
import QtQuick 2.0

Item {
    required property MessageBoard msgBoard

    width: 100; height: 100

    MouseArea {
        anchors.fill: parent
        onClicked: {
            var result = msgBoard.postMessage("Hello from QML")
            console.log("Result of postMessage():", result)
            msgBoard.refresh();
        }
    }
}

Wenn eine C++-Methode einen Parameter vom Typ QObject* hat, kann der Parameterwert von QML über ein Objekt id oder einen JavaScript-Wert var übergeben werden, der auf das Objekt verweist.

QML unterstützt den Aufruf von überladenen C++-Funktionen. Wenn es mehrere C++-Funktionen mit demselben Namen, aber unterschiedlichen Argumenten gibt, wird die richtige Funktion entsprechend der Anzahl und den Typen der übergebenen Argumente aufgerufen.

Werte, die von C++-Methoden zurückgegeben werden, werden in JavaScript-Werte umgewandelt, wenn von JavaScript-Ausdrücken in QML darauf zugegriffen wird.

C++-Methoden und das 'this'-Objekt

Es kann vorkommen, dass Sie eine C++-Methode von einem Objekt abrufen und auf einem anderen Objekt aufrufen wollen. Betrachten Sie das folgende Beispiel innerhalb eines QML-Moduls namens Example:

C++
class Invokable : public QObject
{
    Q_OBJECT
    QML_ELEMENT
public:
    Invokable(QObject *parent = nullptr) : QObject(parent) {}

    Q_INVOKABLE void invoke() { qDebug() << "invoked on " << objectName(); }
};
QML
import QtQml
import Example

Invokable {
    objectName: "parent"
    property Invokable child: Invokable {}
    Component.onCompleted: child.invoke.call(this)
}

Wenn Sie den QML-Code aus einer geeigneten main.cpp laden, sollte er "invoked on parent" ausgeben. Aufgrund eines langjährigen Fehlers ist dies jedoch nicht der Fall. Historisch gesehen ist das 'this'-Objekt von C++-basierten Methoden untrennbar an die Methode gebunden. Eine Änderung dieses Verhaltens für bestehenden Code würde zu subtilen Fehlern führen, da das "this"-Objekt an vielen Stellen implizit ist. Seit Qt 6.5 können Sie sich explizit für das korrekte Verhalten entscheiden und C++-Methoden erlauben, ein 'this'-Objekt zu akzeptieren. Um dies zu tun, fügen Sie das folgende Pragma zu Ihren QML-Dokumenten hinzu:

pragma NativeMethodBehavior: AcceptThisObject

Wenn Sie diese Zeile hinzufügen, wird das obige Beispiel wie erwartet funktionieren.

Signale freilegen

Jedes öffentliche Signal eines QObject-abgeleiteten Typs ist von QML-Code aus zugänglich.

Die QML-Engine erstellt automatisch einen Signal-Handler für jedes Signal eines QObject-abgeleiteten Typs, das von QML aus verwendet wird. Signal-Handler sind immer on<Signal> benannt, wobei <Signal> der Name des Signals ist, wobei der erste Buchstabe groß geschrieben wird. Alle vom Signal übergebenen Parameter sind im Signalhandler über die Parameternamen verfügbar.

Nehmen wir zum Beispiel an, die Klasse MessageBoard hat ein Signal newMessagePosted() mit einem einzigen Parameter, subject:

class MessageBoard : public QObject
{
    Q_OBJECT
public:
   // ...
signals:
   void newMessagePosted(const QString &subject);
};

Wenn der Typ MessageBoard im QML-Typsystem registriert ist, könnte ein in QML deklariertes MessageBoard -Objekt das Signal newMessagePosted() mit einem Signalhandler namens onNewMessagePosted empfangen und den Parameterwert subject untersuchen:

MessageBoard {
    onNewMessagePosted: (subject)=> console.log("New message received:", subject)
}

Wie bei Eigenschaftswerten und Methodenparametern muss auch ein Signalparameter einen Typ haben, der von der QML-Engine unterstützt wird; siehe Datentypkonvertierung zwischen QML und C++. (Die Verwendung eines nicht registrierten Typs führt nicht zu einem Fehler, aber der Parameterwert ist für den Handler nicht zugänglich).

Klassen können mehrere Signale mit demselben Namen haben, aber nur das letzte Signal ist als QML-Signal zugänglich. Beachten Sie, dass Signale mit demselben Namen, aber unterschiedlichen Parametern nicht voneinander unterschieden werden können.

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