qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,2 @@
qt_internal_add_example(bindablesubscription)
qt_internal_add_example(subscription)

View File

@ -0,0 +1,4 @@
TEMPLATE = subdirs
SUBDIRS = \
bindablesubscription \
subscription

View File

@ -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}"
)

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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
*/

View File

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>germany.png</file>
<file>norway.png</file>
<file>finland.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -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;
}

View File

@ -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

View 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>

View File

@ -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}"
)

View 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();
}

View File

@ -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]

View File

@ -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

View File

@ -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

View 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]

View 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