diff options
Diffstat (limited to 'src/corelib/doc/src/objectmodel/bindableproperties.qdoc')
-rw-r--r-- | src/corelib/doc/src/objectmodel/bindableproperties.qdoc | 202 |
1 files changed, 167 insertions, 35 deletions
diff --git a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc index 75cc6f67d5..9b3ea6ae66 100644 --- a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc +++ b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc @@ -1,29 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page bindableproperties.html @@ -45,13 +21,26 @@ uses the encapsulating QObject to store the pointer to the management data structure. + \section1 Why Use Bindable Properties? + + Property bindings are one of the core features of QML. They allow to specify + relationships between different object properties and automatically update + properties' values whenever their dependencies change. Bindable properties + allow to achieve the same not only in QML code, but also in C++. Using + bindable properties can help to simplify your program, by eliminating a lot + of boilerplate code for tracking and reacting to dependency updates of + different objects. + + The \l {Introductory Example} below demonstrates the usage of bindable + properties in C++ code. You can also check \l {Bindable Properties} example + to see how the bindable properties can help to improve your code. + \section1 Introductory Example The binding expression computes the value by reading other QProperty values. Behind the scenes this dependency is tracked. Whenever a change in any property's dependency is detected, the binding expression is re-evaluated and the new - result is applied to the property. This happens lazily, by marking the binding - as dirty and evaluating it only when the property's value is requested. For example: + result is applied to the property. For example: \code QProperty<QString> firstname("John"); @@ -63,20 +52,19 @@ qDebug() << fullname.value(); // Prints "John Smith age: 41" - firstname = "Emma"; // Marks binding expression as dirty + firstname = "Emma"; // Triggers binding reevaluation - qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41" + qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41" // Birthday is coming up - age.setValue(age.value() + 1); + age.setValue(age.value() + 1); // Triggers re-evaluation - qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42" + qDebug() << fullname.value(); // Prints "Emma Smith age: 42" \endcode When a new value is assigned to the \c firstname property, the binding - expression for \c fullname is marked as dirty. So when the last \c qDebug() statement - tries to read the name value of the \c fullname property, the expression is - evaluated again, \c firstname() will be called again and return the new value. + expression for \c fullname is reevaluated. So when the last \c qDebug() statement + tries to read the name value of the \c fullname property, the new value is returned. Since bindings are C++ functions, they may do anything that's possible in C++. This includes calling other functions. If those functions access values @@ -116,6 +104,136 @@ It is therefore recommended to only use trivial setters with bindable properties. + \section1 Writing to a Bindable Property + + Bindable properties inform their dependent properties about each change. + This might trigger change handlers, which in turn might call arbitrary code. + Thus, every write to a bindable property has to be inspected carefully. + The following problems might occur. + + \section2 Writing Intermediate Values to Bindable Properties + + Bindable properties must not be used as variables in algorithms. Each value written + would be communicated to dependent properties. + For example, in the following code, other properties that depend on + \b myProperty would be first informed about the change to \b 42, then about + the change to \b maxValue. + + \badcode + myProperty = somecomputation(); // returning, say, 42 + if (myProperty.value() > maxValue) + myProperty = maxValue; + \endcode + + Instead, perform the computation in a separate variable. Correct usage is shown in the + following example. + + \code + int newValue = someComputation(); + if (newValue > maxValue) + newValue = maxValue; + myProperty = newValue; // only write to the property once + \endcode + + \section2 Writing Bindable Properties in Transitional States + + When a bindable property is a member of a class, each write to that property + might expose the current state to the outside. So bindable properties must + not be written in transient states, when class invariants are not met. + + For example, in a class representing a circle which holds two members + \b radius and \b area consistent, a setter might look like this (where radius + is a bindable property): + + \badcode + void setRadius(double newValue) + { + radius = newValue; // this might trigger change handlers + area = M_PI * radius * radius; + emit radiusChanged(); + } + \endcode + + Here, code triggered in change handlers might use the circle, while it has + the new radius, but still the old area. + + \section1 Bindable Properties with Virtual Setters and Getters + + Property setters and getters should normally be minimal and do nothing but + setting the property; hence it is not normally appropriate for such setters + and getters to be virtual. There is nothing it makes sense for the derived + class to do. + + However some Qt classes can have properties with virtual setters. When + subclassing such a Qt class, overriding such setters requires special care. + + In any case the base implementation \e must be called for the binding to + work correctly. + + The following illustrates this approach. + + \badcode + void DerivedClass::setValue(int val) + { + // do something + BaseClass::setValue(val); + // probably do something else + } + \endcode + + All the common rules and recommendations regarding writing to bindable + properties also apply here. As soon as the base class implementation is + called, all the observers are notified about the change to the property. + This means that class invariants must be met before calling the base + implementation. + + In the rare case where such virtual getters or setters are necessary, the + base class should document the requirements it imposes on overrides. + + \section1 Formulating a Property Binding + + Any C++ expression evaluating to the correct type can be used as a binding + expression and be given to the setBinding() method. However, to formulate + a correct binding, some rules must be followed. + + Dependency tracking only works on bindable properties. It's the developer's + responsibility to ensure that all properties used in the binding expression + are bindable properties. When non-bindable properties are used in a binding + expression, changes to those properties do not trigger updates to the bound + property. No warning or error is generated either at compile-time or at run-time. + The bound property will be updated only when bindable properties used in the + binding expression are changed. + Non-bindable properties might be used in a binding if it's possible + to ensure that markDirty is called on the property being bound on each + change of the non-bindable dependency. + + The bound property might evaluate its binding several times during its lifetime. + The developer must make sure that all objects used in the binding expression + live longer than the binding. + + The bindable property system is not thread-safe. Properties used in the binding + expression on one thread must not be read or modified by any other thread. + An object of a QObject-derived class which has a property with a binding must + not be moved to a different thread. + Also, an object of a QObject-derived class which has a property which is used + in a binding must not be moved to a different thread. In this context, it's + irrelevant whether it's used in a binding of a property in the same object + or in a binding of a property in another object. + + The binding expression should not read from the property it's a binding for. Otherwise, + an evaluation loop exists. + + The binding expression must not write to the property it's a binding for. + + Functions used as bindings as well as all code which is called inside a binding + must not co_await. Doing so can confuse the property system's tracking of dependencies. + + \section1 Bindable Properties and Multithreading + + Bindable properties are not threadsafe, unless stated otherwise. + A bindable property must not be read or modified by any thread other than + the one is was created in. + \section1 Tracking Bindable Properties Sometimes the relationships between properties cannot be expressed using @@ -129,4 +247,18 @@ be called for the current value of the property, register your callback using subscribe() instead. + \section1 Interaction with Q_PROPERTYs + + A \l {The Property System}{Q_PROPERTY} that defines \c BINDABLE can be bound and + used in binding expressions. You can implement such properties using \l {QProperty}, + \l {QObjectBindableProperty}, or \l {QObjectComputedProperty}. + + Q_PROPERTYs without \c BINDABLE can also be bound and be used in binding expressions, + as long as they define a \c NOTIFY signal. You must wrap the property in a \l QBindable + using the \c {QBindable(QObject* obj, const char* property)} constructor. Then, the + property can be bound using \l QBindable::setBinding() or used in a binding + expression via \l QBindable::value(). You must use \c QBindable::value() in binding + expressions instead of the normal property \c READ function (or \c MEMBER) to enable + dependency tracking if the property is not \c BINDABLE. + */ |