6.5.3 clean

This commit is contained in:
kleuter
2023-11-01 18:02:52 +01:00
parent bbe896803b
commit 7018d9e6c8
2170 changed files with 57471 additions and 43550 deletions

View File

@ -6,9 +6,6 @@ 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()

View File

@ -38,8 +38,7 @@ double BindableSubscription::calculateDiscount() const
case Yearly:
return 0.6;
}
Q_ASSERT(false);
return -1;
Q_UNREACHABLE_RETURN(-1);
}
int BindableSubscription::basePrice() const

View File

@ -11,6 +11,10 @@
#include <QPushButton>
#include <QRadioButton>
#include <QSpinBox>
#include <QProperty>
#include <QString>
#include <QDateTimeEdit>
#include <QBindable>
int main(int argc, char *argv[])
{
@ -19,6 +23,8 @@ int main(int argc, char *argv[])
BindableSubscription subscription(&user);
SubscriptionWindow w;
// clazy:excludeall=lambda-in-connect
// when subscription is out of scope so is window
// Initialize subscription data
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly");
@ -49,9 +55,8 @@ int main(int argc, char *argv[])
});
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox");
QObject::connect(ageSpinBox, &QSpinBox::valueChanged, [&](int value) {
user.setAge(value);
});
QBindable<int> ageBindable(ageSpinBox, "value");
user.bindableAge().setBinding([ageBindable](){ return ageBindable.value();});
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");

View File

@ -1,46 +0,0 @@
# 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

@ -1,59 +0,0 @@
<?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

@ -1,53 +0,0 @@
<?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

@ -1,87 +0,0 @@
// 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

