Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
*/
qtbase
src
corelib
doc
src
objectmodel
bindableproperties.qdoc
Generated on
for Qt by
1.14.0