Developpez.com

Club des développeurs et IT pro
Plus de 4 millions de visiteurs uniques par mois

Developpez.com - Qt
X

Choisissez d'abord la catégorieensuite la rubrique :

Viadeo Twitter Facebook Share on Google+   
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

Le système de propriétés de Qt

Qt fournit un système sophistiqué de propriétés semblable à celui qui est mis à disposition par quelques créateurs de compilateurs. Toutefois, comme compilateur et comme bibliothèque indépendante de la plateforme, Qt ne se fonde pas sur les dispositifs non standards de compilateurs comme __property ou [property]. La solution de Qt fonctionne avec n'importe quel compilateur standard de C++ et sous toutes les plateformes supportées par Qt. Elle est basée sur le système de méta-objets qui fournit aussi une communication inter-objets via les signaux et les slots.

Conditions de déclaration de propriétés

Pour déclarer une propriété, utiliser la macro Q_PROPERTY() dans une classe héritant de QObject.

Q_PROPERTY(type name
           READ getFunction
           [WRITE setFunction]
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [CONSTANT]
           [FINAL])

Voici des exemples typiques de déclarations de propriétés, extraits de 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)

Une propriété se comporte comme une donnée de membre d'une classe, mais elle possède des dispositifs additionnels accessibles depuis le système de méta-objets.

  • Une fonction d'accès en lecture, READ, est requise. Elle permet de lire la valeur de la propriété. Idéalement, une fonction constante est utilisée à ce but, et doit retourner soit le type de la propriété, soit un pointeur, soit une référence à ce type. Par exemple, QWidget::focus est une propriété en lecture seule avec la fonction READ, QWidget::hasFocus().
  • Une fonction d'accès optionnelle en écriture, WRITE. Elle sert à définir la valeur d'une propriété. Elle doit retourner void et doit prendre exactement un argument, soit le type de la propriété, soit un pointeur, soit une référence à ce type. Par exemple, QWidget::enabled possède 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.
  • Une fonction de réinitialisation optionnelle, RESET. Elle sert à redéfinir la propriété à sa valeur par défaut, spécifique au contexte. Par exemple, QWidget::cursor possède les fonctions typiques READ et WRITE, QWidget::cursor() et QWidget::setCursor(), et possède aussi une fonction RESET, QWidget::unsetCursor(), puisque ne pas faire appel à QWidget::setCursor() peut signifier réinitialiser au curseur du contexte spécifique. La fonction RESET doit retourner void et ne pas prendre de paramètres.
  • Un signal de notification optionnel, NOTIFY. Si défini, il devrait spécifier un signal existant dans cette classe qui est émis au moment où la valeur de la propriété change.
  • L'attribut DESIGNABLE indique si la propriété peut être visible dans l'éditeur de propriétés d'un outil de design Qt (par exemple, Qt Designer). La plupart des propriétés sont DESIGNABLE (par défaut à true). A la place de true ou false, vous pouvez spécifier une fonction membre booléenne.
  • L'attribut SCRIPTABLE indique si la propriété peut être accessible par un moteur de script (par défaut à true). À la place de true ou false, vous pouvez spécifier une fonction membre booléenne.
  • L'attribut STORED indique si la propriété devrait être considérée comme indépendante ou dépendante d'autres valeurs. Il indique aussi si la valeur de la propriété doit être enregistrée lors du stockage du statut de l'objet. La plupart des propriétés sont STORED (par défaut à true), mais par exemple, STORED de QWidget::minimumWidth() est à false, car sa valeur est juste prise depuis le composant de longueur de la propriété QWidget::minimumSize(), qui est un QSize.
  • L'attribut USER indique si la propriété est désignée en tant que propriété user-interactive ou bien éditable par l'utilisateur pour la classe. Normalement, il y a uniquement une seule propriété USER par classe (par défaut à false). Par exemple, QAbstractButton::checked est la propriété éditable par l'utilisateur pour les boutons (pouvant être cochés). Notez que QItemDelegate obtient et définit une propriété USER de widget.
  • La présence de l'attribut CONSTANT indique que la propriété doit être constante. Pour une instance d'objet donnée, la méthode READ d'une propriété constante doit retourner la même valeur à chaque fois qu'elle est appelée. Cette valeur constante peut être différente pour de différentes instances de l'objet. Une propriété constante ne peut pas avoir une méthode WRITE ou un signal NOTIFY.
  • La présence de l'attribut FINAL indique que la propriété ne sera pas ignorée par une classe dérivée. Il peut être utilisé pour des optimisations de performances dans quelques classes, mais n'est pas mis en vigueur par le moc. Il faut faire attention à ne jamais ignorer une propriété FINAL.