@ -4,6 +4,7 @@
/*!
\title Qt Android Notifier
\example platform/androidnotifier
\examplecategory {Mobile}
\brief Demonstrates calling Java code from Qt in an Android application.
\image androidnotifier.png

View File

@ -1,11 +1,10 @@
# 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)
if(NOT ANDROID)
qt_internal_add_example(cbordump)
qt_internal_add_example(convert)
qt_internal_add_example(savegame)
endif()
if(TARGET Qt6::Widgets)
qt_internal_add_example(streambookmarks)

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(cbordump LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -3,7 +3,8 @@
/*!
\example serialization/cbordump
\examplecategory {Input/Output}
\examplecategory {Data Processing & I/O}
\meta tag {network}
\title Parsing and displaying CBOR data
\brief A demonstration of how to parse files in CBOR format.

View File

@ -14,6 +14,8 @@
#include <stdarg.h>
#include <stdio.h>
using namespace Qt::StringLiterals;
/*
* To regenerate:
* curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml
@ -33,7 +35,7 @@
struct CborTagDescription
{
QCborTag tag;
const char *description; // with space and parentheses
const char *description; // with space and parentheses
};
// CBOR Tags
@ -216,22 +218,18 @@ static const CborTagDescription tagDescriptions[] = {
enum {
// See RFC 7049 section 2.
SmallValueBitLength = 5,
SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
Value8Bit = 24,
Value16Bit = 25,
Value32Bit = 26,
Value64Bit = 27
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
};
enum DumpOption { ShowCompact = 0x01, ShowWidthIndicators = 0x02, ShowAnnotated = 0x04 };
Q_DECLARE_FLAGS(DumpOptions, DumpOption)
CborDumper(QFile *f, DumpOptions opts_);
@ -268,8 +266,7 @@ static int cborNumberSize(quint64 value)
return normalSize;
}
CborDumper::CborDumper(QFile *f, DumpOptions opts_)
: opts(opts_)
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));
@ -316,7 +313,8 @@ QCborError CborDumper::dump()
return err;
}
template <typename T> static inline bool canConvertTo(double v)
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
@ -337,31 +335,32 @@ template <typename T> static inline bool canConvertTo(double v)
return v == floor(v);
}
static QString fpToString(double v, const char *suffix)
static QString fpToString(double v, QLatin1StringView suffix = ""_L1)
{
if (qIsInf(v))
return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf");
return v < 0 ? "-inf"_L1 : "inf"_L1;
if (qIsNaN(v))
return QStringLiteral("nan");
return "nan"_L1;
if (canConvertTo<qint64>(v))
return QString::number(qint64(v)) + ".0" + suffix;
return QString::number(qint64(v)) + ".0"_L1 + suffix;
if (canConvertTo<quint64>(v))
return QString::number(quint64(v)) + ".0" + suffix;
return QString::number(quint64(v)) + ".0"_L1 + suffix;
QString s = QString::number(v, 'g', QLocale::FloatingPointShortest);
if (!s.contains('.') && !s.contains('e'))
s += '.';
s += suffix;
if (!s.contains(u'.') && !s.contains(u'e'))
s += u'.';
if (suffix.size())
s += suffix;
return s;
};
void CborDumper::dumpOne(int nestingLevel)
{
QString indent(1, QLatin1Char(' '));
QString indent(1, u' ');
QString indented = indent;
if (!opts.testFlag(ShowCompact)) {
indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' '));
indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' '));
indent = u'\n' + QString(4 * nestingLevel, u' ');
indented = u'\n' + QString(4 + 4 * nestingLevel, u' ');
}
switch (reader.type()) {
@ -401,7 +400,7 @@ void CborDumper::dumpOne(int nestingLevel)
printStringWidthIndicator(r.data.size());
r = reader.readByteArray();
comma = QLatin1Char(',') + indented;
comma = u',' + indented;
}
} else {
auto r = reader.readString();
@ -410,7 +409,7 @@ void CborDumper::dumpOne(int nestingLevel)
printStringWidthIndicator(r.data.toUtf8().size());
r = reader.readString();
comma = QLatin1Char(',') + indented;
comma = u',' + indented;
}
}
@ -466,7 +465,7 @@ void CborDumper::dumpOne(int nestingLevel)
if (reader.next()) {
printWidthIndicator(quint64(tag));
printf("(");
dumpOne(nestingLevel); // same level!
dumpOne(nestingLevel); // same level!
printf(")");
}
@ -498,15 +497,15 @@ void CborDumper::dumpOne(int nestingLevel)
break;
case QCborStreamReader::Float16:
printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16")));
printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16"_L1)));
reader.next();
break;
case QCborStreamReader::Float:
printf("%s", qPrintable(fpToString(reader.toFloat(), "f")));
printf("%s", qPrintable(fpToString(reader.toFloat(), "f"_L1)));
reader.next();
break;
case QCborStreamReader::Double:
printf("%s", qPrintable(fpToString(reader.toDouble(), "")));
printf("%s", qPrintable(fpToString(reader.toDouble())));
reader.next();
break;
case QCborStreamReader::Invalid:
@ -559,7 +558,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
};
auto printFp = [=](const char *descr, double d) {
QString s = fpToString(d, "");
QString s = fpToString(d);
if (s.size() <= 6)
return print(descr, "%s", qPrintable(s));
return print(descr, "%a", d);
@ -574,7 +573,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
qsizetype size = reader.currentStringChunkSize();
if (size < 0)
return; // error
return; // error
if (size >= ChunkSizeLimit) {
fprintf(stderr, "String length too big, %lli\n", qint64(size));
exit(EXIT_FAILURE);
@ -619,7 +618,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
printf(" %s%s", indent.constData(), section.toHex(' ').constData());
// print the decode
QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' ');
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)
@ -631,7 +630,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
// get the next chunk
size = reader.currentStringChunkSize();
if (size < 0)
return; // error
return; // error
if (size >= ChunkSizeLimit) {
fprintf(stderr, "String length too big, %lli\n", qint64(size));
exit(EXIT_FAILURE);
@ -770,7 +769,9 @@ void CborDumper::printByteArray(const QByteArray &ba)
break;
case quint8(QCborKnownTags::ExpectedBase64url):
printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData());
printf("b64'%s'",
ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)
.constData());
break;
}
}
@ -811,23 +812,20 @@ int main(int argc, char *argv[])
setlocale(LC_ALL, "C");
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool"));
parser.setApplicationDescription("CBOR Dumper tool"_L1);
parser.addHelpOption();
QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")},
QStringLiteral("Use compact form (no line breaks)"));
QCommandLineOption compact({"c"_L1, "compact"_L1}, "Use compact form (no line breaks)"_L1);
parser.addOption(compact);
QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")},
QStringLiteral("Show indicators for width of lengths and integrals"));
QCommandLineOption showIndicators({ "i"_L1, "indicators"_L1 },
"Show indicators for width of lengths and integrals"_L1);
parser.addOption(showIndicators);
QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")},
QStringLiteral("Show bytes and annotated decoding"));
QCommandLineOption verbose({"a"_L1, "annotated"_L1}, "Show bytes and annotated decoding"_L1);
parser.addOption(verbose);
parser.addPositionalArgument(QStringLiteral("[source]"),
QStringLiteral("CBOR file to read from"));
parser.addPositionalArgument("[source]"_L1, "CBOR file to read from"_L1);
parser.process(app);

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(convert LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
@ -18,6 +22,7 @@ qt_add_executable(convert
cborconverter.cpp cborconverter.h
converter.h
datastreamconverter.cpp datastreamconverter.h
debugtextdumper.cpp debugtextdumper.h
jsonconverter.cpp jsonconverter.h
main.cpp
nullconverter.cpp nullconverter.h

View File

@ -3,19 +3,21 @@
#include "cborconverter.h"
#include <QCborArray>
#include <QCborMap>
#include <QCborStreamReader>
#include <QCborStreamWriter>
#include <QCborMap>
#include <QCborArray>
#include <QCborValue>
#include <QDataStream>
#include <QFloat16>
#include <QFile>
#include <QFloat16>
#include <QMetaType>
#include <QTextStream>
#include <stdio.h>
using namespace Qt::StringLiterals;
static CborConverter cborConverter;
static CborDiagnosticDumper cborDiagnosticDumper;
@ -118,33 +120,33 @@ static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrim
}
//! [1]
QString CborDiagnosticDumper::name()
QString CborDiagnosticDumper::name() const
{
return QStringLiteral("cbor-dump");
return "cbor-dump"_L1;
}
Converter::Direction CborDiagnosticDumper::directions()
Converter::Directions CborDiagnosticDumper::directions() const
{
return Out;
return Direction::Out;
}
Converter::Options CborDiagnosticDumper::outputOptions()
Converter::Options CborDiagnosticDumper::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *CborDiagnosticDumper::optionsHelp()
const char *CborDiagnosticDumper::optionsHelp() const
{
return diagnosticHelp;
}
bool CborDiagnosticDumper::probeFile(QIODevice *f)
bool CborDiagnosticDumper::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNREACHABLE();
Q_UNUSED(f);
@ -152,7 +154,8 @@ QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverte
return QVariant();
}
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped;
for (const QString &s : options) {
@ -181,8 +184,7 @@ void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, cons
}
QTextStream out(f);
out << convertFromVariant(contents, Double).toDiagnosticNotation(opts)
<< Qt::endl;
out << convertFromVariant(contents, Double).toDiagnosticNotation(opts) << Qt::endl;
}
CborConverter::CborConverter()
@ -190,37 +192,37 @@ CborConverter::CborConverter()
qRegisterMetaType<QCborTag>();
}
QString CborConverter::name()
QString CborConverter::name() const
{
return "cbor";
}
Converter::Direction CborConverter::directions()
Converter::Directions CborConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options CborConverter::outputOptions()
Converter::Options CborConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *CborConverter::optionsHelp()
const char *CborConverter::optionsHelp() const
{
return cborOptionHelp;
}
bool CborConverter::probeFile(QIODevice *f)
bool CborConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".cbor")))
if (file->fileName().endsWith(".cbor"_L1))
return true;
}
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
}
//! [2]
QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
const char *ptr = nullptr;
if (auto file = qobject_cast<QFile *>(f))
@ -256,7 +258,7 @@ QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
}
//! [2]
//! [3]
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
{
//! [3]
bool useSignature = true;
@ -318,8 +320,9 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
exit(EXIT_FAILURE);
}
//! [4]
QCborValue v = convertFromVariant(contents,
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
QCborValue v =
convertFromVariant(contents,
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
QCborStreamWriter writer(f);
if (useSignature)
writer.append(QCborKnownTags::Signature);

View File

@ -10,13 +10,14 @@ 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
class CborConverter : public Converter
@ -26,13 +27,14 @@ public:
// 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // CBORCONVERTER_H

View File

@ -12,17 +12,19 @@ INSTALLS += target
SOURCES += main.cpp \
cborconverter.cpp \
jsonconverter.cpp \
datastreamconverter.cpp \
debugtextdumper.cpp \
jsonconverter.cpp \
nullconverter.cpp \
textconverter.cpp \
xmlconverter.cpp \
nullconverter.cpp
xmlconverter.cpp
HEADERS += \
converter.h \
cborconverter.h \
jsonconverter.h \
datastreamconverter.h \
debugtextdumper.h \
jsonconverter.h \
nullconverter.h \
textconverter.h \
xmlconverter.h \
nullconverter.h
xmlconverter.h

View File

@ -5,10 +5,10 @@
#define CONVERTER_H
#include <QIODevice>
#include <QList>
#include <QPair>
#include <QVariant>
#include <QVariantMap>
#include <QList>
class VariantOrderedMap : public QList<QPair<QVariant, QVariant>>
{
@ -32,26 +32,25 @@ protected:
public:
static Converter *null;
enum Direction {
In = 1, Out = 2, InOut = 3
};
enum class Direction { In = 1, Out = 2, InOut = In | Out };
Q_DECLARE_FLAGS(Directions, Direction)
enum Option {
SupportsArbitraryMapKeys = 0x01
};
enum Option { SupportsArbitraryMapKeys = 0x01 };
Q_DECLARE_FLAGS(Options, Option)
virtual ~Converter() = 0;
virtual QString name() = 0;
virtual Direction directions() = 0;
virtual Options outputOptions() = 0;
virtual const char *optionsHelp() = 0;
virtual bool probeFile(QIODevice *f) = 0;
virtual QVariant loadFile(QIODevice *f, Converter *&outputConverter) = 0;
virtual void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) = 0;
virtual QString name() const = 0;
virtual Directions directions() const = 0;
virtual Options outputOptions() const = 0;
virtual const char *optionsHelp() const = 0;
virtual bool probeFile(QIODevice *f) const = 0;
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const = 0;
virtual void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
#endif // CONVERTER_H

View File

@ -2,20 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "datastreamconverter.h"
#include "debugtextdumper.h"
#include <QDataStream>
#include <QDebug>
#include <QTextStream>
using namespace Qt::StringLiterals;
static const char dataStreamOptionHelp[] =
"byteorder=host|big|little Byte order to use.\n"
"version=<n> QDataStream version (default: Qt 5.0).\n"
"version=<n> QDataStream version (default: Qt 6.0).\n"
;
static const char signature[] = "qds";
static DataStreamDumper dataStreamDumper;
static DataStreamConverter DataStreamConverter;
static DataStreamConverter dataStreamConverter;
static DebugTextDumper debugTextDumper;
QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map)
{
@ -42,123 +43,43 @@ QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map)
return ds;
}
static QString dumpVariant(const QVariant &v, const QString &indent = QLatin1String("\n"))
{
QString result;
QString indented = indent + QLatin1String(" ");
int type = v.userType();
if (type == qMetaTypeId<VariantOrderedMap>() || type == QMetaType::QVariantMap) {
const auto map = (type == QMetaType::QVariantMap) ?
VariantOrderedMap(v.toMap()) : qvariant_cast<VariantOrderedMap>(v);
result = QLatin1String("Map {");
for (const auto &pair : map) {
result += indented + dumpVariant(pair.first, indented);
result.chop(1); // remove comma
result += QLatin1String(" => ") + dumpVariant(pair.second, indented);
}
result.chop(1); // remove comma
result += indent + QLatin1String("},");
} else if (type == QMetaType::QVariantList) {
const QVariantList list = v.toList();
result = QLatin1String("List [");
for (const auto &item : list)
result += indented + dumpVariant(item, indented);
result.chop(1); // remove comma
result += indent + QLatin1String("],");
} else {
QDebug debug(&result);
debug.nospace() << v << ',';
}
return result;
}
QString DataStreamDumper::name()
{
return QStringLiteral("datastream-dump");
}
Converter::Direction DataStreamDumper::directions()
{
return Out;
}
Converter::Options DataStreamDumper::outputOptions()
{
return SupportsArbitraryMapKeys;
}
const char *DataStreamDumper::optionsHelp()
{
return nullptr;
}
bool DataStreamDumper::probeFile(QIODevice *f)
{
Q_UNUSED(f);
return false;
}
QVariant DataStreamDumper::loadFile(QIODevice *f, Converter *&outputConverter)
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void DataStreamDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
{
Q_UNUSED(options);
QString s = dumpVariant(contents);
s[s.size() - 1] = QLatin1Char('\n'); // replace the comma with newline
QTextStream out(f);
out << s;
}
DataStreamConverter::DataStreamConverter()
{
qRegisterMetaType<VariantOrderedMap>();
}
QString DataStreamConverter::name()
QString DataStreamConverter::name() const
{
return QStringLiteral("datastream");
return "datastream"_L1;
}
Converter::Direction DataStreamConverter::directions()
Converter::Directions DataStreamConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options DataStreamConverter::outputOptions()
Converter::Options DataStreamConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *DataStreamConverter::optionsHelp()
const char *DataStreamConverter::optionsHelp() const
{
return dataStreamOptionHelp;
}
bool DataStreamConverter::probeFile(QIODevice *f)
bool DataStreamConverter::probeFile(QIODevice *f) const
{
return f->isReadable() && f->peek(sizeof(signature) - 1) == signature;
}
QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant DataStreamConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = &dataStreamDumper;
outputConverter = &debugTextDumper;
char c;
if (f->read(sizeof(signature) -1) != signature ||
!f->getChar(&c) || (c != 'l' && c != 'B')) {
if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B')) {
fprintf(stderr, "Could not load QDataStream file: invalid signature.\n");
exit(EXIT_FAILURE);
}
@ -175,9 +96,10 @@ QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter
return result;
}
void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
QDataStream::Version version = QDataStream::Qt_5_0;
QDataStream::Version version = QDataStream::Qt_6_0;
auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder);
for (const QString &option : options) {
const QStringList pair = option.split('=');
@ -213,7 +135,7 @@ void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const
exit(EXIT_FAILURE);
}
char c = order == QDataStream::LittleEndian ? 'l' : 'B';
char c = order == QDataStream::LittleEndian ? 'l' : 'B';
f->write(signature);
f->write(&c, 1);

View File

@ -6,19 +6,6 @@
#include "converter.h"
class DataStreamDumper : 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 DataStreamConverter : public Converter
{
public:
@ -26,13 +13,14 @@ public:
// 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // DATASTREAMCONVERTER_H

View File

@ -0,0 +1,89 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "debugtextdumper.h"
#include <QDebug>
#include <QTextStream>
using namespace Qt::StringLiterals;
// Static instance is declared in datastreamconverter.cpp, since it uses it.
static QString dumpVariant(const QVariant &v, const QString &indent = "\n"_L1)
{
QString result;
QString indented = indent + " "_L1;
int type = v.userType();
if (type == qMetaTypeId<VariantOrderedMap>() || type == QMetaType::QVariantMap) {
const auto map = (type == QMetaType::QVariantMap) ? VariantOrderedMap(v.toMap())
: qvariant_cast<VariantOrderedMap>(v);
result = "Map {"_L1;
for (const auto &pair : map) {
result += indented + dumpVariant(pair.first, indented);
result.chop(1); // remove comma
result += " => "_L1 + dumpVariant(pair.second, indented);
}
result.chop(1); // remove comma
result += indent + "},"_L1;
} else if (type == QMetaType::QVariantList) {
const QVariantList list = v.toList();
result = "List ["_L1;
for (const auto &item : list)
result += indented + dumpVariant(item, indented);
result.chop(1); // remove comma
result += indent + "],"_L1;
} else {
QDebug debug(&result);
debug.nospace() << v << ',';
}
return result;
}
QString DebugTextDumper::name() const
{
return "debugtext-dump"_L1;
}
Converter::Directions DebugTextDumper::directions() const
{
return Direction::Out;
}
Converter::Options DebugTextDumper::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *DebugTextDumper::optionsHelp() const
{
return nullptr;
}
bool DebugTextDumper::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant DebugTextDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void DebugTextDumper::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
Q_UNUSED(options);
QString s = dumpVariant(contents);
s[s.size() - 1] = u'\n'; // replace the comma with newline
QTextStream out(f);
out << s;
}

View File

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

View File

@ -3,7 +3,8 @@
/*!
\example serialization/convert
\examplecategory {Input/Output}
\examplecategory {Data Processing & I/O}
\meta tag {network}
\title Convert Example
\brief The Convert example demonstrates how to convert between different
@ -59,7 +60,7 @@
\section1 The DataStreamConverter Class
The DataStreamConverter class is used to serialize to and from the
QDataStream format. There is also the DataStreamDumper class for outputting
QDataStream format. There is also the DebugTextDumper class for outputting
the data lossless in a non-standardized human readable format.
\section1 The JsonConverter Class

View File

@ -9,10 +9,11 @@
#include <QJsonObject>
#include <QJsonValue>
using namespace Qt::StringLiterals;
static JsonConverter jsonConverter;
static const char jsonOptionHelp[] =
"compact=no|yes Use compact JSON form.\n";
static const char jsonOptionHelp[] = "compact=no|yes Use compact JSON form.\n";
static QJsonDocument convertFromVariant(const QVariant &v)
{
@ -24,34 +25,30 @@ static QJsonDocument convertFromVariant(const QVariant &v)
return doc;
}
JsonConverter::JsonConverter()
QString JsonConverter::name() const
{
return "json"_L1;
}
QString JsonConverter::name()
Converter::Directions JsonConverter::directions() const
{
return "json";
return Direction::InOut;
}
Converter::Direction JsonConverter::directions()
{
return InOut;
}
Converter::Options JsonConverter::outputOptions()
Converter::Options JsonConverter::outputOptions() const
{
return {};
}
const char *JsonConverter::optionsHelp()
const char *JsonConverter::optionsHelp() const
{
return jsonOptionHelp;
}
bool JsonConverter::probeFile(QIODevice *f)
bool JsonConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".json")))
if (file->fileName().endsWith(".json"_L1))
return true;
}
@ -62,7 +59,7 @@ bool JsonConverter::probeFile(QIODevice *f)
return false;
}
QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant JsonConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = this;
@ -87,13 +84,14 @@ QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
return doc.toVariant();
}
void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void JsonConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
QJsonDocument::JsonFormat format = QJsonDocument::Indented;
for (const QString &s : options) {
if (s == QLatin1String("compact=no")) {
if (s == "compact=no"_L1) {
format = QJsonDocument::Indented;
} else if (s == QLatin1String("compact=yes")) {
} else if (s == "compact=yes"_L1) {
format = QJsonDocument::Compact;
} else {
fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s",

View File

@ -8,18 +8,16 @@
class JsonConverter : public Converter
{
public:
JsonConverter();
// 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // JSONCONVERTER_H

View File

@ -11,12 +11,14 @@
#include <stdio.h>
static QList<Converter *> *availableConverters;
using namespace Qt::StringLiterals;
static QList<const Converter *> *availableConverters;
Converter::Converter()
{
if (!availableConverters)
availableConverters = new QList<Converter *>;
availableConverters = new QList<const Converter *>;
availableConverters->append(this);
}
@ -31,64 +33,68 @@ int main(int argc, char *argv[])
QStringList inputFormats;
QStringList outputFormats;
for (Converter *conv : std::as_const(*availableConverters)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
auto direction = conv->directions();
QString name = conv->name();
if (direction & Converter::In)
if (direction.testFlag(Converter::Direction::In))
inputFormats << name;
if (direction & Converter::Out)
if (direction.testFlag(Converter::Direction::Out))
outputFormats << name;
}
inputFormats.sort();
outputFormats.sort();
inputFormats.prepend("auto");
outputFormats.prepend("auto");
inputFormats.prepend("auto"_L1);
outputFormats.prepend("auto"_L1);
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("Qt file format conversion tool"));
parser.setApplicationDescription("Qt file format conversion tool"_L1);
parser.addHelpOption();
QCommandLineOption inputFormatOption(QStringList{"I", "input-format"});
inputFormatOption.setDescription(QLatin1String("Select the input format for the input file. Available formats: ") +
inputFormats.join(", "));
inputFormatOption.setValueName("format");
QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 });
inputFormatOption.setDescription(
"Select the input format for the input file. Available formats: "_L1
+ inputFormats.join(", "_L1));
inputFormatOption.setValueName("format"_L1);
inputFormatOption.setDefaultValue(inputFormats.constFirst());
parser.addOption(inputFormatOption);
QCommandLineOption outputFormatOption(QStringList{"O", "output-format"});
outputFormatOption.setDescription(QLatin1String("Select the output format for the output file. Available formats: ") +
outputFormats.join(", "));
outputFormatOption.setValueName("format");
QCommandLineOption outputFormatOption(QStringList{ "O"_L1, "output-format"_L1 });
outputFormatOption.setDescription(
"Select the output format for the output file. Available formats: "_L1
+ outputFormats.join(", "_L1));
outputFormatOption.setValueName("format"_L1);
outputFormatOption.setDefaultValue(outputFormats.constFirst());
parser.addOption(outputFormatOption);
QCommandLineOption optionOption(QStringList{"o", "option"});
optionOption.setDescription(QStringLiteral("Format-specific options. Use --format-options to find out what options are available."));
optionOption.setValueName("options...");
QCommandLineOption optionOption(QStringList{ "o"_L1, "option"_L1 });
optionOption.setDescription(
"Format-specific options. Use --format-options to find out what options are available."_L1);
optionOption.setValueName("options..."_L1);
optionOption.setDefaultValues({});
parser.addOption(optionOption);
QCommandLineOption formatOptionsOption("format-options");
formatOptionsOption.setDescription(QStringLiteral("Prints the list of valid options for --option for the converter format <format>."));
formatOptionsOption.setValueName("format");
QCommandLineOption formatOptionsOption("format-options"_L1);
formatOptionsOption.setDescription(
"Prints the list of valid options for --option for the converter format <format>."_L1);
formatOptionsOption.setValueName("format"_L1);
parser.addOption(formatOptionsOption);
parser.addPositionalArgument(QStringLiteral("[source]"),
QStringLiteral("File to read from (stdin if none)"));
parser.addPositionalArgument(QStringLiteral("[destination]"),
QStringLiteral("File to write to (stdout if none)"));
parser.addPositionalArgument("[source]"_L1, "File to read from (stdin if none)"_L1);
parser.addPositionalArgument("[destination]"_L1, "File to write to (stdout if none)"_L1);
parser.process(app);
if (parser.isSet(formatOptionsOption)) {
QString format = parser.value(formatOptionsOption);
for (Converter *conv : std::as_const(*availableConverters)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
const char *help = conv->optionsHelp();
if (help)
printf("The following options are available for format '%s':\n\n%s", qPrintable(format), help);
else
if (help) {
printf("The following options are available for format '%s':\n\n%s",
qPrintable(format), help);
} else {
printf("Format '%s' supports no options.\n", qPrintable(format));
}
return EXIT_SUCCESS;
}
}
@ -97,10 +103,10 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
Converter *inconv = nullptr;
const Converter *inconv = nullptr;
QString format = parser.value(inputFormatOption);
if (format != "auto") {
for (Converter *conv : std::as_const(*availableConverters)) {
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
inconv = conv;
break;
@ -113,10 +119,10 @@ int main(int argc, char *argv[])
}
}
Converter *outconv = nullptr;
const Converter *outconv = nullptr;
format = parser.value(outputFormatOption);
if (format != "auto") {
for (Converter *conv : std::as_const(*availableConverters)) {
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
outconv = conv;
break;
@ -155,8 +161,9 @@ int main(int argc, char *argv[])
if (!inconv) {
// probe the input to find a file format
for (Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions() & Converter::In && conv->probeFile(&input)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::In)
&& conv->probeFile(&input)) {
inconv = conv;
break;
}
@ -170,8 +177,9 @@ int main(int argc, char *argv[])
if (!outconv) {
// probe the output to find a file format
for (Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions() & Converter::Out && conv->probeFile(&output)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::Out)
&& conv->probeFile(&output)) {
outconv = conv;
break;
}

View File

@ -3,36 +3,38 @@
#include "nullconverter.h"
using namespace Qt::StringLiterals;
static NullConverter nullConverter;
Converter* Converter::null = &nullConverter;
Converter *Converter::null = &nullConverter;
QString NullConverter::name()
QString NullConverter::name() const
{
return QLatin1String("null");
return "null"_L1;
}
Converter::Direction NullConverter::directions()
Converter::Directions NullConverter::directions() const
{
return Out;
return Direction::Out;
}
Converter::Options NullConverter::outputOptions()
Converter::Options NullConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *NullConverter::optionsHelp()
const char *NullConverter::optionsHelp() const
{
return nullptr;
}
bool NullConverter::probeFile(QIODevice *f)
bool NullConverter::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant NullConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNUSED(f);
Q_UNUSED(outputConverter);
@ -40,10 +42,12 @@ QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter)
return QVariant();
}
void NullConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void NullConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
if (!options.isEmpty()) {
fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qPrintable(options.first()));
fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n",
qPrintable(options.first()));
exit(EXIT_FAILURE);
}

View File

@ -10,13 +10,14 @@ class NullConverter : 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // NULLCONVERTER_H

View File

@ -6,6 +6,8 @@
#include <QFile>
#include <QTextStream>
using namespace Qt::StringLiterals;
static void dumpVariant(QTextStream &out, const QVariant &v)
{
switch (v.userType()) {
@ -42,67 +44,62 @@ static void dumpVariant(QTextStream &out, const QVariant &v)
}
}
QString TextConverter::name()
QString TextConverter::name() const
{
return QStringLiteral("text");
return "text"_L1;
}
Converter::Direction TextConverter::directions()
Converter::Directions TextConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options TextConverter::outputOptions()
Converter::Options TextConverter::outputOptions() const
{
return {};
}
const char *TextConverter::optionsHelp()
const char *TextConverter::optionsHelp() const
{
return nullptr;
}
bool TextConverter::probeFile(QIODevice *f)
bool TextConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f))
return file->fileName().endsWith(QLatin1String(".txt"));
return file->fileName().endsWith(".txt"_L1);
return false;
}
QVariant TextConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant TextConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = this;
QVariantList list;
QTextStream in(f);
QString line ;
QString line;
while (!in.atEnd()) {
in.readLineInto(&line);
bool ok;
qint64 v = line.toLongLong(&ok);
if (ok) {
if (qint64 v = line.toLongLong(&ok); ok)
list.append(v);
continue;
}
double d = line.toDouble(&ok);
if (ok) {
else if (double d = line.toDouble(&ok); ok)
list.append(d);
continue;
}
list.append(line);
else
list.append(line);
}
return list;
}
void TextConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void TextConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
if (!options.isEmpty()) {
fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qPrintable(options.first()));
fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n",
qPrintable(options.first()));
exit(EXIT_FAILURE);
}

View File

@ -8,16 +8,16 @@
class TextConverter : 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // TEXTCONVERTER_H

View File

@ -13,8 +13,9 @@
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
static const char xmlOptionHelp[] =
"compact=no|yes Use compact XML form.\n";
using namespace Qt::StringLiterals;
static const char xmlOptionHelp[] = "compact=no|yes Use compact XML form.\n";
static XmlConverter xmlConverter;
@ -23,7 +24,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options options)
{
QVariantList list;
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("list"))) {
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "list"_L1)) {
xml.readNext();
switch (xml.tokenType()) {
case QXmlStreamReader::StartElement:
@ -47,8 +48,7 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
break;
}
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
}
@ -57,10 +57,11 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
return list;
}
static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Converter::Options options)
static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml,
Converter::Options options)
{
QVariant key, value;
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("entry"))) {
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "entry"_L1)) {
xml.readNext();
switch (xml.tokenType()) {
case QXmlStreamReader::StartElement:
@ -89,8 +90,7 @@ static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Conv
break;
}
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
}
@ -103,11 +103,11 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
QVariantMap map1;
VariantOrderedMap map2;
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("map"))) {
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "map"_L1)) {
xml.readNext();
switch (xml.tokenType()) {
case QXmlStreamReader::StartElement:
if (xml.name() == QLatin1String("entry")) {
if (xml.name() == "entry"_L1) {
auto pair = mapEntryFromXml(xml, options);
if (options & Converter::SupportsArbitraryMapKeys)
map2.append(pair);
@ -134,8 +134,7 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
break;
}
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
}
@ -149,18 +148,18 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options)
{
QStringView name = xml.name();
if (name == QLatin1String("list"))
if (name == "list"_L1)
return listFromXml(xml, options);
if (name == QLatin1String("map"))
if (name == "map"_L1)
return mapFromXml(xml, options);
if (name != QLatin1String("value")) {
if (name != "value"_L1) {
fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n",
xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
exit(EXIT_FAILURE);
}
QXmlStreamAttributes attrs = xml.attributes();
QStringView type = attrs.value(QLatin1String("type"));
QStringView type = attrs.value("type"_L1);
forever {
xml.readNext();
@ -169,8 +168,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement())
break;
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(name.toString()));
exit(EXIT_FAILURE);
}
@ -180,45 +178,45 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
text = text.trimmed();
QVariant result;
bool ok;
if (type.isEmpty()) {
// ok
} else if (type == QLatin1String("number")) {
} else if (type == "number"_L1) {
// try integer first
bool ok;
qint64 v = text.toLongLong(&ok);
if (ok) {
result = v;
} else {
// let's see floating point
double d = text.toDouble(&ok);
result = d;
if (!ok) {
fprintf(stderr, "%lld:%lld: Invalid XML: could not interpret '%s' as a number.\n",
xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
exit(EXIT_FAILURE);
}
result = d;
}
} else if (type == QLatin1String("bytes")) {
} else if (type == "bytes"_L1) {
QByteArray data = text.toLatin1();
QStringView encoding = attrs.value("encoding");
if (encoding == QLatin1String("base64url")) {
if (encoding == "base64url"_L1) {
result = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding);
} else if (encoding == QLatin1String("hex")) {
} else if (encoding == "hex"_L1) {
result = QByteArray::fromHex(data);
} else if (encoding.isEmpty() || encoding == QLatin1String("base64")) {
} else if (encoding.isEmpty() || encoding == "base64"_L1) {
result = QByteArray::fromBase64(data);
} else {
fprintf(stderr, "%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.\n",
xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString()));
exit(EXIT_FAILURE);
}
} else if (type == QLatin1String("string")) {
} else if (type == "string"_L1) {
result = text.toString();
} else if (type == QLatin1String("null")) {
} else if (type == "null"_L1) {
result = QVariant::fromValue(nullptr);
} else if (type == QLatin1String("CBOR simple type")) {
} else if (type == "CBOR simple type"_L1) {
result = QVariant::fromValue(QCborSimpleType(text.toShort()));
} else if (type == QLatin1String("bits")) {
} else if (type == "bits"_L1) {
QBitArray ba;
ba.resize(text.size());
qsizetype n = 0;
@ -238,13 +236,13 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
result = ba;
} else {
int id = QMetaType::UnknownType;
if (type == QLatin1String("datetime"))
if (type == "datetime"_L1)
id = QMetaType::QDateTime;
else if (type == QLatin1String("url"))
else if (type == "url"_L1)
id = QMetaType::QUrl;
else if (type == QLatin1String("uuid"))
else if (type == "uuid"_L1)
id = QMetaType::QUuid;
else if (type == QLatin1String("regex"))
else if (type == "regex"_L1)
id = QMetaType::QRegularExpression;
else
id = QMetaType::fromName(type.toLatin1()).id();
@ -267,8 +265,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
} while (xml.isComment() || xml.isWhitespace());
if (!xml.isEndElement()) {
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(name.toString()));
exit(EXIT_FAILURE);
}
@ -287,9 +284,9 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
variantToXml(xml, v);
xml.writeEndElement();
} else if (type == QMetaType::QVariantMap || type == qMetaTypeId<VariantOrderedMap>()) {
const VariantOrderedMap map = (type == QMetaType::QVariantMap) ?
VariantOrderedMap(v.toMap()) :
qvariant_cast<VariantOrderedMap>(v);
const VariantOrderedMap map = (type == QMetaType::QVariantMap)
? VariantOrderedMap(v.toMap())
: qvariant_cast<VariantOrderedMap>(v);
xml.writeStartElement("map");
for (const auto &pair : map) {
@ -301,7 +298,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
xml.writeEndElement();
} else {
xml.writeStartElement("value");
QString typeString = QStringLiteral("type");
QString typeString = "type"_L1;
switch (type) {
case QMetaType::Short:
case QMetaType::UShort:
@ -399,37 +396,37 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
}
}
QString XmlConverter::name()
QString XmlConverter::name() const
{
return QStringLiteral("xml");
return "xml"_L1;
}
Converter::Direction XmlConverter::directions()
Converter::Directions XmlConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options XmlConverter::outputOptions()
Converter::Options XmlConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *XmlConverter::optionsHelp()
const char *XmlConverter::optionsHelp() const
{
return xmlOptionHelp;
}
bool XmlConverter::probeFile(QIODevice *f)
bool XmlConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".xml")))
if (file->fileName().endsWith(".xml"_L1))
return true;
}
return f->isReadable() && f->peek(5) == "<?xml";
}
QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant XmlConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = this;
@ -445,13 +442,14 @@ QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter)
return v;
}
void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void XmlConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
bool compact = false;
for (const QString &s : options) {
if (s == QLatin1String("compact=no")) {
if (s == "compact=no"_L1) {
compact = false;
} else if (s == QLatin1String("compact=yes")) {
} else if (s == "compact=yes"_L1) {
compact = true;
} else {
fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s",

View File

@ -10,13 +10,14 @@ class XmlConverter : 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;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // XMLCONVERTER_H

View File

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

View File

@ -1,26 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/*
main.cpp
Provides the main function for the RSS news reader example.
*/
#include <QtWidgets>
#include "rsslisting.h"
/*!
Create an application and a main widget. Open the main widget for
user input, and exit with an appropriate return value when it is
closed.
*/
int main(int argc, char **argv)
{
QApplication app(argc, argv);
RSSListing *rsslisting = new RSSListing;
rsslisting->show();
return app.exec();
}

