Définition de types QML à partir de C++
Lorsqu’on étend QML avec du code C++, une classe C++ peut être enregistrée dans le système de types QML afin de permettre son utilisation en tant que type de données au sein du code QML. Bien que les propriétés, méthodes et signaux de toute classe dérivée d'QObject soient accessibles depuis QML, comme expliqué dans la section « Exposition des attributs des types C++ à 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 auprès du système de types. De plus, l’enregistrement peut offrir d’autres fonctionnalités, telles que la possibilité d’utiliser une classe en tant que type d’objet QML instanciable depuis le QML, ou de permettre l’importation et l’utilisation d’une instance singleton de la classe depuis le QML.
Par ailleurs, le Qt Qml module fournit des mécanismes permettant d’implémenter des fonctionnalités 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 illustrés dans le tutoriel « Écrire des extensions QML avec C++ ».)
REMARQUE : tous les en-têtes déclarant 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 avec QML, consultez la page de présentation de l’intégration C++ et QML.
Enregistrement des types C++ dans le système de types QML
Une classe dérivée de ` QObject` peut être enregistrée auprès du système de types QML afin de permettre l’utilisation de ce type en tant que type de données au sein du code QML.
Le moteur permet l’enregistrement de types instanciables et non instanciables. L’enregistrement d’un type instanciable permet d’utiliser une classe C++ 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 à toute énumération déclarée 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 échangés entre QML et C++.
L'enregistrement d'un type non instanciable enregistre également la classe en tant que type de données de cette manière, mais le type ne peut pas être instancié en tant que type d'objet QML à partir de QML. Cela s'avère utile, par exemple, si un type comporte des énumérations qui doivent être exposées à QML, mais que le type lui-même ne doit pas être instanciable.
Pour un guide rapide sur le choix de la bonne approche afin d’exposer des types C++ à QML, consultez la section « 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 liez déjà votre projet au module « QtQml », vous pouvez utiliser à la place le fichier d’en-tête « qqmlregistration.h », qui inclura « qqmlintegration.h », comme suit :
#include <QtQml/qqmlregistration.h>De plus, vos déclarations de classes doivent figurer dans des en-têtes accessibles via le chemin d’inclusion de votre projet. Ces déclarations servent à générer du code d’enregistrement lors de la compilation, et ce code d’enregistrement doit inclure les en-têtes contenant 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 auprès du 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 depuis le QML ; comme l’explique la section « Exposition des attributs des types C++ au QML », les propriétés, méthodes et signaux de toute classe dérivée de ` QObject` sont accessibles depuis le 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 apporter des modifications au 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 des types sous la version majeure indiquée, en utilisant soit le nom de la classe, soit un nom explicitement fourni comme nom de type QML. La ou les versions mineures seront dérivées des révisions associées aux propriétés, méthodes ou signaux. La version mineure par défaut est 0. Vous pouvez restreindre explicitement la disponibilité du type à certaines versions mineures spécifiques en ajoutant la macro QML_ADDED_IN_VERSION() à la déclaration de classe. Les clients peuvent importer les versions appropriées de l’espace de noms afin d’utiliser ce type.
Par exemple, supposons qu’il existe une classe Message dotée 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 = 1Si l'en-tête dans lequel la classe est déclarée n'est pas accessible depuis le chemin d'inclusion de votre projet, vous devrez peut-être modifier ce chemin afin que le code d'enregistrement généré puisse être compilé.
INCLUDEPATH += com/mycompany/messagingCe type peut être utilisé dans une déclaration d'objet en QML, et ses propriétés peuvent être lues et modifiées, comme le montre l'exemple ci-dessous :
import com.mycompany.messaging
Message {
author: "Amelie"
creationDate: new Date()
}Enregistrement des types de valeur
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 QML, il peut être utilisé comme type de propriété dans le code QML. Une instance de ce type peut être manipulée depuis QML ; comme l’explique la section « Exposition des attributs des types C++ à QML », les propriétés et les méthodes de tout type de valeur sont accessibles depuis le code QML.
Contrairement aux types d'objets, les types de valeur doivent porter des noms en minuscules. La méthode recommandée pour les enregistrer consiste à utiliser les macros ` QML_VALUE_TYPE ` ou ` QML_ANONYMOUS `. Il n'existe pas d'équivalent à ` QML_ELEMENT `, car vos classes C++ portent généralement des noms en majuscules. Pour le reste, l'enregistrement est très similaire à celui des types d'objets.
Par exemple, supposons que vous souhaitiez enregistrer un type de valeur person composé de deux chaînes de caractères correspondant au prénom et au 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 certaines autres limitations concernant ce que vous pouvez faire avec les types de valeur :
- Les types de valeur ne peuvent pas être des singletons.
- Les types de valeur doivent pouvoir être construits par défaut et par copie.
- L'utilisation d'un « QProperty » en tant que membre d'un type de valeur pose problème. Les types de valeur sont copiés, et vous devriez alors décider quoi faire des liaisons définies sur l'QProperty. Vous ne devriez pas utiliser d'« QProperty » dans les types de valeur.
- Les types de valeur ne peuvent pas fournir de propriétés associées.
- L’API permettant de définir des extensions pour les types de valeur (QML_EXTENDED) n’est pas publique et est susceptible d’évoluer.
Types de valeur avec des énumérations
L'exposition des énumérations d'un type de valeur vers QML nécessite quelques étapes supplémentaires.
Dans QML, les types de valeur portent des noms en minuscules, et les types dont le nom est en minuscules ne sont généralement pas accessibles depuis le code JavaScript (à moins de spécifier le pragma `ValueTypeBehavior: Addressable`). Si vous disposez d'un type de valeur en C++ comportant une énumération que vous souhaitez exposer à QML, vous devez exposer cette énumération séparément.
Pour résoudre ce problème, utilisez ` QML_FOREIGN_NAMESPACE`. Commencez par créer un type C++ distinct en dérivant de votre type de valeur :
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 génère un espace de noms QML appelé « Person » (en 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.AParallèlement, vous pouvez toujours utiliser votre type de valeur nommé « person » (en minuscules) exactement comme auparavant.
Enregistrement des types non instanciables
Il arrive parfois qu’une classe dérivée de ` QObject` doive être enregistrée auprès du système de types QML, mais pas en tant que type instanciable. C’est par exemple le cas pour 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 une énumération qui doit être accessible depuis QML, mais qui, par ailleurs, ne doit pas être instanciable
- est un type qui doit être fourni à QML via une instance singleton et qui ne doit pas être instanciable depuis QML
Le Qt Qml module fournit plusieurs macros permettant d’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 depuis QML. Cela permet au moteur de convertir de force tout type hérité qui est instanciable depuis QML.
- QML_INTERFACE enregistre un type d’interface Qt existant. Ce type n’est pas instanciable depuis 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 depuis QML effectuera toutefois les conversions d’interface attendues.
- QML_UNCREATABLE(raison) combinée avec QML_ELEMENT ou QML_NAMED_ELEMENT permet d’enregistrer un type C++ nommé qui n’est pas instanciable mais qui doit être identifiable en tant que type par le système de types QML. Cela s’avère utile si les énumérations ou les propriétés associé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 à afficher si une tentative de création d’une instance du type est détectée.
- QML_SINGLETON Associée à ` QML_ELEMENT ` ou ` QML_NAMED_ELEMENT `, cette fonction permet d’enregistrer un type singleton pouvant être importé depuis QML, comme expliqué ci-dessous.
Notez que tous les types C++ enregistrés auprès du système de types QML doivent être dérivés de ` QObject`, même s’ils ne sont pas instanciables.
Enregistrement d’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 que le client ait à instancier manuellement une instance d’objet. Les types singleton de ` QObject `, en particulier, constituent un moyen efficace et pratique de fournir des fonctionnalités ou des valeurs de propriétés globales.
Notez que les types singleton ne sont pas associés à un QQmlContext, car ils sont partagés entre tous les contextes d’un moteur. QObject Les instances de type singleton sont créées et gérées par l’ QQmlEngine, et seront détruites lorsque le moteur sera détruit.
On peut interagir avec un type singleton de type « QObject » de la même manière qu’avec n’importe quel autre type « QObject » ou type instancié, à cette différence près qu’il n’existera qu’une seule instance (construite et gérée par le moteur) et qu’elle doit être référencée par son nom de type plutôt que par son identifiant. Les propriétés Q_PROPERTY des types singleton QObject peuvent être liées, et les fonctions d’ Q_INVOKABLE s des API du module QObject peuvent être utilisées dans les expressions des gestionnaires de signaux. Cela fait des types singleton un moyen idéal pour implémenter des styles ou des thèmes, et ils peuvent également être utilisés à la place des importations de scripts « .pragma library » pour stocker un état global ou 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 part du principe qu’un type singleton QObject a été enregistré dans l’espace de noms « MyThemeModule » avec la version 1.0, et que ce QObject dispose d’un Q_PROPERTY « color » de QColor:
import MyThemeModule 1.0 as Theme
Rectangle {
color: Theme.color // binding.
}Un QJSValue peut également être exposé en tant que type singleton ; toutefois, les clients doivent savoir qu’il n’est pas possible de lier des propriétés à un tel type singleton.
Consultez QML_SINGLETON pour plus d’informations sur la manière d’implémenter et d’enregistrer un nouveau type singleton, ainsi que sur l’utilisation d’un type singleton existant. Consultez la section « Singletons dans QML » pour des informations plus détaillées sur les singletons.
Remarque : les valeursd'énumération des types enregistrés dans QML doivent commencer par une majuscule.
Propriétés finales
Les propriétés déclarées comme « final » à l’aide du modificateur « FINAL » dans « Q_PROPERTY » ne peuvent pas être redéfinies. Cela signifie que toutes les propriétés ou fonctions portant le même nom, déclarées soit en QML, soit en C++ sur des types dérivés, sont ignorées par le moteur QML. Vous devez déclarer les propriétés « FINAL » dans la mesure du possible, afin d’éviter toute redéfinition accidentelle. Une redéfinition de propriété est visible non seulement dans les classes dérivées, mais également dans le code QML s’exécutant dans le contexte de la classe de base. Ce code QML s’attend toutefois généralement à la propriété d’origine. C’est une source fréquente d’erreurs.
Les propriétés déclarées « FINAL » ne peuvent pas non plus être redéfinies par des fonctions en QML, ni par des méthodes « Q_INVOKABLE » en C++.
Révisions et versions de types
De nombreuses fonctions d’enregistrement de types exigent que des versions soient spécifiées pour le type enregistré. Les révisions et versions de types permettent d’introduire de nouvelles propriétés ou méthodes dans la nouvelle version tout en conservant la compatibilité 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 renvoie désormais une valeur différente, car root est également l’ id e 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 perturber le fonctionnement des programmes existants.
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 celles de Q_INVOKABLE, les signaux et les slots peuvent également être balisées 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 indiquées de cette manière sont automatiquement interprétées comme des versions mineures de la version majeure indiquée dans le fichier de projet. Dans ce cas, l’ root n’est disponible que lorsque la version 1.1 ou supérieure d’ MyTypes est importée. Les importations de la version 1.0 d’ MyTypes ne sont pas affectées.
Pour la même raison, les nouveaux types introduits dans les versions ultérieures doivent être marqués à l’aide de la macro QML_ADDED_IN_VERSION.
Cette fonctionnalité du langage permet d’apporter des modifications comportementales sans perturber le fonctionnement des applications existantes. Par conséquent, les auteurs de modules QML doivent toujours penser à documenter les changements intervenus entre les versions mineures, et les utilisateurs de modules QML doivent vérifier que leur application fonctionne toujours correctement avant de déployer une instruction d’import 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. Cela s’avère 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 des propriétés ou des signaux des objets de propriétés 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 doivent souvent être adaptées pour mieux s'intégrer à l'environnement déclaratif. Bien que les meilleurs résultats soient généralement obtenus en modifiant directement les classes d'origine, si cela s'avère impossible ou compliqué par d'autres contraintes, les objets d'extension offrent des possibilités d'extension limitées aux types que vous contrôlez, sans modification directe. 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. Une définition de type étendue permet au programmeur de fournir un type supplémentaire, appelé « type d'extension », lors de l'enregistrement de la classe. Ses membres sont fusionnés de manière transparente avec la classe cible d'origine lorsqu'ils sont utilisés depuis QML. Par exemple :
QLineEdit {
leftMargin: 20
}La propriété ` leftMargin ` est une nouvelle propriété ajoutée à un type C++ existant, ` QLineEdit`, sans modification de son code source.
La macro QML_EXTENDED(extension) sert à 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 classique, dotée d’un constructeur qui prend en paramètre un pointeur QObject. Cependant, la création de la classe d’extension est différée jusqu’à ce que l’on accède à la première propriété étendue. La classe d’extension est alors créée et l’objet cible lui est transmis en tant que parent. Lorsque l’on accède à la propriété de l’objet d’origine, c’est la propriété correspondante de l’objet d’extension qui est utilisée à la place.
Enregistrement des types étrangers
Il peut exister des types C++ qui ne peuvent pas être modifiés pour intégrer les macros mentionnées ci-dessus. Il peut s’agir de types provenant de bibliothèques tierces, ou de types devant respecter certaines contraintes incompatibles avec la présence de ces macros. Vous pouvez toutefois exposer ces types à QML à l’aide de la macro ` QML_FOREIGN `. Pour ce faire, créez une structure distincte composée exclusivement des macros d’enregistrement, comme suit :
// 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 ...
};À partir de ce code, vous obtenez un type QML doté des méthodes et propriétés d’`Immutable3rdParty`, ainsi que des 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 associées
Dans la syntaxe du langage QML, il existe la notion de propriétés associées et de gestionnaires de signaux associés, qui sont des attributs supplémentaires associés à un objet. Essentiellement, ces attributs sont implémentés et fournis par un type d'attachement, et ils peuvent être associés à un objet d'un autre type. Cela contraste avec les propriétés d'objet ordinaires, qui sont fournies par le type d'objet lui-même (ou par le type hérité de l'objet).
Par exemple, l'Item e 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 ` est capable d’accéder aux valeurs de ` Keys.enabled ` et ` Keys.onReturnPressed` et de 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 associés
Dans l’exemple ci-dessus, plusieurs parties sont impliquées :
- Il existe une instance d’un type d’objet attaché anonyme, dotée d’une propriété `
enabled` et d’un signal `returnPressed`, qui a été attachée à l’objet ` Item ` afin de 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é rattachée.
- Keys est le type attachant, qui fournit à l’ objet attaché un qualificateur nommé, « Keys », grâce auquel celui-ci 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 `, lui fournissant ainsi l’accès aux attributs ` enabled ` et ` returnPressed ` de l’instance.
Les mécanismes permettant de fournir des objets associés peuvent être implémentés en C++ en fournissant des classes pour le type d’objet associé et le type d’attache. Pour le type d’objet associé, fournissez une classe dérivée de ` QObject` qui définit les attributs devant être rendus accessibles aux objets associés. Pour le type d’attache, fournissez une classe dérivée de ` QObject` qui :
- implémente une méthode 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 appelle cette méthode afin d’associer une instance du type d’objet attaché à l’ élément de destination spécifié par le paramètre `
object`. Il est d’usage, bien que cela ne soit pas strictement obligatoire, que l’implémentation de cette méthode associe l’instance renvoyée à `object` afin 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é associée. Par conséquent, l’objet associé ne peut pas être supprimé tant que l’
objectde l’« attachee » n’est pas détruite. - est déclaré comme type d'attachement, en ajoutant la macro QML_ATTACHED(attached) à la déclaration de classe. L'argument est le nom du type d'objet attaché
Implémentation 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 « Message » lorsqu’il est publié sur un forum, et également de suivre le moment où le message a expiré sur ce forum. Comme il n’est pas logique d’ajouter ces attributs directement à un « Message » (objet de message), ceux-ci étant davantage liés au contexte du forum, ils pourraient être implémentés en tant qu’attributs associés à un objet « Message » (objet de forum) fournis via un qualificateur « MessageBoard ». Selon les concepts décrits précédemment, les parties impliquées ici sont :
- 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’attache utilisé par les objets «Message» pour accéder aux attributs attachés
Voici un exemple d’implémentation. Tout d’abord, il faut disposer d’un type d’objet attaché doté des propriétés et des signaux nécessaires qui seront accessibles à l’ objet 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é, telle qu'implémentée par `MessageBoardAttachedType`. De plus, ` MessageBoard ` doit être déclaré comme 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!")
}De plus, l’implémentation C++ peut accéder à l’instance de l’objet associé qui a été rattaché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 associées
QQuickAttachedPropertyPropagator peut être sous-classé pour propager les propriétés associées d'un objet parent vers ses enfants, à l'instar de la propagation des objets de type « font » et « palette ». Il prend en charge la propagation via « items », « popups » et « windows ».
Types de modificateurs de propriété
Un type de modificateur de propriété est un type d’objet QML particulier. Une instance de type de modificateur de propriété affecte une propriété (d’une instance d’objet QML) à laquelle elle est appliquée. Il existe deux types différents de modificateurs de propriété :
- les intercepteurs d'écriture de valeur de propriété
- sources de valeur de propriété
Un intercepteur d’écriture de valeur de propriété peut être utilisé pour filtrer ou modifier les valeurs au moment où elles sont écrites dans les 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 valeur de propriété. 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 à l’aide de 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, pour l’instant, d’intercepteurs d’écriture de valeur de propriété.
Sources de valeurs de propriété
Les sources de valeur de propriété sont des types QML capables de mettre à jour automatiquement 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 constituent des exemples de sources de valeur de propriété.
Une source de valeur de propriété peut être implémentée en C++ en créant une sous-classe de ` 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> ` en QML, le moteur lui fournit une référence à cette propriété afin que la valeur de celle-ci puisse être mise à jour.
Par exemple, supposons qu’il existe une classe ` RandomNumberGenerator ` destinée à servir de source de valeur de propriété, de sorte que, lorsqu’elle est appliquée à une propriété QML, elle mette à jour la valeur de cette propriété en lui attribuant un nombre aléatoire différent toutes les 500 millisecondes. De plus, une valeur maximale (`maxValue`) peut être fournie à ce générateur de nombres aléatoires. Cette classe peut être implémentée 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 détecte une utilisation de ` RandomNumberGenerator ` comme source de valeur de propriété, il appelle ` RandomNumberGenerator::setTarget() ` pour fournir au type la propriété à laquelle la source de valeur a été appliquée. Lorsque le minuteur interne de ` RandomNumberGenerator ` se déclenche toutes les 500 millisecondes, il é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 depuis QML en tant que source de valeur de propriété. Ci-dessous, elle est utilisée pour modifier la largeur d’un élément ` 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 les autres égards, les sources de valeur de propriété sont des types QML classiques pouvant posséder des propriétés, des signaux, des méthodes, etc., mais avec la capacité supplémentaire de pouvoir être utilisées pour modifier des valeurs de propriété à l’aide de la syntaxe ` <PropertyValueSource> on <property> `.
Lorsqu’un objet source de valeur de propriété est assigné à une propriété, QML tente d’abord de l’assigner normalement, comme s’il s’agissait d’un type QML classique. Ce n’est que si cette assignation échoue que le moteur appelle la méthode setTarget(). Cela permet au type d’être également utilisé dans d’autres contextes que celui de simple source de valeur.
Spécification des propriétés par défaut et des propriétés parentes pour les types d’objets QML
Tout type dérivé de ` QObject` et enregistré en tant que type d’objet QML instanciable peut, de manière facultative, spécifier une propriété par défaut. Une propriété par défaut est la propriété à laquelle les enfants d’un objet sont automatiquement affectés s’ils ne sont affectés à aucune propriété spécifique.
La propriété par défaut peut être définie en appelant la macro ` Q_CLASSINFO()` pour une classe avec une valeur « DefaultProperty » spécifique. Par exemple, la classe ` MessageBoard ` ci-dessous spécifie sa propriété ` messages ` comme propriété par défaut pour 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 affectés à aucune propriété spécifique. Par exemple :
MessageBoard {
Message { author: "Naomi" }
Message { author: "Clancy" }
}Si messages n’était pas définie comme propriété par défaut, tous les objets Message devraient alors être explicitement affectés à la propriété messages, comme suit :
MessageBoard {
messages: [
Message { author: "Naomi" },
Message { author: "Clancy" }
]
}(À noter que la propriété Item::data est sa propriété par défaut. Tout objet Item ajouté à cette propriété data est également ajouté à la liste des Item::children; l’utilisation de la propriété par défaut permet donc de déclarer des enfants visuels pour un élément sans les attribuer explicitement à la propriété children.)
De plus, vous pouvez déclarer une « ParentProperty » Q_CLASSINFO() afin d’indiquer au moteur QML quelle propriété doit désigner l’objet parent dans la hiérarchie QML. Par exemple, le type Message pourrait ê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 de faux positifs sur certains accès aux propriétés.
Définition d'éléments visuels avec le module « Qt Quick »
Lors de la création d’interfaces utilisateur avec le Qt Quick module, tous les objets QML destinés à être affichés à l’écran doivent dériver du type ` Item `, car il s’agit du type de base de tous les objets visuels dans Qt Quick. Ce type ` Item ` est implémenté par la classe C++ ` QQuickItem `, fournie par le Qt Quick module. Par conséquent, il convient de créer une sous-classe de cette classe lorsqu’il est nécessaire d’implémenter en C++ un type visuel pouvant être intégré à une interface utilisateur basée sur QML.
Consultez la documentation d’ QQuickItem pour plus d’informations. De plus, le tutoriel « Écrire des extensions QML avec C++ » montre comment un élément visuel basé sur ` QQuickItem` peut être implémenté en C++ et intégré dans une interface utilisateur basée sur ` Qt Quick`.
Réception de notifications lors de l’initialisation d’un objet
Pour certains types d’objets QML personnalisés, il peut être avantageux 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. Cela peut par exemple être le cas si l’initialisation est coûteuse, ou si elle ne doit pas être effectuée avant que toutes les valeurs de propriété aient été initialisées.
Le Qt Qml module fournit l’ QQmlParserStatus, qui doit être sous-classée à ces fins. Il définit un certain nombre de méthodes virtuelles qui sont appelées à différentes étapes lors de l’instanciation du composant. Pour recevoir ces notifications, une classe C++ doit hériter de l’ QQmlParserStatus et informer le méta-système Qt à l’aide de la macro Q_INTERFACES().
Par 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 « Présentation - Intégration QML et 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.