===== 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 [[metaobjects|système de méta-objets]] qui fournit aussi une communication inter-objets via [[signalsandslots|les signaux et les slots]]. ==== Conditions de déclaration de propriétés ==== Pour déclarer une propriété, utiliser la macro [[QObject#Q_PROPERTY|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 [[metaobjects|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-prop|QWidget::focus]] est une propriété en lecture seule avec la fonction ''READ'', [[qwidget#focus-prop|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-prop|QWidget::enabled]] possède la fonction ''WRITE'', [[qwidget#enabled-prop|QWidget::setEnabled()]]. Les propriétés en lecture seule n'ont pas besoin de fonctions ''WRITE'', par exemple : [[qwidget#focus-prop|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-prop|QWidget::cursor]] possède les fonctions typiques ''READ'' et ''WRITE'', [[qwidget#cursor-prop|QWidget::cursor()]] et [[qwidget#cursor-prop|QWidget::setCursor()]], et possède aussi une fonction ''RESET'', [[qwidget#cursor-prop|QWidget::unsetCursor()]], puisque ne pas faire appel à [[qwidget#cursor-prop|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, ce signal sera émis à chaque fois que la valeur de la propriété changera. Le signal ne doit prendre qu'un seul paramètre, qui doit lui-même être du même type que la propriété ; le paramètre prendra la nouvelle valeur de la propriété. * 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, [[designer-manual#qt-designer|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-prop|QWidget::minimumWidth()]] est à ''false'', car sa valeur est juste prise depuis le composant de longueur de la propriété [[qwidget#minimumSize-prop|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-prop|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 '''' 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''. Pour la cohérence, utilisez aussi ''QList'' et ''QValueList'' à la place de ''QList'' et ''QValueList''. ==== 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|QObject::property()]] et [[QObject#setProperty|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#down-prop|QAbstractButton::setDown()]] et de [[QObject::setProperty|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; iproperty(i); const char *name = metaproperty.name(); QVariant value = object->property(name); ... } Dans l'extrait ci-dessus, [[QMetaObject#property|QMetaObject::property()]] est utilisé pour obtenir des [[qmetaproperty|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|QObject::property()]] pour obtenir la [[QVariant|valeur]] de la propriété dans l'[[QObject|objet]] actuel. ==== Un exemple simple ==== Supposons que nous avons une classe ''MyClass'', dérivée de [[QObject]] et utilisant la macro [[QObject#Q_OBJECT|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 [[QObject#Q_PROPERTY|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 [[metaobjets|le système de méta-objets]] en utilisant la macro [[QObject#Q_ENUMS|Q_ENUMS()]]. L'enregistrement d'une énumération rend les noms d'énumérateurs disponibles à l'usage pour les appels de [[QObject#setProperty|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é. [[moc|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 [[QObject#Q_ENUMS|Q_ENUMS()]]. Une macro similaire, [[QObject#Q_ENUMS|Q_FLAGS()]], est aussi disponible. Comme [[QObject#Q_ENUMS|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|QObject::setProperty()]] peut accepter ''READ | WRITE''. [[QObject#Q_ENUMS|Q_FLAGS()]] devrait être utilisé pour enregistrer cette énumération. ==== Propriétés dynamiques ==== La fonction [[QObject#setProperty|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 [[QObject#Q_PROPERTY|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|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|QObject::property()]], telles que les propriétés sont déclarées au moment de la compilation avec [[QObject#Q_PROPERTY|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 [[QMetaType#Q_DECLARE_METATYPE|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 [[QObject#Q_PROPERTY|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, [[QObject#Q_CLASSINFO|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|QMetaObject::classInfo()]] pour plus de détails. Voir aussi [[metaobjects|Le système de méta-objets]], [[signalsandslots|Les signaux et les slots]], [[QMetaType#Q_DECLARE_METATYPE|Q_DECLARE_METATYPE()]], [[QMetaType]] et [[QVariant]]. ==== Remerciements ==== Merci à pour la traduction ainsi qu'à et à pour leur relecture !