View File

@ -1,211 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/*
rsslisting.cpp
Provides a widget for displaying news items from RDF news sources.
RDF is an XML-based format for storing items of information (see
http://www.w3.org/RDF/ for details).
The widget itself provides a simple user interface for specifying
the URL of a news source, and controlling the downloading of news.
The widget downloads and parses the XML asynchronously, feeding the
data to an XML reader in pieces. This allows the user to interrupt
its operation, and also allows very large data sources to be read.
*/
#include <QtCore>
#include <QtWidgets>
#include <QtNetwork>
#include "rsslisting.h"
/*
Constructs an RSSListing widget with a simple user interface, and sets
up the XML reader to use a custom handler class.
The user interface consists of a line edit, a push button, and a
list view widget. The line edit is used for entering the URLs of news
sources; the push button starts the process of reading the
news.
*/
RSSListing::RSSListing(QWidget *parent)
: QWidget(parent), currentReply(0)
{
lineEdit = new QLineEdit(this);
lineEdit->setText("http://blog.qt.io/feed/");
fetchButton = new QPushButton(tr("Fetch"), this);
treeWidget = new QTreeWidget(this);
connect(treeWidget, &QTreeWidget::itemActivated,
this, &RSSListing::itemActivated);
QStringList headerLabels;
headerLabels << tr("Title") << tr("Link");
treeWidget->setHeaderLabels(headerLabels);
treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(&manager, &QNetworkAccessManager::finished,
this, &RSSListing::finished);
connect(lineEdit, &QLineEdit::returnPressed, this, &RSSListing::fetch);
connect(fetchButton, &QPushButton::clicked, this, &RSSListing::fetch);
QVBoxLayout *layout = new QVBoxLayout(this);
QHBoxLayout *hboxLayout = new QHBoxLayout;
hboxLayout->addWidget(lineEdit);
hboxLayout->addWidget(fetchButton);
layout->addLayout(hboxLayout);
layout->addWidget(treeWidget);
setWindowTitle(tr("RSS listing example"));
resize(640,480);
}
/*
Starts the network request and connects the needed signals
*/
void RSSListing::get(const QUrl &url)
{
QNetworkRequest request(url);
if (currentReply) {
currentReply->disconnect(this);
currentReply->deleteLater();
}
currentReply = manager.get(request);
connect(currentReply, &QNetworkReply::readyRead, this, &RSSListing::readyRead);
connect(currentReply, &QNetworkReply::metaDataChanged, this, &RSSListing::metaDataChanged);
connect(currentReply, &QNetworkReply::errorOccurred, this, &RSSListing::error);
}
/*
Starts fetching data from a news source specified in the line
edit widget.
The line edit is made read only to prevent the user from modifying its
contents during the fetch; this is only for cosmetic purposes.
The fetch button is disabled, the list view is cleared, and we
define the last list view item to be 0, meaning that there are no
existing items in the list.
A URL is created with the raw contents of the line edit and
a get is initiated.
*/
void RSSListing::fetch()
{
lineEdit->setReadOnly(true);
fetchButton->setEnabled(false);
treeWidget->clear();
xml.clear();
QUrl url(lineEdit->text());
get(url);
}
void RSSListing::metaDataChanged()
{
QUrl redirectionTarget = currentReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (redirectionTarget.isValid()) {
get(redirectionTarget);
}
}
/*
Reads data received from the RDF source.
We read all the available data, and pass it to the XML
stream reader. Then we call the XML parsing function.
*/
void RSSListing::readyRead()
{
int statusCode = currentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode >= 200 && statusCode < 300) {
QByteArray data = currentReply->readAll();
xml.addData(data);
parseXml();
}
}
/*
Finishes processing an HTTP request.
The default behavior is to keep the text edit read only.
If an error has occurred, the user interface is made available
to the user for further input, allowing a new fetch to be
started.
If the HTTP get request has finished, we make the
user interface available to the user for further input.
*/
void RSSListing::finished(QNetworkReply *reply)
{
Q_UNUSED(reply);
lineEdit->setReadOnly(false);
fetchButton->setEnabled(true);
}
/*
Parses the XML data and creates treeWidget items accordingly.
*/
void RSSListing::parseXml()
{
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == u"item")
linkString = xml.attributes().value("rss:about").toString();
currentTag = xml.name().toString();
} else if (xml.isEndElement()) {
if (xml.name() == u"item") {
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, titleString);
item->setText(1, linkString);
treeWidget->addTopLevelItem(item);
titleString.clear();
linkString.clear();
}
} else if (xml.isCharacters() && !xml.isWhitespace()) {
if (currentTag == "title")
titleString += xml.text();
else if (currentTag == "link")
linkString += xml.text();
}
}
if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString();
}
}
/*
Open the link in the browser
*/
void RSSListing::itemActivated(QTreeWidgetItem * item)
{
QDesktopServices::openUrl(QUrl(item->text(1)));
}
void RSSListing::error(QNetworkReply::NetworkError)
{
qWarning("error retrieving RSS feed");
currentReply->disconnect(this);
currentReply->deleteLater();
currentReply = 0;
}

