Définition des types QML à partir de C++
Lors de l'extension de QML avec du code C++, une classe C++ peut être enregistrée dans le système de types de QML pour permettre à la classe d'être utilisée comme type de données dans le code QML. Bien que les propriétés, méthodes et signaux de toute classe dérivée de QObject soient accessibles depuis QML, comme indiqué dans Exposing Attributes of C++ Types to QML, une telle classe ne peut pas être utilisée comme type de données depuis QML tant qu'elle n'est pas enregistrée dans le système de types. En outre, l'enregistrement peut fournir d'autres fonctionnalités, telles que permettre à une classe d'être utilisée comme un type d'objet QML instanciable à partir de QML, ou permettre à une instance singleton de la classe d'être importée et utilisée à partir de QML.
En outre, le module Qt Qml fournit des mécanismes pour mettre en œuvre des caractéristiques spécifiques à QML telles que les propriétés attachées et les propriétés par défaut en C++.
(Notez qu'un certain nombre de concepts importants abordés dans ce document sont démontrés dans le tutoriel Writing QML Extensions with C++ ).
NOTE : Tous les en-têtes qui déclarent des types QML doivent être accessibles sans préfixe à partir du chemin d'inclusion du projet.
Pour plus d'informations sur le C++ et les différentes méthodes d'intégration de QML, voir la page de présentation de l'intégration du C++ et de QML.
Enregistrement des types C++ dans le système de types QML
Une classe dérivée de QObject peut être enregistrée dans le système de types QML pour permettre l'utilisation du type en tant que type de données dans le code QML.
Le moteur permet l'enregistrement de types instanciables et non instanciables. L'enregistrement d'un type instanciable permet à une classe C++ d'être utilisée comme définition d'un type d'objet QML, ce qui permet de l'utiliser dans les déclarations d'objets du code QML pour créer des objets de ce type. L'enregistrement fournit également au moteur des métadonnées de type supplémentaires, permettant au type (et à tout enum déclaré par la classe) d'être utilisé comme type de données pour les valeurs de propriété, les paramètres de méthode et les valeurs de retour, ainsi que les paramètres de signal qui sont échangés entre QML et C++.
L'enregistrement d'un type non substantiel permet également d'enregistrer la classe en tant que type de données de cette manière, mais le type ne peut pas être utilisé instancié en tant que type d'objet QML à partir de QML. Ceci est utile, par exemple, si un type possède des enums qui doivent être exposés à QML mais que le type lui-même ne doit pas être instanciable.
Pour un guide rapide sur le choix de la bonne approche pour exposer les types C++ à QML, voir Choisir la bonne méthode d'intégration entre C++ et QML.
Conditions préalables
Toutes les macros mentionnées ci-dessous sont disponibles dans le fichier d'en-tête qqmlintegration.h du module QtQmlIntegration.
Vous devez ajouter le code suivant aux fichiers qui les utilisent afin de rendre les macros disponibles :
#include <QtQmlIntegration/qqmlintegration.h>Si vous utilisez déjà le module QtQml, vous pouvez utiliser le fichier d'en-tête qqmlregistration.h, qui inclura qqmlintegration.h, comme suit :
#include <QtQml/qqmlregistration.h>En outre, les déclarations de vos classes doivent se trouver dans des en-têtes accessibles via le chemin d'inclusion de votre projet. Les déclarations sont utilisées pour générer le code d'enregistrement au moment de la compilation, et le code d'enregistrement doit inclure les en-têtes qui contiennent les déclarations.
Enregistrement d'un type d'objet instanciable
Toute classe C++ dérivée de QObject peut être enregistrée en tant que définition d'un type d'objet QML. Une fois qu'une classe est enregistrée dans le système de types QML, elle peut être déclarée et instanciée comme n'importe quel autre type d'objet à partir du code QML. Une fois créée, une instance de classe peut être manipulée à partir de QML ; comme l'explique Exposing Attributes of C++ Types to QML, les propriétés, méthodes et signaux de toute classe dérivée de QObject sont accessibles à partir du code QML.
Pour enregistrer une classe dérivée de QObject en tant que type d'objet QML instanciable, ajoutez QML_ELEMENT ou QML_NAMED_ELEMENT(<name>) à la déclaration de la classe. Vous devez également procéder à des ajustements dans le système de construction. Pour qmake, ajoutez CONFIG += qmltypes, QML_IMPORT_NAME et QML_IMPORT_MAJOR_VERSION à votre fichier de projet. Pour CMake, le fichier contenant la classe doit faire partie d'une cible configurée avec qt_add_qml_module(). Cela enregistrera la classe dans l'espace de noms de type sous la version majeure donnée, en utilisant soit le nom de la classe, soit un nom explicitement donné comme nom de type QML. La (les) version(s) mineure(s) sera (seront) dérivée(s) de toutes les révisions attachées aux propriétés, méthodes ou signaux. La version mineure par défaut est 0. Vous pouvez explicitement restreindre le type pour qu'il ne soit disponible qu'à partir de versions mineures spécifiques en ajoutant la macro QML_ADDED_IN_VERSION() à la déclaration de la classe. Les clients peuvent importer les versions appropriées de l'espace de noms afin d'utiliser le type.
Par exemple, supposons qu'il existe une classe Message avec des propriétés author et creationDate:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
Ce type peut être enregistré en ajoutant un espace de noms de type et un numéro de version appropriés au fichier de projet. Par exemple, pour rendre le type disponible dans l'espace de noms com.mycompany.messaging avec la version 1.0 :
qt_add_qml_module(messaging
URI com.mycompany.messaging
VERSION 1.0
SOURCES
message.cpp message.h
)CONFIG += qmltypes QML_IMPORT_NAME = com.mycompany.messaging QML_IMPORT_MAJOR_VERSION = 1
Si l'en-tête dans lequel la classe est déclarée n'est pas accessible à partir du chemin d'inclusion de votre projet, vous devrez peut-être modifier le chemin d'inclusion pour que le code d'enregistrement généré puisse être compilé.
INCLUDEPATH += com/mycompany/messaging
Le type peut être utilisé dans une déclaration d'objet à partir de QML, et ses propriétés peuvent être lues et écrites, comme dans l'exemple ci-dessous :
import com.mycompany.messaging Message { author: "Amelie" creationDate: new Date() }
Enregistrement des types de valeurs
Tout type doté d'une macro Q_GADGET peut être enregistré en tant que type de valeur QML. Une fois qu'un tel type est enregistré dans le système de types de QML, il peut être utilisé comme type de propriété dans le code QML. Une telle instance peut être manipulée à partir de QML ; comme l'explique Exposing Attributes of C++ Types to QML, les propriétés et les méthodes de n'importe quel type de valeur sont accessibles à partir du code QML.
Contrairement aux types d'objets, les types de valeurs doivent être nommés en minuscules. La meilleure façon de les enregistrer est d'utiliser les macros QML_VALUE_TYPE ou QML_ANONYMOUS. Il n'y a pas d'équivalent à QML_ELEMENT car vos classes C++ auront généralement des noms en majuscules. Sinon, l'enregistrement est très similaire à celui des types d'objets.
Par exemple, supposons que vous souhaitiez enregistrer un type de valeur person qui consiste en deux chaînes de caractères pour le prénom et le nom de famille :
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: // ... };
Il existe d'autres limites à ce que vous pouvez faire avec les types de valeur :
- Les types de valeurs ne peuvent pas être des singletons.
- Les types de valeurs doivent pouvoir être construits par défaut et par copie.
- L'utilisation de QProperty comme membre d'un type de valeur est problématique. Les types de valeur sont copiés, et vous devez décider de ce qu'il faut faire avec les liaisons sur QProperty à ce moment-là. Vous ne devez pas utiliser QProperty dans les types de valeur.
- Les types de valeur ne peuvent pas fournir de propriétés attachées.
- L'API permettant de définir les extensions des types de valeur (QML_EXTENDED) n'est pas publique et peut faire l'objet de modifications ultérieures.
Types de valeurs avec énumérations
L'exposition des énumérations d'un type de valeur à QML nécessite quelques étapes supplémentaires.
Les types de valeurs ont des noms en minuscules dans QML et les types avec des noms en minuscules ne sont généralement pas adressables dans le code JavaScript (sauf si vous spécifiez pragma ValueTypeBehavior : Addressable). Si vous avez un type de valeur en C++ avec une énumération que vous voulez exposer à QML, vous devez exposer l'énumération séparément.
Ceci peut être résolu en utilisant QML_FOREIGN_NAMESPACE. Tout d'abord, dérivez votre type de valeur pour créer un type C++ distinct :
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: enum TheEnum { A, B, C }; Q_ENUM(TheEnum) //... }; class PersonDerived: public Person { Q_GADGET };
Exposez ensuite le type dérivé en tant qu'espace de noms étranger :
namespace PersonDerivedForeign
{
Q_NAMESPACE
QML_NAMED_ELEMENT(Person)
QML_FOREIGN_NAMESPACE(PersonDerived)
}Cela produit un espace de noms QML appelé Person (majuscules) avec une énumération appelée TheEnum et les valeurs A, B, et C. Vous pouvez alors écrire ce qui suit en QML :
someProperty: Person.A
En même temps, vous pouvez toujours utiliser votre type de valeur appelé person (minuscule) exactement comme auparavant.
Enregistrement de types non substantiels
Parfois, une classe dérivée de QObject peut avoir besoin d'être enregistrée dans le système de types de QML, mais pas en tant que type instanciable. C'est le cas, par exemple, d'une classe C++ :
- est un type d'interface qui ne doit pas être instanciable
- est un type de classe de base qui n'a pas besoin d'être exposé à QML
- déclare un enum qui doit être accessible à partir de QML, mais qui ne doit pas être instanciable par ailleurs
- est un type qui doit être fourni à QML par l'intermédiaire d'une instance singleton et qui ne doit pas être instanciable à partir de QML
Le module Qt Qml fournit plusieurs macros pour enregistrer des types non instanciables :
- QML_ANONYMOUS enregistre un type C++ qui n'est pas instanciable et auquel on ne peut pas faire référence à partir de QML. Cela permet au moteur de contraindre tous les types hérités qui sont instanciables depuis QML.
- QML_INTERFACE enregistre un type d'interface Qt existant. Le type n'est pas instanciable à partir de QML et vous ne pouvez pas déclarer de propriétés QML avec lui. L'utilisation de propriétés C++ de ce type à partir de QML effectuera les transformations d'interface attendues.
- QML_UNCREATABLE(reason) combiné avec QML_ELEMENT ou QML_NAMED_ELEMENT enregistre un type C++ nommé qui n'est pas instanciable mais qui devrait être identifiable comme un type dans le système de types de QML. Ceci est utile si les enums ou les propriétés attachées d'un type doivent être accessibles depuis QML mais que le type lui-même ne doit pas être instanciable. Le paramètre doit être un message d'erreur à émettre si une tentative de création d'une instance du type est détectée.
- QML_SINGLETON combinée avec QML_ELEMENT ou QML_NAMED_ELEMENT enregistre un type singleton qui peut être importé à partir de QML, comme indiqué ci-dessous.
Notez que tous les types C++ enregistrés dans le système de types de QML doivent être dérivés de QObject, même s'ils ne sont pas substantiels.
Enregistrer des objets singleton avec un type singleton
Un type singleton permet d'exposer des propriétés, des signaux et des méthodes dans un espace de noms sans exiger du client qu'il instancie manuellement une instance d'objet. QObject Les types singleton, en particulier, sont un moyen efficace et pratique de fournir des fonctionnalités ou des valeurs de propriétés globales.
Notez que les types singleton n'ont pas de QQmlContext associé car ils sont partagés par tous les contextes d'un moteur. QObject Les instances de type singleton sont construites et possédées par le QQmlEngine, et seront détruites lorsque le moteur sera détruit.
Il est possible d'interagir avec un type QObject singleton de la même manière qu'avec tout autre type QObject ou instancié, à ceci près qu'il n'existe qu'une seule instance (construite et détenue par le moteur) et qu'elle doit être référencée par le nom du type plutôt que par son identifiant. Les Q_PROPERTY des types singleton QObject peuvent être liés, et les fonctions Q_INVOKABLE des API des modules QObject peuvent être utilisées dans les expressions des gestionnaires de signaux. Cela fait des types singleton un moyen idéal pour mettre en œuvre un style ou un thème, et ils peuvent également être utilisés à la place des importations de scripts ".pragma library" pour stocker l'état global ou pour fournir une fonctionnalité globale.
Une fois enregistré, un type singleton QObject peut être importé et utilisé comme n'importe quelle autre instance QObject exposée à QML. L'exemple suivant suppose qu'un type singleton QObject a été enregistré dans l'espace de noms "MyThemeModule" avec la version 1.0, où ce QObject a une QColor "color" Q_PROPERTY:
import MyThemeModule 1.0 as Theme Rectangle { color: Theme.color // binding. }
Un QJSValue peut également être exposé en tant que type singleton, mais les clients doivent savoir que les propriétés d'un tel type singleton ne peuvent pas être liées.
Voir QML_SINGLETON pour plus d'informations sur l'implémentation et l'enregistrement d'un nouveau type singleton, et sur l'utilisation d'un type singleton existant. Voir Singletons in QML pour des informations plus approfondies sur les singletons.
Note : Les valeurs de l'énumération pour les types enregistrés en QML doivent commencer par une majuscule.
Propriétés finales
Les propriétés déclarées finales à l'aide du modificateur FINAL à Q_PROPERTY ne peuvent pas être remplacées. Cela signifie que toute propriété ou fonction du même nom, déclarée en QML ou en C++ sur des types dérivés, est ignorée par le moteur QML. Dans la mesure du possible, il convient de déclarer les propriétés à l'adresse FINAL afin d'éviter les dérogations accidentelles. Une surcharge d'une propriété est visible non seulement dans les classes dérivées, mais aussi pour le code QML qui exécute le contexte de la classe de base. Ce code QML s'attend généralement à ce que la propriété originale soit utilisée. Il s'agit d'une source fréquente d'erreurs.
Les propriétés déclarées FINAL ne peuvent pas non plus être remplacées par des fonctions en QML ou par des méthodes Q_INVOKABLE en C++.
Révisions et versions des types
De nombreuses fonctions d'enregistrement des types exigent que des versions soient spécifiées pour le type enregistré. Les révisions et les versions des types permettent à de nouvelles propriétés ou méthodes d'exister dans la nouvelle version tout en restant compatibles avec les versions précédentes.
Prenons l'exemple de ces deux fichiers QML :
// main.qml import QtQuick 1.0 Item { id: root MyType {} }
// MyType.qml import MyTypes 1.0 CppType { value: root.x }
où CppType correspond à la classe C++ CppType.
Si l'auteur de CppType ajoute une propriété root à CppType dans une nouvelle version de sa définition de type, root.x se résout maintenant en une valeur différente parce que root est aussi le id du composant de niveau supérieur. L'auteur pourrait spécifier que la nouvelle propriété root est disponible à partir d'une version mineure spécifique. Cela permet d'ajouter de nouvelles propriétés et fonctionnalités aux types existants sans interrompre les programmes en place.
La balise REVISION est utilisée pour indiquer que la propriété root a été ajoutée dans la révision 1 du type. Les méthodes telles que Q_INVOKABLE's, les signaux et les slots peuvent également être marqués pour une révision à l'aide de la macro Q_REVISION:
class CppType : public BaseType { Q_OBJECT Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION(1, 0)) QML_ELEMENT signals: Q_REVISION(1, 0) void rootChanged(); };
Les révisions données de cette manière sont automatiquement interprétées comme des versions mineures de la version majeure donnée dans le fichier de projet. Dans ce cas, root n'est disponible que lorsque MyTypes version 1.1 ou supérieure est importée. Les importations de MyTypes version 1.0 ne sont pas affectées.
Pour la même raison, les nouveaux types introduits dans les versions ultérieures doivent être marqués avec la macro QML_ADDED_IN_VERSION.
Cette caractéristique du langage permet d'apporter des changements de comportement sans interrompre les applications existantes. Par conséquent, les auteurs de modules QML doivent toujours se rappeler de documenter ce qui a changé entre les versions mineures, et les utilisateurs de modules QML doivent vérifier que leur application fonctionne toujours correctement avant de déployer une déclaration d'importation mise à jour.
Les révisions d'une classe de base dont dépend votre type sont automatiquement enregistrées lors de l'enregistrement du type lui-même. Ceci est utile lorsque vous dérivez de classes de base fournies par d'autres auteurs, par exemple lorsque vous étendez des classes du module Qt Quick.
Remarque : le moteur QML ne prend pas en charge les révisions pour les propriétés ou les signaux des objets de propriété groupés et attachés.
Enregistrement des objets d'extension
Lors de l'intégration de classes et de technologies existantes dans QML, les API devront souvent être modifiées pour mieux s'adapter à l'environnement déclaratif. Bien que les meilleurs résultats soient généralement obtenus en modifiant directement les classes d'origine, si cela n'est pas possible ou est compliqué par d'autres préoccupations, les objets d'extension offrent des possibilités d'extension limitées aux types que vous contrôlez sans modifications directes. L'extension des types propres à Qt n'est pas prise en charge.
Les objets d'extension ajoutent des propriétés supplémentaires à un type existant. La définition d'un type étendu permet au programmeur de fournir un type supplémentaire, connu sous le nom de type d'extension, lors de l'enregistrement de la classe. Ses membres sont fusionnés de manière transparente avec la classe cible originale lorsqu'ils sont utilisés à partir de QML. En voici un exemple :
QLineEdit { leftMargin: 20 }
La propriété leftMargin est une nouvelle propriété ajoutée à un type C++ existant, QLineEdit, sans modifier son code source.
La macro QML_EXTENDED(extension) permet d'enregistrer des types étendus. L'argument est le nom d'une autre classe à utiliser comme extension.
Vous pouvez également utiliser QML_EXTENDED_NAMESPACE(namespace) pour enregistrer un espace de noms, et en particulier les énumérations qui y sont déclarées, en tant qu'extension d'un type. Si le type que vous étendez est lui-même un espace de noms, vous devez utiliser QML_NAMESPACE_EXTENDED(namespace) à la place.
Une classe d'extension est une classe QObject ordinaire, avec un constructeur qui prend un pointeur QObject. Cependant, la création de la classe d'extension est retardée jusqu'à ce que l'on accède à la première propriété étendue. La classe d'extension est créée et l'objet cible est transmis en tant que parent. Lorsque l'on accède à la propriété de l'original, la propriété correspondante de l'objet d'extension est utilisée à la place.
Enregistrement des types étrangers
Il peut y avoir des types C++ qui ne peuvent pas être modifiés pour contenir les macros mentionnées ci-dessus. Il peut s'agir de types provenant de bibliothèques tierces ou de types qui doivent remplir un contrat qui contredit la présence de ces macros. Vous pouvez néanmoins exposer ces types à QML en utilisant la macro QML_FOREIGN. Pour ce faire, créez une structure séparée qui consiste entièrement en macros d'enregistrement, comme ceci :
// Contains class Immutable3rdParty #include <3rdpartyheader.h> struct Foreign { Q_GADGET QML_FOREIGN(Immutable3rdParty) QML_NAMED_ELEMENT(Accessible3rdParty) QML_ADDED_IN_VERSION(2, 4) // QML_EXTENDED, QML_SINGLETON ... };
A partir de ce code, vous obtenez un type QML avec les méthodes et les propriétés de Immutable3rdParty, et les traits QML (par exemple : singleton, extended) spécifiés dans Foreign.
Définition de types et d'attributs spécifiques à QML
Fournir des propriétés attachées
Dans la syntaxe du langage QML, il existe une notion de propriétés attachées et de gestionnaires de signaux attachés, qui sont des attributs supplémentaires attachés à un objet. Essentiellement, ces attributs sont mis en œuvre et fournis par un type d'attachement, et ces attributs peuvent être attachés à un objet d'un autre type. Ces attributs peuvent être attachés à un objet d'un autre type, contrairement aux propriétés ordinaires des objets qui sont fournies par le type d'objet lui-même (ou le type hérité de l'objet).
Par exemple, le site Item ci-dessous utilise des propriétés attachées et des gestionnaires attachés :
import QtQuick 2.0 Item { width: 100; height: 100 focus: true Keys.enabled: false Keys.onReturnPressed: console.log("Return key was pressed") }
Ici, l'objet Item peut accéder aux valeurs de Keys.enabled et Keys.onReturnPressed et les définir. Cela permet à l'objet Item d'accéder à ces attributs supplémentaires en tant qu'extension de ses propres attributs existants.
Étapes de mise en œuvre des objets attachés
Dans l'exemple ci-dessus, plusieurs parties sont impliquées :
- Une instance d'un type d'objet attaché anonyme, doté d'une propriété
enabledet d'un signalreturnPressed, a été attachée à l'objet Item pour lui permettre d'accéder à ces attributs et de les définir. - L'objet Item est l'objet attaché, auquel l'instance du type d'objet attaché a été attachée.
- Keys est le type d'attachement, qui fournit à l'attaché un qualificateur nommé, "Keys", grâce auquel il peut accéder aux attributs du type d'objet attaché.
Lorsque le moteur QML traite ce code, il crée une instance unique du type d'objet attaché et attache cette instance à l'objet Item, ce qui lui donne accès aux attributs enabled et returnPressed de l'instance.
Les mécanismes permettant de fournir des objets attachés peuvent être mis en œuvre en C++ en fournissant des classes pour le type d'objet attaché et le type d'attachement. Pour le type d'objet attaché, fournir une classe dérivée de QObject qui définit les attributs à rendre accessibles aux objets attachés. Pour le type d'attachement, fournir une classe dérivée de QObject qui :
- implémente une fonction statique qmlAttachedProperties() avec la signature suivante :
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);
Cette méthode doit renvoyer une instance du type d'objet attaché.
Le moteur QML invoque cette méthode afin d'attacher une instance du type d'objet attaché à l'attachee spécifié par le paramètre
object. Il est d'usage, mais pas strictement obligatoire, que l'implémentation de cette méthode associe l'instance renvoyée àobjectafin d'éviter les fuites de mémoire.Cette méthode est appelée au maximum une fois par le moteur pour chaque instance d'objet attachee, car le moteur met en cache le pointeur de l'instance renvoyée pour les accès ultérieurs à la propriété attachée. Par conséquent, l'objet attaché ne peut pas être supprimé tant que l'attachee
objectn'est pas détruit. - est déclaré comme un type attachant, en ajoutant la macro QML_ATTACHED(attached) à la déclaration de la classe. L'argument est le nom du type d'objet attaché
Mise en œuvre des objets attachés : Un exemple
Prenons par exemple le type Message décrit dans un exemple précédent:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
Supposons qu'il soit nécessaire de déclencher un signal sur un site Message lorsqu'il est publié sur un tableau d'affichage, et de savoir quand le message a expiré sur le tableau d'affichage. Étant donné qu'il n'est pas judicieux d'ajouter ces attributs directement à un Message, car ils sont plus pertinents dans le contexte du tableau d'affichage, ils pourraient être mis en œuvre en tant qu'attributs attachés à un objet Message qui sont fournis par le biais d'un qualificateur "MessageBoard". En ce qui concerne les concepts décrits plus haut, les parties concernées sont les suivantes :
- Une instance d'un type d'objet attaché anonyme, qui fournit un signal
publishedet une propriétéexpired. Ce type est implémenté parMessageBoardAttachedTypeci-dessous - Un objet
Message, qui sera l'objet attaché - Le type
MessageBoard, qui sera le type d'attachement utilisé par les objetsMessagepour accéder aux attributs attachés.
Voici un exemple de mise en œuvre. Tout d'abord, il faut qu'il y ait un type d'objet attaché avec les propriétés et les signaux nécessaires qui seront accessibles à l'attaché:
class MessageBoardAttachedType : public QObject { Q_OBJECT Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged) QML_ANONYMOUS public: MessageBoardAttachedType(QObject *parent); bool expired() const; void setExpired(bool expired); signals: void published(); void expiredChanged(); };
Ensuite, le type d'attachement, MessageBoard, doit déclarer une méthode qmlAttachedProperties() qui renvoie une instance du type d'objet attaché tel qu'il est mis en œuvre par MessageBoardAttachedType. En outre, MessageBoard doit être déclaré en tant que type d'attachement via la macro QML_ATTACHED() :
class MessageBoard : public QObject { Q_OBJECT QML_ATTACHED(MessageBoardAttachedType) QML_ELEMENT public: static MessageBoardAttachedType *qmlAttachedProperties(QObject *object) { return new MessageBoardAttachedType(object); } };
Désormais, un type Message peut accéder aux propriétés et aux signaux du type d'objet attaché :
Message { author: "Amelie" creationDate: new Date() MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00") MessageBoard.onPublished: console.log("Message by", author, "has been published!") }
En outre, l'implémentation C++ peut accéder à l'instance de l'objet attaché qui a été attachée à n'importe quel objet en appelant la fonction qmlAttachedPropertiesObject().
Par exemple :
Message *msg = someMessageInstance() ; MessageBoardAttachedType *attached = qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg)) ; qDebug() << "Value of MessageBoard.expired:" << attached->expired();
Propagation des propriétés attachées
QQuickAttachedPropertyPropagator peut être sous-classée pour propager les propriétés attachées d'un objet parent à ses enfants, de manière similaire à la propagation par font et palette. Elle prend en charge la propagation via items, popups, et windows.
Types de modificateurs de propriétés
Un type de modificateur de propriété est un type d'objet QML particulier. Une instance de type modificateur de propriété affecte une propriété (d'une instance d'objet QML) à laquelle elle est appliquée. Il existe deux types de modificateurs de propriété :
- les intercepteurs d'écriture de valeur de propriété
- les sources de valeur de propriété
Un intercepteur d'écriture de valeur de propriété peut être utilisé pour filtrer ou modifier des valeurs lorsqu'elles sont écrites dans des propriétés. Actuellement, le seul intercepteur d'écriture de valeur de propriété pris en charge est le type Behavior fourni par l'importation QtQuick.
Une source de valeur de propriété peut être utilisée pour mettre à jour automatiquement la valeur d'une propriété au fil du temps. Les clients peuvent définir leurs propres types de sources de valeurs de propriétés. Les différents types d'animation de propriété fournis par l'importation QtQuick sont des exemples de sources de valeur de propriété.
Des instances de type modificateur de propriété peuvent être créées et appliquées à une propriété d'un objet QML grâce à la syntaxe "<ModifierType> on <propertyName>", comme le montre l'exemple suivant :
import QtQuick 2.0 Item { width: 400 height: 50 Rectangle { width: 50 height: 50 color: "red" NumberAnimation on x { from: 0 to: 350 loops: Animation.Infinite duration: 2000 } } }
Cette syntaxe est communément appelée syntaxe "on".
Les clients peuvent enregistrer leurs propres types de sources de valeur de propriété, mais pas les intercepteurs d'écriture de valeur de propriété.
Sources de valeur de propriété
Lessources de valeur de propriété sont des types QML qui peuvent automatiquement mettre à jour la valeur d'une propriété au fil du temps, à l'aide de la syntaxe <PropertyValueSource> on <property>. Par exemple, les différents types d'animation de propriété fournis par le module QtQuick sont des exemples de sources de valeur de propriété.
Une source de valeur de propriété peut être mise en œuvre en C++ en sous-classant QQmlPropertyValueSource et en fournissant une implémentation qui écrit différentes valeurs dans une propriété au fil du temps. Lorsque la source de valeur de propriété est appliquée à une propriété à l'aide de la syntaxe <PropertyValueSource> on <property> dans QML, le moteur lui attribue une référence à cette propriété afin que la valeur de la propriété puisse être mise à jour.
Par exemple, supposons qu'il existe une classe RandomNumberGenerator à mettre à disposition en tant que source de valeur de propriété, de sorte que lorsqu'elle est appliquée à une propriété QML, elle met à jour la valeur de la propriété avec un nombre aléatoire différent toutes les 500 millisecondes. En outre, une valeur maximale peut être fournie à ce générateur de nombres aléatoires. Cette classe peut être mise en œuvre comme suit :
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged); QML_ELEMENT public: RandomNumberGenerator(QObject *parent) : QObject(parent), m_maxValue(100) { QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty())); m_timer.start(500); } int maxValue() const; void setMaxValue(int maxValue); virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; } signals: void maxValueChanged(); private slots: void updateProperty() { m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue)); } private: QQmlProperty m_targetProperty; QTimer m_timer; int m_maxValue; };
Lorsque le moteur QML rencontre une utilisation de RandomNumberGenerator comme source de valeur de propriété, il invoque RandomNumberGenerator::setTarget() pour fournir au type la propriété à laquelle la source de valeur a été appliquée. Lorsque la minuterie interne de RandomNumberGenerator se déclenche toutes les 500 millisecondes, elle écrit une nouvelle valeur numérique dans la propriété spécifiée.
Une fois que la classe RandomNumberGenerator a été enregistrée dans le système de types QML, elle peut être utilisée à partir de QML comme source de valeur de propriété. Ci-dessous, elle est utilisée pour modifier la largeur d'une page Rectangle toutes les 500 millisecondes :
import QtQuick 2.0 Item { width: 300; height: 300 Rectangle { RandomNumberGenerator on width { maxValue: 300 } height: 100 color: "red" } }
À tous autres égards, les sources de valeur de propriété sont des types QML ordinaires qui peuvent avoir des propriétés, des méthodes de signalisation, etc., mais avec la capacité supplémentaire de pouvoir être utilisées pour modifier les valeurs de propriété à l'aide de la syntaxe <PropertyValueSource> on <property>.
Lorsqu'un objet source de valeur de propriété est affecté à une propriété, QML tente d'abord de l'affecter normalement, comme s'il s'agissait d'un type QML ordinaire. Ce n'est que si cette affectation échoue que le moteur appelle la méthode setTarget(). Cela permet au type d'être utilisé dans d'autres contextes que celui d'une source de valeur.
Spécification des propriétés par défaut et des propriétés parent pour les types d'objets QML
Tout type dérivé de QObject enregistré en tant que type d'objet QML instanciable peut, à titre facultatif, spécifier une propriété par défaut pour le type. Une propriété par défaut est la propriété à laquelle les enfants d'un objet sont automatiquement assignés s'ils ne sont pas assignés à une propriété spécifique.
La propriété par défaut peut être définie en appelant la macro Q_CLASSINFO() pour une classe ayant une valeur "DefaultProperty" spécifique. Par exemple, la classe MessageBoard ci-dessous spécifie sa propriété messages comme étant la propriété par défaut de la classe :
class MessageBoard : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) Q_CLASSINFO("DefaultProperty", "messages") QML_ELEMENT public: QQmlListProperty<Message> messages(); private: QList<Message *> m_messages; };
Cela permet aux enfants d'un objet MessageBoard d'être automatiquement affectés à sa propriété messages s'ils ne sont pas affectés à une propriété spécifique. Par exemple :
MessageBoard { Message { author: "Naomi" } Message { author: "Clancy" } }
Si messages n'était pas défini comme la propriété par défaut, tous les objets Message devraient être explicitement assignés à la propriété messages, comme suit :
MessageBoard { messages: [ Message { author: "Naomi" }, Message { author: "Clancy" } ] }
(Par ailleurs, la propriété Item::data est sa propriété par défaut. Tous les objets Item ajoutés à cette propriété data sont également ajoutés à la liste Item::children, de sorte que l'utilisation de la propriété par défaut permet de déclarer des enfants visuels pour un élément sans les affecter explicitement à la propriété children ).
En outre, vous pouvez déclarer une "ParentProperty" Q_CLASSINFO() pour indiquer au moteur QML quelle propriété doit désigner l'objet parent dans la hiérarchie QML. Par exemple, le type Message peut être déclaré comme suit :
class Message : public QObject { Q_OBJECT Q_PROPERTY(QObject* board READ board BINDABLE boardBindable) Q_PROPERTY(QString author READ author BINDABLE authorBindable) Q_CLASSINFO("ParentProperty", "board") QML_ELEMENT public: Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; } QObject *board() const { return m_board.value(); } QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); } QString author() const { return m_author.value(); } QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); } private: QProperty<QObject *> m_board; QProperty<QString> m_author; };
La définition de la propriété parent permet à qmllint et à d'autres outils de mieux comprendre l'intention de votre code et d'éviter les avertissements faussement positifs lors de certains accès à la propriété.
Définition d'éléments visuels avec le module Qt Quick
Lors de la construction d'interfaces utilisateur avec le module Qt Quick tous les objets QML qui doivent être rendus visuellement doivent dériver du type Item, car il s'agit du type de base pour tous les objets visuels dans le module Qt Quick. Ce type Item est mis en œuvre par la classe C++ QQuickItem, qui est fournie par le module Qt Quick . Par conséquent, cette classe doit être sous-classée lorsqu'il est nécessaire d'implémenter un type visuel en C++ qui peut être intégré dans une interface utilisateur basée sur QML.
Voir la documentation QQuickItem pour plus d'informations. En outre, le didacticiel Writing QML Extensions with C++ montre comment un élément visuel basé sur QQuickItem peut être mis en œuvre en C++ et intégré dans une interface utilisateur basée sur Qt Quick.
Réception de notifications pour l'initialisation d'objets
Pour certains types d'objets QML personnalisés, il peut être intéressant de retarder l'initialisation de certaines données jusqu'à ce que l'objet ait été créé et que toutes ses propriétés aient été définies. Par exemple, cela peut être le cas si l'initialisation est coûteuse, ou si l'initialisation ne doit pas être effectuée tant que toutes les valeurs des propriétés n'ont pas été initialisées.
Le module Qt Qml fournit le module QQmlParserStatus qui peut être sous-classé à ces fins. Il définit un certain nombre de méthodes virtuelles qui sont invoquées à différents stades de l'instanciation du composant. Pour recevoir ces notifications, une classe C++ doit hériter de QQmlParserStatus et notifier également le méta-système Qt à l'aide de la macro Q_INTERFACES().
En voici un exemple :
class MyQmlType : public QObject, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) QML_ELEMENT public: virtual void componentComplete() { // Perform some initialization here now that the object is fully created } };
Voir également QML Type Registration Macros et Overview - QML and C++ Integration (Aperçu de l'intégration de QML et 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.