Le système de propriétés
Qt fournit un système de propriétés sophistiqué, similaire à ceux proposés par certains éditeurs de compilateurs. Cependant, en tant que bibliothèque indépendante du compilateur et de la plate-forme, Qt ne repose pas sur des fonctionnalités 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 repose sur le système de méta-objets, qui assure également la communication entre objets via des signaux et des slots.
Conditions requises pour la déclaration de propriétés
Pour déclarer une propriété, utilisez la macro Q_PROPERTY() dans une classe héritant 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és 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 illustrant comment exporter des variables membres en tant que propriétés Qt à 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 dispose de fonctionnalités supplémentaires accessibles via le système de méta-objets.
- Une fonction d’accès de type «
READ» est requise si aucune variable «MEMBER» n’a été spécifiée. Elle sert à lire la valeur de la propriété. Idéalement, on utilise une fonction const à cette fin, et celle-ci 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 une fonction d'READ, QWidget::hasFocus(). Si uneBINDABLEest spécifiée, vous pouvez écrireREAD defaultpour que l'accesseurREADsoit généré à partir deBINDABLE. - La fonction d’accès
WRITEest facultative. Elle sert à définir la valeur de la propriété. Elle doit renvoyer `void` et prendre exactement un argument, soit du type de la propriété, soit un pointeur ou une référence vers ce type. Par exemple, QWidget::enabled dispose de la fonctionWRITEQWidget::setEnabled (). Les propriétés en lecture seule ne nécessitent pas de fonctions d’WRITE. Par exemple, QWidget::focus ne dispose pas de la fonctionWRITE. Si vous spécifiez à la fois une fonctionBINDABLEet une fonctionWRITE default, une fonction d’accèsWRITEsera générée à partir de la fonctionBINDABLE. La fonction d’accèsWRITEgénérée n’ émettra pas explicitement de signal déclaré avecNOTIFY. Vous devez enregistrer le signal en tant que gestionnaire de modification auprès de la fonctionBINDABLE, par exemple à l’aide de Q_OBJECT_BINDABLE_PROPERTY. - Une association de variable de type «
MEMBER» est requise si aucune fonction d’accès «READ» n’est spécifiée. Cela rend la variable membre donnée lisible et modifiable sans qu’il soit nécessaire de créer des fonctions d’accès de type «READ» et «WRITE». Il est toujours possible d’utiliser des fonctions d’accès de type «READ» ou «WRITE» en plus de l’association de variable «MEMBER» (mais pas les deux), si vous avez besoin de contrôler l’accès à la variable. - Une fonction
RESETest facultative. Elle sert à rétablir la valeur par défaut de la propriété, spécifique au contexte. Par exemple, QWidget::cursor dispose des fonctionsREADetWRITEclassiques, QWidget::cursor() et QWidget::setCursor(), ainsi que d’une fonctionRESET, QWidget::unsetCursor(), car l’absence d’appel à QWidget::setCursor() peut signifier le retour au curseur spécifique au contexte. La fonctionRESETdoit renvoyer void et ne prendre aucun paramètre. - Un signal «
NOTIFY» est 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 signaux «NOTIFY» pour les variables «MEMBER» doivent prendre zéro ou un paramètre, qui doit être du même type que la propriété. Le paramètre prendra la nouvelle valeur de la propriété. Le signal `NOTIFY` ne doit être émis que lorsque la propriété a réellement été modifiée, afin d’éviter, par exemple, que les liaisons ne soient réévaluées inutilement dans QML. Ce signal est émis automatiquement lorsque la propriété est modifiée via l’API Qt (QObject::setProperty, QMetaProperty, etc.), mais pas lorsque le `MEMBER` est modifié directement. - Un nombre de version (
REVISION) ou une macro de version (REVISION()) est facultatif. S’il est inclus, il 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 «
DESIGNABLE» indique si la propriété doit être visible dans l’éditeur de propriétés de l’outil de conception d’interface graphique (par exemple, Qt Widgets Designer). La plupart des propriétés sont «DESIGNABLE» (valeur par défaut : true). Les valeurs valides sont « true » et « false ». - L’attribut `
SCRIPTABLE` indique si cette propriété doit être accessible par un moteur de script (valeur par défaut : `true`). Les valeurs valides sont `true` et `false`. - L’attribut `
STORED` indique si la propriété doit être considérée comme existant de manière autonome ou comme dépendant d’autres valeurs. Il indique également si la valeur de la propriété doit être enregistrée lors de la sauvegarde de l’état de l’objet. La plupart des propriétés sont «STORED» (par défaut : true), mais par exemple, QWidget::minimumWidth() a «STORED» false, car sa valeur est simplement tirée de la composante « width » de la propriété QWidget::minimumSize(), qui est une « QSize ». - L’attribut «
USER» indique si la propriété est désignée comme propriété visible par l’utilisateur ou modifiable par l’utilisateur pour la classe. Normalement, il n’y a qu’une seule propriété «USER» par classe (valeur par défaut : « false »). Par exemple, « QAbstractButton::checked » est la propriété modifiable par l’utilisateur pour les boutons (cochables). Notez que « QItemDelegate » permet d’obtenir et de définir la propriété «USER» d’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 vers cette propriété via le système de méta-objets (QMetaProperty).bindablePropertydésigne un membre de classe de type QBindable<T>, où T est le type de la propriété. Cet attribut a été introduit dans Qt 6.0. - La présence de l’attribut `
CONSTANT` indique que la valeur de la propriété est constante. Pour une instance donnée de l’objet, 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 varier d’une instance à l’autre de l’objet. Une propriété constante ne peut pas disposer d’une méthode `WRITE` ni d’un signal `NOTIFY`. FINALLes modificateurs `VIRTUAL` et `OVERRIDE` reflètent la sémantique de leurs équivalents en C++ et QML, permettant de rendre explicite la redéfinition des propriétés au niveau des méta-objets.Remarque : À l’heure actuelle, ces modificateurs ne sont pas imposés par moc. Ils sont reconnus sur le plan syntaxique et sont principalement utilisés pour l’application QML Runtime et les diagnostics des outils. Les versions futures pourraient introduire une validation plus stricte lors de la compilation ainsi que des avertissements en cas de redéfinitions non valides entre modules.
Remarque : si vous souhaitez modifier le comportement d’accès d’une propriété, utilisez le polymorphisme fourni par le C++.
- La présence de l’attribut `
REQUIRED` indique que la propriété doit être définie par un utilisateur de la classe. Ceci n’est pas imposé par moc et s’avère surtout utile pour les classes exposées à QML. En QML, les classes comportant des propriétés `REQUIRED` ne peuvent pas être instanciées tant que toutes les propriétés `REQUIRED` n’ont pas é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 utilisant l’héritage multiple, 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 bien 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)QDate étant un type défini par l’utilisateur, vous devez inclure le fichier d’en-tête <QDate> lors de la déclaration de la propriété.
Pour des raisons historiques, les types de propriété ` QMap ` et ` QList ` sont synonymes de ` QVariantMap ` et ` QVariantList`.
Lecture et écriture des propriétés avec le système de 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 savoir 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é via son accesseur WRITE est la meilleure des deux solutions, car il est plus rapide et offre un meilleur diagnostic au moment de la compilation, mais la définition de la propriété de cette manière nécessite de connaître 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 méthodes 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 de code ci-dessus, la méthode ` QMetaObject::property()` est utilisée pour obtenir des ` metadata ` sur chaque propriété définie dans une classe inconnue. Le nom de la propriété est extrait des métadonnées et transmis à la méthode ` QObject::property()` afin d’obtenir la ` value ` de la propriété dans l’ object` actuel.
Un exemple simple
Supposons que nous ayons une classe MyClass, qui dérive de QObject et qui utilise la macro Q_OBJECT. Nous souhaitons déclarer une propriété dans MyClass afin de suivre une valeur de priorité. Le nom de la propriété sera priority, et son type sera un type d’énumération nommé Priority, défini dans MyClass.
Nous déclarons la propriété à l’aide de la macro ` Q_PROPERTY()` dans la section privée de la classe. La fonction ` READ ` requise s’appelle ` priority`, et nous incluons une fonction ` WRITE ` nommée ` setPriority`. Le type d’énumération doit être enregistré auprès du système de méta-objets à l’aide de la macro ` Q_ENUM()`. L’enregistrement d’un type d’énumération rend les noms des éléments de l’énumération 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 se présenter comme suit :
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 de type const et renvoie le type de propriété. La fonction WRITE renvoie void et possède exactement un paramètre de type propriété. Le Meta-Object Compiler veille au respect de ces exigences. Le contrôle d’égalité dans la fonction WRITE, bien que non obligatoire, constitue une bonne pratique, car il est inutile de signaler un changement et de forcer potentiellement une réévaluation à d’autres endroits si rien n’a changé.
Étant donné un pointeur vers une instance de ` MyClass ` ou un pointeur vers un ` QObject ` qui est une instance de ` MyClass`, nous disposons de 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 constitue le type de propriété est déclaré dans ` MyClass ` et enregistré auprès du système de méta-objets à l’aide de la macro ` Q_ENUM()`. Cela rend les valeurs de l’énumération disponibles sous forme de chaînes de caractères, pouvant ainsi être utilisées comme dans l’appel à ` setProperty()`. Si le type d’énumération avait été déclaré dans une autre classe, son nom complet (c’est-à-dire OtherClass::Priority) aurait été requis, et cette autre classe aurait également dû hériter d’ QObject, puis y enregistrer le type d’énumération à l’aide de la macro Q_ENUM().
Une macro similaire, Q_FLAG(), est également disponible. À l’instar de Q_ENUM(), elle enregistre un type d’énumération, mais elle marque ce type comme étant un ensemble de drapeaux, c’est-à-dire des valeurs pouvant être combinées par un opérateur OR. Une classe d’E/S peut avoir des valeurs d’énumération telles que Read et Write; dans ce cas, la fonction QObject::setProperty() pourrait accepter Read | Write. Il convient d’utiliser la fonction Q_FLAG() pour enregistrer ce type d’énumération.
Propriétés dynamiques
QObject::setProperty() peut également être utilisée pour ajouter de nouvelles propriétés à une instance d’une classe lors 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 l’ 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 la valeur true est renvoyée. Si la valeur n’ est pas compatible avec le type de la propriété, la propriété n’ est pas modifiée et la valeur false est renvoyée. Mais si la propriété portant le nom indiqué n’existe pas dans l’ 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 indiqués est automatiquement ajoutée à l’ QObject, mais la valeur `false` est tout de même renvoyée. Cela signifie qu’un retour de « false » ne peut pas être utilisé pour déterminer si une propriété particulière a bien été définie, à moins que vous ne sachiez à l’avance que cette propriété existe déjà dans l’ QObject.
Notez que les propriétés dynamiques sont ajoutées au cas par cas, 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 invalide de QVariant à QObject::setProperty(). Le constructeur par défaut de QVariant construit un QVariant invalide.
Les propriétés dynamiques peuvent être interrogées à l'aide de ` QObject::property()`, tout comme les propriétés déclarées à la compilation à l'aide de ` 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 des objets QVariant. Cela les rend adaptés à une utilisation aussi bien avec les propriétés statiques déclarées à l’aide de la macro Q_PROPERTY() dans les définitions de classe qu’avec les propriétés dynamiques créées lors de l’exécution.
Ajout d’informations supplémentaires à une classe
Une macro supplémentaire, Q_CLASSINFO(), est associée au système de propriétés ; elle permet d’ajouter des pairesnom-valeur supplémentaires au méta-objet d’une classe. Elle sert par exemple à marquer une propriété comme propriété par défaut dans le contexte de QML Object Types:
Q_CLASSINFO("DefaultProperty", "content")À l’instar des autres métadonnées, les informations relatives aux classes sont accessibles à l’exécution via le 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 implémenter des propriétés liables :
Le premier est une classe générale pour les propriétés liables. Les deux autres ne peuvent être utilisés qu’au sein d’un QObject.
Pour plus d’informations, y compris des exemples, consultez les classes mentionnées ci-dessus ainsi que les conseils généraux sur l’implémentation et l’utilisation des propriétés liables.
Voir également Système de méta-objets, Signaux et slots, Q_DECLARE_METATYPE(), QMetaType, QVariant, Propriétés liables Qt et Définition de types QML à partir de C++.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.