View File

@ -1,55 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef RSSLISTING_H
#define RSSLISTING_H
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QWidget>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QUrl>
QT_BEGIN_NAMESPACE
class QLineEdit;
class QTreeWidget;
class QTreeWidgetItem;
class QPushButton;
QT_END_NAMESPACE
class RSSListing : public QWidget
{
Q_OBJECT
public:
RSSListing(QWidget *widget = nullptr);
public slots:
void fetch();
void finished(QNetworkReply *reply);
void readyRead();
void metaDataChanged();
void itemActivated(QTreeWidgetItem * item);
void error(QNetworkReply::NetworkError);
private:
void parseXml();
void get(const QUrl &url);
QXmlStreamReader xml;
QString currentTag;
QString linkString;
QString titleString;
QNetworkAccessManager manager;
QNetworkReply *currentReply;
QLineEdit *lineEdit;
QTreeWidget *treeWidget;
QPushButton *fetchButton;
};
#endif

View File

@ -1,8 +0,0 @@
HEADERS += rsslisting.h
SOURCES += main.cpp rsslisting.cpp
QT += network widgets
requires(qtConfig(treewidget))
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/rsslisting
INSTALLS += target

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(savegame LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()

View File

@ -6,15 +6,10 @@
#include <QMetaEnum>
#include <QTextStream>
Character::Character()
= default;
Character::Character() = default;
Character::Character(const QString &name,
int level,
Character::ClassType classType) :
mName(name),
mLevel(level),
mClassType(classType)
Character::Character(const QString &name, int level, Character::ClassType classType)
: mName(name), mLevel(level), mClassType(classType)
{
}
@ -48,35 +43,41 @@ void Character::setClassType(Character::ClassType classType)
mClassType = classType;
}
//! [0]
void Character::read(const QJsonObject &json)
//! [fromJson]
Character Character::fromJson(const QJsonObject &json)
{
if (json.contains("name") && json["name"].isString())
mName = json["name"].toString();
Character result;
if (json.contains("level") && json["level"].isDouble())
mLevel = json["level"].toInt();
if (const QJsonValue v = json["name"]; v.isString())
result.mName = v.toString();
if (json.contains("classType") && json["classType"].isDouble())
mClassType = ClassType(json["classType"].toInt());
if (const QJsonValue v = json["level"]; v.isDouble())
result.mLevel = v.toInt();
if (const QJsonValue v = json["classType"]; v.isDouble())
result.mClassType = ClassType(v.toInt());
return result;
}
//! [0]
//! [fromJson]
//! [1]
void Character::write(QJsonObject &json) const
//! [toJson]
QJsonObject Character::toJson() const
{
QJsonObject json;
json["name"] = mName;
json["level"] = mLevel;
json["classType"] = mClassType;
return json;
}
//! [1]
//! [toJson]
void Character::print(int indentation) const
void Character::print(QTextStream &s, int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Name:\t" << mName << "\n";
QTextStream(stdout) << indent << "Level:\t" << mLevel << "\n";
const QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType);
QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType);
QTextStream(stdout) << indent << "Class:\t" << className << "\n";
s << indent << "Name:\t" << mName << "\n"
<< indent << "Level:\t" << mLevel << "\n"
<< indent << "Class:\t" << className << "\n";
}

View File