Les propriétés READ, WRITE et RESET peuvent être héritées et peuvent aussi être virtuelles. Lorsqu'elles sont héritées dans des classes où l'instanciation multiple est utilisée, elles doivent provenir de la première classe héritée.

Le type de propriété peut être n'importe lequel des types supportés par QVariant, ou il peut être 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)

Parce que QDate est un type défini par l'utilisateur, vous devez inclure le fichier d'en-tête <QDate> avec la déclaration de propriété.

Pour les propriétés de QMap, QList et QValueList, la valeur est un QVariant dont la valeur peut être la liste entière ou la map. Notez que la chaîne de caractères de Q_PROPERTY ne peut contenir de virgule, car les virgules séparent les arguments de macros. Par conséquent, vous devez utiliser QMap comme le type de propriété à la place de QMap<QString, QVariant>. Pour la cohérence, utilisez aussi QList et QValueList à la place de QList<QVariant> et QValueList<QVariant>.

Propriétés de lecture et d'écriture avec le système de méta-objets

Une propriété peut être lue et écrite par l'utilisation des fonctions génériques QObject::property() et QObject::setProperty(), sans savoir quoique ce soit sur la classe possesseur, excepté le nom de la propriété. Dans l'extrait de code situé ci-dessous, les appels de QAbstractButton::setDown() et de QObject::setProperty() définissent 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 le biais de son accesseur WRITE est la meilleure des deux solutions car cela représente un moyen plus rapide et car cela donne de meilleurs diagnostics au moment de la compilation, mais la définition d'une propriété de cette manière nécessite ce que vous savez à propos de la classe au moment de la compilation. L'accès aux propriétés par leur nom vous laisse accéder au moment de la compilation aux classes que vous ne connaissez pas. Vous pouvez découvrir une propriété de classe lors de l'exécution par la requête de son QObject, QMetaObject et QMetaProperties.

QObject *object = ...
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 des métadonnées à propos de chaque propriété définie dans une certaine classe inconnue. Le nom de la propriété est cherché depuis la métadonnée puis passé au QObject::property() pour obtenir la valeur de la propriété dans l'objet actuel.

Un exemple simple

Supposons que nous avons une classe MyClass, dérivée de QObject et utilisant la macro Q_OBJECT dans sa section privée. Nous voulons déclarer une propriété dans MyClass pour garder une trace de la valeur prioritaire. Le nom de cette propriété sera priority, et son type sera une énumération nommée Priority, qui est définie dans MyClass.

Nous déclarons la propriété avec la macro Q_PROPERTY() déclarée dans la section privée de la classe. La fonction nécessaire READ est nommée priority, et nous incluons une fonction WRITE nommée setPriority. L'énumération doit être enregistrée avec le système de méta-objets en utilisant la macro Q_ENUMS(). L'enregistrement d'une énumération rend les noms d'énumérateurs disponibles à l'usage pour les appels de QObject::setProperty(). Nous devons aussi fournir nos propres déclarations pour les fonctions READ et WRITE. La déclaration de MyClass doit donc ressembler à cela :

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)
    Q_ENUMS(Priority)
 
public:
    MyClass(QObject *parent = 0);
    ~MyClass();
 
    enum Priority { High, Low, VeryHigh, VeryLow };
 
    void setPriority(Priority priority);
    Priority priority() const;
};

