summaryrefslogtreecommitdiffstats
path: root/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
blob: c84928d67ab584086e7a5035fc088bd8ad1e42f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/****************************************************************************
**
** 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. This happens lazily, by marking the binding
    as dirty and evaluating it only when the property's value is requested. For example:

    \code
    QProperty<QString> firstname("John");
    QProperty<QString> lastname("Smith");
    QProperty<int> age(41);

    QProperty<QString> fullname;
    fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age: " + QString::number(age.value()); });

    qDebug() << fullname.value(); // Prints "John Smith age: 41"

    firstname = "Emma"; // Marks binding expression as dirty

    qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41"

    // Birthday is coming up
    age.setValue(age.value() + 1);

    qDebug() << fullname.value(); // Re-evaluates the binding expression and 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.

    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 dependent on myProperty would be
    first informed about the change to 42, then about the change to 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
    "radius" and "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 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.

*/