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,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(ipc)
add_subdirectory(mimetypes)
add_subdirectory(serialization)
add_subdirectory(tools)
add_subdirectory(platform)
if(QT_FEATURE_permissions)
add_subdirectory(permissions)
endif()
if(QT_FEATURE_thread)
add_subdirectory(threads)
endif()
if(QT_FEATURE_widgets)
add_subdirectory(bindableproperties)
endif()

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

View File

@ -0,0 +1,11 @@
TEMPLATE = subdirs
CONFIG += no_docs_target
SUBDIRS = \
ipc \
mimetypes \
serialization \
tools \
platform
qtConfig(thread): SUBDIRS += threads

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT TARGET Qt6::Widgets)
return()
endif()
if(QT_FEATURE_sharedmemory)
qt_internal_add_example(sharedmemory)
endif()
if(QT_FEATURE_localserver AND TARGET Qt6::Network)
qt_internal_add_example(localfortuneserver)
qt_internal_add_example(localfortuneclient)
endif()

View File

@ -0,0 +1,5 @@
These examples demonstrate IPC support in Qt.
Documentation for these examples can be found via the Examples
link in the main Qt documentation.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example ipc/localfortuneclient
\title Local Fortune Client Example
\ingroup examples-ipc
\brief Demonstrates using QLocalSocket for a simple local service client.
The Local Fortune Client example shows how to create a client for a simple
local service using QLocalSocket. It is intended to be run alongside the
\l{Local Fortune Server Example}.
\image localfortuneclient-example.png Screenshot of the Local Fortune Client example
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example ipc/localfortuneserver
\title Local Fortune Server Example
\ingroup examples-ipc
\brief Demonstrates using QLocalServer and QLocalSocket for serving a simple local service.
The Local Fortune Server example shows how to create a server for a simple
local service. It is intended to be run alongside the
\l{Local Fortune Client Example}
\image localfortuneserver-example.png Screenshot of the Local Fortune Server example
*/

View File

@ -0,0 +1,107 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example ipc/sharedmemory
\title Shared Memory Example
\ingroup examples-ipc
\brief Demonstrates doing inter-process communication using shared memory with
the QSharedMemory class.
The Shared Memory example shows how to use the QSharedMemory class
to implement inter-process communication using shared memory. To
build the example, run make. To run the example, start two instances
of the executable. The main() function creates an \l {QApplication}
{application} and an instance of our example's Dialog class. The
dialog is displayed and then control is passed to the application in
the standard way.
\snippet ipc/sharedmemory/main.cpp 0
Two instances of class Dialog appear.
\image sharedmemory-example_1.png Screenshot of the Shared Memory example
Class Dialog inherits QDialog. It encapsulates the user interface
and an instance of QSharedMemory. It also has two public slots,
loadFromFile() and loadFromMemory() that correspond to the two
buttons on the dialog.
\snippet ipc/sharedmemory/dialog.h 0
The constructor builds the user interface widgets and connects the
clicked() signal of each button to the corresponding slot function.
\snippet ipc/sharedmemory/dialog.cpp 0
Note that "QSharedMemoryExample" is passed to the \l {QSharedMemory}
{QSharedMemory()} constructor to be used as the key. This will be
used by the system as the identifier of the underlying shared memory
segment.
Click the \tt {Load Image From File...} button on one of the
dialogs. The loadFromFile() slot is invoked. First, it tests whether
a shared memory segment is already attached to the process. If so,
that segment is detached from the process, so we can be assured of
starting off the example correctly.
\snippet ipc/sharedmemory/dialog.cpp 1
The user is then asked to select an image file using
QFileDialog::getOpenFileName(). The selected file is loaded into a
QImage. Using a QImage lets us ensure that the selected file is a
valid image, and it also allows us to immediately display the image
in the dialog using setPixmap().
Next the image is streamed into a QBuffer using a QDataStream. This
gives us the size, which we then use to \l {QSharedMemory::}
{create()} our shared memory segment. Creating a shared memory
segment automatically \l {QSharedMemory::attach()} {attaches} the
segment to the process. Using a QBuffer here lets us get a pointer
to the image data, which we then use to do a memcopy() from the
QBuffer into the shared memory segment.
\snippet ipc/sharedmemory/dialog.cpp 2
Note that we \l {QSharedMemory::} {lock()} the shared memory segment
before we copy into it, and we \l {QSharedMemory::} {unlock()} it
again immediately after the copy. This ensures we have exclusive
access to the shared memory segment to do our memcopy(). If some
other process has the segment lock, then our process will block
until the lock becomes available.
Note also that the function does not \l {QSharedMemory::} {detach()}
from the shared memory segment after the memcopy() and
unlock(). Recall that when the last process detaches from a shared
memory segment, the segment is released by the operating
system. Since this process only one that is attached to the shared
memory segment at the moment, if loadFromFile() detached from the
shared memory segment, the segment would be destroyed before we get
to the next step.
When the function returns, if the file you selected was qt.png, your
first dialog looks like this.
\image sharedmemory-example_2.png Screenshot of the Shared Memory example
In the second dialog, click the \tt {Display Image From Shared
Memory} button. The loadFromMemory() slot is invoked. It first \l
{QSharedMemory::attach()} {attaches} the process to the same shared
memory segment created by the first process. Then it \l
{QSharedMemory::lock()} {locks} the segment for exclusive access and
links a QBuffer to the image data in the shared memory segment. It
then streams the data into a QImage and \l {QSharedMemory::unlock()}
{unlocks} the segment.
\snippet ipc/sharedmemory/dialog.cpp 3
In this case, the function does \l {QSharedMemory::} {detach()} from
the segment, because now we are effectively finished using
it. Finally, the QImage is displayed. At this point, both dialogs
should be showing the same image. When you close the first dialog,
the Dialog destructor calls the QSharedMemory destructor, which
detaches from the shared memory segment. Since this is the last
process to be detached from the segment, the operating system will
now release the shared memory.
*/

View File