La fonction READ est constante et retourne le type de la propriété. La fonction WRITE retourne void et possède exactement un paramètre du type de la propriété. Le compilateur de méta-objets l'impose.

Étant donné un pointeur à une instance de MyClass ou un pointeur à une instance de QObject qui s'avère être 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, l'énumération utilisée pour le type de la propriété a été localement déclarée dans MyClass. Si elle avait été déclarée dans une autre classe, son nom pleinement qualifié (par exemple, OtherClass::Priority) serait nécessaire. De plus, cette autre classe devrait aussi hériter de QObject et enregistrer son énumération avec Q_ENUMS().

Une macro similaire, Q_FLAGS(), est aussi disponible. Comme Q_ENUMS(), elle enregistre une énumération, mais marque le type comme étant un ensemble de flags (ou drapeaux), ce qui signifie qu'elles peuvent être combinées avec l'opération logique OU. Une classe d'E/S peut avoir des valeurs d'énumération READ et WRITE, et dans ce cas, QObject::setProperty() peut accepter READ | WRITE. Q_FLAGS() devrait être utilisé pour enregistrer cette énumération.

Propriétés dynamiques

La fonction QObject::setProperty() peut aussi être utilisée pour ajouter de nouvelles propriétés à une instance de classe lors de l'exécution. Lorsqu'elle est appelée avec un nom et une valeur, si une propriété avec le nom donné existe dans le 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 retourné. Si la valeur n'est pas compatible avec le type de la propriété, la propriété n'est pas changée, et false est retourné. Mais si la propriété avec le nom donné n'existe pas dans le QObject (c'est-à-dire, si elle n'a pas été déclarée avec Q_PROPERTY()), une nouvelle propriété avec le nom et la valeur donné est automatiquement ajoutée au QObject, mais false est toujours retourné. Cela signifie qu'un retour de false ne peut être utilisé pour déterminer si une propriété particulière a actuellement été définie, à moins que vous sachiez à l'avance que la propriété existe déjà dans le QObject.

Notez que les propriétés dynamiques sont ajoutées par base d'instance, c'est-à-dire qu'elles sont ajoutée au QObject, non au QMetaObject. Une propriété ne peut être retirée depuis une instance en passant le nom de la propriété et une valeur de QVariant invalide à QObject::setProperty(). Le constructeur par défaut d'un QVariant initialise un QVariant invalide.

Les propriétés dynamiques peuvent être récupérées avec QObject::property(), telles que les propriétés sont déclarées au moment de la compilation avec Q_PROPERTY().

Propriétés et types personnalisés

Les types personnalisés utilisés par les propriétés nécessitent d'être déclarés par l'utilisation de la macro Q_DECLARE_METATYPE() de sorte que leurs valeurs puissent être enregistrées dans des objets de QVariant. Cela les rend adéquats à l'usage avec les propriétés statiques déclarées avec la macro Q_PROPERTY() dans les définitions de classe, et à l'usage avec les propriétés dynamiques créées lors de l'exécution.

Ajouter des informations additionnelles à une classe

La connexion avec le système de propriétés se fait avec une macro additionnelle, Q_CLASSINFO(), qui peut être utilisée pour raccorder la paire nom/valeur à un méta-objet de classe, par exemple :

Q_CLASSINFO("Version", "3.0.0")

Comme les autres métadonnées, l'information de classe est accessible à l'exécution par le biais du méta-objet ; regardez QMetaObject::classInfo() pour plus de détails.

Voir aussi Le système de méta-objets, Les signaux et les slots, Q_DECLARE_METATYPE(), QMetaType et QVariant.

Remerciements

Merci à Louis du Verdier pour la traduction ainsi qu'à Thibaut Cuvelier et à Jacques Thery pour leur relecture !

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt 4.7
Copyright © 2017 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur, une redirection cassée ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
Responsable bénévole de la rubrique Qt : Thibaut Cuvelier -