Document the example showing the benefits of using bindable properties
And mention the example in the bindable properties docs. Pick-to: 6.2 Task-number: QTBUG-97655 Change-Id: I676e90dbda66c2e718c7f6c2240fac608a8653df Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
cee89e70a6
commit
022891bcd8
@ -51,6 +51,8 @@
|
|||||||
#include "bindablesubscription.h"
|
#include "bindablesubscription.h"
|
||||||
#include "bindableuser.h"
|
#include "bindableuser.h"
|
||||||
|
|
||||||
|
//! [binding-expressions]
|
||||||
|
|
||||||
BindableSubscription::BindableSubscription(BindableUser *user) : m_user(user)
|
BindableSubscription::BindableSubscription(BindableUser *user) : m_user(user)
|
||||||
{
|
{
|
||||||
Q_ASSERT(user);
|
Q_ASSERT(user);
|
||||||
@ -62,11 +64,17 @@ BindableSubscription::BindableSubscription(BindableUser *user) : m_user(user)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [binding-expressions]
|
||||||
|
|
||||||
|
//! [set-duration]
|
||||||
|
|
||||||
void BindableSubscription::setDuration(Duration newDuration)
|
void BindableSubscription::setDuration(Duration newDuration)
|
||||||
{
|
{
|
||||||
m_duration = newDuration;
|
m_duration = newDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [set-duration]
|
||||||
|
|
||||||
double BindableSubscription::calculateDiscount() const
|
double BindableSubscription::calculateDiscount() const
|
||||||
{
|
{
|
||||||
switch (m_duration) {
|
switch (m_duration) {
|
||||||
|
@ -56,6 +56,8 @@
|
|||||||
|
|
||||||
class BindableUser;
|
class BindableUser;
|
||||||
|
|
||||||
|
//! [bindable-subscription-class]
|
||||||
|
|
||||||
class BindableSubscription
|
class BindableSubscription
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -84,4 +86,6 @@ private:
|
|||||||
QProperty<bool> m_isValid { false };
|
QProperty<bool> m_isValid { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! [bindable-subscription-class]
|
||||||
|
|
||||||
#endif // BNDABLESUBSCRIPTION_H
|
#endif // BNDABLESUBSCRIPTION_H
|
||||||
|
@ -50,6 +50,8 @@
|
|||||||
|
|
||||||
#include "bindableuser.h"
|
#include "bindableuser.h"
|
||||||
|
|
||||||
|
//! [bindable-user-setters]
|
||||||
|
|
||||||
void BindableUser::setCountry(Country country)
|
void BindableUser::setCountry(Country country)
|
||||||
{
|
{
|
||||||
m_country = country;
|
m_country = country;
|
||||||
@ -59,3 +61,5 @@ void BindableUser::setAge(int age)
|
|||||||
{
|
{
|
||||||
m_age = age;
|
m_age = age;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [bindable-user-setters]
|
||||||
|
@ -53,6 +53,8 @@
|
|||||||
|
|
||||||
#include <QProperty>
|
#include <QProperty>
|
||||||
|
|
||||||
|
//! [bindable-user-class]
|
||||||
|
|
||||||
class BindableUser
|
class BindableUser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -78,4 +80,7 @@ private:
|
|||||||
QProperty<Country> m_country { None };
|
QProperty<Country> m_country { None };
|
||||||
QProperty<int> m_age { 0 };
|
QProperty<int> m_age { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! [bindable-user-class]
|
||||||
|
|
||||||
#endif // BINDABLEUSER_H
|
#endif // BINDABLEUSER_H
|
||||||
|
@ -103,6 +103,7 @@ int main(int argc, char *argv[])
|
|||||||
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");
|
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");
|
||||||
|
|
||||||
// Track price changes
|
// Track price changes
|
||||||
|
//! [update-ui]
|
||||||
auto priceChangeHandler = subscription.bindablePrice().subscribe([&] {
|
auto priceChangeHandler = subscription.bindablePrice().subscribe([&] {
|
||||||
priceDisplay->setText(QString::number(subscription.price()));
|
priceDisplay->setText(QString::number(subscription.price()));
|
||||||
});
|
});
|
||||||
@ -110,6 +111,7 @@ int main(int argc, char *argv[])
|
|||||||
auto priceValidHandler = subscription.bindableIsValid().subscribe([&] {
|
auto priceValidHandler = subscription.bindableIsValid().subscribe([&] {
|
||||||
priceDisplay->setEnabled(subscription.isValid());
|
priceDisplay->setEnabled(subscription.isValid());
|
||||||
});
|
});
|
||||||
|
//! [update-ui]
|
||||||
|
|
||||||
w.show();
|
w.show();
|
||||||
return a.exec();
|
return a.exec();
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,202 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\example bindableproperties
|
||||||
|
\title Bindable Properties Example
|
||||||
|
\brief Demonstrates how the usage of bindable properties can simplify
|
||||||
|
your C++ code.
|
||||||
|
|
||||||
|
In this example we will demonstrate two approaches for expressing the
|
||||||
|
relationships between different objects depending on each other:
|
||||||
|
signal/slot connection-based and bindable property-based. For this
|
||||||
|
purpose we will consider a subscription service model to calculate the
|
||||||
|
cost of the subscription.
|
||||||
|
|
||||||
|
\image bindable_properties_example.png
|
||||||
|
|
||||||
|
\section1 Modelling Subscription System with Signal/Slot Approach
|
||||||
|
|
||||||
|
Let's first consider the usual pre-Qt 6 implementation.
|
||||||
|
To model the subscription service the \c Subscription class is used:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/subscription.h subscription-class
|
||||||
|
|
||||||
|
It stores the information about the subscription and provides corresponding
|
||||||
|
getters, setters, and notifier signals for informing the listeners about the
|
||||||
|
subscription information changes. It also keeps a pointer to an instance of
|
||||||
|
the \c User class.
|
||||||
|
|
||||||
|
The price of the subscription is calculated based on the duration of the
|
||||||
|
subscription:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/subscription.cpp calculate-discount
|
||||||
|
|
||||||
|
And user's location:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/subscription.cpp calculate-base-price
|
||||||
|
|
||||||
|
When the price changes, the \c priceChanged() signal is emitted, to notify the
|
||||||
|
listeners about the change:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/subscription.cpp calculate-price
|
||||||
|
|
||||||
|
Similarly, when the duration of the subscription changes, the \c durationChanged()
|
||||||
|
signal is emitted.
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/subscription.cpp set-duration
|
||||||
|
|
||||||
|
\note Both methods need to check if the data is actually changed and
|
||||||
|
only then emit the signals. \c setDuration() also needs to recalculate
|
||||||
|
the price, when the duration has changed.
|
||||||
|
|
||||||
|
The \c Subscription is not valid unless the user has a valid country and
|
||||||
|
age, so the validity is updated in the following way:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/subscription.cpp update-validity
|
||||||
|
|
||||||
|
The \c User class is simple: it stores country and age of the user and
|
||||||
|
provides the corresponding getters, setters, and notifier signals:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/user.h user-class
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/user.cpp user-setters
|
||||||
|
|
||||||
|
In the \c main() function we initialize instances of \c User and
|
||||||
|
\c Subsrcription:
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/main.cpp init
|
||||||
|
|
||||||
|
And do the proper signal-slot connections, to update the \c user and
|
||||||
|
\c subsrcription data when UI elements change. That is straightforward,
|
||||||
|
so we will skip this part.
|
||||||
|
|
||||||
|
Next, we connect to \c Subscription::priceChanged(), to update the price
|
||||||
|
in the UI when the price changes.
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/main.cpp connect-price-changed
|
||||||
|
|
||||||
|
We also connect to \c Subscription::isValidChanged(), to disable the price
|
||||||
|
display if the subscription isn't valid.
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/main.cpp connect-validity-changed
|
||||||
|
|
||||||
|
Because the subsrcription price and validity also depend on the user's
|
||||||
|
country and age, we also need to connect to the \c User::countryChanged()
|
||||||
|
and \c User::ageChanged() signals and update \c subscription accordingly.
|
||||||
|
|
||||||
|
\snippet bindableproperties/subscription/main.cpp connect-user
|
||||||
|
|
||||||
|
This works, but there are some problems:
|
||||||
|
|
||||||
|
\list
|
||||||
|
\li There's a lot of boilerplate code for the signal-slot connections,
|
||||||
|
to be able to react to changes to \c user or \c subscription. If any of
|
||||||
|
the dependencies of the price changes, we need to remember to emit the
|
||||||
|
corresponding notifier signals, to recalculate the price and update it in
|
||||||
|
the UI.
|
||||||
|
\li If more dependencies for price calculation are added in future, we'll
|
||||||
|
need to add more signal-slot connections and make sure all the dependencies
|
||||||
|
are properly updated whenever any of them changes. The overall complexity
|
||||||
|
will grow, and the code will become harder to maintain.
|
||||||
|
\li The \c Subscription and \c User classes depend on the metaobject system
|
||||||
|
to be able to use the signal/slot mechanism.
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
Can we do better?
|
||||||
|
|
||||||
|
\section1 Modeling Subscription System with Bindbable Properties
|
||||||
|
|
||||||
|
Now let's see how the \l {Qt Bindable Properties} can help to solve the
|
||||||
|
same problem. First, let's have a look at the \c BindableSubscription class,
|
||||||
|
which is similar to the \c Subscription class, but is implemented using the
|
||||||
|
bindable properties:
|
||||||
|
|
||||||
|
\snippet bindableproperties/bindablesubscription/bindablesubscription.h bindable-subscription-class
|
||||||
|
|
||||||
|
The first difference we can notice, is that the data fields are now wrapped
|
||||||
|
inside \l QProperty classes, and the notifier signals (and as a consequence the
|
||||||
|
dependency from the metaobject system) are gone, and new methods returning a
|
||||||
|
\l QBindable for each \l QProperty are added instead. The \c calculatePrice()
|
||||||
|
and \c updateValidty() methods are also removed. We'll see below why they aren't
|
||||||
|
needed anymore.
|
||||||
|
|
||||||
|
The \c BindableUser class differs from the \c User class in a similar way:
|
||||||
|
|
||||||
|
\snippet bindableproperties/bindablesubscription/bindableuser.h bindable-user-class
|
||||||
|
|
||||||
|
The second differenece is in the implementation of these calsses. First of
|
||||||
|
all, the dependencies between \c subscription and \c user are now tracked via
|
||||||
|
binding expressions:
|
||||||
|
|
||||||
|
\snippet bindableproperties/bindablesubscription/bindablesubscription.cpp binding-expressions
|
||||||
|
|
||||||
|
Behind the scenes the bindable properties track the dependency changes and
|
||||||
|
update the property's value whenever a change is detected. So if, for example,
|
||||||
|
user's country or age is changed, subscription's price and validity will be
|
||||||
|
updated automatically.
|
||||||
|
|
||||||
|
Another difference is that the setters are now trivial:
|
||||||
|
|
||||||
|
\snippet bindableproperties/bindablesubscription/bindablesubscription.cpp set-duration
|
||||||
|
|
||||||
|
\snippet bindableproperties/bindablesubscription/bindableuser.cpp bindable-user-setters
|
||||||
|
|
||||||
|
There's no need to check inside the setters if the property's value has
|
||||||
|
actually changed, \l QProperty already does that. The dependent properties
|
||||||
|
will be notified about the change only if the value has actually changed.
|
||||||
|
|
||||||
|
The code for updating the information about the price in the UI is also
|
||||||
|
simplified:
|
||||||
|
|
||||||
|
\snippet bindableproperties/bindablesubscription/main.cpp update-ui
|
||||||
|
|
||||||
|
We subscribe to changes via \c bindablePrice() and \c bindableIsValid()
|
||||||
|
and update the price display accordingly when any of these properties
|
||||||
|
changes the value. The subscriptions will stay alive as long as the
|
||||||
|
corresponding handlers are alive.
|
||||||
|
|
||||||
|
Also note that the copy constructors of both \c BindableSubsrciption and
|
||||||
|
\c BindableUser are disabled, since it's not defined what should happen
|
||||||
|
with their bindings when copying.
|
||||||
|
|
||||||
|
As you can see, the code became much simpler, and the problems mentioned
|
||||||
|
above are solved:
|
||||||
|
|
||||||
|
\list
|
||||||
|
\li The boilerplate code for the signal-slot connections is removed, the
|
||||||
|
dependencies are now tracked automatically.
|
||||||
|
\li The code is easier to maintain. Adding more dependencies in future
|
||||||
|
will only require adding the corresponding bindable properties and setting
|
||||||
|
the binding expressions that reflect the relationships between each other.
|
||||||
|
\li The \c Subscription and \c User classes don't depend on the metaobject
|
||||||
|
system anymore. Of course, you can still expose them to the metaobject
|
||||||
|
system and add \l {Q_PROPERTY}s if you need, and have the advantages of
|
||||||
|
bindable properties both in \c C++ and \c QML code. You can use the
|
||||||
|
\l QObjectBindableProperty class for that.
|
||||||
|
\endlist
|
||||||
|
*/
|
@ -62,8 +62,11 @@
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
//! [init]
|
||||||
User user;
|
User user;
|
||||||
Subscription subscription(&user);
|
Subscription subscription(&user);
|
||||||
|
//! [init]
|
||||||
|
|
||||||
SubscriptionWindow w;
|
SubscriptionWindow w;
|
||||||
|
|
||||||
@ -106,14 +109,20 @@ int main(int argc, char *argv[])
|
|||||||
priceDisplay->setEnabled(subscription.isValid());
|
priceDisplay->setEnabled(subscription.isValid());
|
||||||
|
|
||||||
// Track the price changes
|
// Track the price changes
|
||||||
|
|
||||||
|
//! [connect-price-changed]
|
||||||
QObject::connect(&subscription, &Subscription::priceChanged, [&] {
|
QObject::connect(&subscription, &Subscription::priceChanged, [&] {
|
||||||
priceDisplay->setText(QString::number(subscription.price()));
|
priceDisplay->setText(QString::number(subscription.price()));
|
||||||
});
|
});
|
||||||
|
//! [connect-price-changed]
|
||||||
|
|
||||||
|
//! [connect-validity-changed]
|
||||||
QObject::connect(&subscription, &Subscription::isValidChanged, [&] {
|
QObject::connect(&subscription, &Subscription::isValidChanged, [&] {
|
||||||
priceDisplay->setEnabled(subscription.isValid());
|
priceDisplay->setEnabled(subscription.isValid());
|
||||||
});
|
});
|
||||||
|
//! [connect-validity-changed]
|
||||||
|
|
||||||
|
//! [connect-user]
|
||||||
QObject::connect(&user, &User::countryChanged, [&] {
|
QObject::connect(&user, &User::countryChanged, [&] {
|
||||||
subscription.calculatePrice();
|
subscription.calculatePrice();
|
||||||
subscription.updateValidity();
|
subscription.updateValidity();
|
||||||
@ -122,6 +131,7 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(&user, &User::ageChanged, [&] {
|
QObject::connect(&user, &User::ageChanged, [&] {
|
||||||
subscription.updateValidity();
|
subscription.updateValidity();
|
||||||
});
|
});
|
||||||
|
//! [connect-user]
|
||||||
|
|
||||||
w.show();
|
w.show();
|
||||||
return a.exec();
|
return a.exec();
|
||||||
|
@ -56,6 +56,8 @@ Subscription::Subscription(User *user) : m_user(user)
|
|||||||
Q_ASSERT(user);
|
Q_ASSERT(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [calculate-price]
|
||||||
|
|
||||||
void Subscription::calculatePrice()
|
void Subscription::calculatePrice()
|
||||||
{
|
{
|
||||||
const auto oldPrice = m_price;
|
const auto oldPrice = m_price;
|
||||||
@ -65,6 +67,10 @@ void Subscription::calculatePrice()
|
|||||||
emit priceChanged();
|
emit priceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [calculate-price]
|
||||||
|
|
||||||
|
//! [set-duration]
|
||||||
|
|
||||||
void Subscription::setDuration(Duration newDuration)
|
void Subscription::setDuration(Duration newDuration)
|
||||||
{
|
{
|
||||||
if (newDuration != m_duration) {
|
if (newDuration != m_duration) {
|
||||||
@ -74,6 +80,10 @@ void Subscription::setDuration(Duration newDuration)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [set-duration]
|
||||||
|
|
||||||
|
//! [calculate-discount]
|
||||||
|
|
||||||
double Subscription::calculateDiscount() const
|
double Subscription::calculateDiscount() const
|
||||||
{
|
{
|
||||||
switch (m_duration) {
|
switch (m_duration) {
|
||||||
@ -88,6 +98,10 @@ double Subscription::calculateDiscount() const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [calculate-discount]
|
||||||
|
|
||||||
|
//! [calculate-base-price]
|
||||||
|
|
||||||
int Subscription::basePrice() const
|
int Subscription::basePrice() const
|
||||||
{
|
{
|
||||||
if (m_user->country() == User::None)
|
if (m_user->country() == User::None)
|
||||||
@ -96,6 +110,10 @@ int Subscription::basePrice() const
|
|||||||
return (m_user->country() == User::Norway) ? 100 : 80;
|
return (m_user->country() == User::Norway) ? 100 : 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [calculate-base-price]
|
||||||
|
|
||||||
|
//! [update-validity]
|
||||||
|
|
||||||
void Subscription::updateValidity()
|
void Subscription::updateValidity()
|
||||||
{
|
{
|
||||||
bool isValid = m_isValid;
|
bool isValid = m_isValid;
|
||||||
@ -104,3 +122,5 @@ void Subscription::updateValidity()
|
|||||||
if (m_isValid != isValid)
|
if (m_isValid != isValid)
|
||||||
emit isValidChanged();
|
emit isValidChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [update-validity]
|
||||||
|
@ -56,6 +56,8 @@
|
|||||||
|
|
||||||
class User;
|
class User;
|
||||||
|
|
||||||
|
//! [subscription-class]
|
||||||
|
|
||||||
class Subscription : public QObject
|
class Subscription : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -88,4 +90,6 @@ private:
|
|||||||
bool m_isValid = false;
|
bool m_isValid = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! [subscription-class]
|
||||||
|
|
||||||
#endif // SUBSCRIPTION_H
|
#endif // SUBSCRIPTION_H
|
||||||
|
@ -50,6 +50,8 @@
|
|||||||
|
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
|
|
||||||
|
//! [user-setters]
|
||||||
|
|
||||||
void User::setCountry(Country country)
|
void User::setCountry(Country country)
|
||||||
{
|
{
|
||||||
if (m_country != country) {
|
if (m_country != country) {
|
||||||
@ -65,3 +67,5 @@ void User::setAge(int age)
|
|||||||
emit ageChanged();
|
emit ageChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! [user-setters]
|
||||||
|
@ -53,6 +53,8 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
//! [user-class]
|
||||||
|
|
||||||
class User : public QObject
|
class User : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -79,4 +81,6 @@ private:
|
|||||||
Country m_country = Country::None;
|
Country m_country = Country::None;
|
||||||
int m_age = 0;
|
int m_age = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! [user-class]
|
||||||
#endif // USER_H
|
#endif // USER_H
|
||||||
|
@ -45,6 +45,20 @@
|
|||||||
uses the encapsulating QObject to store the pointer to the
|
uses the encapsulating QObject to store the pointer to the
|
||||||
management data structure.
|
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
|
\section1 Introductory Example
|
||||||
|
|
||||||
The binding expression computes the value by reading other QProperty values.
|
The binding expression computes the value by reading other QProperty values.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user