/**************************************************************************** ** ** 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$ ** ****************************************************************************/ /*! \page bindableproperties.html \title Qt Bindable Properties \brief Qt's bindable properties. \ingroup qt-basic-concepts \keyword Qt's Bindable Properties Qt provides bindable properties. Bindable properties are properties which either have a value or are specified using any C++ function, typically a C++ lambda expression. In case they are specified using a C++ function, they are updated automatically whenever their dependencies change. Bindable properties are implemented in the class QProperty, which consists of the data object and a pointer to a management data structure, and in class QObjectBindableProperty, which consists only of the data object and uses the encapsulating QObject to store the pointer to the management data structure. \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. For example: \code QProperty firstname("John"); QProperty lastname("Smith"); QProperty age(41); QProperty fullname; fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age: " + QString::number(age.value()); }); qDebug() << fullname.value(); // Prints "John Smith age: 41" firstname = "Emma"; // Triggers binding reevaluation qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41" // Birthday is coming up age.setValue(age.value() + 1); // Triggers re-evaluation 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 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 held by QProperty, they automatically become dependencies to the binding. Binding expressions may use properties of any type, so in the above example the age is an integer and folded into the string value using conversion to integer, but the dependency is fully tracked. \section1 Bindable Property Getters and Setters When a class has a bindable property, either using QProperty or QObjectBindableProperty, special care has to be taken when formulating getters and setters for that property. \section2 Bindable Property Getters To ensure proper operation of the automatic dependency-tracking system, every possible code path in a getter needs to read from the underlying property object. In addition, the property must not be written inside the getter. Design patterns which recompute or refresh anything in the getter are not compatible with bindable properties. It is therefore recommended to only use trivial getters with bindable properties. \section2 Bindable Property Setters To ensure proper operation of the automatic dependency-tracking system, every possible code path in a setter needs to write to the underlying property object, even if the value did not change. Any other code in a setter has a high propability of being incorrect. Any code doing updates based on the new value is most likely a bug, as this code won't be executed when the property is changed through a binding. 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 recomendations 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 bindings. Instead you may need to run custom code whenever the value of a property changes and instead of assigning the value to another property, pass it to other parts of your application. For example writing data into a network socket or printing debug output. QProperty provides two mechanisms for tracking. You can register for a callback function to be called whenever the value of a property changes, by using onValueChanged(). If you want the callback to also be called for the current value of the property, register your callback using subscribe() instead. */