@ -8,15 +8,15 @@
#include <QObject>
#include <QString>
QT_FORWARD_DECLARE_CLASS(QTextStream)
//! [0]
class Character
{
Q_GADGET
public:
enum ClassType {
Warrior, Mage, Archer
};
enum ClassType { Warrior, Mage, Archer };
Q_ENUM(ClassType)
Character();
@ -31,10 +31,11 @@ public:
ClassType classType() const;
void setClassType(ClassType classType);
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
static Character fromJson(const QJsonObject &json);
QJsonObject toJson() const;
void print(QTextStream &s, int indentation = 0) const;
void print(int indentation = 0) const;
private:
QString mName;
int mLevel = 0;

View File

@ -3,7 +3,7 @@
/*!
\example serialization/savegame
\examplecategory {Input/Output}
\examplecategory {Data Processing & I/O}
\title JSON Save Game Example
\brief The JSON Save Game example demonstrates how to save and load a
@ -11,11 +11,12 @@
Many games provide save functionality, so that the player's progress through
the game can be saved and loaded at a later time. The process of saving a
game generally involves serializing each game object's member variables
to a file. Many formats can be used for this purpose, one of which is JSON.
With QJsonDocument, you also have the ability to serialize a document in a
\l {RFC 7049} {CBOR} format, which is great if you
don't want the save file to be readable, or if you need to keep the file size down.
game generally involves serializing each game object's member variables to a
file. Many formats can be used for this purpose, one of which is JSON. With
QJsonDocument, you also have the ability to serialize a document in a \l
{RFC 7049} {CBOR} format, which is great if you don't want the save file to
be easy to read (but see \l {Parsing and displaying CBOR data} for how it \e
can be read), or if you need to keep the file size down.
In this example, we'll demonstrate how to save and load a simple game to
and from JSON and binary formats.
@ -25,45 +26,83 @@
The Character class represents a non-player character (NPC) in our game, and
stores the player's name, level, and class type.
It provides read() and write() functions to serialise its member variables.
It provides static fromJson() and non-static toJson() functions to
serialise itself.
\note This pattern (fromJson()/toJson()) works because QJsonObjects can be
constructed independent of an owning QJsonDocument, and because the data
types being (de)serialized here are value types, so can be copied. When
serializing to another format — for example XML or QDataStream, which require passing
a document-like object — or when the object identity is important (QObject
subclasses, for example), other patterns may be more suitable. See the
\l{xml/dombookmarks} and \l{xml/streambookmarks} examples for XML, and the
implementation of \l QListWidgetItem::read() and \l QListWidgetItem::write()
for idiomatic QDataStream serialization. The \c{print()} functions in this example
are good examples of QTextStream serialization, even though they, of course, lack
the deserialization side.
\snippet serialization/savegame/character.h 0
Of particular interest to us are the read and write function
Of particular interest to us are the fromJson() and toJson() function
implementations:
\snippet serialization/savegame/character.cpp 0
\snippet serialization/savegame/character.cpp fromJson
In the read() function, we assign Character's members values from the
QJsonObject argument. You can use either \l QJsonObject::operator[]() or
QJsonObject::value() to access values within the JSON object; both are
const functions and return QJsonValue::Undefined if the key is invalid. We
check if the keys are valid before attempting to read them with
QJsonObject::contains().
In the fromJson() function, we construct a local \c result Character object
and assign \c{result}'s members values from the QJsonObject argument. You
can use either \l QJsonObject::operator[]() or QJsonObject::value() to
access values within the JSON object; both are const functions and return
QJsonValue::Undefined if the key is invalid. In particular, the \c{is...}
functions (for example \l QJsonValue::isString(), \l
QJsonValue::isDouble()) return \c false for QJsonValue::Undefined, so we
can check for existence as well as the correct type in a single lookup.
\snippet serialization/savegame/character.cpp 1
If a value does not exist in the JSON object, or has the wrong type, we
don't write to the corresponding \c result member, either, thereby
preserving any values the default constructor may have set. This means
default values are centrally defined in one location (the default
constructor) and need not be repeated in serialisation code
(\l{https://en.wikipedia.org/wiki/Don%27t_repeat_yourself}{DRY}).
In the write() function, we do the reverse of the read() function; assign
values from the Character object to the JSON object. As with accessing
values, there are two ways to set values on a QJsonObject:
\l QJsonObject::operator[]() and QJsonObject::insert(). Both will override
any existing value at the given key.
Observe the use of
\l{https://en.cppreference.com/w/cpp/language/if#If_statements_with_initializer}
{C++17 if-with-initializer} to separate scoping and checking of the variable \c v.
This means we can keep the variable name short, because its scope is limited.
Next up is the Level class:
Compare that to the naïve approach using \c QJsonObject::contains():
\badcode
if (json.contains("name") && json["name"].isString())
result.mName = json["name"].toString();
\endcode
which, beside being less readable, requires a total of three lookups (no,
the compiler will \e not optimize these into one), so is three times
slower and repeats \c{"name"} three times (violating the DRY principle).
\snippet serialization/savegame/character.cpp toJson
In the toJson() function, we do the reverse of the fromJson() function;
assign values from the Character object to a new JSON object we then
return. As with accessing values, there are two ways to set values on a
QJsonObject: \l QJsonObject::operator[]() and \l QJsonObject::insert().
Both will override any existing value at the given key.
\section1 The Level Class
\snippet serialization/savegame/level.h 0
We want to have several levels in our game, each with several NPCs, so we
keep a QList of Character objects. We also provide the familiar read() and
write() functions.
We want the levels in our game to each each have several NPCs, so we keep a QList
of Character objects. We also provide the familiar fromJson() and toJson()
functions.
\snippet serialization/savegame/level.cpp 0
\snippet serialization/savegame/level.cpp fromJson
Containers can be written and read to and from JSON using QJsonArray. In our
Containers can be written to and read from JSON using QJsonArray. In our
case, we construct a QJsonArray from the value associated with the key
\c "npcs". Then, for each QJsonValue element in the array, we call
toObject() to get the Character's JSON object. The Character object can then
read their JSON and be appended to our NPC array.
toObject() to get the Character's JSON object. Character::fromJson() can
then turn that QJSonObject into a Character object to append to our NPC array.
\note \l{Container Classes}{Associate containers} can be written by storing
the key in each value object (if it's not already). With this approach, the
@ -71,11 +110,13 @@
element is used as the key to construct the container when reading it back
in.
\snippet serialization/savegame/level.cpp 1
\snippet serialization/savegame/level.cpp toJson
Again, the write() function is similar to the read() function, except
Again, the toJson() function is similar to the fromJson() function, except
reversed.
\section1 The Game Class
Having established the Character and Level classes, we can move on to
the Game class:
@ -87,26 +128,43 @@
Next, we provide accessors for the player and levels. We then expose three
functions: newGame(), saveGame() and loadGame().
The read() and write() functions are used by saveGame() and loadGame().
The read() and toJson() functions are used by saveGame() and loadGame().
\snippet serialization/savegame/game.cpp 0
\div{class="admonition note"}\b{Note:}
Despite \c Game being a value class, we assume that the author wants a game to have
identity, much like your main window would have. We therefore don't use a
static fromJson() function, which would create a new object, but a read()
function we can call on existing objects. There's a 1:1 correspondence
between read() and fromJson(), in that one can be implemented in terms of
the other:
\code
void read(const QJsonObject &json) { *this = fromJson(json); }
static Game fromObject(const QJsonObject &json) { Game g; g.read(json); return g; }
\endcode
We just use what's more convenient for callers of the functions.
\enddiv
\snippet serialization/savegame/game.cpp newGame
To setup a new game, we create the player and populate the levels and their
NPCs.
\snippet serialization/savegame/game.cpp 1
\snippet serialization/savegame/game.cpp read
The first thing we do in the read() function is tell the player to read
itself. We then clear the level array so that calling loadGame() on the
same Game object twice doesn't result in old levels hanging around.
The read() function starts by replacing the player with the
one read from JSON. We then clear() the level array so that calling
loadGame() on the same Game object twice doesn't result in old levels
hanging around.
We then populate the level array by reading each Level from a QJsonArray.
\snippet serialization/savegame/game.cpp 2
\snippet serialization/savegame/game.cpp toJson
We write the game to JSON similarly to how we write Level.
Writing the game to JSON is similar to writing a level.
\snippet serialization/savegame/game.cpp 3
\snippet serialization/savegame/game.cpp loadGame
When loading a saved game in loadGame(), the first thing we do is open the
save file based on which format it was saved to; \c "save.json" for JSON,
@ -120,14 +178,16 @@
After constructing the QJsonDocument, we instruct the Game object to read
itself and then return \c true to indicate success.
\snippet serialization/savegame/game.cpp 4
\snippet serialization/savegame/game.cpp saveGame
Not surprisingly, saveGame() looks very much like loadGame(). We determine
the file extension based on the format, print a warning and return \c false
if the opening of the file fails. We then write the Game object to a
QJsonDocument, and call either QJsonDocument::toJson() or to
QJsonDocument::toBinaryData() to save the game, depending on which format
was specified.
QJsonObject. To save the game in the format that was specified, we
convert the JSON object into either a QJsonDocument for a subsequent
QJsonDocument::toJson() call, or a QCborValue for QCborValue::toCbor().
\section1 Tying It All Together
We are now ready to enter main():

View File

@ -11,6 +11,8 @@
#include <QRandomGenerator>
#include <QTextStream>
using namespace Qt::StringLiterals;
Character Game::player() const
{
return mPlayer;
@ -21,52 +23,45 @@ QList<Level> Game::levels() const
return mLevels;
}
//! [0]
//! [newGame]
void Game::newGame()
{
mPlayer = Character();
mPlayer.setName(QStringLiteral("Hero"));
mPlayer.setName("Hero"_L1);
mPlayer.setClassType(Character::Archer);
mPlayer.setLevel(QRandomGenerator::global()->bounded(15, 21));
mLevels.clear();
mLevels.reserve(2);
Level village(QStringLiteral("Village"));
Level village("Village"_L1);
QList<Character> villageNpcs;
villageNpcs.reserve(2);
villageNpcs.append(Character(QStringLiteral("Barry the Blacksmith"),
QRandomGenerator::global()->bounded(8, 11),
Character::Warrior));
villageNpcs.append(Character(QStringLiteral("Terry the Trader"),
QRandomGenerator::global()->bounded(6, 8),
Character::Warrior));
villageNpcs.append(Character("Barry the Blacksmith"_L1,
QRandomGenerator::global()->bounded(8, 11), Character::Warrior));
villageNpcs.append(Character("Terry the Trader"_L1,
QRandomGenerator::global()->bounded(6, 8), Character::Warrior));
village.setNpcs(villageNpcs);
mLevels.append(village);
Level dungeon(QStringLiteral("Dungeon"));
Level dungeon("Dungeon"_L1);
QList<Character> dungeonNpcs;
dungeonNpcs.reserve(3);
dungeonNpcs.append(Character(QStringLiteral("Eric the Evil"),
QRandomGenerator::global()->bounded(18, 26),
Character::Mage));
dungeonNpcs.append(Character(QStringLiteral("Eric's Left Minion"),
QRandomGenerator::global()->bounded(5, 7),
Character::Warrior));
dungeonNpcs.append(Character(QStringLiteral("Eric's Right Minion"),
QRandomGenerator::global()->bounded(4, 9),
Character::Warrior));
dungeonNpcs.append(Character("Eric the Evil"_L1,
QRandomGenerator::global()->bounded(18, 26), Character::Mage));
dungeonNpcs.append(Character("Eric's Left Minion"_L1,
QRandomGenerator::global()->bounded(5, 7), Character::Warrior));
dungeonNpcs.append(Character("Eric's Right Minion"_L1,
QRandomGenerator::global()->bounded(4, 9), Character::Warrior));
dungeon.setNpcs(dungeonNpcs);
mLevels.append(dungeon);
}
//! [0]
//! [newGame]
//! [3]
//! [loadGame]
bool Game::loadGame(Game::SaveFormat saveFormat)
{
QFile loadFile(saveFormat == Json
? QStringLiteral("save.json")
: QStringLiteral("save.dat"));
QFile loadFile(saveFormat == Json ? "save.json"_L1 : "save.dat"_L1);
if (!loadFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open save file.");
@ -76,85 +71,72 @@ bool Game::loadGame(Game::SaveFormat saveFormat)
QByteArray saveData = loadFile.readAll();
QJsonDocument loadDoc(saveFormat == Json
? QJsonDocument::fromJson(saveData)
: QJsonDocument(QCborValue::fromCbor(saveData).toMap().toJsonObject()));
? QJsonDocument::fromJson(saveData)
: QJsonDocument(QCborValue::fromCbor(saveData).toMap().toJsonObject()));
read(loadDoc.object());
QTextStream(stdout) << "Loaded save for "
<< loadDoc["player"]["name"].toString()
<< " using "
<< (saveFormat != Json ? "CBOR" : "JSON") << "...\n";
QTextStream(stdout) << "Loaded save for " << loadDoc["player"]["name"].toString()
<< " using " << (saveFormat != Json ? "CBOR" : "JSON") << "...\n";
return true;
}
//! [3]
//! [loadGame]
//! [4]
//! [saveGame]
bool Game::saveGame(Game::SaveFormat saveFormat) const
{
QFile saveFile(saveFormat == Json
? QStringLiteral("save.json")
: QStringLiteral("save.dat"));
QFile saveFile(saveFormat == Json ? "save.json"_L1 : "save.dat"_L1);
if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning("Couldn't open save file.");
return false;
}
QJsonObject gameObject;
write(gameObject);
saveFile.write(saveFormat == Json
? QJsonDocument(gameObject).toJson()
: QCborValue::fromJsonValue(gameObject).toCbor());
QJsonObject gameObject = toJson();
saveFile.write(saveFormat == Json ? QJsonDocument(gameObject).toJson()
: QCborValue::fromJsonValue(gameObject).toCbor());
return true;
}
//! [4]
//! [saveGame]
//! [1]
//! [read]
void Game::read(const QJsonObject &json)
{
if (json.contains("player") && json["player"].isObject())
mPlayer.read(json["player"].toObject());
if (const QJsonValue v = json["player"]; v.isObject())
mPlayer = Character::fromJson(v.toObject());
if (json.contains("levels") && json["levels"].isArray()) {
QJsonArray levelArray = json["levels"].toArray();
if (const QJsonValue v = json["levels"]; v.isArray()) {
const QJsonArray levels = v.toArray();
mLevels.clear();
mLevels.reserve(levelArray.size());
for (const QJsonValue &v : levelArray) {
QJsonObject levelObject = v.toObject();
Level level;
level.read(levelObject);
mLevels.append(level);
}
mLevels.reserve(levels.size());
for (const QJsonValue &level : levels)
mLevels.append(Level::fromJson(level.toObject()));
}
}
//! [1]
//! [read]
//! [2]
void Game::write(QJsonObject &json) const
//! [toJson]
QJsonObject Game::toJson() const
{
QJsonObject playerObject;
mPlayer.write(playerObject);
json["player"] = playerObject;
QJsonObject json;
json["player"] = mPlayer.toJson();
QJsonArray levelArray;
for (const Level &level : mLevels) {
QJsonObject levelObject;
level.write(levelObject);
levelArray.append(levelObject);
}
json["levels"] = levelArray;
QJsonArray levels;
for (const Level &level : mLevels)
levels.append(level.toJson());
json["levels"] = levels;
return json;
}
//! [2]
//! [toJson]
void Game::print(int indentation) const
void Game::print(QTextStream &s, int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Player\n";
mPlayer.print(indentation + 1);
s << indent << "Player\n";
mPlayer.print(s, indentation + 1);
QTextStream(stdout) << indent << "Levels\n";
s << indent << "Levels\n";
for (const Level &level : mLevels)
level.print(indentation + 1);
level.print(s, indentation + 1);
}

View File

@ -10,13 +10,13 @@
#include <QJsonObject>
#include <QList>
QT_FORWARD_DECLARE_CLASS(QTextStream)
//! [0]
class Game
{
public:
enum SaveFormat {
Json, Binary
};
enum SaveFormat { Json, Binary };
Character player() const;
QList<Level> levels() const;
@ -26,9 +26,10 @@ public:
bool saveGame(SaveFormat saveFormat) const;
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
QJsonObject toJson() const;
void print(QTextStream &s, int indentation = 0) const;
void print(int indentation = 0) const;
private:
Character mPlayer;
QList<Level> mLevels;

View File

@ -6,9 +6,7 @@
#include <QJsonArray>
#include <QTextStream>
Level::Level(const QString &name) : mName(name)
{
}
Level::Level(const QString &name) : mName(name) { }
QString Level::name() const
{
@ -25,46 +23,43 @@ void Level::setNpcs(const QList<Character> &npcs)
mNpcs = npcs;
}
//! [0]
void Level::read(const QJsonObject &json)
//! [fromJson]
Level Level::fromJson(const QJsonObject &json)
{
if (json.contains("name") && json["name"].isString())
mName = json["name"].toString();
Level result;
if (json.contains("npcs") && json["npcs"].isArray()) {
QJsonArray npcArray = json["npcs"].toArray();
mNpcs.clear();
mNpcs.reserve(npcArray.size());
for (const QJsonValue &v : npcArray) {
QJsonObject npcObject = v.toObject();
Character npc;
npc.read(npcObject);
mNpcs.append(npc);
}
if (const QJsonValue v = json["name"]; v.isString())
result.mName = v.toString();
if (const QJsonValue v = json["npcs"]; v.isArray()) {
const QJsonArray npcs = v.toArray();
result.mNpcs.reserve(npcs.size());
for (const QJsonValue &npc : npcs)
result.mNpcs.append(Character::fromJson(npc.toObject()));
}
}
//! [0]
//! [1]
void Level::write(QJsonObject &json) const
return result;
}
//! [fromJson]
//! [toJson]
QJsonObject Level::toJson() const
{
QJsonObject json;
json["name"] = mName;
QJsonArray npcArray;
for (const Character &npc : mNpcs) {
QJsonObject npcObject;
npc.write(npcObject);
npcArray.append(npcObject);
}
for (const Character &npc : mNpcs)
npcArray.append(npc.toJson());
json["npcs"] = npcArray;
return json;
}
//! [1]
//! [toJson]
void Level::print(int indentation) const
void Level::print(QTextStream &s, int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Name:\t" << mName << "\n";
QTextStream(stdout) << indent << "NPCs:\n";
s << indent << "Name:\t" << mName << "\n" << indent << "NPCs:\n";
for (const Character &character : mNpcs)
character.print(2);
character.print(s, indentation + 1);
}

View File

@ -9,6 +9,8 @@
#include <QJsonObject>
#include <QList>
QT_FORWARD_DECLARE_CLASS(QTextStream)
//! [0]
class Level
{
@ -21,10 +23,11 @@ public:
QList<Character> npcs() const;
void setNpcs(const QList<Character> &npcs);
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
static Level fromJson(const QJsonObject &json);
QJsonObject toJson() const;
void print(QTextStream &s, int indentation = 0) const;
void print(int indentation = 0) const;
private:
QString mName;
QList<Character> mNpcs;

View File

@ -17,9 +17,9 @@ int main(int argc, char *argv[])
const QStringList args = QCoreApplication::arguments();
const bool newGame
= args.size() <= 1 || QString::compare(args[1], "load"_L1, Qt::CaseInsensitive) == 0;
= args.size() <= 1 || QString::compare(args[1], "load"_L1, Qt::CaseInsensitive) != 0;
const bool json
= args.size() <= 2 || QString::compare(args[2], "binary"_L1, Qt::CaseInsensitive) == 0;
= args.size() <= 2 || QString::compare(args[2], "binary"_L1, Qt::CaseInsensitive) != 0;
Game game;
if (newGame)
@ -29,8 +29,9 @@ int main(int argc, char *argv[])
// Game is played; changes are made...
//! [0]
//! [1]
QTextStream(stdout) << "Game ended in the following state:\n";
game.print();
QTextStream s(stdout);
s << "Game ended in the following state:\n";
game.print(s);
if (!game.saveGame(json ? Game::Json : Game::Binary))
return 1;

View File

@ -6,6 +6,4 @@ SUBDIRS = \
qtHaveModule(widgets) {
SUBDIRS += streambookmarks
qtHaveModule(network): SUBDIRS += \
rsslisting
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -3,169 +3,221 @@
/*!
\example serialization/streambookmarks
\examplecategory {Data Processing & I/O}
\meta tag {network}
\title QXmlStream Bookmarks Example
\examplecategory {Input/Output}
\brief Demonstrates how to read and write to XBEL files.
\brief Demonstrates how to read and write XBEL files.
\ingroup xml-examples
The QXmlStream Bookmarks example provides a reader for XML Bookmark
Exchange Language (XBEL) files using Qt's QXmlStreamReader class
for reading, and QXmlStreamWriter class for writing the files.
The QXmlStream Bookmarks example provides a viewer for XML Bookmark Exchange
Language (XBEL) files. It can read bookmarks using Qt's QXmlStreamReader and
write them back out again using QXmlStreamWriter. As this example aims to
show how to use these reader and writer types, it provides no means to open
a bookmark, add a new one, or merge two bookmark files, and only minimal
scope for editing bookmarks. None the less, it could surely be extended with
such features, if desired.
\image xmlstreamexample-screenshot.png
\image screenshot.png
\section1 XbelWriter Class Definition
The \c XbelWriter class contains a private instance of QXmlStreamWriter,
which provides an XML writer with a streaming API. \c XbelWriter also
has a reference to the QTreeWidget instance where the bookmark hierarchy
is stored.
The \c XbelWriter class takes a \l{QTreeWidget}{tree widget} describing a
hierarchy of folders containing bookmarks. Its \c writeFile() provides the
means to write out this hierarchy, in XBEL format, to a given output device.
Internally, it records the tree widget it was given and packages a private
instance of QXmlStreamWriter, which provides it with the means to stream
XML. It has an internal \c writeItem() to write each item in its tree.
\snippet serialization/streambookmarks/xbelwriter.h 0
\section1 XbelWriter Class Implementation
The \c XbelWriter constructor accepts a \a treeWidget to initialize within
its definition. We enable \l{QXmlStreamWriter}'s auto-formatting property
to ensure line-breaks and indentations are added automatically to empty
sections between elements, increasing readability as the data is split into
several lines.
The \c XbelWriter constructor accepts the \a treeWidget it will describe. It
stores that and enables \l{QXmlStreamWriter}'s auto-formatting property.
This last splits the data into several lines, with indentation to indicate
the structure of the tree, which makes the XML output easier to read.
\snippet serialization/streambookmarks/xbelwriter.cpp 0
The \c writeFile() function accepts a QIODevice object and sets it using
\c setDevice(). This function then writes the document type
definition(DTD), the start element, the version, and \c{treeWidget}'s
top-level items.
The \c writeFile() function accepts a QIODevice object and directs its
QXmlStreamWriter member to write to this device, using \c setDevice(). This
function then writes the document type definition(DTD), the start element,
the version, and delegates writing of each of the \c{treeWidget}'s top-level
items to \c writeItem(). Finally, it closes the document and returns.
\snippet serialization/streambookmarks/xbelwriter.cpp 1
The \c writeItem() function accepts a QTreeWidgetItem object and writes it
to the stream, depending on its \c tagName, which can either be a "folder",
"bookmark", or "separator".
The \c writeItem() function accepts a QTreeWidgetItem object and writes to
its XML stream a representation of the object, which depends on its \c
UserRole, which can be one of a \c{"folder"}, \c{"bookmark"},
or \c{"separator"}. Within each folder, it calls itself recursively on each
child item, to recursively include a representation of each child within the
folder's XML element.
\snippet serialization/streambookmarks/xbelwriter.cpp 2
\section1 XbelReader Class Definition
The \c XbelReader contains a private instance of QXmlStreamReader, the
companion class to QXmlStreamWriter. \c XbelReader also contains a
reference to the QTreeWidget that is used to group the bookmarks according
to their hierarchy.
The \c XbelReader takes a \l{QTreeWidget}{tree widget} to populate with
items describing a bookmark hierarchy. It supports reading XBEL data from a
QIODevice as a source of these items. If parsing of the XBEL data fails, it
can report what went wrong.
Internally, it records the QTreeWidget that it will populate and packages an
instance of QXmlStreamReader, the companion class to QXmlStreamWriter, which
it will use to read XBEL data.
\snippet serialization/streambookmarks/xbelreader.h 0
\section1 XbelReader Class Implementation
The \c XbelReader constructor accepts a QTreeWidget to initialize the
\c treeWidget within its definition. A QStyle object is used to set
\c{treeWidget}'s style property. The \c folderIcon is set to QIcon::Normal
mode where the pixmap is only displayed when the user is not interacting
with the icon. The QStyle::SP_DirClosedIcon, QStyle::SP_DirOpenIcon, and
QStyle::SP_FileIcon correspond to standard pixmaps that follow the style
of your GUI.
Since the XBEL reader is only concerned with reading XML elements, it makes
extensive use of the \l{QXmlStreamReader::}{readNextStartElement()}
convenience function.
The \c XbelReader constructor requires a QTreeWidget that it will populate.
It populates the tree widget's style with suitable icons: a folder icon that
changes form to indicate whether each folder as open or closed; and a
standard file icon for the individual bookmarks within those folders.
\snippet serialization/streambookmarks/xbelreader.cpp 0
The \c read() function accepts a QIODevice and sets it using
\l{QXmlStreamReader::}{setDevice()}. The actual process of reading only
takes place if the file is a valid XBEL 1.0 file. Note that the XML input
needs to be well-formed to be accepted by QXmlStreamReader. Otherwise, the
\l{QXmlStreamReader::}{raiseError()} function is used to display an error
message. Since the XBEL reader is only concerned with reading XML elements,
it makes extensive use of the \l{QXmlStreamReader::}{readNextStartElement()}
convenience function.
The \c read() function accepts a QIODevice. It directs its QXmlStreamReader
member to read content from that device. Note that the XML input must be
well-formed to be accepted by QXmlStreamReader. First it reads the outer
structure and verifies the content is an XBEL 1.0 file; if it is, \c read()
delegates the actual reading of content to the internal \c readXBEL().
Otherwise, the \l{QXmlStreamReader::}{raiseError()} function is used to
record an error message. The reader itself may also do the same if it
encounters errors in the input. When \c read() has finished, it returns
true if there were no errors.
\snippet serialization/streambookmarks/xbelreader.cpp 1
The \c errorString() function is used if an error occurred, in order to
obtain a description of the error complete with line and column number
information.
If \c read() returns false, its caller can obtain a description of the
error, complete with line and column number within the stream, by calling
the \c errorString() function.
\snippet serialization/streambookmarks/xbelreader.cpp 2
The \c readXBEL() function reads the name of a startElement and calls
the appropriate function to read it, depending on whether if its a
"folder", "bookmark" or "separator". Otherwise, it calls
\l{QXmlStreamReader::}{skipCurrentElement()}. The Q_ASSERT() macro is used
to provide a pre-condition for the function.
The \c readXBEL() function reads the name of a startElement and calls the
appropriate function to read it, depending on whether if its tag name
is \c{"folder"}, \c{"bookmark"} or \c{"separator"}. Any other elements
encountered are skipped. The function starts with a precondition, verifying
that the XML reader has just opened an \c{"xbel"} element.
\snippet serialization/streambookmarks/xbelreader.cpp 3
The \c readTitle() function reads the bookmark's title.
\snippet serialization/streambookmarks/xbelreader.cpp 4
The \c readSeparator() function creates a separator and sets its flags.
The text is set to 30 "0xB7", the HEX equivalent for period. The element
is then skipped using \l{QXmlStreamReader::}{skipCurrentElement()}.
The \c readBookmark() function creates a new editable item representing a
single bookmark. It records the XML \c{"href"} attribute of the current
element as second column text of the item and provisionally sets its first
column text to \c{"Unknown title"} before scanning the rest of the element
for a title element to over-ride that, skipping any unrecognized child
elements.
\snippet serialization/streambookmarks/xbelreader.cpp 5
The \c readTitle() function reads a bookmark's title and records it as the
title (first column text) of the item for which it was called.
\snippet serialization/streambookmarks/xbelreader.cpp 6
The \c readSeparator() function creates a separator and sets its flags. The
separator item's text is set to 30 centered dots. The rest of the element is
then skipped using \l{QXmlStreamReader::}{skipCurrentElement()}.
\snippet serialization/streambookmarks/xbelreader.cpp 6
The \c readFolder() function creates an item and iterates the content of the
folder element, adding children to this item to represent the contents of
the folder element. The loop over folder content is similar in form to the
one in \c readXBEL(), save that it now accepts a title element to set the
title of the folder.
\snippet serialization/streambookmarks/xbelreader.cpp 7
The \c createChildItem() helper function creates a new tree widget item
that's either a child of the given item or, if no parent item is given, a
direct child of the tree widget. It sets the new item's \c UserRole to the
tag name of the current XML element, matching how XbelWriter::writeFile()
uses that \c UserRole.
\snippet serialization/streambookmarks/xbelreader.cpp 8
\section1 MainWindow Class Definition
The \c MainWindow class is a subclass of QMainWindow, with a
\c File menu and a \c Help menu.
The \c MainWindow class is a subclass of QMainWindow, with a \c File menu
and a \c Help menu.
\snippet serialization/streambookmarks/mainwindow.h 0
\section1 MainWindow Class Implementation
The \c MainWindow constructor instantiates the QTreeWidget object, \c
treeWidget and sets its header with a QStringList object, \c labels.
The constructor also invokes \c createActions() and \c createMenus()
to set up the menus and their corresponding actions. The \c statusBar()
is used to display the message "Ready" and the window's size is fixed
to 480x320 pixels.
The \c MainWindow constructor sets up its QTreeWidget object, \c treeWidget,
as its own central widget, with column headings for the title and location
of each book-mark. It configures a custom menu that enables the user to
perform actions on individual bookmarks within the tree widget.
It invokes \c createMenus() to set up its own menus and their corresponding
actions. It sets its title, announces itself as ready and sets its size to a
reasonable proportion of the available screen space.
\snippet serialization/streambookmarks/mainwindow.cpp 0
The \c open() function enables the user to open an XBEL file using
QFileDialog::getOpenFileName(). A warning message is displayed along
with the \c fileName and \c errorString if the file cannot be read or
if there is a parse error.
A custom menu, triggered when the user right-clicks on a bookmark, provides
for copying the bookmark as a link or directing a desktop browser to open
the URL it references. This menu is implemented (when relevant features are
enabled) by \c onCustomContextMenuRequested().
\snippet serialization/streambookmarks/mainwindow.cpp 1
The \c saveAs() function displays a QFileDialog, prompting the user for
a \c fileName using QFileDialog::getSaveFileName(). Similar to the
\c open() function, this function also displays a warning message if
the file cannot be written to.
The \c createMenus() function creates the \c fileMenu and \c helpMenu and
adds QAction objects to them, bound variously to the \c open(), \c saveAs()
and \c about() functions, along with QWidget::close() and
QApplication::aboutQt(). The connections are as shown below:
\snippet serialization/streambookmarks/mainwindow.cpp 2
The \c about() function displays a QMessageBox with a brief description
of the example.
\snippet serialization/streambookmarks/mainwindow.cpp 3
In order to implement the \c open(), \c saveAs(), \c exit(), \c about()
and \c aboutQt() functions, we connect them to QAction objects and
add them to the \c fileMenu and \c helpMenu. The connections are as shown
below:
\snippet serialization/streambookmarks/mainwindow.cpp 5
The \c createMenus() function creates the \c fileMenu and \c helpMenu
and adds the QAction objects to them in order to create the menu shown
in the screenshot below:
This creates the menu shown in the screenshots below:
\table
\row
\li \inlineimage xmlstreamexample-filemenu.png
\li \inlineimage xmlstreamexample-helpmenu.png
\li \inlineimage filemenu.png
\li \inlineimage helpmenu.png
\endtable
The \c open() function, when triggered, offers the user a file dialog to use
to select a bookmarks file. If a file is selected, it is parsed using an \c
XBelReader to populate the \c treeWidget with bookmarks. If problems arise
with opening or parsing the file, a suitable warning message is displayed to
the user, including file name and error message. Otherwise, the bookmarks
read from the file are displayed and the window's status bar briefly reports
that the file has been loaded.
\snippet serialization/streambookmarks/mainwindow.cpp 3
The \c saveAs() function displays a QFileDialog, prompting the user for a \c
fileName, to which to save a copy of the bookmarks data. Similar to the \c
open() function, this function also displays a warning message if the file
cannot be written to.
\snippet serialization/streambookmarks/mainwindow.cpp 4
The \c about() function displays a QMessageBox with a brief description of
the example, or general information about Qt and the version of it in use.
\snippet serialization/streambookmarks/mainwindow.cpp 5
\section1 \c{main()} Function
The \c main() function instantiates \c MainWindow and invokes the \c show()
function.
function to display it, then its \c open(), as this is most likely what the
user shall want to do first.
\snippet serialization/streambookmarks/main.cpp 0
See the \l{http://pyxml.sourceforge.net/topics/xbel/}
{XML Bookmark Exchange Language Resource Page} for more information
about XBEL files.
See the \l{https://pyxml.sourceforge.net/topics/xbel/} {XML Bookmark
Exchange Language Resource Page} for more information about XBEL files.
*/

View File

@ -3,66 +3,66 @@
<xbel version="1.0">
<folder folded="no">
<title>Qt Resources</title>
<bookmark href="http://qt.io/">
<bookmark href="https://www.qt.io/">
<title>Qt home page</title>
</bookmark>
<bookmark href="https://www.qt.io/partners/">
<bookmark href="https://www.qt.io/contact-us/partners">
<title>Qt Partners</title>
</bookmark>
<bookmark href="https://www.qt.io/qt-training/">
<title>Training</title>
<bookmark href="https://www.qt.io/qt-professional-services">
<title>Professional Services</title>
</bookmark>
<bookmark href="http://doc.qt.io/">
<title>Qt 5 documentation</title>
</bookmark>
<bookmark href="http://qt-project.org/faq/">
<title>Frequently Asked Questions</title>
<bookmark href="https://doc.qt.io/">
<title>Qt Documentation</title>
</bookmark>
<folder folded="yes">
<title>Community Resources</title>
<bookmark href="http://www.qtcentre.org/content/">
<bookmark href="https://contribute.qt-project.org">
<title>The Qt Project</title>
</bookmark>
<bookmark href="https://www.qtcentre.org/content/">
<title>Qt Centre</title>
</bookmark>
<bookmark href="http://www.qtforum.org/">
<title>QtForum.org</title>
<bookmark href="https://forum.qt.io/">
<title>Forum.Qt.org</title>
</bookmark>
<bookmark href="http://digitalfanatics.org/projects/qt_tutorial/">
<bookmark href="https://digitalfanatics.org/projects/qt_tutorial/">
<title>The Independent Qt Tutorial</title>
</bookmark>
<bookmark href="http://www.qtforum.de/">
<bookmark href="https://www.qtforum.de/">
<title>German Qt Forum</title>
</bookmark>
<bookmark href="http://www.korone.net/">
<bookmark href="https://www.qt-dev.com/">
<title>Korean Qt Community Site</title>
</bookmark>
<bookmark href="http://prog.org.ru/">
<bookmark href="http://www.prog.org.ru/">
<title>Russian Qt Forum</title>
</bookmark>
</folder>
</folder>
<folder folded="no">
<title>Online Dictionaries</title>
<bookmark href="http://www.dictionary.com/">
<bookmark href="https://www.dictionary.com/">
<title>Dictionary.com</title>
</bookmark>
<bookmark href="http://www.m-w.com/">
<bookmark href="https://www.merriam-webster.com/">
<title>Merriam-Webster Online</title>
</bookmark>
<bookmark href="http://dictionary.cambridge.org/">
<bookmark href="https://dictionary.cambridge.org/">
<title>Cambridge Dictionaries Online</title>
</bookmark>
<bookmark href="http://www.onelook.com/">
<bookmark href="https://www.onelook.com/">
<title>OneLook Dictionary Search</title>
</bookmark>
<separator/>
<bookmark href="http://dict.tu-chemnitz.de/">
<title>TU Chemnitz German-English Dictionary</title>
<bookmark href="https://dict.tu-chemnitz.de/">
<title>BEOLINGUS, a service of TU Chemnitz</title>
</bookmark>
<separator/>
<bookmark href="http://atilf.atilf.fr/tlf.htm">
<title>Trésor de la Langue Française informatisé</title>
</bookmark>
<bookmark href="http://dictionnaires.atilf.fr/dictionnaires/ACADEMIE/">
<bookmark href="https://www.dictionnaire-academie.fr/">
<title>Dictionnaire de l'Académie Française</title>
</bookmark>
</folder>

View File

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

View File

@ -1,22 +1,33 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "mainwindow.h"
#include "xbelreader.h"
#include "xbelwriter.h"
//! [0]
MainWindow::MainWindow()
{
QStringList labels;
labels << tr("Title") << tr("Location");
#include <QFileDialog>
#include <QHeaderView>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QTreeWidget>
treeWidget = new QTreeWidget;
#include <QAction>
#if QT_CONFIG(clipboard)
# include <QClipboard>
#endif
#include <QDesktopServices>
#include <QApplication>
#include <QScreen>
using namespace Qt::StringLiterals;
//! [0]
MainWindow::MainWindow() : treeWidget(new QTreeWidget)
{
treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
treeWidget->setHeaderLabels(labels);
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
treeWidget->setHeaderLabels(QStringList{tr("Title"), tr("Location")});
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeWidget, &QWidget::customContextMenuRequested,
this, &MainWindow::onCustomContextMenuRequested);
@ -33,7 +44,8 @@ MainWindow::MainWindow()
}
//! [0]
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
//! [1]
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
{
const QTreeWidgetItem *item = treeWidget->itemAt(pos);
@ -49,78 +61,10 @@ void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
else if (action == openAction)
QDesktopServices::openUrl(QUrl(url));
}
#endif // !QT_NO_CONTEXTMENU && !QT_NO_CLIPBOARD
//! [1]
void MainWindow::open()
{
QString fileName =
QFileDialog::getOpenFileName(this, tr("Open Bookmark File"),
QDir::currentPath(),
tr("XBEL Files (*.xbel *.xml)"));
if (fileName.isEmpty())
return;
treeWidget->clear();
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot read file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName),
file.errorString()));
return;
}
XbelReader reader(treeWidget);
if (!reader.read(&file)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Parse error in file %1:\n\n%2")
.arg(QDir::toNativeSeparators(fileName),
reader.errorString()));
} else {
statusBar()->showMessage(tr("File loaded"), 2000);
}
}
#endif // QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
//! [1]
//! [2]
void MainWindow::saveAs()
{
QString fileName =
QFileDialog::getSaveFileName(this, tr("Save Bookmark File"),
QDir::currentPath(),
tr("XBEL Files (*.xbel *.xml)"));
if (fileName.isEmpty())
return;
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot write file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName),
file.errorString()));
return;
}
XbelWriter writer(treeWidget);
if (writer.writeFile(&file))
statusBar()->showMessage(tr("File saved"), 2000);
}
//! [2]
//! [3]
void MainWindow::about()
{
QMessageBox::about(this, tr("About QXmlStream Bookmarks"),
tr("The <b>QXmlStream Bookmarks</b> example demonstrates how to use Qt's "
"QXmlStream classes to read and write XML documents."));
}
//! [3]
//! [5]
void MainWindow::createMenus()
{
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
@ -137,6 +81,71 @@ void MainWindow::createMenus()
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(tr("&About"), this, &MainWindow::about);
helpMenu->addAction(tr("About &Qt"), qApp, &QCoreApplication::quit);
helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
}
//! [2]
//! [3]
void MainWindow::open()
{
QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath());
fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
if (fileDialog.exec() != QDialog::Accepted)
return;
treeWidget->clear();
const QString fileName = fileDialog.selectedFiles().constFirst();
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot read file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString()));
return;
}
XbelReader reader(treeWidget);
if (!reader.read(&file)) {
QMessageBox::warning(
this, tr("QXmlStream Bookmarks"),
tr("Parse error in file %1:\n\n%2")
.arg(QDir::toNativeSeparators(fileName), reader.errorString()));
} else {
statusBar()->showMessage(tr("File loaded"), 2000);
}
}
//! [3]
//! [4]
void MainWindow::saveAs()
{
QFileDialog fileDialog(this, tr("Save Bookmark File"), QDir::currentPath());
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
fileDialog.setDefaultSuffix("xbel"_L1);
fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
if (fileDialog.exec() != QDialog::Accepted)
return;
const QString fileName = fileDialog.selectedFiles().constFirst();
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot write file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString()));
return;
}
XbelWriter writer(treeWidget);
if (writer.writeFile(&file))
statusBar()->showMessage(tr("File saved"), 2000);
}
//! [4]
//! [5]
void MainWindow::about()
{
QMessageBox::about(this, tr("About QXmlStream Bookmarks"),
tr("The <b>QXmlStream Bookmarks</b> example demonstrates how to use Qt's "
"QXmlStream classes to read and write XML documents."));
}
//! [5]

