Propriétés liables de Qt
Qt fournit des propriétés liables. Les propriétés liables sont des propriétés qui ont soit une valeur, soit sont définies à l'aide d'une fonction C++, généralement une expression lambda C++. Lorsqu'elles sont définies à l'aide d'une fonction C++, elles sont mises à jour automatiquement dès que leurs dépendances changent.
Les propriétés liables sont implémentées dans la classe ` QProperty`, qui se compose de l’objet de données et d’un pointeur vers une structure de données de gestion, ainsi que dans la classe ` QObjectBindableProperty`, qui se compose uniquement de l’objet de données et utilise la classe d’encapsulation ` QObject ` pour stocker le pointeur vers la structure de données de gestion.
Pourquoi utiliser des propriétés liables ?
Les liaisons de propriétés constituent l’une des fonctionnalités essentielles de QML. Elles permettent de définir des relations entre différentes propriétés d’objets et de mettre à jour automatiquement les valeurs de ces propriétés dès que leurs dépendances changent. Les propriétés liables permettent d’obtenir le même résultat non seulement dans le code QML, mais aussi en C++. L’utilisation des propriétés liables peut contribuer à simplifier votre programme, en éliminant une grande partie du code récurrent nécessaire pour suivre et réagir aux mises à jour des dépendances entre différents objets.
L'exemple d'introduction ci-dessous illustre l'utilisation des propriétés liables dans du code C++. Vous pouvez également consulter l'exemple « Propriétés liables » pour découvrir comment celles-ci peuvent vous aider à améliorer votre code.
Exemple d'introduction
L'expression de liaison calcule la valeur en lisant les valeurs d'autres propriétés « QProperty s ». En arrière-plan, cette dépendance est suivie. Chaque fois qu'un changement dans la dépendance d'une propriété est détecté, l'expression de liaison est réévaluée et le nouveau résultat est appliqué à la propriété. Par exemple :
QProperty<QString> firstname("John");
QProperty<QString> nom_de_famille("Smith");
QProperty<int> âge(41);
QProperty<QString> nomComplet;
nomComplet.setBinding([&]() { return prénom.value() + " " + nom.value() + " âge : " + QString::number(âge.value()); });
qDebug() << fullname.value(); // Prints "John Smith age: 41"
prénom = "Emma"; // Déclenche la réévaluation des liaisons
qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41"
// L'anniversaire approche
age.setValue(age.value() + 1); // Déclenche une réévaluation
qDebug() << fullname.value(); // Prints "Emma Smith age: 42"Lorsqu’une nouvelle valeur est attribuée à la propriété ` firstname `, l’expression de liaison de ` fullname ` est réévaluée. Ainsi, lorsque la dernière instruction ` qDebug() ` tente de lire la valeur du nom de la propriété ` fullname `, c’est la nouvelle valeur qui est renvoyée.
Les liaisons étant des fonctions C++, elles peuvent effectuer toutes les opérations possibles en C++. Cela inclut l’appel d’autres fonctions. Si ces fonctions accèdent à des valeurs détenues par QProperty, elles deviennent automatiquement des dépendances de la liaison.
Les expressions de liaison peuvent utiliser des propriétés de n’importe quel type ; ainsi, dans l’exemple ci-dessus, l’âge est un entier et est converti en valeur de chaîne à l’aide d’une conversion en entier, mais la dépendance est entièrement suivie.
Méthodes getter et setter des propriétés liables
Lorsqu’une classe dispose d’une propriété liable, qu’elle utilise QProperty ou QObjectBindableProperty, il convient d’apporter un soin particulier à la formulation des accesseurs et des mutateurs de cette propriété.
Accesseurs de propriétés liables
Pour garantir le bon fonctionnement du système de suivi automatique des dépendances, chaque chemin de code possible dans un accesseur doit lire à partir de l’objet de propriété sous-jacent. De plus, la propriété ne doit pas être modifiée à l’intérieur de l’accesseur. Les modèles de conception qui recalculent ou actualisent quoi que ce soit dans l’accesseur ne sont pas compatibles avec les propriétés liables.
Il est donc recommandé de n'utiliser que des getters simples avec les propriétés liables.
Setteurs de propriétés liables
Pour garantir le bon fonctionnement du système de suivi automatique des dépendances, chaque chemin de code possible dans un setter doit écrire dans l’objet de propriété sous-jacent, même si la valeur n’a pas changé.
Tout autre code présent dans un setter a de fortes chances d’être incorrect. Tout code effectuant des mises à jour en fonction de la nouvelle valeur est très probablement un bogue, car ce code ne sera pas exécuté lorsque la propriété est modifiée via une liaison.
Il est donc recommandé de n'utiliser que des setters triviaux avec les propriétés liables.
Écriture dans une propriété liable
Les propriétés liables informent leurs propriétés dépendantes de chaque modification. Cela peut déclencher des gestionnaires de changement, qui à leur tour peuvent appeler du code arbitraire. Ainsi, chaque écriture dans une propriété liable doit être inspectée avec soin. Les problèmes suivants peuvent survenir.
Écriture de valeurs intermédiaires dans des propriétés liables
Les propriétés liables ne doivent pas être utilisées comme variables dans des algorithmes. Chaque valeur écrite serait communiquée aux propriétés dépendantes. Par exemple, dans le code suivant, les autres propriétés qui dépendent de `myProperty` seraient d’abord informées de la modification à 42, puis de celle à `maxValue`.
myProperty = somecomputation(); // returning, say, 42
if (myProperty.value() > maxValue)
myProperty = maxValue;Effectuez plutôt le calcul dans une variable distincte. L’exemple suivant illustre l’utilisation correcte.
int newValue = someComputation();
if (newValue > maxValue)
newValue = maxValue;
myProperty = newValue; // only write to the property onceÉcriture dans des propriétés liables dans des états transitoires
Lorsqu’une propriété liable est un membre d’une classe, chaque écriture sur cette propriété risque d’exposer l’état actuel à l’extérieur. Les propriétés liables ne doivent donc pas être modifiées dans des états transitoires, lorsque les invariants de la classe ne sont pas respectés.
Par exemple, dans une classe représentant un cercle qui maintient la cohérence entre ses deux membres « radius » et « area », un setter pourrait ressembler à ceci (où « radius » est une propriété liable) :
void setRadius(double newValue)
{
radius = newValue; // this might trigger change handlers
area = M_PI * radius * radius;
emit radiusChanged();
}Ici, le code déclenché dans les gestionnaires de changement pourrait utiliser le cercle alors qu’il possède le nouveau rayon, mais toujours l’ancienne aire.
Propriétés liables avec des setters et getters virtuels
Les setters et getters de propriétés doivent normalement être minimalistes et se contenter de définir la propriété ; il n’est donc généralement pas approprié que ces setters et getters soient virtuels. Il n’y a rien que la classe dérivée ait logiquement à faire.
Cependant, certaines classes Qt peuvent posséder des propriétés dotées de setters virtuels. Lors de la création d’une sous-classe d’une telle classe Qt, la redéfinition de ces setters nécessite une attention particulière.
Dans tous les cas, l’implémentation de base doit être appelée pour que la liaison fonctionne correctement.
L'exemple suivant illustre cette approche.
void DerivedClass::setValue(int val)
{
// do something
BaseClass::setValue(val);
// probably do something else
}Toutes les règles et recommandations courantes concernant l’écriture dans des propriétés liables s’appliquent également ici. Dès que l’implémentation de la classe de base est appelée, tous les observateurs sont notifiés du changement apporté à la propriété. Cela signifie que les invariants de classe doivent être respectés avant d’appeler l’implémentation de la classe de base.
Dans les rares cas où de tels getters ou setters virtuels sont nécessaires, la classe de base doit documenter les exigences qu’elle impose aux redéfinitions.
Formulation d’un liaison de propriété
Toute expression C++ dont le résultat est du type approprié peut être utilisée comme expression de liaison et transmise à la méthode setBinding(). Cependant, pour formuler une liaison correcte, certaines règles doivent être respectées.
Le suivi des dépendances ne fonctionne que sur les propriétés liables. Il incombe au développeur de s’assurer que toutes les propriétés utilisées dans l’expression de liaison sont des propriétés liables. Lorsque des propriétés non liables sont utilisées dans une expression de liaison, les modifications apportées à ces propriétés ne déclenchent pas de mise à jour de la propriété liée. Aucun avertissement ni aucune erreur n’est généré, ni lors de la compilation, ni lors de l’exécution. La propriété liée ne sera mise à jour que lorsque les propriétés liables utilisées dans l’expression de liaison seront modifiées. Des propriétés non liables peuvent être utilisées dans une liaison s’il est possible de garantir que la méthode `markDirty` est appelée sur la propriété liée à chaque modification de la dépendance non liable.
La propriété liée peut évaluer sa liaison plusieurs fois au cours de son cycle de vie. Le développeur doit s’assurer que tous les objets utilisés dans l’expression de liaison ont une durée de vie supérieure à celle de la liaison.
Le système de propriétés liables n’est pas sûr inter-thread. Les propriétés utilisées dans l’expression de liaison sur un thread ne doivent pas être lues ni modifiées par un autre thread. Un objet d’une classe dérivée de ` QObject` qui possède une propriété avec une liaison ne doit pas être déplacé vers un autre thread. De même, un objet d’une classe dérivée de ` QObject` qui possède une propriété utilisée dans une liaison ne doit pas être déplacé vers un autre thread. Dans ce contexte, peu importe qu’il soit utilisé dans une liaison d’une propriété du même objet ou dans une liaison d’une propriété d’un autre objet.
L’expression de liaison ne doit pas lire la propriété à laquelle elle est liée. Sinon, il existe une boucle d’évaluation.
L’expression de liaison ne doit pas écrire dans la propriété à laquelle elle est liée.
Les fonctions utilisées comme liaisons, ainsi que tout le code appelé à l’intérieur d’une liaison, ne doivent pas utiliser `co_await`. Cela pourrait perturber le suivi des dépendances par le système de propriétés.
Propriétés liables et multithreading
Les propriétés liables ne sont pas sûres pour le multithreading, sauf indication contraire. Une propriété liable ne doit pas être lue ni modifiée par un autre thread que celui dans lequel elle a été créée.
Suivi des propriétés liables
Il arrive parfois que les relations entre les propriétés ne puissent pas être exprimées à l’aide de liaisons. Vous pouvez alors avoir besoin d’exécuter du code personnalisé chaque fois que la valeur d’une propriété change et, au lieu d’attribuer cette valeur à une autre propriété, de la transmettre à d’autres parties de votre application. Par exemple, pour écrire des données dans un socket réseau ou afficher des messages de débogage. ` QProperty ` fournit deux mécanismes de suivi.
Vous pouvez enregistrer une fonction de rappel qui sera appelée chaque fois que la valeur d’une propriété change, à l’aide de `onValueChanged()`. Si vous souhaitez que la fonction de rappel soit également appelée pour la valeur actuelle de la propriété, enregistrez plutôt votre fonction de rappel à l’aide de `subscribe()`.
Interaction avec Q_PROPERTY
Une propriété Q_PROPERTY définissant l’ BINDABLE peut être liée et utilisée dans des expressions de liaison. Vous pouvez implémenter de telles propriétés à l’aide de QProperty, QObjectBindableProperty ou QObjectComputedProperty.
Les Q_PROPERTY sans BINDABLE peuvent également être liées et utilisées dans des expressions de liaison, à condition qu’elles définissent un signal NOTIFY. Vous devez encapsuler la propriété dans un QBindable à l’aide du constructeur QBindable(QObject* obj, const char* property). La propriété peut alors être liée à l’aide de QBindable::setBinding() ou utilisée dans une expression de liaison via QBindable::value(). Vous devez utiliser QBindable::value() dans les expressions de liaison à la place de la fonction normale de la propriété READ (ou MEMBER) pour activer le suivi des dépendances si la propriété n’est pas de type BINDABLE.
© 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.