Sur cette page

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 READ est nécessaire si aucune variable MEMBER n'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 fonction READ, QWidget::hasFocus(). Si un BINDABLE est spécifié, vous pouvez écrire READ default pour que l'accesseur READ soit généré à partir du BINDABLE.
  • La fonction d'accès WRITE est 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 fonction WRITE QWidget::setEnabled (). Les propriétés en lecture seule n'ont pas besoin de fonctions WRITE. Par exemple, QWidget::focus n'a pas de fonction WRITE. Si vous spécifiez à la fois BINDABLE et WRITE default, un accesseur WRITE sera généré à partir de BINDABLE. L'accesseur WRITE généré n' émettra pas explicitement de signal déclaré avec NOTIFY. Vous devez enregistrer le signal en tant que gestionnaire de changement pour BINDABLE, par exemple en utilisant Q_OBJECT_BINDABLE_PROPERTY.
  • Une association de variable MEMBER est nécessaire si aucune fonction d'accès READ n'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ès READ et WRITE. Il est toujours possible d'utiliser les fonctions d'accès READ ou WRITE en plus de l'association de variables MEMBER (mais pas les deux), si vous devez contrôler l'accès à la variable.
  • La fonction RESET est facultative. Par exemple, QWidget::cursor possède les fonctions typiques READ et WRITE, QWidget::cursor() et QWidget::setCursor(), ainsi qu'une fonction RESET, QWidget::unsetCursor(), car aucun appel à QWidget::setCursor() ne peut signifier la réinitialisation à la valeur par défaut spécifique au contexte. La fonction RESET doit retourner void et ne prendre aucun paramètre.
  • Le 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 prend 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 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 REVISION ou une macro REVISION() 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 DESIGNABLE indique 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 sont DESIGNABLE (true par défaut). Les valeurs valides sont true et false.
  • L'attribut SCRIPTABLE indique 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 STORED indique 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 sont STORED (true par défaut), mais par exemple, QWidget::minimumWidth() a STORED false, parce que sa valeur est juste prise 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 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é USER par 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é USER d'un widget.
  • L'attribut BINDABLE bindableProperty indique 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). bindableProperty nomme 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 CONSTANT indique 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, OVERRIDE reflè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 REQUIRED indique 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.