mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-02 07:15:27 +08:00
qt 6.5.1 original
This commit is contained in:
2
examples/corelib/bindableproperties/CMakeLists.txt
Normal file
2
examples/corelib/bindableproperties/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
qt_internal_add_example(bindablesubscription)
|
||||
qt_internal_add_example(subscription)
|
@ -0,0 +1,4 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = \
|
||||
bindablesubscription \
|
||||
subscription
|
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(bindablesubscription LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/bindableproperties/bindablesubscription")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(bindablesubscription
|
||||
../shared/subscriptionwindow.cpp ../shared/subscriptionwindow.h ../shared/subscriptionwindow.ui
|
||||
main.cpp
|
||||
bindablesubscription.cpp bindablesubscription.h
|
||||
bindableuser.cpp bindableuser.h
|
||||
)
|
||||
|
||||
target_link_libraries(bindablesubscription PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(countries_resource_files
|
||||
"../shared/finland.png"
|
||||
"../shared/germany.png"
|
||||
"../shared/norway.png"
|
||||
)
|
||||
|
||||
qt_add_resources(bindablesubscription "countries"
|
||||
PREFIX
|
||||
"/"
|
||||
BASE
|
||||
"../shared"
|
||||
FILES
|
||||
${countries_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS bindablesubscription
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "bindablesubscription.h"
|
||||
#include "bindableuser.h"
|
||||
|
||||
//! [binding-expressions]
|
||||
|
||||
BindableSubscription::BindableSubscription(BindableUser *user) : m_user(user)
|
||||
{
|
||||
Q_ASSERT(user);
|
||||
|
||||
m_price.setBinding([this] { return qRound(calculateDiscount() * m_duration * basePrice()); });
|
||||
|
||||
m_isValid.setBinding([this] {
|
||||
return m_user->country() != BindableUser::Country::AnyCountry && m_user->age() > 12;
|
||||
});
|
||||
}
|
||||
|
||||
//! [binding-expressions]
|
||||
|
||||
//! [set-duration]
|
||||
|
||||
void BindableSubscription::setDuration(Duration newDuration)
|
||||
{
|
||||
m_duration = newDuration;
|
||||
}
|
||||
|
||||
//! [set-duration]
|
||||
|
||||
double BindableSubscription::calculateDiscount() const
|
||||
{
|
||||
switch (m_duration) {
|
||||
case Monthly:
|
||||
return 1;
|
||||
case Quarterly:
|
||||
return 0.9;
|
||||
case Yearly:
|
||||
return 0.6;
|
||||
}
|
||||
Q_ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int BindableSubscription::basePrice() const
|
||||
{
|
||||
if (m_user->country() == BindableUser::Country::AnyCountry)
|
||||
return 0;
|
||||
|
||||
return (m_user->country() == BindableUser::Country::Norway) ? 100 : 80;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef BINDABLESUBSCRIPTION_H
|
||||
#define BINDABLESUBSCRIPTION_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QProperty>
|
||||
|
||||
class BindableUser;
|
||||
|
||||
//! [bindable-subscription-class]
|
||||
|
||||
class BindableSubscription
|
||||
{
|
||||
public:
|
||||
enum Duration { Monthly = 1, Quarterly = 3, Yearly = 12 };
|
||||
|
||||
BindableSubscription(BindableUser *user);
|
||||
BindableSubscription(const BindableSubscription &) = delete;
|
||||
|
||||
int price() const { return m_price; }
|
||||
QBindable<int> bindablePrice() { return &m_price; }
|
||||
|
||||
Duration duration() const { return m_duration; }
|
||||
void setDuration(Duration newDuration);
|
||||
QBindable<Duration> bindableDuration() { return &m_duration; }
|
||||
|
||||
bool isValid() const { return m_isValid; }
|
||||
QBindable<bool> bindableIsValid() { return &m_isValid; }
|
||||
|
||||
private:
|
||||
double calculateDiscount() const;
|
||||
int basePrice() const;
|
||||
|
||||
BindableUser *m_user;
|
||||
QProperty<Duration> m_duration { Monthly };
|
||||
QProperty<int> m_price { 0 };
|
||||
QProperty<bool> m_isValid { false };
|
||||
};
|
||||
|
||||
//! [bindable-subscription-class]
|
||||
|
||||
#endif // BNDABLESUBSCRIPTION_H
|
@ -0,0 +1,22 @@
|
||||
QT += widgets
|
||||
TARGET = bindablesubscription
|
||||
|
||||
SOURCES += main.cpp \
|
||||
bindablesubscription.cpp \
|
||||
bindableuser.cpp \
|
||||
../shared/subscriptionwindow.cpp
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/bindableproperties/bindablesubscription
|
||||
INSTALLS += target
|
||||
|
||||
FORMS += \
|
||||
../shared/subscriptionwindow.ui
|
||||
|
||||
HEADERS += \
|
||||
bindablesubscription.h \
|
||||
bindableuser.h \
|
||||
../shared/subscriptionwindow.h
|
||||
|
||||
RESOURCES += \
|
||||
../shared/countries.qrc
|
||||
|
@ -0,0 +1,18 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "bindableuser.h"
|
||||
|
||||
//! [bindable-user-setters]
|
||||
|
||||
void BindableUser::setCountry(Country country)
|
||||
{
|
||||
m_country = country;
|
||||
}
|
||||
|
||||
void BindableUser::setAge(int age)
|
||||
{
|
||||
m_age = age;
|
||||
}
|
||||
|
||||
//! [bindable-user-setters]
|
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef BINDABLEUSER_H
|
||||
#define BINDABLEUSER_H
|
||||
|
||||
#include <QLocale>
|
||||
#include <QProperty>
|
||||
|
||||
//! [bindable-user-class]
|
||||
|
||||
class BindableUser
|
||||
{
|
||||
public:
|
||||
using Country = QLocale::Territory;
|
||||
|
||||
public:
|
||||
BindableUser() = default;
|
||||
BindableUser(const BindableUser &) = delete;
|
||||
|
||||
Country country() const { return m_country; }
|
||||
void setCountry(Country country);
|
||||
QBindable<Country> bindableCountry() { return &m_country; }
|
||||
|
||||
int age() const { return m_age; }
|
||||
void setAge(int age);
|
||||
QBindable<int> bindableAge() { return &m_age; }
|
||||
|
||||
private:
|
||||
QProperty<Country> m_country { QLocale::AnyTerritory };
|
||||
QProperty<int> m_age { 0 };
|
||||
};
|
||||
|
||||
//! [bindable-user-class]
|
||||
|
||||
#endif // BINDABLEUSER_H
|
@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "../shared/subscriptionwindow.h"
|
||||
#include "bindablesubscription.h"
|
||||
#include "bindableuser.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
BindableUser user;
|
||||
BindableSubscription subscription(&user);
|
||||
|
||||
SubscriptionWindow w;
|
||||
|
||||
// Initialize subscription data
|
||||
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly");
|
||||
QObject::connect(monthly, &QRadioButton::clicked, [&] {
|
||||
subscription.setDuration(BindableSubscription::Monthly);
|
||||
});
|
||||
QRadioButton *quarterly = w.findChild<QRadioButton *>("btnQuarterly");
|
||||
QObject::connect(quarterly, &QRadioButton::clicked, [&] {
|
||||
subscription.setDuration(BindableSubscription::Quarterly);
|
||||
});
|
||||
QRadioButton *yearly = w.findChild<QRadioButton *>("btnYearly");
|
||||
QObject::connect(yearly, &QRadioButton::clicked, [&] {
|
||||
subscription.setDuration(BindableSubscription::Yearly);
|
||||
});
|
||||
|
||||
// Initialize user data
|
||||
QPushButton *germany = w.findChild<QPushButton *>("btnGermany");
|
||||
QObject::connect(germany, &QPushButton::clicked, [&] {
|
||||
user.setCountry(BindableUser::Country::Germany);
|
||||
});
|
||||
QPushButton *finland = w.findChild<QPushButton *>("btnFinland");
|
||||
QObject::connect(finland, &QPushButton::clicked, [&] {
|
||||
user.setCountry(BindableUser::Country::Finland);
|
||||
});
|
||||
QPushButton *norway = w.findChild<QPushButton *>("btnNorway");
|
||||
QObject::connect(norway, &QPushButton::clicked, [&] {
|
||||
user.setCountry(BindableUser::Country::Norway);
|
||||
});
|
||||
|
||||
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox");
|
||||
QObject::connect(ageSpinBox, &QSpinBox::valueChanged, [&](int value) {
|
||||
user.setAge(value);
|
||||
});
|
||||
|
||||
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");
|
||||
|
||||
// Track price changes
|
||||
//! [update-ui]
|
||||
auto priceChangeHandler = subscription.bindablePrice().subscribe([&] {
|
||||
QLocale lc{QLocale::AnyLanguage, user.country()};
|
||||
priceDisplay->setText(lc.toCurrencyString(subscription.price() / subscription.duration()));
|
||||
});
|
||||
|
||||
auto priceValidHandler = subscription.bindableIsValid().subscribe([&] {
|
||||
priceDisplay->setEnabled(subscription.isValid());
|
||||
});
|
||||
//! [update-ui]
|
||||
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,178 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\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 Modeling 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 Subscription:
|
||||
|
||||
\snippet bindableproperties/subscription/main.cpp init
|
||||
|
||||
And do the proper signal-slot connections to update the \c user and
|
||||
\c subscription 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 subscription 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
|
||||
in order to properly track changes to both \c user and \c subscription.
|
||||
If any of the dependencies of the price changes, we need to remember to emit the
|
||||
corresponding notifier signals, recalculate the price, and update it in
|
||||
the UI.
|
||||
\li If more dependencies for price calculation are added in the 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
|
||||
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 difference is in the implementation of these classes. 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 BindableSubscription 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 the 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
|
||||
*/
|
7
examples/corelib/bindableproperties/shared/countries.qrc
Normal file
7
examples/corelib/bindableproperties/shared/countries.qrc
Normal file
@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>germany.png</file>
|
||||
<file>norway.png</file>
|
||||
<file>finland.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
examples/corelib/bindableproperties/shared/finland.png
Normal file
BIN
examples/corelib/bindableproperties/shared/finland.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
examples/corelib/bindableproperties/shared/germany.png
Normal file
BIN
examples/corelib/bindableproperties/shared/germany.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 483 B |
BIN
examples/corelib/bindableproperties/shared/norway.png
Normal file
BIN
examples/corelib/bindableproperties/shared/norway.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
@ -0,0 +1,16 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "subscriptionwindow.h"
|
||||
#include "ui_subscriptionwindow.h"
|
||||
|
||||
SubscriptionWindow::SubscriptionWindow(QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::SubscriptionWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
SubscriptionWindow::~SubscriptionWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef SUBSCRIPTIONWINDOW_H
|
||||
#define SUBSCRIPTIONWINDOW_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class SubscriptionWindow;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class User;
|
||||
|
||||
class SubscriptionWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SubscriptionWindow(QWidget *parent = nullptr);
|
||||
~SubscriptionWindow();
|
||||
|
||||
private:
|
||||
Ui::SubscriptionWindow *ui;
|
||||
};
|
||||
|
||||
#endif // SUBSCRIPTIONWINDOW_H
|
280
examples/corelib/bindableproperties/shared/subscriptionwindow.ui
Normal file
280
examples/corelib/bindableproperties/shared/subscriptionwindow.ui
Normal file
@ -0,0 +1,280 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SubscriptionWindow</class>
|
||||
<widget class="QWidget" name="SubscriptionWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>639</width>
|
||||
<height>269</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Subscription</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0,0,0">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnGermany">
|
||||
<property name="toolTip">
|
||||
<string>Germany</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/germany.png</normaloff>:/germany.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnNorway">
|
||||
<property name="toolTip">
|
||||
<string>Norway</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/norway.png</normaloff>:/norway.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnFinland">
|
||||
<property name="toolTip">
|
||||
<string>Finland</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/finland.png</normaloff>:/finland.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="ageLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Age</string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="ageSpinBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="intervalLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Interval</string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="btnMonthly">
|
||||
<property name="text">
|
||||
<string>Monthly</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="btnQuarterly">
|
||||
<property name="text">
|
||||
<string>Quarterly</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="btnYearly">
|
||||
<property name="text">
|
||||
<string>Yearly</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="priceLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Price/month</string>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="priceDisplay">
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,50 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(subscription LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/bindableproperties/subscription")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(subscription
|
||||
../shared/subscriptionwindow.cpp ../shared/subscriptionwindow.h ../shared/subscriptionwindow.ui
|
||||
main.cpp
|
||||
subscription.cpp subscription.h
|
||||
user.cpp user.h
|
||||
)
|
||||
|
||||
target_link_libraries(subscription PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(countries_resource_files
|
||||
"../shared/finland.png"
|
||||
"../shared/germany.png"
|
||||
"../shared/norway.png"
|
||||
)
|
||||
|
||||
qt_add_resources(subscription "countries"
|
||||
PREFIX
|
||||
"/"
|
||||
BASE
|
||||
"../shared"
|
||||
FILES
|
||||
${countries_resource_files}
|
||||
)
|
||||
|
||||
install(TARGETS subscription
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
92
examples/corelib/bindableproperties/subscription/main.cpp
Normal file
92
examples/corelib/bindableproperties/subscription/main.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "../shared/subscriptionwindow.h"
|
||||
#include "subscription.h"
|
||||
#include "user.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QRadioButton>
|
||||
#include <QSpinBox>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
//! [init]
|
||||
User user;
|
||||
Subscription subscription(&user);
|
||||
//! [init]
|
||||
|
||||
SubscriptionWindow w;
|
||||
|
||||
// Initialize subscription data
|
||||
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly");
|
||||
QObject::connect(monthly, &QRadioButton::clicked, &subscription, [&] {
|
||||
subscription.setDuration(Subscription::Monthly);
|
||||
});
|
||||
QRadioButton *quarterly = w.findChild<QRadioButton *>("btnQuarterly");
|
||||
QObject::connect(quarterly, &QRadioButton::clicked, &subscription, [&] {
|
||||
subscription.setDuration(Subscription::Quarterly);
|
||||
});
|
||||
QRadioButton *yearly = w.findChild<QRadioButton *>("btnYearly");
|
||||
QObject::connect(yearly, &QRadioButton::clicked, &subscription, [&] {
|
||||
subscription.setDuration(Subscription::Yearly);
|
||||
});
|
||||
|
||||
// Initialize user data
|
||||
QPushButton *germany = w.findChild<QPushButton *>("btnGermany");
|
||||
QObject::connect(germany, &QPushButton::clicked, &user, [&] {
|
||||
user.setCountry(User::Country::Germany);
|
||||
});
|
||||
QPushButton *finland = w.findChild<QPushButton *>("btnFinland");
|
||||
QObject::connect(finland, &QPushButton::clicked, &user, [&] {
|
||||
user.setCountry(User::Country::Finland);
|
||||
});
|
||||
QPushButton *norway = w.findChild<QPushButton *>("btnNorway");
|
||||
QObject::connect(norway, &QPushButton::clicked, &user, [&] {
|
||||
user.setCountry(User::Country::Norway);
|
||||
});
|
||||
|
||||
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox");
|
||||
QObject::connect(ageSpinBox, &QSpinBox::valueChanged, &user, [&](int value) {
|
||||
user.setAge(value);
|
||||
});
|
||||
|
||||
// Initialize price data
|
||||
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");
|
||||
priceDisplay->setText(QString::number(subscription.price()));
|
||||
priceDisplay->setEnabled(subscription.isValid());
|
||||
|
||||
// Track the price changes
|
||||
|
||||
//! [connect-price-changed]
|
||||
QObject::connect(&subscription, &Subscription::priceChanged, [&] {
|
||||
QLocale lc{QLocale::AnyLanguage, user.country()};
|
||||
priceDisplay->setText(lc.toCurrencyString(subscription.price() / subscription.duration()));
|
||||
});
|
||||
//! [connect-price-changed]
|
||||
|
||||
//! [connect-validity-changed]
|
||||
QObject::connect(&subscription, &Subscription::isValidChanged, [&] {
|
||||
priceDisplay->setEnabled(subscription.isValid());
|
||||
});
|
||||
//! [connect-validity-changed]
|
||||
|
||||
//! [connect-user]
|
||||
QObject::connect(&user, &User::countryChanged, [&] {
|
||||
subscription.calculatePrice();
|
||||
subscription.updateValidity();
|
||||
});
|
||||
|
||||
QObject::connect(&user, &User::ageChanged, [&] {
|
||||
subscription.updateValidity();
|
||||
});
|
||||
//! [connect-user]
|
||||
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "subscription.h"
|
||||
#include "user.h"
|
||||
|
||||
Subscription::Subscription(User *user) : m_user(user)
|
||||
{
|
||||
Q_ASSERT(user);
|
||||
}
|
||||
|
||||
//! [calculate-price]
|
||||
|
||||
void Subscription::calculatePrice()
|
||||
{
|
||||
const auto oldPrice = m_price;
|
||||
|
||||
m_price = qRound(calculateDiscount() * m_duration * basePrice());
|
||||
if (m_price != oldPrice)
|
||||
emit priceChanged();
|
||||
}
|
||||
|
||||
//! [calculate-price]
|
||||
|
||||
//! [set-duration]
|
||||
|
||||
void Subscription::setDuration(Duration newDuration)
|
||||
{
|
||||
if (newDuration != m_duration) {
|
||||
m_duration = newDuration;
|
||||
calculatePrice();
|
||||
emit durationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//! [set-duration]
|
||||
|
||||
//! [calculate-discount]
|
||||
|
||||
double Subscription::calculateDiscount() const
|
||||
{
|
||||
switch (m_duration) {
|
||||
case Monthly:
|
||||
return 1;
|
||||
case Quarterly:
|
||||
return 0.9;
|
||||
case Yearly:
|
||||
return 0.6;
|
||||
}
|
||||
Q_ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! [calculate-discount]
|
||||
|
||||
//! [calculate-base-price]
|
||||
|
||||
int Subscription::basePrice() const
|
||||
{
|
||||
if (m_user->country() == User::Country::AnyTerritory)
|
||||
return 0;
|
||||
|
||||
return (m_user->country() == User::Country::Norway) ? 100 : 80;
|
||||
}
|
||||
|
||||
//! [calculate-base-price]
|
||||
|
||||
//! [update-validity]
|
||||
|
||||
void Subscription::updateValidity()
|
||||
{
|
||||
bool isValid = m_isValid;
|
||||
m_isValid = m_user->country() != User::Country::AnyTerritory && m_user->age() > 12;
|
||||
|
||||
if (m_isValid != isValid)
|
||||
emit isValidChanged();
|
||||
}
|
||||
|
||||
//! [update-validity]
|
@ -0,0 +1,48 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef SUBSCRIPTION_H
|
||||
#define SUBSCRIPTION_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
class User;
|
||||
|
||||
//! [subscription-class]
|
||||
|
||||
class Subscription : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Duration { Monthly = 1, Quarterly = 3, Yearly = 12 };
|
||||
|
||||
Subscription(User *user);
|
||||
|
||||
void calculatePrice();
|
||||
int price() const { return m_price; }
|
||||
|
||||
Duration duration() const { return m_duration; }
|
||||
void setDuration(Duration newDuration);
|
||||
|
||||
bool isValid() const { return m_isValid; }
|
||||
void updateValidity();
|
||||
|
||||
signals:
|
||||
void priceChanged();
|
||||
void durationChanged();
|
||||
void isValidChanged();
|
||||
|
||||
private:
|
||||
double calculateDiscount() const;
|
||||
int basePrice() const;
|
||||
|
||||
QPointer<User> m_user;
|
||||
Duration m_duration = Monthly;
|
||||
int m_price = 0;
|
||||
bool m_isValid = false;
|
||||
};
|
||||
|
||||
//! [subscription-class]
|
||||
|
||||
#endif // SUBSCRIPTION_H
|
@ -0,0 +1,22 @@
|
||||
QT += widgets
|
||||
TARGET = subscription
|
||||
|
||||
SOURCES += main.cpp \
|
||||
subscription.cpp \
|
||||
user.cpp \
|
||||
../shared/subscriptionwindow.cpp
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/bindableproperties/subscription
|
||||
INSTALLS += target
|
||||
|
||||
FORMS += \
|
||||
../shared/subscriptionwindow.ui
|
||||
|
||||
HEADERS += \
|
||||
subscription.h \
|
||||
user.h \
|
||||
../shared/subscriptionwindow.h
|
||||
|
||||
RESOURCES += \
|
||||
../shared/countries.qrc
|
||||
|
24
examples/corelib/bindableproperties/subscription/user.cpp
Normal file
24
examples/corelib/bindableproperties/subscription/user.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "user.h"
|
||||
|
||||
//! [user-setters]
|
||||
|
||||
void User::setCountry(Country country)
|
||||
{
|
||||
if (m_country != country) {
|
||||
m_country = country;
|
||||
emit countryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void User::setAge(int age)
|
||||
{
|
||||
if (m_age != age) {
|
||||
m_age = age;
|
||||
emit ageChanged();
|
||||
}
|
||||
}
|
||||
|
||||
//! [user-setters]
|
36
examples/corelib/bindableproperties/subscription/user.h
Normal file
36
examples/corelib/bindableproperties/subscription/user.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef USER_H
|
||||
#define USER_H
|
||||
|
||||
#include <QLocale>
|
||||
#include <QObject>
|
||||
|
||||
//! [user-class]
|
||||
|
||||
class User : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Country = QLocale::Territory;
|
||||
|
||||
public:
|
||||
Country country() const { return m_country; }
|
||||
void setCountry(Country country);
|
||||
|
||||
int age() const { return m_age; }
|
||||
void setAge(int age);
|
||||
|
||||
signals:
|
||||
void countryChanged();
|
||||
void ageChanged();
|
||||
|
||||
private:
|
||||
Country m_country { QLocale::AnyTerritory };
|
||||
int m_age { 0 };
|
||||
};
|
||||
|
||||
//! [user-class]
|
||||
#endif // USER_H
|
Reference in New Issue
Block a user