Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
bindableproperties.qdoc
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4/*!
5 \page bindableproperties.html
6 \title Qt Bindable Properties
7 \brief Qt's bindable properties.
8
9 \ingroup qt-basic-concepts
10 \keyword Qt's Bindable Properties
11
12 Qt provides bindable properties. Bindable properties are properties
13 which either have a value or are specified using any C++ function,
14 typically a C++ lambda expression.
15 In case they are specified using a C++ function, they are
16 updated automatically whenever their dependencies change.
17
18 Bindable properties are implemented in the class QProperty, which
19 consists of the data object and a pointer to a management data structure, and
20 in class QObjectBindableProperty, which consists only of the data object and
21 uses the encapsulating QObject to store the pointer to the
22 management data structure.
23
24 \section1 Why Use Bindable Properties?
25
26 Property bindings are one of the core features of QML. They allow to specify
27 relationships between different object properties and automatically update
28 properties' values whenever their dependencies change. Bindable properties
29 allow to achieve the same not only in QML code, but also in C++. Using
30 bindable properties can help to simplify your program, by eliminating a lot
31 of boilerplate code for tracking and reacting to dependency updates of
32 different objects.
33
34 The \l {Introductory Example} below demonstrates the usage of bindable
35 properties in C++ code. You can also check \l {Bindable Properties} example
36 to see how the bindable properties can help to improve your code.
37
38 \section1 Introductory Example
39
40 The binding expression computes the value by reading other QProperty values.
41 Behind the scenes this dependency is tracked. Whenever a change in any property's
42 dependency is detected, the binding expression is re-evaluated and the new
43 result is applied to the property. For example:
44
45 \code
46 QProperty<QString> firstname("John");
47 QProperty<QString> lastname("Smith");
48 QProperty<int> age(41);
49
50 QProperty<QString> fullname;
51 fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age: " + QString::number(age.value()); });
52
53 qDebug() << fullname.value(); // Prints "John Smith age: 41"
54
55 firstname = "Emma"; // Triggers binding reevaluation
56
57 qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41"
58
59 // Birthday is coming up
60 age.setValue(age.value() + 1); // Triggers re-evaluation
61
62 qDebug() << fullname.value(); // Prints "Emma Smith age: 42"
63 \endcode
64
65 When a new value is assigned to the \c firstname property, the binding
66 expression for \c fullname is reevaluated. So when the last \c qDebug() statement
67 tries to read the name value of the \c fullname property, the new value is returned.
68
69 Since bindings are C++ functions, they may do anything that's possible
70 in C++. This includes calling other functions. If those functions access values
71 held by QProperty, they automatically become dependencies to the binding.
72
73 Binding expressions may use properties of any type, so in the above example the age
74 is an integer and folded into the string value using conversion to integer, but
75 the dependency is fully tracked.
76
77 \section1 Bindable Property Getters and Setters
78
79 When a class has a bindable property, either using QProperty
80 or QObjectBindableProperty, special care has to be taken when formulating
81 getters and setters for that property.
82
83 \section2 Bindable Property Getters
84
85 To ensure proper operation of the automatic dependency-tracking system,
86 every possible code path in a getter needs to read from the underlying
87 property object.
88 In addition, the property must not be written inside the getter.
89 Design patterns which recompute or refresh anything in the getter
90 are not compatible with bindable properties.
91
92 It is therefore recommended to only use trivial getters with bindable properties.
93
94 \section2 Bindable Property Setters
95
96 To ensure proper operation of the automatic dependency-tracking system,
97 every possible code path in a setter needs to write to the underlying
98 property object, even if the value did not change.
99
100 Any other code in a setter has a high propability of being incorrect.
101 Any code doing updates based on the new value is most likely a bug,
102 as this code won't be executed when the property is changed
103 through a binding.
104
105 It is therefore recommended to only use trivial setters with bindable properties.
106
107 \section1 Writing to a Bindable Property
108
109 Bindable properties inform their dependent properties about each change.
110 This might trigger change handlers, which in turn might call arbitrary code.
111 Thus, every write to a bindable property has to be inspected carefully.
112 The following problems might occur.
113
114 \section2 Writing Intermediate Values to Bindable Properties
115
116 Bindable properties must not be used as variables in algorithms. Each value written
117 would be communicated to dependent properties.
118 For example, in the following code, other properties that depend on
119 \b myProperty would be first informed about the change to \b 42, then about
120 the change to \b maxValue.
121
122 \badcode
123 myProperty = somecomputation(); // returning, say, 42
124 if (myProperty.value() > maxValue)
125 myProperty = maxValue;
126 \endcode
127
128 Instead, perform the computation in a separate variable. Correct usage is shown in the
129 following example.
130
131 \code
132 int newValue = someComputation();
133 if (newValue > maxValue)
134 newValue = maxValue;
135 myProperty = newValue; // only write to the property once
136 \endcode
137
138 \section2 Writing Bindable Properties in Transitional States
139
140 When a bindable property is a member of a class, each write to that property
141 might expose the current state to the outside. So bindable properties must
142 not be written in transient states, when class invariants are not met.
143
144 For example, in a class representing a circle which holds two members
145 \b radius and \b area consistent, a setter might look like this (where radius
146 is a bindable property):
147
148 \badcode
149 void setRadius(double newValue)
150 {
151 radius = newValue; // this might trigger change handlers
152 area = M_PI * radius * radius;
153 emit radiusChanged();
154 }
155 \endcode
156
157 Here, code triggered in change handlers might use the circle, while it has
158 the new radius, but still the old area.
159
160 \section1 Bindable Properties with Virtual Setters and Getters
161
162 Property setters and getters should normally be minimal and do nothing but
163 setting the property; hence it is not normally appropriate for such setters
164 and getters to be virtual. There is nothing it makes sense for the derived
165 class to do.
166
167 However some Qt classes can have properties with virtual setters. When
168 subclassing such a Qt class, overriding such setters requires special care.
169
170 In any case the base implementation \e must be called for the binding to
171 work correctly.
172
173 The following illustrates this approach.
174
175 \badcode
176 void DerivedClass::setValue(int val)
177 {
178 // do something
179 BaseClass::setValue(val);
180 // probably do something else
181 }
182 \endcode
183
184 All the common rules and recommendations regarding writing to bindable
185 properties also apply here. As soon as the base class implementation is
186 called, all the observers are notified about the change to the property.
187 This means that class invariants must be met before calling the base
188 implementation.
189
190 In the rare case where such virtual getters or setters are necessary, the
191 base class should document the requirements it imposes on overrides.
192
193 \section1 Formulating a Property Binding
194
195 Any C++ expression evaluating to the correct type can be used as a binding
196 expression and be given to the setBinding() method. However, to formulate
197 a correct binding, some rules must be followed.
198
199 Dependency tracking only works on bindable properties. It's the developer's
200 responsibility to ensure that all properties used in the binding expression
201 are bindable properties. When non-bindable properties are used in a binding
202 expression, changes to those properties do not trigger updates to the bound
203 property. No warning or error is generated either at compile-time or at run-time.
204 The bound property will be updated only when bindable properties used in the
205 binding expression are changed.
206 Non-bindable properties might be used in a binding if it's possible
207 to ensure that markDirty is called on the property being bound on each
208 change of the non-bindable dependency.
209
210 The bound property might evaluate its binding several times during its lifetime.
211 The developer must make sure that all objects used in the binding expression
212 live longer than the binding.
213
214 The bindable property system is not thread-safe. Properties used in the binding
215 expression on one thread must not be read or modified by any other thread.
216 An object of a QObject-derived class which has a property with a binding must
217 not be moved to a different thread.
218 Also, an object of a QObject-derived class which has a property which is used
219 in a binding must not be moved to a different thread. In this context, it's
220 irrelevant whether it's used in a binding of a property in the same object
221 or in a binding of a property in another object.
222
223 The binding expression should not read from the property it's a binding for. Otherwise,
224 an evaluation loop exists.
225
226 The binding expression must not write to the property it's a binding for.
227
228 Functions used as bindings as well as all code which is called inside a binding
229 must not co_await. Doing so can confuse the property system's tracking of dependencies.
230
231 \section1 Bindable Properties and Multithreading
232
233 Bindable properties are not threadsafe, unless stated otherwise.
234 A bindable property must not be read or modified by any thread other than
235 the one is was created in.
236
237 \section1 Tracking Bindable Properties
238
239 Sometimes the relationships between properties cannot be expressed using
240 bindings. Instead you may need to run custom code whenever the value of a property
241 changes and instead of assigning the value to another property, pass it to
242 other parts of your application. For example writing data into a network socket
243 or printing debug output. QProperty provides two mechanisms for tracking.
244
245 You can register for a callback function to be called whenever the value of
246 a property changes, by using onValueChanged(). If you want the callback to also
247 be called for the current value of the property, register your callback using
248 subscribe() instead.
249
250 \section1 Interaction with Q_PROPERTYs
251
252 A \l {The Property System}{Q_PROPERTY} that defines \c BINDABLE can be bound and
253 used in binding expressions. You can implement such properties using \l {QProperty},
254 \l {QObjectBindableProperty}, or \l {QObjectComputedProperty}.
255
256 Q_PROPERTYs without \c BINDABLE can also be bound and be used in binding expressions,
257 as long as they define a \c NOTIFY signal. You must wrap the property in a \l QBindable
258 using the \c {QBindable(QObject* obj, const char* property)} constructor. Then, the
259 property can be bound using \l QBindable::setBinding() or used in a binding
260 expression via \l QBindable::value(). You must use \c QBindable::value() in binding
261 expressions instead of the normal property \c READ function (or \c MEMBER) to enable
262 dependency tracking if the property is not \c BINDABLE.
263
264*/