View File

@ -22,13 +22,13 @@ public slots:
void open();
void saveAs();
void about();
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
void onCustomContextMenuRequested(const QPoint &pos);
#endif
private:
void createMenus();
QTreeWidget *treeWidget;
QTreeWidget *const treeWidget;
};
//! [0]

View File

@ -8,7 +8,7 @@ SOURCES = main.cpp \
QT += widgets
requires(qtConfig(filedialog))
EXAMPLE_FILES = frank.xbel jennifer.xbel
EXAMPLE_FILES = jennifer.xbel
# install
target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/streambookmarks

View File

@ -1,20 +1,21 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "xbelreader.h"
#include <QStyle>
#include <QTreeWidget>
using namespace Qt::StringLiterals;
//! [0]
XbelReader::XbelReader(QTreeWidget *treeWidget)
: treeWidget(treeWidget)
XbelReader::XbelReader(QTreeWidget *treeWidget) : treeWidget(treeWidget)
{
QStyle *style = treeWidget->style();
folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirClosedIcon),
QIcon::Normal, QIcon::Off);
folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirOpenIcon),
QIcon::Normal, QIcon::On);
folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirClosedIcon), QIcon::Normal,
QIcon::Off);
folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On);
bookmarkIcon.addPixmap(style->standardPixmap(QStyle::SP_FileIcon));
}
//! [0]
@ -25,12 +26,10 @@ bool XbelReader::read(QIODevice *device)
xml.setDevice(device);
if (xml.readNextStartElement()) {
if (xml.name() == QLatin1String("xbel")
&& xml.attributes().value(versionAttribute()) == QLatin1String("1.0")) {
if (xml.name() == "xbel"_L1 && xml.attributes().value("version"_L1) == "1.0"_L1)
readXBEL();
} else {
else
xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file."));
}
}
return !xml.error();
@ -50,15 +49,15 @@ QString XbelReader::errorString() const
//! [3]
void XbelReader::readXBEL()
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("xbel"));
Q_ASSERT(xml.isStartElement() && xml.name() == "xbel"_L1);
while (xml.readNextStartElement()) {
if (xml.name() == QLatin1String("folder"))
readFolder(0);
else if (xml.name() == QLatin1String("bookmark"))
readBookmark(0);
else if (xml.name() == QLatin1String("separator"))
readSeparator(0);
if (xml.name() == "folder"_L1)
readFolder(nullptr);
else if (xml.name() == "bookmark"_L1)
readBookmark(nullptr);
else if (xml.name() == "separator"_L1)
readSeparator(nullptr);
else
xml.skipCurrentElement();
}
@ -66,75 +65,76 @@ void XbelReader::readXBEL()
//! [3]
//! [4]
void XbelReader::readTitle(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("title"));
QString title = xml.readElementText();
item->setText(0, title);
}
//! [4]
//! [5]
void XbelReader::readSeparator(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("separator"));
QTreeWidgetItem *separator = createChildItem(item);
separator->setFlags(item->flags() & ~Qt::ItemIsSelectable);
separator->setText(0, QString(30, u'\xB7'));
xml.skipCurrentElement();
}
//! [5]
void XbelReader::readFolder(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("folder"));
QTreeWidgetItem *folder = createChildItem(item);
bool folded = (xml.attributes().value(foldedAttribute()) != QLatin1String("no"));
folder->setExpanded(!folded);
while (xml.readNextStartElement()) {
if (xml.name() == QLatin1String("title"))
readTitle(folder);
else if (xml.name() == QLatin1String("folder"))
readFolder(folder);
else if (xml.name() == QLatin1String("bookmark"))
readBookmark(folder);
else if (xml.name() == QLatin1String("separator"))
readSeparator(folder);
else
xml.skipCurrentElement();
}
}
void XbelReader::readBookmark(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("bookmark"));
Q_ASSERT(xml.isStartElement() && xml.name() == "bookmark"_L1);
QTreeWidgetItem *bookmark = createChildItem(item);
bookmark->setFlags(bookmark->flags() | Qt::ItemIsEditable);
bookmark->setIcon(0, bookmarkIcon);
bookmark->setText(0, QObject::tr("Unknown title"));
bookmark->setText(1, xml.attributes().value(hrefAttribute()).toString());
bookmark->setText(1, xml.attributes().value("href"_L1).toString());
while (xml.readNextStartElement()) {
if (xml.name() == QLatin1String("title"))
if (xml.name() == "title"_L1)
readTitle(bookmark);
else
xml.skipCurrentElement();
}
}
//! [4]
//! [5]
void XbelReader::readTitle(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "title"_L1);
item->setText(0, xml.readElementText());
}
//! [5]
//! [6]
void XbelReader::readSeparator(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "separator"_L1);
constexpr char16_t midDot = u'\xB7';
static const QString dots(30, midDot);
QTreeWidgetItem *separator = createChildItem(item);
separator->setFlags(item ? item->flags() & ~Qt::ItemIsSelectable : Qt::ItemFlags{});
separator->setText(0, dots);
xml.skipCurrentElement();
}
//! [6]
//! [7]
void XbelReader::readFolder(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "folder"_L1);
QTreeWidgetItem *folder = createChildItem(item);
bool folded = xml.attributes().value("folded"_L1) != "no"_L1;
folder->setExpanded(!folded);
while (xml.readNextStartElement()) {
if (xml.name() == "title"_L1)
readTitle(folder);
else if (xml.name() == "folder"_L1)
readFolder(folder);
else if (xml.name() == "bookmark"_L1)
readBookmark(folder);
else if (xml.name() == "separator"_L1)
readSeparator(folder);
else
xml.skipCurrentElement();
}
}
//! [7]
//! [8]
QTreeWidgetItem *XbelReader::createChildItem(QTreeWidgetItem *item)
{
QTreeWidgetItem *childItem;
if (item) {
childItem = new QTreeWidgetItem(item);
} else {
childItem = new QTreeWidgetItem(treeWidget);
}
QTreeWidgetItem *childItem = item ? new QTreeWidgetItem(item) : new QTreeWidgetItem(treeWidget);
childItem->setData(0, Qt::UserRole, xml.name().toString());
return childItem;
}
//! [8]

