mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-05 08:45:25 +08:00
qt 6.5.1 original
This commit is contained in:
40
examples/qtconcurrent/imagescaling/CMakeLists.txt
Normal file
40
examples/qtconcurrent/imagescaling/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(imagescaling LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qtconcurrent/imagescaling")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Concurrent Core Gui Network Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(imagescaling
|
||||
downloaddialog.cpp downloaddialog.h downloaddialog.ui
|
||||
imagescaling.cpp imagescaling.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(imagescaling PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(imagescaling PRIVATE
|
||||
Qt6::Concurrent
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Network
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
install(TARGETS imagescaling
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
BIN
examples/qtconcurrent/imagescaling/doc/images/imagescaling.webp
Normal file
BIN
examples/qtconcurrent/imagescaling/doc/images/imagescaling.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1,180 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example imagescaling
|
||||
\meta tags {widgets, threads, network}
|
||||
\title Image Scaling
|
||||
\ingroup qtconcurrentexamples
|
||||
\brief Demonstrates how to asynchronously download and scale images.
|
||||
|
||||
This example shows how to use the QFuture, QPromise, and QFutureWatcher
|
||||
classes to download a collection of images from the network and scale them,
|
||||
without blocking the UI.
|
||||
|
||||
\image imagescaling.webp
|
||||
|
||||
The application consists of the following steps:
|
||||
|
||||
\list 1
|
||||
\li Download images form the list of URLs specified by the user.
|
||||
\li Scale the images.
|
||||
\li Show the scaled images in a grid layout.
|
||||
\endlist
|
||||
|
||||
Let's start with the download:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 8
|
||||
|
||||
The \c download() method takes a list of URLs and returns a QFuture. The QFuture
|
||||
stores the byte array data received for each downloaded image. To store the data
|
||||
inside the QFuture, we create a QPromise object and report that it has started to
|
||||
indicate the start of the download:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 9
|
||||
\dots
|
||||
\snippet imagescaling/imagescaling.cpp 13
|
||||
|
||||
The future associated with the promise is returned to the caller.
|
||||
|
||||
Without going into details yet, let's note that the promise object is wrapped
|
||||
inside a QSharedPointer. This will be explained later.
|
||||
|
||||
We use QNetworkAccessManager to send network requests and download data for each
|
||||
url:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 10
|
||||
|
||||
And here starts the interesting part:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 11
|
||||
\dots
|
||||
|
||||
Instead of connecting to QNetworkReply's signals using the QObject::connect()
|
||||
method, we use QtFuture::connect(). It works similar to QObject::connect(), but
|
||||
returns a QFuture object, that becomes available as soon as the
|
||||
QNetworkReply::finished() signal is emitted. This allows us to attach continuations
|
||||
and failure handlers, as it is done in the example.
|
||||
|
||||
In the continuation attached via \l{QFuture::then}{.then()}, we check if the
|
||||
user has requested to cancel the download. If that's the case, we stop
|
||||
processing the request. By calling the \l QPromise::finish() method, we notify
|
||||
the user that processing has been finished.
|
||||
In case the network request has ended with an error, we throw an exception. The
|
||||
exception will be handled in the failure handler attached using the
|
||||
\l{QFuture::onFailed}{.onFailed()} method.
|
||||
Note that we have two failure handlers: the first one captures the network
|
||||
errors, the second one all other exceptions thrown during the execution. Both handlers
|
||||
save the exception inside the promise object (to be handled by the caller of the
|
||||
\c download() method) and report that the computation has finished. Also note that,
|
||||
for simplicity, in case of an error we interrupt all pending downloads.
|
||||
|
||||
If the request has not been canceled and no error occurred, we read the data from
|
||||
the network reply and add it to the list of results of the promise object:
|
||||
|
||||
\dots
|
||||
\snippet imagescaling/imagescaling.cpp 12
|
||||
\dots
|
||||
|
||||
If the number of results stored inside the promise object is equal to the number
|
||||
of the \c {url}s to be downloaded, there are no more requests to process, so we also
|
||||
report that the promise has finished.
|
||||
|
||||
As mentioned earlier, we've wrapped the promise inside a QSharedPointer.
|
||||
Since the promise object is shared between handlers connected to each network reply,
|
||||
we need to copy and use the promise object in multiple places simultaneously. Hence,
|
||||
a QSharedPointer is used.
|
||||
|
||||
The \c download() method is called from the \c Images::process method. It is invoked
|
||||
when the user presses the \e {"Add URLs"} button:
|
||||
|
||||
\dots
|
||||
\snippet imagescaling/imagescaling.cpp 1
|
||||
\dots
|
||||
|
||||
After clearing the possible leftovers from previous download, we create a dialog
|
||||
so that the user can specify the URLs for the images to download. Based on the
|
||||
specified URL count, we initialize the layout where the images will be shown and
|
||||
start the download. The future returned by the \c download() method is saved, so that
|
||||
the user can cancel the download if needed:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 3
|
||||
\dots
|
||||
|
||||
Next, we attach a continuation to handle the scaling step.
|
||||
More on that later:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 4
|
||||
\dots
|
||||
|
||||
After that we attach \l {QFuture::}{onCanceled()} and \l {QFuture::}{onFailed()}
|
||||
handlers:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 5
|
||||
\dots
|
||||
|
||||
The handler attached via the \l {QFuture::onCanceled}{.onCanceled()} method
|
||||
will be called if the user has pressed the \e "Cancel" button:
|
||||
|
||||
\dots
|
||||
\snippet imagescaling/imagescaling.cpp 2
|
||||
\dots
|
||||
|
||||
The \c cancel() method simply aborts all the pending requests:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 7
|
||||
|
||||
The handlers attached via \l {QFuture::onFailed}{.onFailed()} method will be
|
||||
called in case an error occurred during one of the previous steps.
|
||||
For example, if a network error has been saved inside the promise during the
|
||||
download step, it will be propagated to the handler that takes
|
||||
\l QNetworkReply::NetworkError as argument.
|
||||
|
||||
If the \c downloadFuture is not canceled, and didn't report any error, the
|
||||
scaling continuation is executed.
|
||||
|
||||
Since the scaling may be computationally heavy, and we don't want to block
|
||||
the main thread, we use \l QtConcurrent::run(), to launch the scaling step
|
||||
in a new thread.
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 16
|
||||
|
||||
Since the scaling is launched in a separate thread, the user can potentially
|
||||
decide to close the application while the scaling operation is in progress.
|
||||
To handle such situations gracefully, we pass the \l QFuture returned by
|
||||
\l QtConcurrent::run() to the \l QFutureWatcher instance.
|
||||
|
||||
The watcher's \l QFutureWatcher::finished signal is connected to the
|
||||
\c Images::scaleFinished slot:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 6
|
||||
|
||||
This slot is responsible for showing the scaled images in the UI, and also
|
||||
for handling the errors that could potentially happen during scaling:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 15
|
||||
|
||||
The error reporting is implemented by returning an optional from the
|
||||
\c Images::scaled() method:
|
||||
|
||||
\snippet imagescaling/imagescaling.cpp 14
|
||||
|
||||
The \c Images::OptionalImages type here is simply a typedef for \c std::optional:
|
||||
|
||||
\snippet imagescaling/imagescaling.h 1
|
||||
|
||||
\note We cannot handle the errors from the async scaling operation using
|
||||
the \l {QFuture::onFailed}{.onFailed()} handler, because the handler needs
|
||||
to be executed in the context of \c Images object in the UI thread.
|
||||
If the user closes the application while the async computation is done,
|
||||
the \c Images object will be destroyed, and accessing its members from the
|
||||
continuation will lead to a crash. Using \l QFutureWatcher and its signals
|
||||
allows us to avoid the problem, because the signals are disconnected when
|
||||
the \l QFutureWatcher is destroyed, so the related slots will never be
|
||||
executed in a destroyed context.
|
||||
|
||||
The rest of the code is straightforward, you can check the example project for
|
||||
more details.
|
||||
|
||||
\include examples-run.qdocinc
|
||||
*/
|
40
examples/qtconcurrent/imagescaling/downloaddialog.cpp
Normal file
40
examples/qtconcurrent/imagescaling/downloaddialog.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2020 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
#include "downloaddialog.h"
|
||||
#include "ui_downloaddialog.h"
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
DownloadDialog::DownloadDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DownloadDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->urlLineEdit->setPlaceholderText(tr("Enter the URL of an image to download"));
|
||||
|
||||
connect(ui->addUrlButton, &QPushButton::clicked, this, [this] {
|
||||
const auto text = ui->urlLineEdit->text();
|
||||
if (!text.isEmpty()) {
|
||||
ui->urlListWidget->addItem(text);
|
||||
ui->urlLineEdit->clear();
|
||||
}
|
||||
});
|
||||
connect(ui->urlListWidget, &QListWidget::itemSelectionChanged, this, [this] {
|
||||
ui->removeUrlButton->setEnabled(!ui->urlListWidget->selectedItems().empty());
|
||||
});
|
||||
connect(ui->clearUrlsButton, &QPushButton::clicked, ui->urlListWidget, &QListWidget::clear);
|
||||
connect(ui->removeUrlButton, &QPushButton::clicked, this,
|
||||
[this] { qDeleteAll(ui->urlListWidget->selectedItems()); });
|
||||
}
|
||||
|
||||
DownloadDialog::~DownloadDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QList<QUrl> DownloadDialog::getUrls() const
|
||||
{
|
||||
QList<QUrl> urls;
|
||||
for (auto row = 0; row < ui->urlListWidget->count(); ++row)
|
||||
urls.push_back(QUrl(ui->urlListWidget->item(row)->text()));
|
||||
return urls;
|
||||
}
|
28
examples/qtconcurrent/imagescaling/downloaddialog.h
Normal file
28
examples/qtconcurrent/imagescaling/downloaddialog.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2020 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
#ifndef DOWNLOADDIALOG_H
|
||||
#define DOWNLOADDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class DownloadDialog;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class DownloadDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DownloadDialog(QWidget *parent = nullptr);
|
||||
~DownloadDialog();
|
||||
|
||||
QList<QUrl> getUrls() const;
|
||||
|
||||
private:
|
||||
Ui::DownloadDialog *ui;
|
||||
};
|
||||
|
||||
#endif // DOWNLOADDIALOG_H
|
119
examples/qtconcurrent/imagescaling/downloaddialog.ui
Normal file
119
examples/qtconcurrent/imagescaling/downloaddialog.ui
Normal file
@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DownloadDialog</class>
|
||||
<widget class="QDialog" name="DownloadDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>489</width>
|
||||
<height>333</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="urlLineEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="urlListWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addUrlButton">
|
||||
<property name="text">
|
||||
<string>Add URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeUrlButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="clearUrlsButton">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>DownloadDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>DownloadDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
236
examples/qtconcurrent/imagescaling/imagescaling.cpp
Normal file
236
examples/qtconcurrent/imagescaling/imagescaling.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "imagescaling.h"
|
||||
#include "downloaddialog.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDialog(this))
|
||||
{
|
||||
resize(800, 600);
|
||||
|
||||
addUrlsButton = new QPushButton(tr("Add URLs"));
|
||||
//! [1]
|
||||
connect(addUrlsButton, &QPushButton::clicked, this, &Images::process);
|
||||
//! [1]
|
||||
|
||||
cancelButton = new QPushButton(tr("Cancel"));
|
||||
cancelButton->setEnabled(false);
|
||||
//! [2]
|
||||
connect(cancelButton, &QPushButton::clicked, this, &Images::cancel);
|
||||
//! [2]
|
||||
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||||
buttonLayout->addWidget(addUrlsButton);
|
||||
buttonLayout->addWidget(cancelButton);
|
||||
buttonLayout->addStretch();
|
||||
|
||||
statusBar = new QStatusBar();
|
||||
|
||||
imagesLayout = new QGridLayout();
|
||||
|
||||
mainLayout = new QVBoxLayout();
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
mainLayout->addLayout(imagesLayout);
|
||||
mainLayout->addStretch();
|
||||
mainLayout->addWidget(statusBar);
|
||||
setLayout(mainLayout);
|
||||
|
||||
//! [6]
|
||||
connect(&scalingWatcher, &QFutureWatcher<QList<QImage>>::finished,
|
||||
this, &Images::scaleFinished);
|
||||
//! [6]
|
||||
}
|
||||
|
||||
Images::~Images()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
//! [3]
|
||||
void Images::process()
|
||||
{
|
||||
// Clean previous state
|
||||
replies.clear();
|
||||
addUrlsButton->setEnabled(false);
|
||||
|
||||
if (downloadDialog->exec() == QDialog::Accepted) {
|
||||
|
||||
const auto urls = downloadDialog->getUrls();
|
||||
if (urls.empty())
|
||||
return;
|
||||
|
||||
cancelButton->setEnabled(true);
|
||||
|
||||
initLayout(urls.size());
|
||||
|
||||
downloadFuture = download(urls);
|
||||
statusBar->showMessage(tr("Downloading..."));
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
downloadFuture
|
||||
.then([this](auto) {
|
||||
cancelButton->setEnabled(false);
|
||||
updateStatus(tr("Scaling..."));
|
||||
//! [16]
|
||||
scalingWatcher.setFuture(QtConcurrent::run(Images::scaled,
|
||||
downloadFuture.results()));
|
||||
//! [16]
|
||||
})
|
||||
//! [4]
|
||||
//! [5]
|
||||
.onCanceled([this] {
|
||||
updateStatus(tr("Download has been canceled."));
|
||||
})
|
||||
.onFailed([this](QNetworkReply::NetworkError error) {
|
||||
updateStatus(tr("Download finished with error: %1").arg(error));
|
||||
// Abort all pending requests
|
||||
abortDownload();
|
||||
})
|
||||
.onFailed([this](const std::exception &ex) {
|
||||
updateStatus(tr(ex.what()));
|
||||
})
|
||||
//! [5]
|
||||
.then([this]() {
|
||||
cancelButton->setEnabled(false);
|
||||
addUrlsButton->setEnabled(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//! [7]
|
||||
void Images::cancel()
|
||||
{
|
||||
statusBar->showMessage(tr("Canceling..."));
|
||||
|
||||
downloadFuture.cancel();
|
||||
abortDownload();
|
||||
}
|
||||
//! [7]
|
||||
|
||||
//! [15]
|
||||
void Images::scaleFinished()
|
||||
{
|
||||
const OptionalImages result = scalingWatcher.result();
|
||||
if (result.has_value()) {
|
||||
const auto scaled = result.value();
|
||||
showImages(scaled);
|
||||
updateStatus(tr("Finished"));
|
||||
} else {
|
||||
updateStatus(tr("Failed to extract image data."));
|
||||
}
|
||||
addUrlsButton->setEnabled(true);
|
||||
}
|
||||
//! [15]
|
||||
|
||||
//! [8]
|
||||
QFuture<QByteArray> Images::download(const QList<QUrl> &urls)
|
||||
{
|
||||
//! [8]
|
||||
//! [9]
|
||||
QSharedPointer<QPromise<QByteArray>> promise(new QPromise<QByteArray>());
|
||||
promise->start();
|
||||
//! [9]
|
||||
|
||||
//! [10]
|
||||
for (const auto &url : urls) {
|
||||
QSharedPointer<QNetworkReply> reply(qnam.get(QNetworkRequest(url)));
|
||||
replies.push_back(reply);
|
||||
//! [10]
|
||||
|
||||
//! [11]
|
||||
QtFuture::connect(reply.get(), &QNetworkReply::finished).then([=] {
|
||||
if (promise->isCanceled()) {
|
||||
if (!promise->future().isFinished())
|
||||
promise->finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
if (!promise->future().isFinished())
|
||||
throw reply->error();
|
||||
}
|
||||
//! [12]
|
||||
promise->addResult(reply->readAll());
|
||||
|
||||
// Report finished on the last download
|
||||
if (promise->future().resultCount() == urls.size())
|
||||
promise->finish();
|
||||
//! [12]
|
||||
}).onFailed([promise] (QNetworkReply::NetworkError error) {
|
||||
promise->setException(std::make_exception_ptr(error));
|
||||
promise->finish();
|
||||
}).onFailed([promise] {
|
||||
const auto ex = std::make_exception_ptr(
|
||||
std::runtime_error("Unknown error occurred while downloading."));
|
||||
promise->setException(ex);
|
||||
promise->finish();
|
||||
});
|
||||
}
|
||||
//! [11]
|
||||
|
||||
//! [13]
|
||||
return promise->future();
|
||||
}
|
||||
//! [13]
|
||||
|
||||
//! [14]
|
||||
Images::OptionalImages Images::scaled(const QList<QByteArray> &data)
|
||||
{
|
||||
QList<QImage> scaled;
|
||||
for (const auto &imgData : data) {
|
||||
QImage image;
|
||||
image.loadFromData(imgData);
|
||||
if (image.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
scaled.push_back(image.scaled(100, 100, Qt::KeepAspectRatio));
|
||||
}
|
||||
|
||||
return scaled;
|
||||
}
|
||||
//! [14]
|
||||
|
||||
void Images::showImages(const QList<QImage> &images)
|
||||
{
|
||||
for (int i = 0; i < images.size(); ++i) {
|
||||
labels[i]->setAlignment(Qt::AlignCenter);
|
||||
labels[i]->setPixmap(QPixmap::fromImage(images[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void Images::initLayout(qsizetype count)
|
||||
{
|
||||
// Clean old images
|
||||
QLayoutItem *child;
|
||||
while ((child = imagesLayout->takeAt(0)) != nullptr) {
|
||||
child->widget()->setParent(nullptr);
|
||||
delete child->widget();
|
||||
delete child;
|
||||
}
|
||||
labels.clear();
|
||||
|
||||
// Init the images layout for the new images
|
||||
const auto dim = int(qSqrt(qreal(count))) + 1;
|
||||
for (int i = 0; i < dim; ++i) {
|
||||
for (int j = 0; j < dim; ++j) {
|
||||
QLabel *imageLabel = new QLabel;
|
||||
imageLabel->setFixedSize(100, 100);
|
||||
imagesLayout->addWidget(imageLabel, i, j);
|
||||
labels.append(imageLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Images::updateStatus(const QString &msg)
|
||||
{
|
||||
statusBar->showMessage(msg);
|
||||
}
|
||||
|
||||
void Images::abortDownload()
|
||||
{
|
||||
for (auto reply : replies)
|
||||
reply->abort();
|
||||
}
|
53
examples/qtconcurrent/imagescaling/imagescaling.h
Normal file
53
examples/qtconcurrent/imagescaling/imagescaling.h
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
#ifndef IMAGESCALING_H
|
||||
#define IMAGESCALING_H
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QtConcurrent>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <optional>
|
||||
|
||||
class DownloadDialog;
|
||||
class Images : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Images(QWidget *parent = nullptr);
|
||||
~Images();
|
||||
|
||||
void initLayout(qsizetype count);
|
||||
|
||||
QFuture<QByteArray> download(const QList<QUrl> &urls);
|
||||
void updateStatus(const QString &msg);
|
||||
void showImages(const QList<QImage> &images);
|
||||
void abortDownload();
|
||||
|
||||
public slots:
|
||||
void process();
|
||||
void cancel();
|
||||
|
||||
private slots:
|
||||
void scaleFinished();
|
||||
|
||||
private:
|
||||
//! [1]
|
||||
using OptionalImages = std::optional<QList<QImage>>;
|
||||
//! [1]
|
||||
static OptionalImages scaled(const QList<QByteArray> &data);
|
||||
|
||||
QPushButton *addUrlsButton;
|
||||
QPushButton *cancelButton;
|
||||
QVBoxLayout *mainLayout;
|
||||
QList<QLabel *> labels;
|
||||
QGridLayout *imagesLayout;
|
||||
QStatusBar *statusBar;
|
||||
DownloadDialog *downloadDialog;
|
||||
|
||||
QNetworkAccessManager qnam;
|
||||
QList<QSharedPointer<QNetworkReply>> replies;
|
||||
QFuture<QByteArray> downloadFuture;
|
||||
QFutureWatcher<OptionalImages> scalingWatcher;
|
||||
};
|
||||
|
||||
#endif // IMAGESCALING_H
|
14
examples/qtconcurrent/imagescaling/imagescaling.pro
Normal file
14
examples/qtconcurrent/imagescaling/imagescaling.pro
Normal file
@ -0,0 +1,14 @@
|
||||
QT += concurrent widgets network
|
||||
CONFIG += exceptions
|
||||
requires(qtConfig(filedialog))
|
||||
|
||||
SOURCES += main.cpp imagescaling.cpp \
|
||||
downloaddialog.cpp
|
||||
HEADERS += imagescaling.h \
|
||||
downloaddialog.h
|
||||
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/qtconcurrent/imagescaling
|
||||
INSTALLS += target
|
||||
|
||||
FORMS += \
|
||||
downloaddialog.ui
|
17
examples/qtconcurrent/imagescaling/main.cpp
Normal file
17
examples/qtconcurrent/imagescaling/main.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
#include "imagescaling.h"
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc,argv);
|
||||
app.setOrganizationName("QtProject");
|
||||
app.setApplicationName(QObject::tr("Image Downloading and Scaling"));
|
||||
|
||||
Images imageView;
|
||||
imageView.setWindowTitle(QObject::tr("Image Downloading and Scaling"));
|
||||
imageView.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
Reference in New Issue
Block a user