Interagir avec des objets QML à partir de C++
Tous les types d'objets QML sont des types dérivés de QObject, qu'ils soient implémentés en interne par le moteur ou définis par des sources tierces. Cela signifie que le moteur QML peut utiliser le système Qt Meta Object pour instancier dynamiquement n'importe quel type d'objet QML et inspecter les objets créés.
Ceci est utile pour créer des objets QML à partir de code C++, que ce soit pour afficher un objet QML qui peut être rendu visuellement, ou pour intégrer des données d'objet QML non visuelles dans une application C++. Une fois qu'un objet QML est créé, il peut être inspecté à partir du code C++ afin de lire et d'écrire dans les propriétés, d'invoquer des méthodes et de recevoir des notifications de signaux.
Pour plus d'informations sur le C++ et les différentes méthodes d'intégration QML, voir la page de présentation de l'intégration C++ et QML.
Chargement d'objets QML à partir de C++
Un document QML peut être chargé avec QQmlComponent ou QQuickView. QQmlComponent charge un document QML en tant qu'objet C++ qui peut ensuite être modifié à partir du code C++. QQuickView fait de même, mais comme QQuickView est une classe dérivée de QWindow, l'objet chargé sera également rendu dans un affichage visuel ; QQuickView est généralement utilisé pour intégrer un objet QML affichable dans l'interface utilisateur d'une application.
Par exemple, supposons qu'il existe un fichier MyItem.qml qui ressemble à ceci :
import QtQuick Item { width: 100; height: 100 }
Ce document QML peut être chargé avec QQmlComponent ou QQuickView avec le code C++ suivant. L'utilisation de QQmlComponent nécessite d'appeler QQmlComponent::create() pour créer une nouvelle instance du composant, tandis que QQuickView crée automatiquement une instance du composant, qui est accessible via 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(); |
Ce object est l'instance du composant MyItem.qml qui a été créé. Vous pouvez maintenant modifier les propriétés de l'élément en utilisant QObject::setProperty() ou QQmlProperty::write() :
object->setProperty("width", 500); QQmlProperty(object, "width").write(500);
La différence entre QObject::setProperty() et QQmlProperty::write() est que cette dernière supprimera la liaison en plus de définir la valeur de la propriété. Par exemple, supposons que l'affectation width ci-dessus ait été une liaison avec height:
width: height
Si le height du Item change après l'appel du object->setProperty("width", 500), le width sera à nouveau mis à jour, car la liaison reste active. Toutefois, si le height change après l'appel du QQmlProperty(object, "width").write(500), le width ne sera pas modifié, car la liaison n'existe plus.
Il est également possible de convertir l'objet en son type réel et d'appeler des méthodes avec une sécurité au moment de la compilation. Dans ce cas, l'objet de base de MyItem.qml est un Item, qui est défini par la classe QQuickItem:
QQuickItem *item = qobject_cast<QQuickItem*>(object); item->setWidth(500);
Vous pouvez également vous connecter à tout signal ou appeler des méthodes définies dans le composant en utilisant QMetaObject::invokeMethod() et QObject::connect(). Pour plus d'informations, voir ci-dessous les rubriques Invoquer des méthodes QML et Se connecter à des signaux QML.
Accès aux objets QML via des interfaces C++ bien définies
La meilleure façon d'interagir avec QML à partir de C++ est de définir une interface pour le faire en C++ et d'y accéder dans QML lui-même. Avec d'autres méthodes, le refactoring de votre code QML peut facilement conduire à la rupture de votre interaction QML / C++. Il est également utile de raisonner sur l'interaction entre QML et le code C++, car le fait qu'elle soit pilotée par QML peut être plus facilement raisonné par les utilisateurs et les outils tels que qmllint. Accéder à QML à partir de C++ conduira à un code QML qui ne peut être compris sans vérifier manuellement qu'aucun code C++ extérieur ne modifie un composant QML donné, et même dans ce cas, l'étendue de l'accès peut changer au fil du temps, ce qui fait de l'utilisation continue de cette stratégie un fardeau pour la maintenance.
Pour laisser QML piloter l'interaction, il faut d'abord définir une interface C++ :
class CppInterface : public QObject { Q_OBJECT QML_ELEMENT // ... };
En utilisant une approche pilotée par QML, il est possible d'interagir avec cette interface de deux manières :
Singletons
Une option consiste à enregistrer l'interface en tant que singleton en ajoutant la macro QML_SINGLETON à l'interface, l'exposant ainsi à tous les composants. Ensuite, l'interface devient disponible par le biais d'une simple déclaration d'importation :
import my.company.module Item { Component.onCompleted: { CppInterface.foo(); } }
Utilisez cette approche si vous avez besoin de votre interface à d'autres endroits que le composant racine, car le simple fait de transmettre un objet nécessiterait de le transmettre explicitement à d'autres composants via une propriété ou d'utiliser la méthode lente et non recommandée de l'utilisation de l'accès non qualifié.
Propriétés initiales
Une autre option consiste à marquer l'interface comme non créable via QML_UNCREATABLE et à la fournir au composant QML racine en utilisant QQmlComponent::createWithInitialProperties() et une propriété requise du côté QML.
Votre composant racine peut ressembler à ceci :
import QtQuick
Item {
required property CppInterface interface
Component.onCompleted: {
interface.foo();
}
}Le fait de marquer la propriété comme obligatoire ici protège le composant contre la création sans que la propriété de l'interface ne soit définie.
Vous pouvez ensuite initialiser votre composant de la même manière que celle décrite dans Chargement d'objets QML à partir de C++, à l'exception de l'utilisation de createWithInitialProperties():
component.createWithInitialProperties(QVariantMap{{u"interface"_s, QVariant::fromValue<CppInterface *>(new CppInterface)}});
Cette méthode est à privilégier si vous savez que votre interface ne doit être disponible que pour le composant racine. Elle permet également de se connecter plus facilement aux signaux et aux slots de l'interface du côté C++.
Si aucune de ces méthodes ne répond à vos besoins, vous pouvez envisager l'utilisation de modèles C++.
Accès aux objets QML chargés par nom d'objet
Les composants QML sont essentiellement des arbres d'objets dont les enfants ont des frères et sœurs et leurs propres enfants. Les objets enfants des composants QML peuvent être localisés à l'aide de la propriété QObject::objectName avec QObject::findChild(). Par exemple, si l'élément racine de MyItem.qml a un élément enfant Rectangle:
import QtQuick Item { width: 100; height: 100 Rectangle { anchors.fill: parent objectName: "rect" } }
L'enfant pourrait être localisé comme suit :
Notez qu'un objet peut avoir plusieurs enfants avec le même objectName. Par exemple, ListView crée plusieurs instances de son délégué, donc si son délégué est déclaré avec un objectName particulier, le ListView aura plusieurs enfants avec le même objectName. Dans ce cas, QObject::findChildren() peut être utilisé pour trouver tous les enfants ayant un objectName correspondant.
Attention : Bien qu'il soit possible d'accéder aux objets QML à partir de C++ et de les manipuler, ce n'est pas l'approche recommandée, sauf à des fins de test et de prototypage. L'un des points forts de l'intégration de QML et de C++ est la possibilité d'implémenter des interfaces utilisateur en QML séparément de la logique C++ et du backend de l'ensemble de données, ce qui échoue si le côté C++ commence à manipuler QML directement. Une telle approche rend également difficile la modification de l'interface utilisateur QML sans affecter son homologue C++.
Accès aux membres d'un type d'objet QML à partir de C++
Propriétés
Toutes les propriétés déclarées dans un objet QML sont automatiquement accessibles à partir de C++. Étant donné un objet QML comme celui-ci :
La valeur de la propriété someNumber peut être définie et lue en utilisant QQmlProperty, ou QObject::setProperty() et QObject::property() :
QQmlEngine moteur ;QQmlComponent component(&engine, "MyItem.qml") ;QObject *objet = 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) ;
Vous devez toujours utiliser QObject::setProperty(), QQmlProperty ou QMetaProperty::write() pour modifier la valeur d'une propriété QML, afin de vous assurer que le moteur QML est informé de la modification de la propriété. Par exemple, supposons que vous ayez un type personnalisé PushButton avec une propriété buttonText qui reflète en interne la valeur d'une variable membre m_buttonText. Modifier la variable membre directement comme ceci n'est pas une bonne idée :
//bad code QQmlComponent component(engine, "MyButton.qml"); PushButton *button = qobject_cast<PushButton*>(component.create()); button->m_buttonText = "Click me";
Puisque la valeur est modifiée directement, le système de méta-objets de Qt est contourné et le moteur QML n'est pas informé de la modification de la propriété. Cela signifie que les liaisons de propriétés à buttonText ne seront pas mises à jour et que les gestionnaires de onButtonTextChanged ne seront pas appelés.
Invoquer des méthodes QML
Toutes les méthodes QML sont exposées au système de méta-objets et peuvent être appelées à partir de C++ à l'aide de QMetaObject::invokeMethod(). Vous pouvez spécifier des types pour les paramètres et la valeur de retour après le caractère deux-points, comme le montre l'extrait de code ci-dessous. Cela peut être utile, par exemple, lorsque vous souhaitez connecter un signal en C++ avec une certaine signature à une méthode définie par QML. Si vous omettez les types, la signature C++ utilisera QVariant.
Voici une application C++ qui appelle une méthode QML en utilisant QMetaObject::invokeMethod() :
| QML | // MyItem.qml import QtQuick Item { function myQmlFunction(msg: string) : string { console.log("Got message:", msg) return "some return value" } } |
| C++ | // main.cppQQmlEngine moteur ;QQmlComponent component(&engine, "MyItem.qml") ;QObject *objet = component.create() ;QString returnValue ;QString msg = "Hello from C++";QMetaObject::invokeMethod(object, "myQmlFunction",Q_RETURN_ARG(QString, returnedValue),Q_ARG(QStringmsg)) ; qDebug() << "QML function returned:" << returnedValue; supprimer l' objet ; |
Remarquez le paramètre et le type de retour spécifiés après les deux points. Vous pouvez utiliser des types de valeurs et des types d'objets comme noms de types.
Si le type est omis ou spécifié comme var dans QML, vous devez passer QVariant comme type avec Q_RETURN_ARG() et Q_ARG() lors de l'appel à QMetaObject::invokeMethod.
Connexion aux signaux QML
Tous les signaux QML sont automatiquement disponibles pour C++ et peuvent être connectés à l'aide de QObject::connect() comme n'importe quel signal Qt C++ ordinaire. En retour, tout signal C++ peut être reçu par un objet QML à l'aide de gestionnaires de signaux.
Voici un composant QML avec un signal nommé qmlSignal qui est émis avec un paramètre de type chaîne de caractères. Ce signal est connecté au slot d'un objet C++ à l'aide de QObject::connect(), de sorte que la méthode cppSlot() est appelée chaque fois que le signal qmlSignal est émis :
class MyClass : public QObject {Q_OBJECTpublic 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() ; } |
Un type d'objet QML dans un paramètre de signal est traduit par un pointeur sur la classe en C++ :
class MyClass : public QObject {Q_OBJECTpublic 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.