View File

@ -21,13 +21,8 @@ public:
//! [1]
bool read(QIODevice *device);
QString errorString() const;
static inline QString versionAttribute() { return QStringLiteral("version"); }
static inline QString hrefAttribute() { return QStringLiteral("href"); }
static inline QString foldedAttribute() { return QStringLiteral("folded"); }
private:
//! [2]
void readXBEL();

View File

@ -1,18 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "xbelwriter.h"
#include "xbelreader.h"
static inline QString yesValue() { return QStringLiteral("yes"); }
static inline QString noValue() { return QStringLiteral("no"); }
static inline QString titleElement() { return QStringLiteral("title"); }
#include <QTreeWidget>
using namespace Qt::StringLiterals;
//! [0]
XbelWriter::XbelWriter(const QTreeWidget *treeWidget)
: treeWidget(treeWidget)
XbelWriter::XbelWriter(const QTreeWidget *treeWidget) : treeWidget(treeWidget)
{
xml.setAutoFormatting(true);
}
@ -24,9 +21,9 @@ bool XbelWriter::writeFile(QIODevice *device)
xml.setDevice(device);
xml.writeStartDocument();
xml.writeDTD(QStringLiteral("<!DOCTYPE xbel>"));
xml.writeStartElement(QStringLiteral("xbel"));
xml.writeAttribute(XbelReader::versionAttribute(), QStringLiteral("1.0"));
xml.writeDTD("<!DOCTYPE xbel>"_L1);
xml.writeStartElement("xbel"_L1);
xml.writeAttribute("version"_L1, "1.0"_L1);
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i)
writeItem(treeWidget->topLevelItem(i));
@ -39,21 +36,21 @@ bool XbelWriter::writeFile(QIODevice *device)
void XbelWriter::writeItem(const QTreeWidgetItem *item)
{
QString tagName = item->data(0, Qt::UserRole).toString();
if (tagName == QLatin1String("folder")) {
if (tagName == "folder"_L1) {
bool folded = !item->isExpanded();
xml.writeStartElement(tagName);
xml.writeAttribute(XbelReader::foldedAttribute(), folded ? yesValue() : noValue());
xml.writeTextElement(titleElement(), item->text(0));
xml.writeAttribute("folded"_L1, folded ? "yes"_L1 : "no"_L1);
xml.writeTextElement("title"_L1, item->text(0));
for (int i = 0; i < item->childCount(); ++i)
writeItem(item->child(i));
xml.writeEndElement();
} else if (tagName == QLatin1String("bookmark")) {
} else if (tagName == "bookmark"_L1) {
xml.writeStartElement(tagName);
if (!item->text(1).isEmpty())
xml.writeAttribute(XbelReader::hrefAttribute(), item->text(1));
xml.writeTextElement(titleElement(), item->text(0));
xml.writeAttribute("href"_L1, item->text(1));
xml.writeTextElement("title"_L1, item->text(0));
xml.writeEndElement();
} else if (tagName == QLatin1String("separator")) {
} else if (tagName == "separator"_L1) {
xml.writeEmptyElement(tagName);
}
}

View File

@ -1,8 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_example(semaphores)
qt_internal_add_example(waitconditions)
if(NOT ANDROID)
qt_internal_add_example(semaphores)
qt_internal_add_example(waitconditions)
endif()
if(TARGET Qt6::Widgets)
qt_internal_add_example(mandelbrot)
qt_internal_add_example(queuedcustomtype)

View File

@ -3,7 +3,7 @@
/*!
\example threads/mandelbrot
\title Mandelbrot Example
\title Mandelbrot
\ingroup qtconcurrent-mtexamples
\brief The Mandelbrot example demonstrates multi-thread programming
@ -151,7 +151,7 @@
it needs to access \c{RenderThread}'s member variables (e.g., in
\c render()).
The \c forever keyword is, like \c foreach, a Qt pseudo-keyword.
The \c forever keyword is a Qt pseudo-keyword.
\snippet threads/mandelbrot/renderthread.cpp 4
\snippet threads/mandelbrot/renderthread.cpp 5

View File

@ -2,15 +2,14 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mandelbrotwidget.h"
#include "renderthread.h"
#include <QApplication>
#include <QScreen>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QDebug>
#include <QRect>
using namespace Qt::StringLiterals;
//! [0]
int main(int argc, char *argv[])
@ -18,10 +17,10 @@ int main(int argc, char *argv[])
QApplication app(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("Qt Mandelbrot Example");
parser.setApplicationDescription(u"Qt Mandelbrot Example"_s);
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption passesOption("passes", "Number of passes (1-8)", "passes");
QCommandLineOption passesOption(u"passes"_s, u"Number of passes (1-8)"_s, u"passes"_s);
parser.addOption(passesOption);
parser.process(app);

View File

@ -4,19 +4,20 @@
#include "mandelbrotwidget.h"
#include <QGesture>
#include <QGestureEvent>
#include <QKeyEvent>
#include <QPainter>
#include <math.h>
//! [0]
const double DefaultCenterX = -0.637011;
const double DefaultCenterY = -0.0395159;
const double DefaultScale = 0.00403897;
constexpr double DefaultCenterX = -0.637011;
constexpr double DefaultCenterY = -0.0395159;
constexpr double DefaultScale = 0.00403897;
const double ZoomInFactor = 0.8;
const double ZoomOutFactor = 1 / ZoomInFactor;
const int ScrollStep = 20;
constexpr double ZoomInFactor = 0.8;
constexpr double ZoomOutFactor = 1 / ZoomInFactor;
constexpr int ScrollStep = 20;
//! [0]
//! [1]
@ -46,7 +47,8 @@ void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
if (pixmap.isNull()) {
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignCenter|Qt::TextWordWrap, tr("Rendering initial image, please wait..."));
painter.drawText(rect(), Qt::AlignCenter|Qt::TextWordWrap,
tr("Rendering initial image, please wait..."));
//! [2] //! [3]
return;
//! [3] //! [4]
@ -60,47 +62,47 @@ void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
//! [6] //! [7]
} else {
//! [7] //! [8]
auto previewPixmap = qFuzzyCompare(pixmap.devicePixelRatio(), qreal(1))
const auto previewPixmap = qFuzzyCompare(pixmap.devicePixelRatio(), qreal(1))
? pixmap
: pixmap.scaled(pixmap.deviceIndependentSize().toSize(), Qt::KeepAspectRatio,
Qt::SmoothTransformation);
double scaleFactor = pixmapScale / curScale;
int newWidth = int(previewPixmap.width() * scaleFactor);
int newHeight = int(previewPixmap.height() * scaleFactor);
int newX = pixmapOffset.x() + (previewPixmap.width() - newWidth) / 2;
int newY = pixmapOffset.y() + (previewPixmap.height() - newHeight) / 2;
const double scaleFactor = pixmapScale / curScale;
const int newWidth = int(previewPixmap.width() * scaleFactor);
const int newHeight = int(previewPixmap.height() * scaleFactor);
const int newX = pixmapOffset.x() + (previewPixmap.width() - newWidth) / 2;
const int newY = pixmapOffset.y() + (previewPixmap.height() - newHeight) / 2;
painter.save();
painter.translate(newX, newY);
painter.scale(scaleFactor, scaleFactor);
QRectF exposed = painter.transform().inverted().mapRect(rect()).adjusted(-1, -1, 1, 1);
const QRectF exposed = painter.transform().inverted().mapRect(rect())
.adjusted(-1, -1, 1, 1);
painter.drawPixmap(exposed, previewPixmap, exposed);
painter.restore();
}
//! [8] //! [9]
QFontMetrics metrics = painter.fontMetrics();
const QFontMetrics metrics = painter.fontMetrics();
if (!info.isEmpty()){
int infoWidth = metrics.horizontalAdvance(info);
int infoHeight = metrics.height();
const int infoWidth = metrics.horizontalAdvance(info);
const int infoHeight = (infoWidth/width() + 1) * (metrics.height() + 5);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 127));
infoHeight = (infoWidth/width()+1) * (infoHeight + 5);
painter.drawRect((width() - infoWidth) / 2 - 5, 0, infoWidth + 10, infoHeight);
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignHCenter|Qt::AlignTop|Qt::TextWordWrap, info);
}
int helpWidth = metrics.horizontalAdvance(help);
int helpHeight = metrics.height();
const int helpWidth = metrics.horizontalAdvance(help);
const int helpHeight = (helpWidth/width() + 1) * (metrics.height() + 5);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(0, 0, 0, 127));
helpHeight = (helpWidth/width()+1) * (helpHeight + 5);
painter.drawRect((width() - helpWidth) / 2 - 5, height()-helpHeight, helpWidth + 10, helpHeight);
painter.drawRect((width() - helpWidth) / 2 - 5, height()-helpHeight, helpWidth + 10,
helpHeight);
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignHCenter|Qt::AlignBottom|Qt::TextWordWrap, help);
@ -184,8 +186,8 @@ void MandelbrotWidget::mouseReleaseEvent(QMouseEvent *event)
lastDragPos = QPoint();
const auto pixmapSize = pixmap.deviceIndependentSize().toSize();
int deltaX = (width() - pixmapSize.width()) / 2 - pixmapOffset.x();
int deltaY = (height() - pixmapSize.height()) / 2 - pixmapOffset.y();
const int deltaX = (width() - pixmapSize.width()) / 2 - pixmapOffset.x();
const int deltaY = (height() - pixmapSize.height()) / 2 - pixmapOffset.y();
scroll(deltaX, deltaY);
}
}

View File

@ -4,11 +4,14 @@
#ifndef MANDELBROTWIDGET_H
#define MANDELBROTWIDGET_H
#include <QGestureEvent>
#include <QPixmap>
#include <QWidget>
#include "renderthread.h"
#include <QPixmap>
#include <QWidget>
QT_BEGIN_NAMESPACE
class QGestureEvent;
QT_END_NAMESPACE
//! [0]
class MandelbrotWidget : public QWidget

View File

@ -4,7 +4,6 @@
#include "renderthread.h"
#include <QImage>
#include <QElapsedTimer>
#include <QTextStream>
@ -70,16 +69,16 @@ void RenderThread::run()
//! [3]
//! [4]
int halfWidth = resultSize.width() / 2;
const int halfWidth = resultSize.width() / 2;
//! [4] //! [5]
int halfHeight = resultSize.height() / 2;
const int halfHeight = resultSize.height() / 2;
QImage image(resultSize, QImage::Format_RGB32);
image.setDevicePixelRatio(devicePixelRatio);
int pass = 0;
while (pass < numPasses) {
const int MaxIterations = (1 << (2 * pass + 6)) + 32;
const int Limit = 4;
constexpr int Limit = 4;
bool allBlack = true;
timer.restart();

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(semaphores LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()

View File

@ -7,9 +7,9 @@
#include <stdlib.h>
//! [0]
const int DataSize = 100000;
constexpr int DataSize = 100000;
const int BufferSize = 8192;
constexpr int BufferSize = 8192;
char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(waitconditions LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()