Qt Bindbare Eigenschaften
Qt bietet bindbare Eigenschaften. Bindbare Eigenschaften sind Eigenschaften, die entweder einen Wert haben oder mit einer C++-Funktion, typischerweise einem C++-Lambda-Ausdruck, angegeben werden. Wenn sie mit einer C++-Funktion angegeben werden, werden sie automatisch aktualisiert, wenn sich ihre Abhängigkeiten ändern.
Bindungsfähige Eigenschaften sind in der Klasse QProperty implementiert, die aus dem Datenobjekt und einem Zeiger auf eine Verwaltungsdatenstruktur besteht, und in der Klasse QObjectBindableProperty, die nur aus dem Datenobjekt besteht und die kapselnde QObject verwendet, um den Zeiger auf die Verwaltungsdatenstruktur zu speichern.
Warum bindbare Eigenschaften verwenden?
Eigenschaftsbindungen sind eine der Kernfunktionen von QML. Sie ermöglichen es, Beziehungen zwischen verschiedenen Objekteigenschaften festzulegen und die Werte von Eigenschaften automatisch zu aktualisieren, wenn sich ihre Abhängigkeiten ändern. Bindbare Eigenschaften ermöglichen es, dasselbe nicht nur in QML-Code, sondern auch in C++ zu erreichen. Die Verwendung von bindbaren Eigenschaften kann dazu beitragen, Ihr Programm zu vereinfachen, indem eine Menge von Boilerplate-Code für die Verfolgung und Reaktion auf Aktualisierungen von Abhängigkeiten verschiedener Objekte eliminiert wird.
Das einleitende Beispiel unten demonstriert die Verwendung von bindbaren Eigenschaften in C++-Code. Sie können sich auch das Beispiel für bindbare Eigenschaften ansehen, um zu sehen, wie die bindbaren Eigenschaften helfen können, Ihren Code zu verbessern.
Einführendes Beispiel
Der Bindungsausdruck berechnet den Wert durch Lesen anderer QProperty Werte. Hinter den Kulissen wird diese Abhängigkeit nachverfolgt. Sobald eine Änderung in der Abhängigkeit einer Eigenschaft festgestellt wird, wird der Bindungsausdruck neu ausgewertet und das neue Ergebnis auf die Eigenschaft angewendet. Zum Beispiel:
QProperty<QString> vorname("John");QProperty<QString> lastname("Smith");QProperty<int> Alter(41);QProperty<QString> vollname; vollname.setBinding([&]() { return vorname.wert() + " " + nachname.wert() + " alter: " + QString::number(age.value()); }); qDebug() << fullname.value(); // Prints "John Smith age: 41" vorname = "Emma"; // Löst eine Neubewertung der Bindung aus qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41" // Der Geburtstag steht bevorage.setValue(age.value() + 1); // Löst eine Neubewertung aus qDebug() << fullname.value(); // Prints "Emma Smith age: 42"
Wenn der Eigenschaft firstname
ein neuer Wert zugewiesen wird, wird der Bindungsausdruck für fullname
neu ausgewertet. Wenn also die letzte Anweisung qDebug()
versucht, den Namenswert der Eigenschaft fullname
zu lesen, wird der neue Wert zurückgegeben.
Da Bindungen C++-Funktionen sind, können sie alles tun, was in C++ möglich ist. Dazu gehört auch der Aufruf anderer Funktionen. Wenn diese Funktionen auf Werte zugreifen, die von QProperty gehalten werden, werden sie automatisch zu Abhängigkeiten von der Bindung.
Bindungsausdrücke können Eigenschaften beliebigen Typs verwenden, so dass im obigen Beispiel das Alter eine ganze Zahl ist und mittels Konvertierung in eine ganze Zahl in den String-Wert gefaltet wird, aber die Abhängigkeit wird vollständig verfolgt.
Getter und Setter für bindbare Eigenschaften
Wenn eine Klasse über eine bindungsfähige Eigenschaft verfügt, entweder über QProperty oder QObjectBindableProperty, muss bei der Formulierung von Gettern und Settern für diese Eigenschaft besonders sorgfältig vorgegangen werden.
Getter für bindbare Eigenschaften
Um den ordnungsgemäßen Betrieb des automatischen Systems zur Verfolgung von Abhängigkeiten zu gewährleisten, muss jeder mögliche Codepfad in einem Getter aus dem zugrunde liegenden Eigenschaftsobjekt gelesen werden. Darüber hinaus darf die Eigenschaft nicht innerhalb des Getters geschrieben werden. Entwurfsmuster, die irgendetwas im Getter neu berechnen oder aktualisieren, sind nicht mit bindbaren Eigenschaften kompatibel.
Es wird daher empfohlen, nur triviale Getter mit bindbaren Eigenschaften zu verwenden.
Bindbare Eigenschaftssetzer
Um den ordnungsgemäßen Betrieb des automatischen Systems zur Verfolgung von Abhängigkeiten zu gewährleisten, muss jeder mögliche Codepfad in einem Setter in das zugrunde liegende Eigenschaftsobjekt schreiben, selbst wenn sich der Wert nicht geändert hat.
Jeder andere Code in einem Setter ist mit hoher Wahrscheinlichkeit fehlerhaft. Jeder Code, der Aktualisierungen auf der Grundlage des neuen Wertes vornimmt, ist höchstwahrscheinlich ein Fehler, da dieser Code nicht ausgeführt wird, wenn die Eigenschaft durch eine Bindung geändert wird.
Es wird daher empfohlen, nur triviale Setter mit bindbaren Eigenschaften zu verwenden.
Schreiben in eine bindungsfähige Eigenschaft
Bindbare Eigenschaften informieren ihre abhängigen Eigenschaften über jede Änderung. Dies kann Change-Handler auslösen, die wiederum beliebigen Code aufrufen können. Daher muss jedes Schreiben auf eine bindbare Eigenschaft sorgfältig geprüft werden. Die folgenden Probleme können auftreten.
Schreiben von Zwischenwerten in bindbare Eigenschaften
Bindable Properties dürfen nicht als Variablen in Algorithmen verwendet werden. Jeder geschriebene Wert würde an abhängige Eigenschaften weitergegeben werden. Im folgenden Code würden zum Beispiel andere Eigenschaften, die von myProperty abhängen, zuerst über die Änderung von 42 und dann über die Änderung von maxValue informiert werden.
myProperty = somecomputation(); // returning, say, 42 if (myProperty.value() > maxValue) myProperty = maxValue;
Führen Sie stattdessen die Berechnung in einer separaten Variablen durch. Die korrekte Verwendung wird im folgenden Beispiel gezeigt.
int newValue = someComputation(); if (newValue > maxValue) newValue = maxValue; myProperty = newValue; // only write to the property once
Schreiben von bindbaren Eigenschaften in Übergangszuständen
Wenn eine bindbare Eigenschaft ein Mitglied einer Klasse ist, kann jeder Schreibvorgang in diese Eigenschaft den aktuellen Zustand nach außen hin preisgeben. Daher dürfen bindbare Eigenschaften nicht in Übergangszuständen geschrieben werden, wenn die Klasseninvarianten nicht erfüllt sind.
In einer Klasse, die einen Kreis repräsentiert und zwei konsistente Mitglieder Radius und Fläche hat, könnte ein Setter beispielsweise so aussehen (wobei Radius eine bindbare Eigenschaft ist):
void setRadius(double newValue) { radius = newValue; // this might trigger change handlers area = M_PI * radius * radius; emit radiusChanged(); }
In diesem Fall könnte Code, der in Change-Handlern ausgelöst wird, den Kreis verwenden, während er den neuen Radius, aber immer noch die alte Fläche hat.
Bindbare Eigenschaften mit virtuellen Settern und Gettern
Setter und Getter von Eigenschaften sollten normalerweise minimal sein und nichts anderes tun, als die Eigenschaft zu setzen; daher ist es normalerweise nicht angebracht, dass solche Setter und Getter virtuell sind. Es gibt nichts, was die abgeleitete Klasse sinnvollerweise tun sollte.
Einige Qt-Klassen können jedoch Eigenschaften mit virtuellen Setzern haben. Bei der Subklassifizierung einer solchen Qt-Klasse erfordert das Überschreiben solcher Setter besondere Sorgfalt.
In jedem Fall muss die Basisimplementierung aufgerufen werden, damit die Bindung korrekt funktioniert.
Im Folgenden wird dieser Ansatz veranschaulicht.
void DerivedClass::setValue(int val) { // do something BaseClass::setValue(val); // probably do something else }
Alle allgemeinen Regeln und Empfehlungen bezüglich des Schreibens auf bindbare Eigenschaften gelten auch hier. Sobald die Implementierung der Basisklasse aufgerufen wird, werden alle Beobachter über die Änderung der Eigenschaft informiert. Das bedeutet, dass die Klasseninvarianten vor dem Aufruf der Basisimplementierung erfüllt sein müssen.
In den seltenen Fällen, in denen solche virtuellen Getter oder Setter notwendig sind, sollte die Basisklasse die Anforderungen dokumentieren, die sie an Überschreibungen stellt.
Formulierung einer Eigenschaftsbindung
Jeder C++-Ausdruck, der auf den richtigen Typ ausgewertet wird, kann als Bindungsausdruck verwendet und an die Methode setBinding() übergeben werden. Um jedoch eine korrekte Bindung zu formulieren, müssen einige Regeln beachtet werden.
Die Verfolgung von Abhängigkeiten funktioniert nur bei bindbaren Eigenschaften. Es liegt in der Verantwortung des Entwicklers, sicherzustellen, dass alle im Bindungsausdruck verwendeten Eigenschaften bindungsfähige Eigenschaften sind. Wenn in einem Bindungsausdruck nicht bindungsfähige Eigenschaften verwendet werden, lösen Änderungen an diesen Eigenschaften keine Aktualisierungen an der gebundenen Eigenschaft aus. Weder zur Kompilierungszeit noch zur Laufzeit wird eine Warnung oder ein Fehler erzeugt. Die gebundene Eigenschaft wird nur aktualisiert, wenn die im Bindungsausdruck verwendeten bindbaren Eigenschaften geändert werden. Nicht-bindbare Eigenschaften können in einer Bindung verwendet werden, wenn sichergestellt werden kann, dass markDirty bei jeder Änderung der nicht-bindbaren Abhängigkeit auf der gebundenen Eigenschaft aufgerufen wird.
Die gebundene Eigenschaft kann ihre Bindung mehrmals während ihrer Lebensdauer auswerten. Der Entwickler muss sicherstellen, dass alle im Bindungsausdruck verwendeten Objekte länger leben als die Bindung.
Das System der bindbaren Eigenschaften ist nicht thread-sicher. Eigenschaften, die im Bindungsausdruck eines Threads verwendet werden, dürfen von keinem anderen Thread gelesen oder verändert werden. Ein Objekt einer von QObject abgeleiteten Klasse, das eine Eigenschaft mit einer Bindung hat, darf nicht auf einen anderen Thread verschoben werden. Auch ein Objekt einer von QObject abgeleiteten Klasse, das eine Eigenschaft hat, die in einer Bindung verwendet wird, darf nicht in einen anderen Thread verschoben werden. In diesem Zusammenhang ist es unerheblich, ob sie in einem Datenfluss einer Eigenschaft desselben Objekts oder in einem Datenfluss einer Eigenschaft eines anderen Objekts verwendet wird.
Der Bindungsausdruck sollte nicht von der Eigenschaft gelesen werden, für die er eine Bindung darstellt. Andernfalls entsteht eine Auswertungsschleife.
Der Bindungsausdruck darf nicht in die Eigenschaft schreiben, für die er eine Bindung darstellt.
Funktionen, die als Bindungen verwendet werden, sowie der gesamte Code, der innerhalb einer Bindung aufgerufen wird, dürfen nicht co_await. Dies kann die Verfolgung von Abhängigkeiten durch das Eigenschaftssystem durcheinander bringen.
Bindbare Eigenschaften und Multithreading
Bindungsfähige Eigenschaften sind nicht thread-sicher, sofern nicht anders angegeben. Eine bindbare Eigenschaft darf von keinem anderen Thread als dem, in dem sie erstellt wurde, gelesen oder verändert werden.
Verfolgung von bindbaren Eigenschaften
Manchmal können die Beziehungen zwischen Eigenschaften nicht durch Bindungen ausgedrückt werden. Stattdessen müssen Sie möglicherweise benutzerdefinierten Code ausführen, sobald sich der Wert einer Eigenschaft ändert, und den Wert an andere Teile Ihrer Anwendung weitergeben, anstatt ihn einer anderen Eigenschaft zuzuweisen. Zum Beispiel das Schreiben von Daten in einen Netzwerk-Socket oder das Drucken von Debug-Ausgaben. QProperty bietet zwei Mechanismen für die Verfolgung.
Sie können eine Callback-Funktion registrieren, die immer dann aufgerufen wird, wenn sich der Wert einer Eigenschaft ändert, indem Sie onValueChanged() verwenden. Wenn Sie möchten, dass der Callback auch für den aktuellen Wert der Eigenschaft aufgerufen wird, registrieren Sie Ihren Callback stattdessen mit subscribe().
Interaktion mit Q_PROPERTYs
Eine Q_PROPERTY, die BINDABLE
definiert, kann gebunden und in Bindungsausdrücken verwendet werden. Sie können solche Eigenschaften mit QProperty, QObjectBindableProperty, oder QObjectComputedProperty implementieren.
Q_PROPERTYs ohne BINDABLE
können ebenfalls gebunden und in Bindungsausdrücken verwendet werden, sofern sie ein NOTIFY
Signal definieren. Sie müssen die Eigenschaft mit Hilfe des QBindable(QObject* obj, const char* property)
Konstruktors in eine QBindable verpacken. Dann kann die Eigenschaft mit QBindable::setBinding() gebunden oder mit QBindable::value() in einem Bindungsausdruck verwendet werden. Sie müssen QBindable::value()
in Bindungsausdrücken anstelle der normalen Eigenschaft READ
Funktion (oder MEMBER
) verwenden, um die Abhängigkeitsverfolgung zu ermöglichen, wenn die Eigenschaft nicht BINDABLE
ist.
© 2025 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.