@ -0,0 +1,10 @@
requires(qtHaveModule(widgets))
TEMPLATE = subdirs
qtConfig(sharedmemory): SUBDIRS = sharedmemory
qtHaveModule(network) {
QT_FOR_CONFIG += network
qtConfig(localserver): SUBDIRS += localfortuneserver localfortuneclient
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(localfortuneclient LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/ipc/localfortuneclient")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(localfortuneclient
client.cpp client.h
main.cpp
)
set_target_properties(localfortuneclient PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(localfortuneclient PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS localfortuneclient
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,115 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include <QtNetwork>
#include "client.h"
Client::Client(QWidget *parent)
: QDialog(parent),
hostLineEdit(new QLineEdit("fortune")),
getFortuneButton(new QPushButton(tr("Get Fortune"))),
statusLabel(new QLabel(tr("This examples requires that you run the "
"Local Fortune Server example as well."))),
socket(new QLocalSocket(this))
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
QLabel *hostLabel = new QLabel(tr("&Server name:"));
hostLabel->setBuddy(hostLineEdit);
statusLabel->setWordWrap(true);
getFortuneButton->setDefault(true);
QPushButton *quitButton = new QPushButton(tr("Quit"));
QDialogButtonBox *buttonBox = new QDialogButtonBox;
buttonBox->addButton(getFortuneButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
in.setDevice(socket);
in.setVersion(QDataStream::Qt_5_10);
connect(hostLineEdit, &QLineEdit::textChanged,
this, &Client::enableGetFortuneButton);
connect(getFortuneButton, &QPushButton::clicked,
this, &Client::requestNewFortune);
connect(quitButton, &QPushButton::clicked, this, &Client::close);
connect(socket, &QLocalSocket::readyRead, this, &Client::readFortune);
connect(socket, &QLocalSocket::errorOccurred, this, &Client::displayError);
QGridLayout *mainLayout = new QGridLayout(this);
mainLayout->addWidget(hostLabel, 0, 0);
mainLayout->addWidget(hostLineEdit, 0, 1);
mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
setWindowTitle(QGuiApplication::applicationDisplayName());
hostLineEdit->setFocus();
}
void Client::requestNewFortune()
{
getFortuneButton->setEnabled(false);
blockSize = 0;
socket->abort();
socket->connectToServer(hostLineEdit->text());
}
void Client::readFortune()
{
if (blockSize == 0) {
// Relies on the fact that QDataStream serializes a quint32 into
// sizeof(quint32) bytes
if (socket->bytesAvailable() < (int)sizeof(quint32))
return;
in >> blockSize;
}
if (socket->bytesAvailable() < blockSize || in.atEnd())
return;
QString nextFortune;
in >> nextFortune;
if (nextFortune == currentFortune) {
QTimer::singleShot(0, this, &Client::requestNewFortune);
return;
}
currentFortune = nextFortune;
statusLabel->setText(currentFortune);
getFortuneButton->setEnabled(true);
}
void Client::displayError(QLocalSocket::LocalSocketError socketError)
{
switch (socketError) {
case QLocalSocket::ServerNotFoundError:
QMessageBox::information(this, tr("Local Fortune Client"),
tr("The host was not found. Please make sure "
"that the server is running and that the "
"server name is correct."));
break;
case QLocalSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("Local Fortune Client"),
tr("The connection was refused by the peer. "
"Make sure the fortune server is running, "
"and check that the server name "
"is correct."));
break;
case QLocalSocket::PeerClosedError:
break;
default:
QMessageBox::information(this, tr("Local Fortune Client"),
tr("The following error occurred: %1.")
.arg(socket->errorString()));
}
getFortuneButton->setEnabled(true);
}
void Client::enableGetFortuneButton()
{
getFortuneButton->setEnabled(!hostLineEdit->text().isEmpty());
}

View File

@ -0,0 +1,42 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CLIENT_H
#define CLIENT_H
#include <QDialog>
#include <QDataStream>
#include <QLocalSocket>
QT_BEGIN_NAMESPACE
class QLabel;
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
class Client : public QDialog
{
Q_OBJECT
public:
explicit Client(QWidget *parent = nullptr);
private slots:
void requestNewFortune();
void readFortune();
void displayError(QLocalSocket::LocalSocketError socketError);
void enableGetFortuneButton();
private:
QLineEdit *hostLineEdit;
QPushButton *getFortuneButton;
QLabel *statusLabel;
QLocalSocket *socket;
QDataStream in;
quint32 blockSize;
QString currentFortune;
};
#endif

View File

@ -0,0 +1,8 @@
HEADERS = client.h
SOURCES = client.cpp \
main.cpp
QT += network widgets
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/ipc/localfortuneclient
INSTALLS += target

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "client.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGuiApplication::setApplicationDisplayName(Client::tr("Local Fortune Client"));
Client client;
client.show();
return app.exec();
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(localfortuneserver LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/ipc/localfortuneserver")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)
qt_standard_project_setup()
qt_add_executable(localfortuneserver
main.cpp
server.cpp server.h
)
set_target_properties(localfortuneserver PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(localfortuneserver PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Widgets
)
install(TARGETS localfortuneserver
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,8 @@
HEADERS = server.h
SOURCES = server.cpp \
main.cpp
QT += network widgets
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/ipc/localfortuneserver
INSTALLS += target

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "server.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGuiApplication::setApplicationDisplayName(Server::tr("Local Fortune Server"));
Server server;
server.show();
return app.exec();
}

View File

@ -0,0 +1,70 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "server.h"
#include <QtWidgets>
#include <QtNetwork>
Server::Server(QWidget *parent)
: QDialog(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
server = new QLocalServer(this);
if (!server->listen("fortune")) {
QMessageBox::critical(this, tr("Local Fortune Server"),
tr("Unable to start the server: %1.")
.arg(server->errorString()));
close();
return;
}
QLabel *statusLabel = new QLabel;
statusLabel->setWordWrap(true);
statusLabel->setText(tr("The server is running.\n"
"Run the Local Fortune Client example now."));
fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
QPushButton *quitButton = new QPushButton(tr("Quit"));
quitButton->setAutoDefault(false);
connect(quitButton, &QPushButton::clicked, this, &Server::close);
connect(server, &QLocalServer::newConnection, this, &Server::sendFortune);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(quitButton);
buttonLayout->addStretch(1);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(buttonLayout);
setWindowTitle(QGuiApplication::applicationDisplayName());
}
void Server::sendFortune()
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_10);
const int fortuneIndex = QRandomGenerator::global()->bounded(0, fortunes.size());
const QString &message = fortunes.at(fortuneIndex);
out << quint32(message.size());
out << message;
QLocalSocket *clientConnection = server->nextPendingConnection();
connect(clientConnection, &QLocalSocket::disconnected,
clientConnection, &QLocalSocket::deleteLater);
clientConnection->write(block);
clientConnection->flush();
clientConnection->disconnectFromServer();
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef SERVER_H
#define SERVER_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
class QLocalServer;
QT_END_NAMESPACE
class Server : public QDialog
{
Q_OBJECT
public:
explicit Server(QWidget *parent = nullptr);
private slots:
void sendFortune();
private:
QLocalServer *server;
QStringList fortunes;
};
#endif

View File

@ -0,0 +1,37 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(sharedmemory LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/ipc/sharedmemory")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(sharedmemory
dialog.cpp dialog.h dialog.ui
main.cpp
)
set_target_properties(sharedmemory PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(sharedmemory PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
install(TARGETS sharedmemory
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,155 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "dialog.h"
#include <QFileDialog>
#include <QBuffer>
/*!
\class Dialog
\brief This class is a simple example of how to use QSharedMemory.
It is a simple dialog that presents a few buttons. To compile the
example, run make in qt/examples/ipc. Then run the executable twice
to create two processes running the dialog. In one of the processes,
press the button to load an image into a shared memory segment, and
then select an image file to load. Once the first process has loaded
and displayed the image, in the second process, press the button to
read the same image from shared memory. The second process displays
the same image loaded from its new loaction in shared memory.
*/
/*!
The class contains a data member \l {QSharedMemory} {sharedMemory},
which is initialized with the key "QSharedMemoryExample" to force
all instances of Dialog to access the same shared memory segment.
The constructor also connects the clicked() signal from each of the
three dialog buttons to the slot function appropriate for handling
each button.
*/
//! [0]
Dialog::Dialog(QWidget *parent)
: QDialog(parent), sharedMemory("QSharedMemoryExample")
{
ui.setupUi(this);
connect(ui.loadFromFileButton, &QPushButton::clicked,
this, &Dialog::loadFromFile);
connect(ui.loadFromSharedMemoryButton, &QPushButton::clicked,
this, &Dialog::loadFromMemory);
setWindowTitle(tr("SharedMemory Example"));
}
//! [0]
/*!
This slot function is called when the \tt {Load Image From File...}
button is pressed on the firs Dialog process. First, it tests
whether the process is already connected to a shared memory segment
and, if so, detaches from that segment. This ensures that we always
start the example from the beginning if we run it multiple times
with the same two Dialog processes. After detaching from an existing
shared memory segment, the user is prompted to select an image file.
The selected file is loaded into a QImage. The QImage is displayed
in the Dialog and streamed into a QBuffer with a QDataStream.
Next, it gets a new shared memory segment from the system big enough
to hold the image data in the QBuffer, and it locks the segment to
prevent the second Dialog process from accessing it. Then it copies
the image from the QBuffer into the shared memory segment. Finally,
it unlocks the shared memory segment so the second Dialog process
can access it.
After this function runs, the user is expected to press the \tt
{Load Image from Shared Memory} button on the second Dialog process.
\sa loadFromMemory()
*/
//! [1]
void Dialog::loadFromFile()
{
if (sharedMemory.isAttached())
detach();
ui.label->setText(tr("Select an image file"));
QString fileName = QFileDialog::getOpenFileName(0, QString(), QString(),
tr("Images (*.png *.xpm *.jpg)"));
QImage image;
if (!image.load(fileName)) {
ui.label->setText(tr("Selected file is not an image, please select another."));
return;
}
ui.label->setPixmap(QPixmap::fromImage(image));
//! [1] //! [2]
// load into shared memory
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);
out << image;
int size = buffer.size();
if (!sharedMemory.create(size)) {
if (sharedMemory.error() == QSharedMemory::AlreadyExists) {
sharedMemory.attach();
} else {
ui.label->setText(tr("Unable to create or attach to shared memory segment: %1")
.arg(sharedMemory.errorString()));
return;
}
}
sharedMemory.lock();
char *to = (char*)sharedMemory.data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(sharedMemory.size(), size));
sharedMemory.unlock();
}
//! [2]
/*!
This slot function is called in the second Dialog process, when the
user presses the \tt {Load Image from Shared Memory} button. First,
it attaches the process to the shared memory segment created by the
first Dialog process. Then it locks the segment for exclusive
access, copies the image data from the segment into a QBuffer, and
streams the QBuffer into a QImage. Then it unlocks the shared memory
segment, detaches from it, and finally displays the QImage in the
Dialog.
\sa loadFromFile()
*/
//! [3]
void Dialog::loadFromMemory()
{
if (!sharedMemory.attach()) {
ui.label->setText(tr("Unable to attach to shared memory segment.\n" \
"Load an image first."));
return;
}
QBuffer buffer;
QDataStream in(&buffer);
QImage image;
sharedMemory.lock();
buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
buffer.open(QBuffer::ReadOnly);
in >> image;
sharedMemory.unlock();
sharedMemory.detach();
ui.label->setPixmap(QPixmap::fromImage(image));
}
//! [3]
/*!
This private function is called by the destructor to detach the
process from its shared memory segment. When the last process
detaches from a shared memory segment, the system releases the
shared memory.
*/
void Dialog::detach()
{
if (!sharedMemory.detach())
ui.label->setText(tr("Unable to detach from shared memory."));
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QSharedMemory>
#include "ui_dialog.h"
//! [0]
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
public slots:
void loadFromFile();
void loadFromMemory();
private:
void detach();
private:
Ui::Dialog ui;
QSharedMemory sharedMemory;
};
//! [0]
#endif

View File

@ -0,0 +1,47 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>451</width>
<height>322</height>
</rect>
</property>
<property name="windowTitle" >
<string>Dialog</string>
</property>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QPushButton" name="loadFromFileButton" >
<property name="text" >
<string>Load Image From File...</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Launch two of these dialogs. In the first, press the top button and load an image from a file. In the second, press the bottom button and display the loaded image from shared memory.</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QPushButton" name="loadFromSharedMemoryButton" >
<property name="text" >
<string>Display Image From Shared Memory</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "dialog.h"
//! [0]
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
Dialog dialog;
dialog.show();
return application.exec();
}
//! [0]

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,16 @@
QT += widgets
requires(qtConfig(filedialog))
SOURCES += main.cpp \
dialog.cpp
HEADERS += dialog.h
# Forms and resources
FORMS += dialog.ui
EXAMPLE_FILES = *.png
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/ipc/sharedmemory
INSTALLS += target

View File

@ -0,0 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(TARGET Qt6::Widgets)
qt_internal_add_example(mimetypebrowser)
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1,30 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mimetypes/mimetypebrowser
\ingroup examples-mimetype
\title MIME Type Browser Example
\brief Shows the hierarchy of MIME types and
can be used to determine the MIME type of a file.
\image mimetypebrowser.png Screenshot of the MIME Type Browser Example
\e {MIME Type Browser} is intended to be a tool for exploring MIME types
rather than an example showing the typical usage of Qt's MIME API.
\include examples-run.qdocinc
\section1 Main Window
The main window consists of a tree view displaying the hierarchy of MIME types
based on the model MimetypeModel inheriting QStandardItemModel on the left and
a QTextBrowser for showing detailed information about the selected MIME type
on the right.
It has a main menu with an option \uicontrol{File/Detect File Type}, which
lets you pick a file and then displays its MIME type.
For more information, see QMimeType and QMimeDatabase.
*/

View File

@ -0,0 +1,38 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(mimetypebrowser LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/mimetypes/mimetypebrowser")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(mimetypebrowser
main.cpp
mainwindow.cpp mainwindow.h
mimetypemodel.cpp mimetypemodel.h
)
set_target_properties(mimetypebrowser PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE FALSE
)
target_link_libraries(mimetypebrowser PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
install(TARGETS mimetypebrowser
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,29 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
#include <QApplication>
#include <QScreen>
#include <QCommandLineParser>
#include <QCommandLineOption>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
parser.process(app);
MainWindow mainWindow;
const QRect availableGeometry = mainWindow.screen()->availableGeometry();
mainWindow.resize(availableGeometry.width() / 3, availableGeometry.height() / 2);
mainWindow.show();
return app.exec();
}

View File

@ -0,0 +1,149 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
#include "mimetypemodel.h"
#include <QAction>
#include <QApplication>
#include <QFileDialog>
#include <QInputDialog>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QSplitter>
#include <QStatusBar>
#include <QTextEdit>
#include <QTreeView>
#include <QFileInfo>
#include <QItemSelectionModel>
#include <QMimeDatabase>
#include <QMimeType>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, m_model(new MimetypeModel(this))
, m_treeView(new QTreeView(this))
, m_detailsText(new QTextEdit(this))
, m_findIndex(0)
{
setWindowTitle(tr("Qt Mime Database Browser"));
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
QAction *detectFileAction =
fileMenu->addAction(tr("&Detect File Type..."), this, &MainWindow::detectFile);
detectFileAction->setShortcuts(QKeySequence::Open);
QAction *exitAction = fileMenu->addAction(tr("E&xit"), qApp, &QApplication::quit);
exitAction->setShortcuts(QKeySequence::Quit);
QMenu *findMenu = menuBar()->addMenu(tr("&Edit"));
QAction *findAction =
findMenu->addAction(tr("&Find..."), this, &MainWindow::find);
findAction->setShortcuts(QKeySequence::Find);
m_findNextAction = findMenu->addAction(tr("Find &Next"), this, &MainWindow::findNext);
m_findNextAction->setShortcuts(QKeySequence::FindNext);
m_findPreviousAction = findMenu->addAction(tr("Find &Previous"), this, &MainWindow::findPrevious);
m_findPreviousAction->setShortcuts(QKeySequence::FindPrevious);
menuBar()->addMenu(tr("&About"))->addAction(tr("&About Qt"), qApp, &QApplication::aboutQt);
QSplitter *centralSplitter = new QSplitter(this);
setCentralWidget(centralSplitter);
m_treeView->setUniformRowHeights(true);
m_treeView->setModel(m_model);
const auto items = m_model->findItems("application/octet-stream", Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive);
if (!items.isEmpty())
m_treeView->expand(m_model->indexFromItem(items.constFirst()));
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &MainWindow::currentChanged);
centralSplitter->addWidget(m_treeView);
m_detailsText->setReadOnly(true);
centralSplitter->addWidget(m_detailsText);
updateFindActions();
}
void MainWindow::currentChanged(const QModelIndex &index)
{
if (index.isValid())
m_detailsText->setText(MimetypeModel::formatMimeTypeInfo(m_model->mimeType(index)));
else
m_detailsText->clear();
}
void MainWindow::selectAndGoTo(const QModelIndex &index)
{
m_treeView->scrollTo(index, QAbstractItemView::PositionAtCenter);
m_treeView->setCurrentIndex(index);
}
void MainWindow::detectFile()
{
const QString fileName = QFileDialog::getOpenFileName(this, tr("Choose File"));
if (fileName.isEmpty())
return;
QMimeDatabase mimeDatabase;
const QFileInfo fi(fileName);
const QMimeType mimeType = mimeDatabase.mimeTypeForFile(fi);
const QModelIndex index = mimeType.isValid()
? m_model->indexForMimeType(mimeType.name()) : QModelIndex();
if (index.isValid()) {
statusBar()->showMessage(tr("\"%1\" is of type \"%2\"").arg(fi.fileName(), mimeType.name()));
selectAndGoTo(index);
} else {
QMessageBox::information(this, tr("Unknown File Type"),
tr("The type of %1 could not be determined.")
.arg(QDir::toNativeSeparators(fileName)));
}
}
void MainWindow::updateFindActions()
{
const bool findNextPreviousEnabled = m_findMatches.size() > 1;
m_findNextAction->setEnabled(findNextPreviousEnabled);
m_findPreviousAction->setEnabled(findNextPreviousEnabled);
}
void MainWindow::findPrevious()
{
if (--m_findIndex < 0)
m_findIndex = m_findMatches.size() - 1;
if (m_findIndex >= 0)
selectAndGoTo(m_findMatches.at(m_findIndex));
}
void MainWindow::findNext()
{
if (++m_findIndex >= m_findMatches.size())
m_findIndex = 0;
if (m_findIndex < m_findMatches.size())
selectAndGoTo(m_findMatches.at(m_findIndex));
}
void MainWindow::find()
{
QInputDialog inputDialog(this);
inputDialog.setWindowTitle(tr("Find"));
inputDialog.setLabelText(tr("Text:"));
if (inputDialog.exec() != QDialog::Accepted)
return;
const QString value = inputDialog.textValue().trimmed();
if (value.isEmpty())
return;
m_findMatches.clear();
m_findIndex = 0;
const QList<QStandardItem *> items =
m_model->findItems(value, Qt::MatchContains | Qt::MatchFixedString | Qt::MatchRecursive);
for (const QStandardItem *item : items)
m_findMatches.append(m_model->indexFromItem(item));
statusBar()->showMessage(tr("%n mime types match \"%1\".", 0, m_findMatches.size()).arg(value));
updateFindActions();
if (!m_findMatches.isEmpty())
selectAndGoTo(m_findMatches.constFirst());
}

View File

@ -0,0 +1,42 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QModelIndexList>
QT_FORWARD_DECLARE_CLASS(QAction)
QT_FORWARD_DECLARE_CLASS(QTextEdit)
QT_FORWARD_DECLARE_CLASS(QTreeView)
class MimetypeModel;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
private slots:
void currentChanged(const QModelIndex &);
void detectFile();
void find();
void findNext();
void findPrevious();
private:
void selectAndGoTo(const QModelIndex &index);
void updateFindActions();
MimetypeModel *m_model;
QTreeView *m_treeView;
QTextEdit *m_detailsText;
QAction *m_findNextAction;
QAction *m_findPreviousAction;
QModelIndexList m_findMatches;
int m_findIndex;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,17 @@
TEMPLATE = app
QT += widgets
requires(qtConfig(treeview))
CONFIG -= app_bundle
CONFIG += c++11
SOURCES += \
main.cpp \
mimetypemodel.cpp \
mainwindow.cpp
HEADERS += \
mimetypemodel.h \
mainwindow.h
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/mimetypes/mimetypebrowser
INSTALLS += target

View File

@ -0,0 +1,164 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mimetypemodel.h"
#include <QDebug>
#include <QIcon>
#include <QMimeDatabase>
#include <QTextStream>
#include <QVariant>
#include <algorithm>
Q_DECLARE_METATYPE(QMimeType)
typedef QList<QStandardItem *> StandardItemList;
enum { mimeTypeRole = Qt::UserRole + 1, iconQueriedRole = Qt::UserRole + 2 };
QT_BEGIN_NAMESPACE
bool operator<(const QMimeType &t1, const QMimeType &t2)
{
return t1.name() < t2.name();
}
QT_END_NAMESPACE
static StandardItemList createRow(const QMimeType &t)
{
const QVariant v = QVariant::fromValue(t);
QStandardItem *nameItem = new QStandardItem(t.name());
const Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
nameItem->setData(v, mimeTypeRole);
nameItem->setData(QVariant(false), iconQueriedRole);
nameItem->setFlags(flags);
nameItem->setToolTip(t.comment());
return StandardItemList{nameItem};
}
MimetypeModel::MimetypeModel(QObject *parent)
: QStandardItemModel(0, ColumnCount, parent)
{
setHorizontalHeaderLabels(QStringList{tr("Name")});
populate();
}
QVariant MimetypeModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DecorationRole || !index.isValid() || index.data(iconQueriedRole).toBool())
return QStandardItemModel::data(index, role);
QStandardItem *item = itemFromIndex(index);
const QString iconName = qvariant_cast<QMimeType>(item->data(mimeTypeRole)).iconName();
if (!iconName.isEmpty())
item->setIcon(QIcon::fromTheme(iconName));
item->setData(QVariant(true), iconQueriedRole);
return item->icon();
}
QMimeType MimetypeModel::mimeType(const QModelIndex &index) const
{
return qvariant_cast<QMimeType>(index.data(mimeTypeRole));
}
void MimetypeModel::populate()
{
typedef QList<QMimeType>::Iterator Iterator;
QMimeDatabase mimeDatabase;
QList<QMimeType> allTypes = mimeDatabase.allMimeTypes();
// Move top level types to rear end of list, sort this partition,
// create top level items and truncate the list.
Iterator end = allTypes.end();
const Iterator topLevelStart =
std::stable_partition(allTypes.begin(), end,
[](const QMimeType &t) { return !t.parentMimeTypes().isEmpty(); });
std::stable_sort(topLevelStart, end);
for (Iterator it = topLevelStart; it != end; ++it) {
const StandardItemList row = createRow(*it);
appendRow(row);
m_nameIndexHash.insert(it->name(), indexFromItem(row.constFirst()));
}
allTypes.erase(topLevelStart, end);
while (!allTypes.isEmpty()) {
// Find a type inheriting one that is already in the model.
end = allTypes.end();
auto nameIndexIt = m_nameIndexHash.constEnd();
for (Iterator it = allTypes.begin(); it != end; ++it) {
nameIndexIt = m_nameIndexHash.constFind(it->parentMimeTypes().constFirst());
if (nameIndexIt != m_nameIndexHash.constEnd())
break;
}
if (nameIndexIt == m_nameIndexHash.constEnd()) {
qWarning() << "Orphaned mime types:" << allTypes;
break;
}
// Move types inheriting the parent type to rear end of list, sort this partition,
// append the items to parent and truncate the list.
const QString &parentName = nameIndexIt.key();
const Iterator start =
std::stable_partition(allTypes.begin(), end, [parentName](const QMimeType &t)
{ return !t.parentMimeTypes().contains(parentName); });
std::stable_sort(start, end);
QStandardItem *parentItem = itemFromIndex(nameIndexIt.value());
for (Iterator it = start; it != end; ++it) {
const StandardItemList row = createRow(*it);
parentItem->appendRow(row);
m_nameIndexHash.insert(it->name(), indexFromItem(row.constFirst()));
}
allTypes.erase(start, end);
}
}
QTextStream &operator<<(QTextStream &stream, const QStringList &list)
{
for (int i = 0, size = list.size(); i < size; ++i) {
if (i)
stream << ", ";
stream << list.at(i);
}
return stream;
}
QString MimetypeModel::formatMimeTypeInfo(const QMimeType &t)
{
QString result;
QTextStream str(&result);
str << "<html><head/><body><h3><center>" << t.name() << "</center></h3><br><table>";
const QStringList &aliases = t.aliases();
if (!aliases.isEmpty())
str << "<tr><td>Aliases:</td><td>" << " (" << aliases << ')';
str << "</td></tr>"
<< "<tr><td>Comment:</td><td>" << t.comment() << "</td></tr>"
<< "<tr><td>Icon name:</td><td>" << t.iconName() << "</td></tr>"
<< "<tr><td>Generic icon name</td><td>" << t.genericIconName() << "</td></tr>";
const QString &filter = t.filterString();
if (!filter.isEmpty())
str << "<tr><td>Filter:</td><td>" << t.filterString() << "</td></tr>";
const QStringList &patterns = t.globPatterns();
if (!patterns.isEmpty())
str << "<tr><td>Glob patterns:</td><td>" << patterns << "</td></tr>";
const QStringList &parentMimeTypes = t.parentMimeTypes();
if (!parentMimeTypes.isEmpty())
str << "<tr><td>Parent types:</td><td>" << t.parentMimeTypes() << "</td></tr>";
QStringList suffixes = t.suffixes();
if (!suffixes.isEmpty()) {
str << "<tr><td>Suffixes:</td><td>";
const QString &preferredSuffix = t.preferredSuffix();
if (!preferredSuffix.isEmpty()) {
suffixes.removeOne(preferredSuffix);
str << "<b>" << preferredSuffix << "</b> ";
}
str << suffixes << "</td></tr>";
}
str << "</table></body></html>";
return result;
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MIMETYPEMODEL_H
#define MIMETYPEMODEL_H
#include <QStandardItemModel>
#include <QHash>
QT_FORWARD_DECLARE_CLASS(QMimeType)
class MimetypeModel : public QStandardItemModel
{
Q_OBJECT
public:
enum Columns { NameColumn, ColumnCount };
explicit MimetypeModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
QMimeType mimeType(const QModelIndex &) const;
QModelIndex indexForMimeType(const QString &name) const
{ return m_nameIndexHash.value(name); }
static QString formatMimeTypeInfo(const QMimeType &);
private:
typedef QHash<QString, QModelIndex> NameIndexHash;
void populate();
NameIndexHash m_nameIndexHash;
};
#endif // MIMETYPEMODEL_H

View File

@ -0,0 +1,4 @@
TEMPLATE = subdirs
qtHaveModule(widgets): SUBDIRS += \
mimetypebrowser

View File

@ -0,0 +1,46 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(permissions LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/permissions")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(permissions
MANUAL_FINALIZATION
main.cpp
)
set_target_properties(permissions PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.examples.permissions"
QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
)
target_link_libraries(permissions PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
install(TARGETS permissions
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)
if(APPLE AND NOT CMAKE_GENERATOR STREQUAL "Xcode")
add_custom_command(TARGET permissions
POST_BUILD COMMAND codesign -s - permissions.app)
endif()
qt_finalize_executable(permissions)

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Testing BluetoothAlways</string>
<key>NSCalendarsUsageDescription</key>
<string>Testing Calendars</string>
<key>NSCameraUsageDescription</key>
<string>Testing Camera</string>
<key>NSContactsUsageDescription</key>
<string>Testing Contacts</string>
<key>NSHealthShareUsageDescription</key>
<string>Testing HealthShare</string>
<key>NSHealthUpdateUsageDescription</key>
<string>Testing HealthUpdate</string>
<key>NSLocationUsageDescription</key>
<string>Testing Location on macOS</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Testing Location when in use on iOS</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Testing Location always and when in use on iOS</string>
<key>NSMicrophoneUsageDescription</key>
<string>Testing Microphone</string>
</dict>
</plist>

View File

@ -0,0 +1,53 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.qtproject.example"
android:installLocation="auto"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:versionName="-- %%INSERT_VERSION_NAME%% --">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<!-- %%INSERT_PERMISSIONS -->
<!-- %%INSERT_FEATURES -->
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />
<application
android:name="org.qtproject.qt.android.bindings.QtApplication"
android:hardwareAccelerated="true"
android:label="-- %%INSERT_APP_NAME%% --"
android:requestLegacyExternalStorage="true"
android:allowNativeHeapPointerTagging="false"
android:allowBackup="true"
android:fullBackupOnly="false">
<activity
android:name="org.qtproject.qt.android.bindings.QtActivity"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:label="-- %%INSERT_APP_NAME%% --"
android:launchMode="singleTop"
android:screenOrientation="unspecified"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
</activity>
</application>
</manifest>

View File

@ -0,0 +1,87 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtCore/qmetaobject.h>
#include <QtWidgets/qapplication.h>
#include <QtWidgets/qwidget.h>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qlayout.h>
#include <QtWidgets/qmessagebox.h>
#if !QT_CONFIG(permissions)
#error "This example requires the permissions feature, which is not enabled on this platform"
#endif
#include <QtCore/qpermissions.h>
class PermissionWidget : public QWidget
{
Q_OBJECT
public:
explicit PermissionWidget(QWidget *parent = nullptr) : QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
static const QPermission permissions[] = {
QCameraPermission{},
QMicrophonePermission{},
QBluetoothPermission{},
QContactsPermission{},
QCalendarPermission{},
QLocationPermission{}
};
for (auto permission : permissions) {
auto permissionName = QString::fromLatin1(permission.type().name());
QPushButton *button = new QPushButton(permissionName.sliced(1, permissionName.length() - 11));
connect(button, &QPushButton::clicked, this, &PermissionWidget::buttonClicked);
button->setProperty("permission", QVariant::fromValue(permission));
layout->addWidget(button);
}
QPalette pal = palette();
pal.setBrush(QPalette::Window, QGradient(QGradient::HappyAcid));
setPalette(pal);
}
private:
void buttonClicked()
{
auto *button = static_cast<QPushButton*>(sender());
auto permission = button->property("permission").value<QPermission>();
Q_ASSERT(permission.type().isValid());
switch (qApp->checkPermission(permission)) {
case Qt::PermissionStatus::Undetermined:
qApp->requestPermission(permission, this,
[button](const QPermission &permission) {
Q_UNUSED(permission);
emit button->clicked(); // Try again
}
);
return;
case Qt::PermissionStatus::Denied:
QMessageBox::warning(this, button->text(),
tr("Permission is needed to use %1. Please grant permission "\
"to this application in the system settings.").arg(button->text()));
return;
case Qt::PermissionStatus::Granted:
break; // Proceed
}
// All good, can use the feature
QMessageBox::information(this, button->text(),
tr("Accessing %1").arg(button->text()));
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
PermissionWidget widget;
widget.show();
return app.exec();
}
#include "main.moc"

View File

@ -0,0 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(ANDROID)
add_subdirectory(androidnotifier)
endif()

View File

@ -0,0 +1,53 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(androidnotifier LANGUAGES CXX)
if(NOT ANDROID)
message(FATAL_ERROR "Example only works on Android")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_standard_project_setup()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/platform/androidnotifier")
qt_add_executable(androidnotifier
MANUAL_FINALIZATION
main.cpp
notificationclient.cpp
notificationclient.h
)
target_link_libraries(androidnotifier PRIVATE
Qt6::Widgets
)
set_property(TARGET androidnotifier APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/android)
qt_finalize_executable(androidnotifier)
set(qml_resource_files
"images/happy.png"
"images/sad.png"
)
qt_add_resources(androidnotifier "main"
PREFIX
"/"
FILES
${qml_resource_files}
)
install(TARGETS androidnotifier
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,54 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.qtproject.example.androidnotifier"
android:installLocation="auto"
android:versionCode="1"
android:versionName="1.0">
<!-- The comment below will be replaced with dependencies permissions upon deployment.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The comment below will be replaced with dependencies permissions upon deployment.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true" />
<application
android:name="org.qtproject.qt.android.bindings.QtApplication"
android:extractNativeLibs="true"
android:hardwareAccelerated="true"
android:label="Qt Notifier"
android:requestLegacyExternalStorage="true"
android:icon="@drawable/icon"
android:allowBackup="true"
android:fullBackupOnly="false">
<activity
android:name="org.qtproject.qt.android.bindings.QtActivity"
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:label="Qt Notifier"
android:launchMode="singleTop"
android:screenOrientation="unspecified"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data
android:name="android.app.background_running"
android:value="false"/>
<meta-data
android:name="android.app.extract_android_style"
android:value="none" />
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,46 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
package org.qtproject.example.androidnotifier;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.BitmapFactory;
import android.app.NotificationChannel;
public class NotificationClient
{
public static void notify(Context context, String message) {
try {
NotificationManager m_notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder m_builder;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel notificationChannel;
notificationChannel = new NotificationChannel("Qt", "Qt Notifier", importance);
m_notificationManager.createNotificationChannel(notificationChannel);
m_builder = new Notification.Builder(context, notificationChannel.getId());
} else {
m_builder = new Notification.Builder(context);
}
Bitmap icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
m_builder.setSmallIcon(R.drawable.icon)
.setLargeIcon(icon)
.setContentTitle("A message from Qt!")
.setContentText(message)
.setDefaults(Notification.DEFAULT_SOUND)
.setColor(Color.GREEN)
.setAutoCancel(true);
m_notificationManager.notify(0, m_builder.build());
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,20 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
SOURCES += \
main.cpp \
notificationclient.cpp
HEADERS += \
notificationclient.h
RESOURCES += \
main.qrc
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/platform/androidnotifier
INSTALLS += target
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
OTHER_FILES += \
android/src/org/qtproject/example/androidnotifier/NotificationClient.java \
android/AndroidManifest.xml

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,60 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\title Qt Android Notifier
\example platform/androidnotifier
\brief Demonstrates calling Java code from Qt in an Android application.
\image androidnotifier.png
This example demonstrates how to add a custom Java class to an Android
application, and how to call it using the JNI convenience APIs in Qt.
Click on one of the smiley faces to send a notification in the status bar
of the Android screen.
\include examples-run.qdocinc
\section1 Calling Java Methods from C++ Code
We define a custom Java class called \c NotificationClient in the
NotificationClient.java file:
\quotefromfile platform/androidnotifier/android/src/org/qtproject/example/androidnotifier/NotificationClient.java
\skipto org.qtproject.example.androidnotifier
\printuntil /^\}/
In the NotificationClient C++ class header file, \c notificationclient.h, we
declare a simple C++ API to display notifications on an Android device. It
consists of a single string property, \c notification, and a slot,
\c updateAndroidNotification(), that calls the Java code:
\snippet platform/androidnotifier/notificationclient.h Qt Notification Class
We connect the \c notificationChanged() signal to the
\c updateAndroidNotification() slot to update the notification text when the
\c notification text changes:
\snippet platform/androidnotifier/notificationclient.cpp notification changed signal
The \c updateAndroidNotification() function calls the Java method responsible
for sending the notification from the Android platform APIs. First, we construct
a Java string \c jstring from the notification string, then pass the \c jstring
object as a parameter to the \c notify() method in Java:
\snippet platform/androidnotifier/notificationclient.cpp Send notification message to Java
The call to the Java meethod use \l QJniObject which relies on the Java Native
Interface (JNI) APIs to communicate with Java. Also, in the previous snippet,
we are passing the app's context object which the the static Java code can use
to tap into the app's specific properties and APIs.
To make sure our smiley buttons do what they are supposed to, we add the
the following code to change the notification text if either of them are
clicked:
\snippet platform/androidnotifier/main.cpp Connect button signals
\sa {Qt for Android}
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,56 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "notificationclient.h"
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLabel>
#include <QFont>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
QPushButton happyButton;
happyButton.setIcon(QIcon(":/images/happy.png"));
happyButton.setIconSize(QSize(happyButton.width(), 120));
QPushButton sadButton;
sadButton.setIcon(QIcon(":/images/sad.png"));
sadButton.setIconSize(QSize(sadButton.width(), 120));
QVBoxLayout mainLayout;
QHBoxLayout labelLayout;
QLabel label = QLabel("Click a smiley to notify your mood");
QFont font = label.font();
font.setPointSize(20);
label.setFont(font);
labelLayout.addWidget(&label);
labelLayout.setAlignment(Qt::AlignHCenter);
mainLayout.addLayout(&labelLayout);
QHBoxLayout smileysLayout;
smileysLayout.addWidget(&sadButton);
smileysLayout.addWidget(&happyButton);
smileysLayout.setAlignment(Qt::AlignCenter);
mainLayout.addLayout(&smileysLayout);
widget.setLayout(&mainLayout);
//! [Connect button signals]
QObject::connect(&happyButton, &QPushButton::clicked, []() {
NotificationClient().setNotification("The user is happy!");
});
QObject::connect(&sadButton, &QPushButton::clicked, []() {
NotificationClient().setNotification("The user is sad!");
});
//! [Connect button signals]
widget.show();
return a.exec();
}

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>images/happy.png</file>
<file>images/sad.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,43 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "notificationclient.h"
#include <QtCore/qjniobject.h>
#include <QtCore/qcoreapplication.h>
NotificationClient::NotificationClient(QObject *parent)
: QObject(parent)
{
connect(this, &NotificationClient::notificationChanged,
this, &NotificationClient::updateAndroidNotification);
}
void NotificationClient::setNotification(const QString &notification)
{
if (m_notification == notification)
return;
//! [notification changed signal]
m_notification = notification;
emit notificationChanged();
//! [notification changed signal]
}
QString NotificationClient::notification() const
{
return m_notification;
}
//! [Send notification message to Java]
void NotificationClient::updateAndroidNotification()
{
QJniObject javaNotification = QJniObject::fromString(m_notification);
QJniObject::callStaticMethod<void>(
"org/qtproject/example/androidnotifier/NotificationClient",
"notify",
"(Landroid/content/Context;Ljava/lang/String;)V",
QNativeInterface::QAndroidApplication::context(),
javaNotification.object<jstring>());
}
//! [Send notification message to Java]

View File

@ -0,0 +1,29 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef NOTIFICATIONCLIENT_H
#define NOTIFICATIONCLIENT_H
#include <QObject>
//! [Qt Notification Class]
class NotificationClient : public QObject
{
Q_OBJECT
public:
explicit NotificationClient(QObject *parent = 0);
void setNotification(const QString &notification);
QString notification() const;
signals:
void notificationChanged();
private slots:
void updateAndroidNotification();
private:
QString m_notification;
};
//! [Qt Notification Class]
#endif // NOTIFICATIONCLIENT_H

View File

@ -0,0 +1,4 @@
TEMPLATE = subdirs
CONFIG += no_docs_target
android: SUBDIRS += androidnotifier

View File

@ -0,0 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_example(cbordump)
qt_internal_add_example(convert)
qt_internal_add_example(savegame)
if(TARGET Qt6::Network AND TARGET Qt6::Widgets)
qt_internal_add_example(rsslisting)
endif()
if(TARGET Qt6::Widgets)
qt_internal_add_example(streambookmarks)
endif()

View File

@ -0,0 +1,29 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(cbordump LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/cbordump")
find_package(Qt6 REQUIRED COMPONENTS Core)
qt_standard_project_setup()
qt_add_executable(cbordump
main.cpp
)
target_link_libraries(cbordump PRIVATE
Qt6::Core
)
install(TARGETS cbordump
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,13 @@
QT += core
QT -= gui
TARGET = cbordump
CONFIG += cmdline
TEMPLATE = app
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/cbordump
INSTALLS += target
SOURCES += main.cpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,54 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example serialization/cbordump
\examplecategory {Input/Output}
\title Parsing and displaying CBOR data
\brief A demonstration of how to parse files in CBOR format.
This example shows how to use the QCborStreamReader class directly to parse
CBOR content. The \c cbordump program reads content in CBOR format from
files or standard input and dumps the decoded content to stdout in a
human-readable format. It can output in CBOR diagnostic notation (which is
similar to JSON), or it can produce a verbose output where each byte input
is displayed with its encoding beside it.
\sa QCborStreamReader
\image cbordump.png
\section1 The CborDumper Class
The CborDumper class contains a QCborStreamReader object that is initialized
using the QFile object argument passed to the CborDumper constructor. Based
on the arguments the dump function calls either dumpOne() or
dumpOneDetailed() to dump the contents to standard output,
\snippet serialization/cbordump/main.cpp 0
\section2 The dumpOne() Function
Switching on QCborStreamReader::type() enables printing appropriate to the
type of the current value in the stream. If the type is an array or map, the
value's content is iterated over, and for each entry the dumpOne() function
is called recursively with a higher indentation argument. If the type is a
tag, it is printed out and dumpOne() is called once without increasing the
indentation argument.
\section2 The dumpOneDetailed() Function
This function dumps out both the incoming bytes and the decoded contents
on the same line. It uses lambda functions to print out the bytes and
decoded content, but otherwise has a similar structure as dumpOne().
\section1 CborTagDescription
The \c tagDescriptions table, describing the CBOR tags available, is
automatically generated from an XML file available from the iana.org
website. When \c dumpOneDetailed() reports a tag, it uses its description
from this table.
\sa {CBOR Support in Qt}
*/

View File

@ -0,0 +1,859 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QCborStreamReader>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QCoreApplication>
#include <QFile>
#include <QLocale>
#include <QStack>
#include <locale.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
/*
* To regenerate:
* curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml
* xsltproc tag-transform.xslt cbor-tags.xml
*
* The XHTML URL mentioned in the comment below is a human-readable version of
* the same resource.
*/
/* TODO (if possible): fix XSLT to replace each newline and surrounding space in
a semantics entry with a single space, instead of using a raw string to wrap
each, propagating the spacing from the XML to the output of cbordump. Also
auto-purge dangling spaces from the ends of generated lines.
*/
// GENERATED CODE
struct CborTagDescription
{
QCborTag tag;
const char *description; // with space and parentheses
};
// CBOR Tags
static const CborTagDescription tagDescriptions[] = {
// from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
{ QCborTag(0),
R"r( (Standard date/time string; see Section 3.4.1 [RFC8949]))r" },
{ QCborTag(1),
R"r( (Epoch-based date/time; see Section 3.4.2 [RFC8949]))r" },
{ QCborTag(2),
R"r( (Positive bignum; see Section 3.4.3 [RFC8949]))r" },
{ QCborTag(3),
R"r( (Negative bignum; see Section 3.4.3 [RFC8949]))r" },
{ QCborTag(4),
R"r( (Decimal fraction; see Section 3.4.4 [RFC8949]))r" },
{ QCborTag(5),
R"r( (Bigfloat; see Section 3.4.4 [RFC8949]))r" },
{ QCborTag(16),
R"r( (COSE Single Recipient Encrypted Data Object [RFC9052]))r" },
{ QCborTag(17),
R"r( (COSE Mac w/o Recipients Object [RFC9052]))r" },
{ QCborTag(18),
R"r( (COSE Single Signer Data Object [RFC9052]))r" },
{ QCborTag(19),
R"r( (COSE standalone V2 countersignature [RFC9338]))r" },
{ QCborTag(21),
R"r( (Expected conversion to base64url encoding; see Section 3.4.5.2 [RFC8949]))r" },
{ QCborTag(22),
R"r( (Expected conversion to base64 encoding; see Section 3.4.5.2 [RFC8949]))r" },
{ QCborTag(23),
R"r( (Expected conversion to base16 encoding; see Section 3.4.5.2 [RFC8949]))r" },
{ QCborTag(24),
R"r( (Encoded CBOR data item; see Section 3.4.5.1 [RFC8949]))r" },
{ QCborTag(25),
R"r( (reference the nth previously seen string))r" },
{ QCborTag(26),
R"r( (Serialised Perl object with classname and constructor arguments))r" },
{ QCborTag(27),
R"r( (Serialised language-independent object with type name and constructor arguments))r" },
{ QCborTag(28),
R"r( (mark value as (potentially) shared))r" },
{ QCborTag(29),
R"r( (reference nth marked value))r" },
{ QCborTag(30),
R"r( (Rational number))r" },
{ QCborTag(31),
R"r( (Absent value in a CBOR Array))r" },
{ QCborTag(32),
R"r( (URI; see Section 3.4.5.3 [RFC8949]))r" },
{ QCborTag(33),
R"r( (base64url; see Section 3.4.5.3 [RFC8949]))r" },
{ QCborTag(34),
R"r( (base64; see Section 3.4.5.3 [RFC8949]))r" },
{ QCborTag(35),
R"r( (Regular expression; see Section 2.4.4.3 [RFC7049]))r" },
{ QCborTag(36),
R"r( (MIME message; see Section 3.4.5.3 [RFC8949]))r" },
{ QCborTag(37),
R"r( (Binary UUID (RFC4122, Section 4.1.2)))r" },
{ QCborTag(38),
R"r( (Language-tagged string [RFC9290]))r" },
{ QCborTag(39),
R"r( (Identifier))r" },
{ QCborTag(40),
R"r( (Multi-dimensional Array, row-major order [RFC8746]))r" },
{ QCborTag(41),
R"r( (Homogeneous Array [RFC8746]))r" },
{ QCborTag(42),
R"r( (IPLD content identifier))r" },
{ QCborTag(43),
R"r( (YANG bits datatype; see Section 6.7. [RFC9254]))r" },
{ QCborTag(44),
R"r( (YANG enumeration datatype; see Section 6.6. [RFC9254]))r" },
{ QCborTag(45),
R"r( (YANG identityref datatype; see Section 6.10. [RFC9254]))r" },
{ QCborTag(46),
R"r( (YANG instance-identifier datatype; see Section 6.13. [RFC9254]))r" },
{ QCborTag(47),
R"r( (YANG Schema Item iDentifier (sid); see Section 3.2. [RFC9254]))r" },
{ QCborTag(52),
R"r( (IPv4, [prefixlen,IPv4], [IPv4,prefixpart] [RFC9164]))r" },
{ QCborTag(54),
R"r( (IPv6, [prefixlen,IPv6], [IPv6,prefixpart] [RFC9164]))r" },
{ QCborTag(61),
R"r( (CBOR Web Token (CWT) [RFC8392]))r" },
{ QCborTag(63),
R"r( (Encoded CBOR Sequence ))r" },
{ QCborTag(64),
R"r( (uint8 Typed Array [RFC8746]))r" },
{ QCborTag(65),
R"r( (uint16, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(66),
R"r( (uint32, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(67),
R"r( (uint64, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(68),
R"r( (uint8 Typed Array, clamped arithmetic [RFC8746]))r" },
{ QCborTag(69),
R"r( (uint16, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(70),
R"r( (uint32, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(71),
R"r( (uint64, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(72),
R"r( (sint8 Typed Array [RFC8746]))r" },
{ QCborTag(73),
R"r( (sint16, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(74),
R"r( (sint32, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(75),
R"r( (sint64, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(76),
R"r( ((reserved) [RFC8746]))r" },
{ QCborTag(77),
R"r( (sint16, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(78),
R"r( (sint32, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(79),
R"r( (sint64, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(80),
R"r( (IEEE 754 binary16, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(81),
R"r( (IEEE 754 binary32, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(82),
R"r( (IEEE 754 binary64, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(83),
R"r( (IEEE 754 binary128, big endian, Typed Array [RFC8746]))r" },
{ QCborTag(84),
R"r( (IEEE 754 binary16, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(85),
R"r( (IEEE 754 binary32, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(86),
R"r( (IEEE 754 binary64, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(87),
R"r( (IEEE 754 binary128, little endian, Typed Array [RFC8746]))r" },
{ QCborTag(96),
R"r( (COSE Encrypted Data Object [RFC9052]))r" },
{ QCborTag(97),
R"r( (COSE MACed Data Object [RFC9052]))r" },
{ QCborTag(98),
R"r( (COSE Signed Data Object [RFC9052]))r" },
{ QCborTag(100),
R"r( (Number of days since the epoch date 1970-01-01 [RFC8943]))r" },
{ QCborTag(101),
R"r( (alternatives as given by the uint + 128; see Section 9.1))r" },
{ QCborTag(103),
R"r( (Geographic Coordinates))r" },
{ QCborTag(104),
R"r( (Geographic Coordinate Reference System WKT or EPSG number))r" },
{ QCborTag(110),
R"r( (relative object identifier (BER encoding); SDNV sequence [RFC9090]))r" },
{ QCborTag(111),
R"r( (object identifier (BER encoding) [RFC9090]))r" },
{ QCborTag(112),
R"r( (object identifier (BER encoding), relative to 1.3.6.1.4.1 [RFC9090]))r" },
{ QCborTag(120),
R"r( (Internet of Things Data Point))r" },
{ QCborTag(260),
R"r( (Network Address (IPv4 or IPv6 or MAC Address) (DEPRECATED in favor of 52 and 54
for IP addresses) [http://www.employees.oRg/~RaviR/CboR-netwoRk.txt]))r" },
{ QCborTag(261),
R"r( (Network Address Prefix (IPv4 or IPv6 Address + Mask Length) (DEPRECATED in favor of 52 and 54
for IP addresses) [https://github.Com/toRaviR/CBOR-Tag-SpeCs/blob/masteR/netwoRkPReFix.md]))r" },
{ QCborTag(271),
R"r( (DDoS Open Threat Signaling (DOTS) signal channel object,
as defined in [RFC9132]))r" },
{ QCborTag(1004),
R"r( ( full-date string [RFC8943]))r" },
{ QCborTag(1040),
R"r( (Multi-dimensional Array, column-major order [RFC8746]))r" },
{ QCborTag(55799),
R"r( (Self-described CBOR; see Section 3.4.6 [RFC8949]))r" },
{ QCborTag(55800),
R"r( (indicates that the file contains CBOR Sequences [RFC9277]))r" },
{ QCborTag(55801),
R"r( (indicates that the file starts with a CBOR-Labeled Non-CBOR Data label. [RFC9277]))r" },
{ QCborTag(-1), nullptr }
};
// END GENERATED CODE
enum {
// See RFC 7049 section 2.
SmallValueBitLength = 5,
SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
Value8Bit = 24,
Value16Bit = 25,
Value32Bit = 26,
Value64Bit = 27
};
//! [0]
struct CborDumper
{
enum DumpOption {
ShowCompact = 0x01,
ShowWidthIndicators = 0x02,
ShowAnnotated = 0x04
};
Q_DECLARE_FLAGS(DumpOptions, DumpOption)
CborDumper(QFile *f, DumpOptions opts_);
QCborError dump();
private:
void dumpOne(int nestingLevel);
void dumpOneDetailed(int nestingLevel);
void printByteArray(const QByteArray &ba);
void printWidthIndicator(quint64 value, char space = '\0');
void printStringWidthIndicator(quint64 value);
QCborStreamReader reader;
QByteArray data;
QStack<quint8> byteArrayEncoding;
qint64 offset = 0;
DumpOptions opts;
};
//! [0]
Q_DECLARE_OPERATORS_FOR_FLAGS(CborDumper::DumpOptions)
static int cborNumberSize(quint64 value)
{
int normalSize = 1;
if (value > std::numeric_limits<quint32>::max())
normalSize += 8;
else if (value > std::numeric_limits<quint16>::max())
normalSize += 4;
else if (value > std::numeric_limits<quint8>::max())
normalSize += 2;
else if (value >= Value8Bit)
normalSize += 1;
return normalSize;
}
CborDumper::CborDumper(QFile *f, DumpOptions opts_)
: opts(opts_)
{
// try to mmap the file, this is faster
char *ptr = reinterpret_cast<char *>(f->map(0, f->size(), QFile::MapPrivateOption));
if (ptr) {
// worked
data = QByteArray::fromRawData(ptr, f->size());
reader.addData(data);
} else if ((opts & ShowAnnotated) || f->isSequential()) {
// details requires full contents, so allocate memory
data = f->readAll();
reader.addData(data);
} else {
// just use the QIODevice
reader.setDevice(f);
}
}
QCborError CborDumper::dump()
{
byteArrayEncoding << quint8(QCborKnownTags::ExpectedBase16);
if (!reader.lastError()) {
if (opts & ShowAnnotated)
dumpOneDetailed(0);
else
dumpOne(0);
}
QCborError err = reader.lastError();
offset = reader.currentOffset();
if (err) {
fflush(stdout);
fprintf(stderr, "cbordump: decoding failed at %lld: %s\n",
offset, qPrintable(err.toString()));
if (!data.isEmpty())
fprintf(stderr, " bytes at %lld: %s\n", offset,
data.mid(offset, 9).toHex(' ').constData());
} else {
if (!opts.testFlag(ShowAnnotated))
printf("\n");
if (offset < data.size() || (reader.device() && reader.device()->bytesAvailable()))
fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
}
return err;
}
template <typename T> static inline bool canConvertTo(double v)
{
using TypeInfo = std::numeric_limits<T>;
// The [conv.fpint] (7.10 Floating-integral conversions) section of the
// standard says only exact conversions are guaranteed. Converting
// integrals to floating-point with loss of precision has implementation-
// defined behavior whether the next higher or next lower is returned;
// converting FP to integral is UB if it can't be represented.;
static_assert(TypeInfo::is_integer);
double supremum = ldexp(1, TypeInfo::digits);
if (v >= supremum)
return false;
if (v < TypeInfo::min()) // either zero or a power of two, so it's exact
return false;
// we're in range
return v == floor(v);
}
static QString fpToString(double v, const char *suffix)
{
if (qIsInf(v))
return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf");
if (qIsNaN(v))
return QStringLiteral("nan");
if (canConvertTo<qint64>(v))
return QString::number(qint64(v)) + ".0" + suffix;
if (canConvertTo<quint64>(v))
return QString::number(quint64(v)) + ".0" + suffix;
QString s = QString::number(v, 'g', QLocale::FloatingPointShortest);
if (!s.contains('.') && !s.contains('e'))
s += '.';
s += suffix;
return s;
};
void CborDumper::dumpOne(int nestingLevel)
{
QString indent(1, QLatin1Char(' '));
QString indented = indent;
if (!opts.testFlag(ShowCompact)) {
indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' '));
indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' '));
}
switch (reader.type()) {
case QCborStreamReader::UnsignedInteger: {
quint64 u = reader.toUnsignedInteger();
printf("%llu", u);
reader.next();
printWidthIndicator(u);
return;
}
case QCborStreamReader::NegativeInteger: {
quint64 n = quint64(reader.toNegativeInteger());
if (n == 0) // -2^64 (wrapped around)
printf("-18446744073709551616");
else
printf("-%llu", n);
reader.next();
printWidthIndicator(n);
return;
}
case QCborStreamReader::ByteArray:
case QCborStreamReader::String: {
bool isLengthKnown = reader.isLengthKnown();
if (!isLengthKnown) {
printf("(_ ");
++offset;
}
QString comma;
if (reader.isByteArray()) {
auto r = reader.readByteArray();
while (r.status == QCborStreamReader::Ok) {
printf("%s", qPrintable(comma));
printByteArray(r.data);
printStringWidthIndicator(r.data.size());
r = reader.readByteArray();
comma = QLatin1Char(',') + indented;
}
} else {
auto r = reader.readString();
while (r.status == QCborStreamReader::Ok) {
printf("%s\"%s\"", qPrintable(comma), qPrintable(r.data));
printStringWidthIndicator(r.data.toUtf8().size());
r = reader.readString();
comma = QLatin1Char(',') + indented;
}
}
if (!isLengthKnown && !reader.lastError())
printf(")");
break;
}
case QCborStreamReader::Array:
case QCborStreamReader::Map: {
const char *delimiters = (reader.isArray() ? "[]" : "{}");
printf("%c", delimiters[0]);
if (reader.isLengthKnown()) {
quint64 len = reader.length();
reader.enterContainer();
printWidthIndicator(len, ' ');
} else {
reader.enterContainer();
offset = reader.currentOffset();
printf("_ ");
}
const char *comma = "";
while (!reader.lastError() && reader.hasNext()) {
printf("%s%s", comma, qPrintable(indented));
comma = ",";
dumpOne(nestingLevel + 1);
if (reader.parentContainerType() != QCborStreamReader::Map)
continue;
if (reader.lastError())
break;
printf(": ");
dumpOne(nestingLevel + 1);
}
if (!reader.lastError()) {
reader.leaveContainer();
printf("%s%c", qPrintable(indent), delimiters[1]);
}
break;
}
case QCborStreamReader::Tag: {
QCborTag tag = reader.toTag();
printf("%llu", quint64(tag));
if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
|| tag == QCborKnownTags::ExpectedBase64url)
byteArrayEncoding.push(quint8(tag));
if (reader.next()) {
printWidthIndicator(quint64(tag));
printf("(");
dumpOne(nestingLevel); // same level!
printf(")");
}
if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
|| tag == QCborKnownTags::ExpectedBase64url)
byteArrayEncoding.pop();
break;
}
case QCborStreamReader::SimpleType:
switch (reader.toSimpleType()) {
case QCborSimpleType::False:
printf("false");
break;
case QCborSimpleType::True:
printf("true");
break;
case QCborSimpleType::Null:
printf("null");
break;
case QCborSimpleType::Undefined:
printf("undefined");
break;
default:
printf("simple(%u)", quint8(reader.toSimpleType()));
break;
}
reader.next();
break;
case QCborStreamReader::Float16:
printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16")));
reader.next();
break;
case QCborStreamReader::Float:
printf("%s", qPrintable(fpToString(reader.toFloat(), "f")));
reader.next();
break;
case QCborStreamReader::Double:
printf("%s", qPrintable(fpToString(reader.toDouble(), "")));
reader.next();
break;
case QCborStreamReader::Invalid:
return;
}
offset = reader.currentOffset();
}
void CborDumper::dumpOneDetailed(int nestingLevel)
{
auto tagDescription = [](QCborTag tag) {
for (auto entry : tagDescriptions) {
if (entry.tag == tag)
return entry.description;
if (entry.tag > tag)
break;
}
return "";
};
auto printOverlong = [](int actualSize, quint64 value) {
if (cborNumberSize(value) != actualSize)
printf(" (overlong)");
};
auto print = [=](const char *descr, const char *fmt, ...) {
qint64 prevOffset = offset;
offset = reader.currentOffset();
if (prevOffset == offset)
return;
QByteArray bytes = data.mid(prevOffset, offset - prevOffset);
QByteArray indent(nestingLevel * 2, ' ');
printf("%-50s # %s ", (indent + bytes.toHex(' ')).constData(), descr);
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
if (strstr(fmt, "%ll")) {
// Only works because all callers below that use %ll, use it as the
// first arg
va_start(va, fmt);
quint64 value = va_arg(va, quint64);
va_end(va);
printOverlong(bytes.size(), value);
}
puts("");
};
auto printFp = [=](const char *descr, double d) {
QString s = fpToString(d, "");
if (s.size() <= 6)
return print(descr, "%s", qPrintable(s));
return print(descr, "%a", d);
};
auto printString = [=](const char *descr) {
constexpr qsizetype ChunkSizeLimit = std::numeric_limits<int>::max();
QByteArray indent(nestingLevel * 2, ' ');
const char *chunkStr = (reader.isLengthKnown() ? "" : "chunk ");
int width = 48 - indent.size();
int bytesPerLine = qMax(width / 3, 5);
qsizetype size = reader.currentStringChunkSize();
if (size < 0)
return; // error
if (size >= ChunkSizeLimit) {
fprintf(stderr, "String length too big, %lli\n", qint64(size));
exit(EXIT_FAILURE);
}
// if asking for the current string chunk changes the offset, then it
// was chunked
print(descr, "(indeterminate length)");
QByteArray bytes(size, Qt::Uninitialized);
auto r = reader.readStringChunk(bytes.data(), bytes.size());
while (r.status == QCborStreamReader::Ok) {
// We'll have to decode the length's width directly from CBOR...
const char *lenstart = data.constData() + offset;
const char *lenend = lenstart + 1;
quint8 additionalInformation = (*lenstart & SmallValueMask);
// Decode this number directly from CBOR (see RFC 7049 section 2)
if (additionalInformation >= Value8Bit) {
if (additionalInformation == Value8Bit)
lenend += 1;
else if (additionalInformation == Value16Bit)
lenend += 2;
else if (additionalInformation == Value32Bit)
lenend += 4;
else
lenend += 8;
}
{
QByteArray lenbytes = QByteArray::fromRawData(lenstart, lenend - lenstart);
printf("%-50s # %s %slength %llu",
(indent + lenbytes.toHex(' ')).constData(), descr, chunkStr, quint64(size));
printOverlong(lenbytes.size(), size);
puts("");
}
offset = reader.currentOffset();
for (int i = 0; i < r.data; i += bytesPerLine) {
QByteArray section = bytes.mid(i, bytesPerLine);
printf(" %s%s", indent.constData(), section.toHex(' ').constData());
// print the decode
QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' ');
printf("%s # \"", spaces.constData());
auto ptr = reinterpret_cast<const uchar *>(section.constData());
for (int j = 0; j < section.size(); ++j)
printf("%c", ptr[j] >= 0x80 || ptr[j] < 0x20 ? '.' : ptr[j]);
puts("\"");
}
// get the next chunk
size = reader.currentStringChunkSize();
if (size < 0)
return; // error
if (size >= ChunkSizeLimit) {
fprintf(stderr, "String length too big, %lli\n", qint64(size));
exit(EXIT_FAILURE);
}
bytes.resize(size);
r = reader.readStringChunk(bytes.data(), bytes.size());
}
};
if (reader.lastError())
return;
switch (reader.type()) {
case QCborStreamReader::UnsignedInteger: {
quint64 u = reader.toUnsignedInteger();
reader.next();
if (u < 65536 || (u % 100000) == 0)
print("Unsigned integer", "%llu", u);
else
print("Unsigned integer", "0x%llx", u);
return;
}
case QCborStreamReader::NegativeInteger: {
quint64 n = quint64(reader.toNegativeInteger());
reader.next();
print("Negative integer", n == 0 ? "-18446744073709551616" : "-%llu", n);
return;
}
case QCborStreamReader::ByteArray:
case QCborStreamReader::String: {
bool isLengthKnown = reader.isLengthKnown();
const char *descr = (reader.isString() ? "Text string" : "Byte string");
if (!isLengthKnown)
++nestingLevel;
printString(descr);
if (reader.lastError())
return;
if (!isLengthKnown) {
--nestingLevel;
print("Break", "");
}
break;
}
case QCborStreamReader::Array:
case QCborStreamReader::Map: {
const char *descr = (reader.isArray() ? "Array" : "Map");
if (reader.isLengthKnown()) {
quint64 len = reader.length();
reader.enterContainer();
print(descr, "length %llu", len);
} else {
reader.enterContainer();
print(descr, "(indeterminate length)");
}
while (!reader.lastError() && reader.hasNext())
dumpOneDetailed(nestingLevel + 1);
if (!reader.lastError()) {
reader.leaveContainer();
print("Break", "");
}
break;
}
case QCborStreamReader::Tag: {
QCborTag tag = reader.toTag();
reader.next();
print("Tag", "%llu%s", quint64(tag), tagDescription(tag));
dumpOneDetailed(nestingLevel + 1);
break;
}
case QCborStreamReader::SimpleType: {
QCborSimpleType st = reader.toSimpleType();
reader.next();
switch (st) {
case QCborSimpleType::False:
print("Simple Type", "false");
break;
case QCborSimpleType::True:
print("Simple Type", "true");
break;
case QCborSimpleType::Null:
print("Simple Type", "null");
break;
case QCborSimpleType::Undefined:
print("Simple Type", "undefined");
break;
default:
print("Simple Type", "%u", quint8(st));
break;
}
break;
}
case QCborStreamReader::Float16: {
double d = reader.toFloat16();
reader.next();
printFp("Float16", d);
break;
}
case QCborStreamReader::Float: {
double d = reader.toFloat();
reader.next();
printFp("Float", d);
break;
}
case QCborStreamReader::Double: {
double d = reader.toDouble();
reader.next();
printFp("Double", d);
break;
}
case QCborStreamReader::Invalid:
return;
}
offset = reader.currentOffset();
}
void CborDumper::printByteArray(const QByteArray &ba)
{
switch (byteArrayEncoding.top()) {
default:
printf("h'%s'", ba.toHex(' ').constData());
break;
case quint8(QCborKnownTags::ExpectedBase64):
printf("b64'%s'", ba.toBase64().constData());
break;
case quint8(QCborKnownTags::ExpectedBase64url):
printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData());
break;
}
}
void printIndicator(quint64 value, qint64 previousOffset, qint64 offset, char space)
{
int normalSize = cborNumberSize(value);
int actualSize = offset - previousOffset;
if (actualSize != normalSize) {
Q_ASSERT(actualSize > 1);
actualSize -= 2;
printf("_%d", qPopulationCount(uint(actualSize)));
if (space)
printf("%c", space);
}
}
void CborDumper::printWidthIndicator(quint64 value, char space)
{
qint64 previousOffset = offset;
offset = reader.currentOffset();
if (opts & ShowWidthIndicators)
printIndicator(value, previousOffset, offset, space);
}
void CborDumper::printStringWidthIndicator(quint64 value)
{
qint64 previousOffset = offset;
offset = reader.currentOffset();
if (opts & ShowWidthIndicators)
printIndicator(value, previousOffset, offset - uint(value), '\0');
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
setlocale(LC_ALL, "C");
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool"));
parser.addHelpOption();
QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")},
QStringLiteral("Use compact form (no line breaks)"));
parser.addOption(compact);
QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")},
QStringLiteral("Show indicators for width of lengths and integrals"));
parser.addOption(showIndicators);
QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")},
QStringLiteral("Show bytes and annotated decoding"));
parser.addOption(verbose);
parser.addPositionalArgument(QStringLiteral("[source]"),
QStringLiteral("CBOR file to read from"));
parser.process(app);
CborDumper::DumpOptions opts;
if (parser.isSet(compact))
opts |= CborDumper::ShowCompact;
if (parser.isSet(showIndicators))
opts |= CborDumper::ShowWidthIndicators;
if (parser.isSet(verbose))
opts |= CborDumper::ShowAnnotated;
QStringList files = parser.positionalArguments();
if (files.isEmpty())
files << "-";
for (const QString &file : std::as_const(files)) {
QFile f(file);
if (file == "-" ? f.open(stdin, QIODevice::ReadOnly) : f.open(QIODevice::ReadOnly)) {
if (files.size() > 1)
printf("/ From \"%s\" /\n", qPrintable(file));
CborDumper dumper(&f, opts);
QCborError err = dumper.dump();
if (err)
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://www.iana.org/assignments" xmlns="http://www.iana.org/assignments" xmlns:_="http://www.iana.org/assignments" xmlns:DEFAULT="http://www.iana.org/assignments" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
<xsl:template match="/a:registry[@id='cbor-tags']">struct CborTagDescription
{
QCborTag tag;
const char *description; // with space and parentheses
};
// <xsl:value-of select="a:registry/a:title"/>
static const CborTagDescription tagDescriptions[] = {
// from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
<xsl:for-each select="a:registry/a:record">
<xsl:sort select="a:value" data-type="number"/>
<xsl:if test="a:semantics != ''">
<xsl:call-template name="row"/>
</xsl:if>
</xsl:for-each> { QCborTag(-1), nullptr }
};
</xsl:template>
<xsl:template name="row"> { QCborTag(<xsl:value-of select="a:value"/>),
R"r( (<xsl:value-of select="a:semantics"/> <xsl:call-template name="xref"/>))r" },
</xsl:template><!-- fn:replace(a:semantics, '\s+', ' ') -->
<xsl:template name="xref"><xsl:if test="a:xref/@type = 'rfc'"> [<xsl:value-of
select="translate(a:xref/@data,'rfc','RFC')"/>]</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,36 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(convert LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/convert")
find_package(Qt6 REQUIRED COMPONENTS Core)
qt_standard_project_setup()
qt_add_executable(convert
cborconverter.cpp cborconverter.h
converter.h
datastreamconverter.cpp datastreamconverter.h
jsonconverter.cpp jsonconverter.h
main.cpp
nullconverter.cpp nullconverter.h
textconverter.cpp textconverter.h
xmlconverter.cpp xmlconverter.h
)
target_link_libraries(convert PRIVATE
Qt6::Core
)
install(TARGETS convert
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,336 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "cborconverter.h"
#include <QCborStreamReader>
#include <QCborStreamWriter>
#include <QCborMap>
#include <QCborArray>
#include <QCborValue>
#include <QDataStream>
#include <QFloat16>
#include <QFile>
#include <QMetaType>
#include <QTextStream>
#include <stdio.h>
static CborConverter cborConverter;
static CborDiagnosticDumper cborDiagnosticDumper;
static const char cborOptionHelp[] =
"convert-float-to-int=yes|no Write integers instead of floating point, if no\n"
" loss of precision occurs on conversion.\n"
"float16=yes|always|no Write using half-precision floating point.\n"
" If 'always', won't check for loss of precision.\n"
"float32=yes|always|no Write using single-precision floating point.\n"
" If 'always', won't check for loss of precision.\n"
"signature=yes|no Prepend the CBOR signature to the file output.\n"
;
static const char diagnosticHelp[] =
"extended=no|yes Use extended CBOR diagnostic format.\n"
"line-wrap=yes|no Split output into multiple lines.\n"
;
QT_BEGIN_NAMESPACE
QDataStream &operator<<(QDataStream &ds, QCborTag tag)
{
return ds << quint64(tag);
}
QDataStream &operator>>(QDataStream &ds, QCborTag &tag)
{
quint64 v;
ds >> v;
tag = QCborTag(v);
return ds;
}
QT_END_NAMESPACE
// We can't use QCborValue::toVariant directly because that would destroy
// non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we
// have our own set of converter functions so we can keep the keys properly.
static QVariant convertCborValue(const QCborValue &value);
//! [0]
static QVariant convertCborMap(const QCborMap &map)
{
VariantOrderedMap result;
result.reserve(map.size());
for (auto pair : map)
result.append({ convertCborValue(pair.first), convertCborValue(pair.second) });
return QVariant::fromValue(result);
}
static QVariant convertCborArray(const QCborArray &array)
{
QVariantList result;
result.reserve(array.size());
for (auto value : array)
result.append(convertCborValue(value));
return result;
}
static QVariant convertCborValue(const QCborValue &value)
{
if (value.isArray())
return convertCborArray(value.toArray());
if (value.isMap())
return convertCborMap(value.toMap());
return value.toVariant();
}
//! [0]
enum TrimFloatingPoint { Double, Float, Float16 };
//! [1]
static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
{
if (v.userType() == QMetaType::QVariantList) {
const QVariantList list = v.toList();
QCborArray array;
for (const QVariant &v : list)
array.append(convertFromVariant(v, fpTrimming));
return array;
}
if (v.userType() == qMetaTypeId<VariantOrderedMap>()) {
const auto m = qvariant_cast<VariantOrderedMap>(v);
QCborMap map;
for (const auto &pair : m)
map.insert(convertFromVariant(pair.first, fpTrimming),
convertFromVariant(pair.second, fpTrimming));
return map;
}
if (v.userType() == QMetaType::Double && fpTrimming != Double) {
float f = float(v.toDouble());
if (fpTrimming == Float16)
return float(qfloat16(f));
return f;
}
return QCborValue::fromVariant(v);
}
//! [1]
QString CborDiagnosticDumper::name()
{
return QStringLiteral("cbor-dump");
}
Converter::Direction CborDiagnosticDumper::directions()
{
return Out;
}
Converter::Options CborDiagnosticDumper::outputOptions()
{
return SupportsArbitraryMapKeys;
}
const char *CborDiagnosticDumper::optionsHelp()
{
return diagnosticHelp;
}
bool CborDiagnosticDumper::probeFile(QIODevice *f)
{
Q_UNUSED(f);
return false;
}
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverter)
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
{
QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped;
for (const QString &s : options) {
QStringList pair = s.split('=');
if (pair.size() == 2) {
if (pair.first() == "line-wrap") {
opts &= ~QCborValue::LineWrapped;
if (pair.last() == "yes") {
opts |= QCborValue::LineWrapped;
continue;
} else if (pair.last() == "no") {
continue;
}
}
if (pair.first() == "extended") {
opts &= ~QCborValue::ExtendedFormat;
if (pair.last() == "yes")
opts |= QCborValue::ExtendedFormat;
continue;
}
}
fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s",
qPrintable(s), diagnosticHelp);
exit(EXIT_FAILURE);
}
QTextStream out(f);
out << convertFromVariant(contents, Double).toDiagnosticNotation(opts)
<< Qt::endl;
}
CborConverter::CborConverter()
{
qRegisterMetaType<QCborTag>();
}
QString CborConverter::name()
{
return "cbor";
}
Converter::Direction CborConverter::directions()
{
return InOut;
}
Converter::Options CborConverter::outputOptions()
{
return SupportsArbitraryMapKeys;
}
const char *CborConverter::optionsHelp()
{
return cborOptionHelp;
}
bool CborConverter::probeFile(QIODevice *f)
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".cbor")))
return true;
}
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
}
//! [2]
QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
{
const char *ptr = nullptr;
if (auto file = qobject_cast<QFile *>(f))
ptr = reinterpret_cast<char *>(file->map(0, file->size()));
QByteArray mapped = QByteArray::fromRawData(ptr, ptr ? f->size() : 0);
QCborStreamReader reader(mapped);
if (!ptr)
reader.setDevice(f);
if (reader.isTag() && reader.toTag() == QCborKnownTags::Signature)
reader.next();
QCborValue contents = QCborValue::fromCbor(reader);
qint64 offset = reader.currentOffset();
if (reader.lastError()) {
fprintf(stderr, "Error loading CBOR contents (byte %lld): %s\n", offset,
qPrintable(reader.lastError().toString()));
fprintf(stderr, " bytes: %s\n",
(ptr ? mapped.mid(offset, 9) : f->read(9)).toHex(' ').constData());
exit(EXIT_FAILURE);
} else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) {
fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
}
if (outputConverter == nullptr)
outputConverter = &cborDiagnosticDumper;
else if (outputConverter == null)
return QVariant();
else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys))
return contents.toVariant();
return convertCborValue(contents);
}
//! [2]
//! [3]
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
{
//! [3]
bool useSignature = true;
bool useIntegers = true;
enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
for (const QString &s : options) {
QStringList pair = s.split('=');
if (pair.size() == 2) {
if (pair.first() == "convert-float-to-int") {
if (pair.last() == "yes") {
useIntegers = true;
continue;
} else if (pair.last() == "no") {
useIntegers = false;
continue;
}
}
if (pair.first() == "float16") {
if (pair.last() == "no") {
useFloat16 = No;
continue;
} else if (pair.last() == "yes") {
useFloat16 = Yes;
continue;
} else if (pair.last() == "always") {
useFloat16 = Always;
continue;
}
}
if (pair.first() == "float32") {
if (pair.last() == "no") {
useFloat = No;
continue;
} else if (pair.last() == "yes") {
useFloat = Yes;
continue;
} else if (pair.last() == "always") {
useFloat = Always;
continue;
}
}
if (pair.first() == "signature") {
if (pair.last() == "yes") {
useSignature = true;
continue;
} else if (pair.last() == "no") {
useSignature = false;
continue;
}
}
}
fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s",
qPrintable(s), cborOptionHelp);
exit(EXIT_FAILURE);
}
//! [4]
QCborValue v = convertFromVariant(contents,
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
QCborStreamWriter writer(f);
if (useSignature)
writer.append(QCborKnownTags::Signature);
QCborValue::EncodingOptions opts;
if (useIntegers)
opts |= QCborValue::UseIntegers;
if (useFloat != No)
opts |= QCborValue::UseFloat;
if (useFloat16 != No)
opts |= QCborValue::UseFloat16;
v.toCbor(writer, opts);
}
//! [4]

View File

@ -0,0 +1,38 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CBORCONVERTER_H
#define CBORCONVERTER_H
#include "converter.h"
class CborDiagnosticDumper : public Converter
{
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
};
class CborConverter : public Converter
{
public:
CborConverter();
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
};
#endif // CBORCONVERTER_H

Some files were not shown because too many files have changed in this diff Show More