Exposer les attributs des types C++ à QML
QML peut facilement être étendu avec des fonctionnalités définies dans le code C++. En raison de l'intégration étroite du moteur QML avec le système de méta-objets Qt, toute fonctionnalité exposée de manière appropriée par une classe dérivée de QObject ou un type Q_GADGET est accessible à partir du code QML. Cela permet aux données et aux fonctions C++ d'être accessibles directement à partir de QML, souvent avec peu ou pas de modifications.
Le moteur QML a la capacité d'introspecter les instances QObject par l'intermédiaire du système de méta-objets. Cela signifie que tout code QML peut accéder aux membres suivants d'une instance d'une classe dérivée de QObject:
- Propriétés
- Méthodes (à condition qu'il s'agisse de slots publics ou qu'elles soient marquées par Q_INVOKABLE)
- Signaux
(En outre, les enums sont disponibles s'ils ont été déclarés avec Q_ENUM. Voir Conversion des types de données entre QML et C++ pour plus de détails).
En général, ces éléments sont accessibles à partir de QML, qu'une classe dérivée de QObject ait été enregistrée ou non dans le système de types de QML. Cependant, si une classe doit être utilisée d'une manière qui nécessite que le moteur accède à des informations de type supplémentaires - par exemple, si la classe elle-même doit être utilisée comme paramètre de méthode ou comme propriété, ou si l'un de ses types enum doit être utilisé de cette manière - alors la classe peut avoir besoin d'être enregistrée. L'enregistrement est recommandé pour tous les types que vous utilisez en QML, car seuls les types enregistrés peuvent être analysés au moment de la compilation.
L'enregistrement est nécessaire pour les types Q_GADGET, car ils ne dérivent pas d'une base commune connue et ne peuvent pas être mis à disposition automatiquement. Sans enregistrement, leurs propriétés et méthodes sont inaccessibles.
Vous pouvez rendre les types C++ d'un module différent disponibles dans votre propre module en ajoutant une dépendance à votre appel qt_add_qml_module en utilisant l'option DEPENDENCIES. Vous pouvez, par exemple, vouloir dépendre de QtQuick pour que vos types C++ exposés à QML puissent utiliser QColor comme arguments de méthode et valeurs de retour. QtQuick expose QColor comme couleur de type de valeur. De telles dépendances peuvent être automatiquement déduites au moment de l'exécution, mais vous ne devez pas vous y fier.
Notez également qu'un certain nombre de concepts importants abordés dans ce document sont démontrés dans le tutoriel Writing QML Extensions with C++ (Écrire des extensions QML avec C++ ).
Pour plus d'informations sur le C++ et les différentes méthodes d'intégration QML, voir la page de présentation du C++ et de l'intégration QML.
Gestion des types de données et propriété
Toute donnée transférée de C++ à QML, qu'il s'agisse d'une valeur de propriété, d'un paramètre de méthode, d'une valeur de retour ou d'une valeur de paramètre de signal, doit être d'un type pris en charge par le moteur QML.
Par défaut, le moteur prend en charge un certain nombre de types C++ de Qt et peut les convertir automatiquement de manière appropriée lorsqu'ils sont utilisés à partir de QML. En outre, les classes C++ enregistrées dans le système de types QML peuvent être utilisées comme types de données, de même que leurs enums s'ils sont enregistrés de manière appropriée. Voir Conversion des types de données entre QML et C++ pour plus d'informations.
En outre, les règles de propriété des données sont prises en considération lorsque les données sont transférées de C++ à QML. Voir Propriété des données pour plus de détails.
Exposition des propriétés
Une propriété peut être spécifiée pour toute classe dérivée de QObject à l'aide de la macro Q_PROPERTY(). Une propriété est un membre de la classe avec une fonction de lecture associée et une fonction d'écriture optionnelle.
Toutes les propriétés d'une classe QObject-dérivée ou Q_GADGET sont accessibles à partir de QML.
Par exemple, voici une classe Message avec une propriété author. Comme spécifié par le macro-appel Q_PROPERTY, cette propriété est accessible en lecture par la méthode author() et en écriture par la méthode setAuthor():
Remarque : n'utilisez pas typedef ou using pour les types Q_PROPERTY, car cela perturberait moc. Cela peut faire échouer certaines comparaisons de types.
Au lieu de :
using FooEnum = Foo::Enum;
class Bar : public QObject
{
Q_OBJECT
Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
};Se référer directement au type :
class Bar : public QObject { Q_OBJECT Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged) };
Pour rendre Message disponible, vous devez utiliser QML_ELEMENT en C++ et qt_add_qml_module en CMake.
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; };
Une instance de Message peut être transmise en tant que propriété requise à un fichier appelé MyItem.qml pour le rendre disponible :
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(); }
Ensuite, la propriété author peut être lue à partir de MyItem.qml:
// 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() } }
Pour une interopérabilité maximale avec QML, toute propriété accessible en écriture doit être associée à un signal NOTIFY qui est émis chaque fois que la valeur de la propriété a changé. Cela permet d'utiliser la propriété avec la liaison de propriété, qui est une fonctionnalité essentielle de QML qui renforce les relations entre les propriétés en mettant automatiquement à jour une propriété lorsque la valeur de l'une de ses dépendances change.
Dans l'exemple ci-dessus, le signal NOTIFY associé à la propriété author est authorChanged, comme spécifié dans l'appel à la macro Q_PROPERTY(). Cela signifie que chaque fois que le signal est émis - comme c'est le cas lorsque l'auteur change dans Message::setAuthor() - cela notifie au moteur QML que toutes les liaisons impliquant la propriété author doivent être mises à jour, et qu'en retour, le moteur mettra à jour la propriété text en appelant à nouveau Message::author().
Si la propriété author était accessible en écriture mais n'avait pas de signal NOTIFY associé, la valeur text serait initialisée avec la valeur initiale renvoyée par Message::author() mais ne serait pas mise à jour en fonction des modifications ultérieures apportées à cette propriété. En outre, toute tentative de liaison à la propriété à partir de QML produira un avertissement d'exécution de la part du moteur.
Remarque : il est recommandé de nommer le signal NOTIFY <property>Changed où <property> est le nom de la propriété. Le gestionnaire de signal de changement de propriété associé généré par le moteur QML prendra toujours la forme on<Property>Changed, quel que soit le nom du signal C++ associé, il est donc recommandé que le nom du signal suive cette convention afin d'éviter toute confusion.
Notes sur l'utilisation des signaux de notification
Pour éviter les boucles ou les évaluations excessives, les développeurs doivent s'assurer que le signal de changement de propriété n'est émis que lorsque la valeur de la propriété a réellement changé. En outre, si une propriété ou un groupe de propriétés est rarement utilisé, il est permis d'utiliser le même signal NOTIFY pour plusieurs propriétés. Cela doit être fait avec précaution afin de ne pas nuire aux performances.
La présence d'un signal NOTIFY entraîne une légère surcharge. Dans certains cas, la valeur d'une propriété est fixée lors de la construction de l'objet et ne change pas par la suite. Le cas le plus courant est celui où un type utilise des propriétés groupées, et où l'objet de propriété groupée est alloué une fois, et n'est libéré que lorsque l'objet est supprimé. Dans ce cas, l'attribut CONSTANT peut être ajouté à la déclaration de la propriété au lieu d'un signal NOTIFY.
L'attribut CONSTANT ne doit être utilisé que pour les propriétés dont la valeur est définie et finalisée uniquement dans le constructeur de la classe. Toutes les autres propriétés qui veulent être utilisées dans des liaisons doivent avoir un signal NOTIFY à la place.
Propriétés avec des types d'objets
Les propriétés de type objet sont accessibles à partir de QML, à condition que le type d'objet ait été enregistré de manière appropriée dans le système de types de QML.
Par exemple, le type Message peut avoir une propriété body de type MessageBody*:
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) // ... }
Supposons que le type Message ait été enregistré dans le système de types QML, ce qui lui permet d'être utilisé comme type d'objet à partir du code QML :
Message { // ... }
Si le type MessageBody était également enregistré dans le système de types, il serait possible d'affecter MessageBody à la propriété body d'un Message, le tout à partir du code QML :
Message { body: MessageBody { text: "Hello, world!" } }
Propriétés avec types de liste d'objets
Les propriétés contenant des listes de types dérivés de QObject peuvent également être exposées à QML. À cette fin, toutefois, il convient d'utiliser QQmlListProperty plutôt que QList<T> comme type de propriété. En effet, QList n'est pas un type dérivé de QObject et ne peut donc pas fournir les caractéristiques de propriété QML nécessaires par l'intermédiaire du système de méta-objets Qt, telles que les notifications de signal lorsqu'une liste est modifiée.
Par exemple, la classe MessageBoard ci-dessous possède une propriété messages de type QQmlListProperty qui stocke une liste d'instances Message:
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; };
La fonction MessageBoard::messages() crée et renvoie simplement un QQmlListProperty à partir de son membre QList<T> m_messages, en transmettant les fonctions de modification de liste appropriées requises par le constructeur QQmlListProperty:
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); }
Notez que le type de classe modèle pour QQmlListProperty - dans ce cas, Message - doit être enregistré dans le système de types QML.
Propriétés groupées
Toute propriété de type objet en lecture seule est accessible à partir du code QML en tant que propriété groupée. Ceci peut être utilisé pour exposer un groupe de propriétés liées qui décrivent un ensemble d'attributs pour un type.
Par exemple, supposons que la propriété Message::author soit de type MessageAuthor plutôt qu'une simple chaîne de caractères, avec des sous-propriétés name et 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; };
La propriété author pourrait être écrite à l'aide de la syntaxe de propriété groupée de QML, comme suit :
Message { author.name: "Alexandra" author.email: "alexandra@mail.com" }
Un type exposé en tant que propriété groupée diffère d'une propriété de type objet dans la mesure où la propriété groupée est en lecture seule et est initialisée à une valeur valide par l'objet parent lors de la construction. Les sous-propriétés de la propriété groupée peuvent être modifiées à partir de QML, mais l'objet de la propriété groupée lui-même ne changera jamais, alors qu'une propriété de type objet peut se voir attribuer une nouvelle valeur d'objet à partir de QML à tout moment. Ainsi, la durée de vie d'un objet groupé est strictement contrôlée par l'implémentation du parent C++, alors qu'une propriété de type objet peut être librement créée et détruite par le code QML.
Exposer des méthodes (y compris les slots Qt)
Toute méthode d'un type dérivé de QObject est accessible à partir du code QML si elle est :
- une méthode publique marquée par la macro Q_INVOKABLE()
- une méthode qui est un slot Qt public
Par exemple, la classe MessageBoard ci-dessous possède une méthode postMessage() qui a été marquée avec la macro Q_INVOKABLE, ainsi qu'une méthode refresh() qui est un slot public :
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"; } } ;
Si une instance de MessageBoard était définie comme propriété requise pour un fichier MyItem.qml, alors MyItem.qml pourrait invoquer les deux méthodes comme indiqué dans les exemples ci-dessous :
| 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 |
Si une méthode C++ a un paramètre de type QObject*, la valeur du paramètre peut être transmise par QML à l'aide d'un objet id ou d'une valeur JavaScript var qui fait référence à l'objet.
QML prend en charge l'appel de fonctions C++ surchargées. S'il existe plusieurs fonctions C++ portant le même nom mais ayant des arguments différents, la fonction correcte sera appelée en fonction du nombre et des types d'arguments fournis.
Les valeurs renvoyées par les méthodes C++ sont converties en valeurs JavaScript lorsqu'on y accède à partir d'expressions JavaScript dans QML.
Méthodes C++ et objet "this
Il se peut que vous souhaitiez récupérer une méthode C++ d'un objet et l'appeler sur un autre objet. Prenons l'exemple suivant, dans un module QML appelé Example:
| C++ | |
| QML | import QtQml import Example Invokable { objectName: "parent" property Invokable child: Invokable {} Component.onCompleted: child.invoke.call(this) } |
Si vous chargez le code QML à partir d'un main.cpp approprié, il devrait afficher "invoked on parent". Cependant, en raison d'un bogue de longue date, ce n'est pas le cas. Historiquement, l'objet "this" des méthodes C++ est indissociable de la méthode. Changer ce comportement pour le code existant provoquerait des erreurs subtiles puisque l'objet "this" est implicite à de nombreux endroits. Depuis Qt 6.5, vous pouvez explicitement opter pour le comportement correct et permettre aux méthodes C++ d'accepter un objet "this". Pour ce faire, ajoutez le pragma suivant à vos documents QML :
pragma NativeMethodBehavior: AcceptThisObject
Avec cette ligne ajoutée, l'exemple ci-dessus fonctionnera comme prévu.
Surcharge de toString()
Si vous fournissez une méthode Q_INVOKABLE appelée toString (sans arguments), cette méthode sera utilisée pour convertir l'objet en chaîne de caractères à la place de l'implémentation native de toString de JavaScript.
Exposition des signaux
Tout signal public d'un type dérivé de QObject est accessible à partir du code QML.
Le moteur QML crée automatiquement un gestionnaire de signal pour tout signal d'un type dérivé de QObject qui est utilisé à partir de QML. Les gestionnaires de signaux sont toujours nommés on<Signal> où <Signal> est le nom du signal, avec la première lettre en majuscule. Tous les paramètres transmis par le signal sont disponibles dans le gestionnaire de signal par le biais des noms de paramètres.
Par exemple, supposons que la classe MessageBoard possède un signal newMessagePosted() avec un seul paramètre, subject:
class MessageBoard : public QObject { Q_OBJECT public: // ... signals: void newMessagePosted(const QString &subject); };
Si le type MessageBoard est enregistré dans le système de types QML, un objet MessageBoard déclaré en QML pourrait recevoir le signal newMessagePosted() à l'aide d'un gestionnaire de signal nommé onNewMessagePosted et examiner la valeur du paramètre subject:
MessageBoard { onNewMessagePosted: (subject)=> console.log("New message received:", subject) }
Comme pour les valeurs de propriété et les paramètres de méthode, un paramètre de signal doit avoir un type pris en charge par le moteur QML ; voir Conversion des types de données entre QML et C++. (L'utilisation d'un type non enregistré ne génère pas d'erreur, mais la valeur du paramètre ne sera pas accessible à partir du gestionnaire).
Les classes peuvent avoir plusieurs signaux portant le même nom, mais seul le signal final est accessible en tant que signal QML. Notez que les signaux portant le même nom mais ayant des paramètres différents ne peuvent pas être distingués les uns des autres.
Voir également QML Type Registration Macros et Définir les types QML à partir de C++.
© 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.