Le système de propriétés
Qt fournit un système de propriétés sophistiqué similaire à ceux fournis par certains vendeurs de compilateurs. Cependant, en tant que bibliothèque indépendante du compilateur et de la plate-forme, Qt ne s'appuie pas sur des fonctions de compilateur non standard telles que __property ou [property]. La solution Qt fonctionne avec n'importe quel compilateur C++ standard sur toutes les plates-formes prises en charge par Qt. Elle est basée sur le Meta-Object System qui fournit également une communication inter-objets via des signaux et des slots.
Exigences pour la déclaration des propriétés
Pour déclarer une propriété, utilisez la macro Q_PROPERTY() dans une classe qui hérite de QObject.
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int | REVISION(int[, int])]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[BINDABLE bindableProperty]
[CONSTANT]
[FINAL]
[VIRTUAL]
[OVERRIDE]
[REQUIRED])Voici quelques exemples typiques de déclarations de propriétés tirées de la classe QWidget.
Q_PROPERTY(bool focus READ hasFocus) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
Voici un exemple montrant comment exporter des variables membres en tant que propriétés Qt XML à l'aide du mot-clé MEMBER. Notez qu'un signal NOTIFY doit être spécifié pour permettre les liaisons de propriétés QML.
Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged) Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged) Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged) //... signals: void colorChanged(); void spacingChanged(); void textChanged(const QString &newText); private: QColor m_color; qreal m_spacing; QString m_text;
Une propriété se comporte comme un membre de données de classe, mais elle possède des caractéristiques supplémentaires accessibles par l'intermédiaire du système de méta-objets.
- Une fonction d'accès
READest nécessaire si aucune variableMEMBERn'a été spécifiée. Elle permet de lire la valeur de la propriété. Idéalement, une fonction const est utilisée à cette fin et doit renvoyer soit le type de la propriété, soit une référence const à ce type. Par exemple, QWidget::focus est une propriété en lecture seule avec la fonctionREAD, QWidget::hasFocus(). Si unBINDABLEest spécifié, vous pouvez écrireREAD defaultpour que l'accesseurREADsoit généré à partir duBINDABLE. - La fonction d'accès
WRITEest facultative. Elle sert à définir la valeur de la propriété. Elle doit retourner void et doit prendre exactement un argument, soit du type de la propriété, soit un pointeur ou une référence à ce type. Par exemple, QWidget::enabled a la fonctionWRITEQWidget::setEnabled (). Les propriétés en lecture seule n'ont pas besoin de fonctionsWRITE. Par exemple, QWidget::focus n'a pas de fonctionWRITE. Si vous spécifiez à la foisBINDABLEetWRITE default, un accesseurWRITEsera généré à partir deBINDABLE. L'accesseurWRITEgénéré n' émettra pas explicitement de signal déclaré avecNOTIFY. Vous devez enregistrer le signal en tant que gestionnaire de changement pourBINDABLE, par exemple en utilisant Q_OBJECT_BINDABLE_PROPERTY. - Une association de variable
MEMBERest nécessaire si aucune fonction d'accèsREADn'est spécifiée. Cela rend la variable membre donnée lisible et inscriptible sans qu'il soit nécessaire de créer les fonctions d'accèsREADetWRITE. Il est toujours possible d'utiliser les fonctions d'accèsREADouWRITEen plus de l'association de variablesMEMBER(mais pas les deux), si vous devez contrôler l'accès à la variable. - La fonction
RESETest facultative. Par exemple, QWidget::cursor possède les fonctions typiquesREADetWRITE, QWidget::cursor() et QWidget::setCursor(), ainsi qu'une fonctionRESET, QWidget::unsetCursor(), car aucun appel à QWidget::setCursor() ne peut signifier la réinitialisation à la valeur par défaut spécifique au contexte. La fonctionRESETdoit retourner void et ne prendre aucun paramètre. - Le signal
NOTIFYest facultatif. S'il est défini, il doit spécifier un signal existant dans cette classe qui est émis chaque fois que la valeur de la propriété change. Les signauxNOTIFYpour les variablesMEMBERdoivent prendre zéro ou un paramètre, qui doit être du même type que la propriété. Le paramètre prend la nouvelle valeur de la propriété. Le signalNOTIFYne doit être émis que lorsque la propriété a réellement été modifiée, afin d'éviter que les liaisons ne soient inutilement réévaluées dans QML, par exemple. Le signal est émis automatiquement lorsque la propriété est modifiée via l'API Qt (QObject::setProperty, QMetaProperty, etc.), mais pas lorsque le MEMBRE est modifié directement. - Un numéro
REVISIONou une macroREVISION()est facultatif. Si elle est incluse, elle définit la propriété et son signal de notification à utiliser dans une révision particulière de l'API (généralement pour l'exposition à QML). S'il n'est pas inclus, la valeur par défaut est 0. - L'attribut
DESIGNABLEindique si la propriété doit être visible dans l'éditeur de propriétés de l'outil de conception GUI (par ex, Qt Widgets Designer). La plupart des propriétés sontDESIGNABLE(true par défaut). Les valeurs valides sont true et false. - L'attribut
SCRIPTABLEindique si cette propriété doit être accessible par un moteur de script (true par défaut). Les valeurs valides sont true et false. - L'attribut
STOREDindique si la propriété doit être considérée comme existant seule ou comme dépendant d'autres valeurs. Il indique également si la valeur de la propriété doit être sauvegardée lors du stockage de l'état de l'objet. La plupart des propriétés sontSTORED(true par défaut), mais par exemple, QWidget::minimumWidth() aSTOREDfalse, parce que sa valeur est juste prise de la composante width de la propriété QWidget::minimumSize(), qui est une QSize. - L'attribut
USERindique si la propriété est désignée comme la propriété orientée utilisateur ou la propriété éditable par l'utilisateur pour la classe. Normalement, il n'y a qu'une seule propriétéUSERpar classe (par défaut, false). Par exemple, QAbstractButton::checked est la propriété modifiable par l'utilisateur pour les boutons (à cocher). Notez que QItemDelegate obtient et définit la propriétéUSERd'un widget. - L'attribut
BINDABLE bindablePropertyindique que la propriété prend en charge les liaisons et qu'il est possible de définir et d'inspecter les liaisons avec cette propriété via le système de métaobjets (QMetaProperty).bindablePropertynomme un membre de classe de type QBindable<T>, où T est le type de propriété. Cet attribut a été introduit dans Qt 6.0. - La présence de l'attribut
CONSTANTindique que la valeur de la propriété est constante. Pour une instance d'objet donnée, la méthode READ d'une propriété constante doit renvoyer la même valeur chaque fois qu'elle est appelée. Cette valeur constante peut être différente pour différentes instances de l'objet. Une propriété constante ne peut pas avoir de méthode WRITE ni de signal NOTIFY. FINALLes modificateurs C++,VIRTUAL,OVERRIDEreflètent la sémantique de leurs homologues C++ et QML, ce qui permet de rendre explicite la superposition de propriétés au niveau du méta-objet.Note : Actuellement, ces modificateurs ne sont pas appliqués par moc. Ils sont reconnus syntaxiquement et sont principalement utilisés pour l'application de QML Runtime et les diagnostics de l'outil. Les versions futures pourront introduire une validation plus stricte au moment de la compilation et des avertissements pour les surcharges non valides dans les modules.
Remarque : si vous souhaitez modifier le comportement d'accès à une propriété, utilisez le polymorphisme fourni par C++.
- La présence de l'attribut
REQUIREDindique que la propriété doit être définie par un utilisateur de la classe. Ceci n'est pas appliqué par moc, et est surtout utile pour les classes exposées à QML. En QML, les classes ayant des propriétés REQUIRED ne peuvent être instanciées que si toutes les propriétés REQUIRED ont été définies.
Les fonctions READ, WRITE et RESET peuvent être héritées. Elles peuvent également être virtuelles. Lorsqu'elles sont héritées dans des classes où l'héritage multiple est utilisé, elles doivent provenir de la première classe héritée.
Le type de propriété peut être n'importe quel type pris en charge par QVariant ou un type défini par l'utilisateur. Dans cet exemple, la classe QDate est considérée comme un type défini par l'utilisateur.
Q_PROPERTY(QDate date READ getDate WRITE setDate)Comme QDate est un type défini par l'utilisateur, vous devez inclure le fichier d'en-tête <QDate> dans la déclaration de propriété.
Pour des raisons historiques, les types de propriétés QMap et QList sont synonymes de QVariantMap et QVariantList.
Lecture et écriture des propriétés avec le système des méta-objets
Une propriété peut être lue et écrite à l'aide des fonctions génériques QObject::property() et QObject::setProperty(), sans rien connaître de la classe propriétaire, si ce n'est le nom de la propriété. Dans l'extrait de code ci-dessous, l'appel à QAbstractButton::setDown() et l'appel à QObject::setProperty() définissent tous deux la propriété "down".
QPushButton *button = new QPushButton; QObject *object = button; button->setDown(true); object->setProperty("down", true);
L'accès à une propriété par l'intermédiaire de son accesseur WRITE est la meilleure des deux méthodes, car elle est plus rapide et fournit de meilleurs diagnostics au moment de la compilation, mais la définition de la propriété de cette manière exige que vous connaissiez la classe au moment de la compilation. L'accès aux propriétés par leur nom vous permet d'accéder à des classes que vous ne connaissez pas au moment de la compilation. Vous pouvez découvrir les propriétés d'une classe au moment de l'exécution en interrogeant ses propriétés QObject, QMetaObject et QMetaProperties.
QObject *object = new QObject; const QMetaObject *metaobject = object->metaObject(); int count = metaobject->propertyCount(); for (int i=0; i<count; ++i) { QMetaProperty metaproperty = metaobject->property(i); const char *name = metaproperty.name(); QVariant value = object->property(name); //... }
Dans l'extrait ci-dessus, QMetaObject::property() est utilisé pour obtenir metadata à propos de chaque propriété définie dans une classe inconnue. Le nom de la propriété est extrait des métadonnées et transmis à QObject::property() pour obtenir la value de la propriété dans la object actuelle.
Un exemple simple
Supposons que nous ayons une classe MyClass, qui est dérivée de QObject et qui utilise la macro Q_OBJECT. Nous voulons déclarer une propriété dans MyClass pour garder trace d'une valeur de priorité. Le nom de la propriété sera priority, et son type sera un type d'énumération nommé Priority, qui est défini dans MyClass.
Nous déclarons la propriété avec la macro Q_PROPERTY() dans la section privée de la classe. La fonction READ requise est nommée priority, et nous incluons une fonction WRITE nommée setPriority. Le type d'énumération doit être enregistré auprès du Meta-Object System à l'aide de la macro Q_ENUM(). L'enregistrement d'un type d'énumération rend les noms des énumérateurs disponibles pour être utilisés dans les appels à QObject::setProperty(). Nous devons également fournir nos propres déclarations pour les fonctions READ et WRITE. La déclaration de MyClass pourrait alors ressembler à ceci :
class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) public: MyClass(QObject *parent = nullptr); ~MyClass(); enum Priority { High, Low, VeryHigh, VeryLow }; Q_ENUM(Priority) void setPriority(Priority priority) { if (m_priority == priority) return; m_priority = priority; emit priorityChanged(priority); } Priority priority() const { return m_priority; } signals: void priorityChanged(Priority); private: Priority m_priority; };
La fonction READ est const et renvoie le type de propriété. La fonction WRITE renvoie void et a exactement un paramètre du type de propriété. Le Meta-Object Compiler applique ces exigences. La vérification de l'égalité dans la fonction WRITE, bien qu'elle ne soit pas obligatoire, est une bonne pratique car il n'est pas utile de notifier et de potentiellement forcer une réévaluation à d'autres endroits si rien n'a changé.
Étant donné un pointeur sur une instance de MyClass ou un pointeur sur un QObject qui est une instance de MyClass, nous avons deux façons de définir sa propriété de priorité :
MyClass *myinstance = new MyClass; QObject *object = myinstance; myinstance->setPriority(MyClass::VeryHigh); object->setProperty("priority", "VeryHigh");
Dans l'exemple, le type d'énumération qui est le type de propriété est déclaré dans MyClass et enregistré dans le Meta-Object System à l'aide de la macro Q_ENUM(). Les valeurs de l'énumération sont ainsi disponibles sous forme de chaînes de caractères et peuvent être utilisées comme dans l'appel à setProperty(). Si le type d'énumération avait été déclaré dans une autre classe, son nom pleinement qualifié (c'est-à-dire, OtherClass::Priority) serait nécessaire, et cette autre classe devrait également hériter de QObject et y enregistrer le type d'énumération à l'aide de la macro Q_ENUM().
Une macro similaire, Q_FLAG(), est également disponible. Comme Q_ENUM(), elle enregistre un type d'énumération, mais elle indique que le type est un ensemble de drapeaux, c'est-à-dire des valeurs qui peuvent être combinées par OU. Une classe d'E/S peut avoir les valeurs d'énumération Read et Write, puis QObject::setProperty() peut accepter Read | Write. Q_FLAG() doit être utilisé pour enregistrer ce type d'énumération.
Propriétés dynamiques
QObject::setProperty() peut également être utilisé pour ajouter de nouvelles propriétés à une instance d'une classe au moment de l'exécution. Lorsqu'elle est appelée avec un nom et une valeur, si une propriété portant le nom donné existe dans QObject, et si la valeur donnée est compatible avec le type de la propriété, la valeur est stockée dans la propriété, et true est renvoyé. Si la valeur n'est pas compatible avec le type de la propriété, la propriété n' est pas modifiée et false est renvoyé. Mais si la propriété portant le nom donné n'existe pas dans QObject (c'est-à-dire si elle n'a pas été déclarée avec Q_PROPERTY()), une nouvelle propriété portant le nom et la valeur donnés est automatiquement ajoutée à QObject, mais false est toujours renvoyé. Cela signifie qu'un retour de false ne peut pas être utilisé pour déterminer si une propriété particulière a effectivement été définie, à moins que vous ne sachiez à l'avance que la propriété existe déjà dans le fichier QObject.
Notez que les propriétés dynamiques sont ajoutées à chaque instance, c'est-à-dire qu'elles sont ajoutées à QObject, et non à QMetaObject. Une propriété peut être supprimée d'une instance en passant le nom de la propriété et une valeur QVariant invalide à QObject::setProperty(). Le constructeur par défaut de QVariant construit un QVariant invalide.
Les propriétés dynamiques peuvent être interrogées avec QObject::property(), tout comme les propriétés déclarées à la compilation avec Q_PROPERTY().
Propriétés et types personnalisés
Les types personnalisés utilisés par les propriétés doivent être enregistrés à l'aide de la macro Q_DECLARE_METATYPE() afin que leurs valeurs puissent être stockées dans les objets QVariant. Ils peuvent donc être utilisés à la fois avec les propriétés statiques déclarées à l'aide de la macro Q_PROPERTY() dans les définitions de classe et avec les propriétés dynamiques créées au moment de l'exécution.
Ajout d'informations supplémentaires à une classe
Une macro supplémentaire, Q_CLASSINFO(), est connectée au système de propriétés et peut être utilisée pour attacher des paires nom-valeur supplémentaires au méta-objet d'une classe. Cette macro est utilisée par exemple pour marquer une propriété comme étant la propriété par défaut dans le contexte des types d'objets QML:
Q_CLASSINFO("DefaultProperty", "content")
Comme d'autres métadonnées, les informations sur les classes sont accessibles à l'exécution par l'intermédiaire du méta-objet ; voir QMetaObject::classInfo() pour plus de détails.
Utilisation des propriétés liables
Trois types différents peuvent être utilisés pour mettre en œuvre des propriétés liables :
Le premier est une classe générale pour les propriétés liantes. Les deux derniers ne peuvent être utilisés qu'à l'intérieur d'une QObject.
Pour plus d'informations, y compris des exemples, voir les classes mentionnées ci-dessus et les conseils généraux sur l'implémentation et l'utilisation des propriétés liantes.
Voir aussi Meta-Object System, Signals and Slots, Q_DECLARE_METATYPE(), QMetaType, QVariant, Qt Bindable Properties, et Defining QML Types from 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.