summaryrefslogtreecommitdiffstats
path: root/examples/qtconcurrent/imagescaling/imagescaling.cpp
diff options
context:
space:
mode:
authorSona Kurazyan <sona.kurazyan@qt.io>2020-11-09 16:44:26 +0100
committerSona Kurazyan <sona.kurazyan@qt.io>2020-11-12 08:56:14 +0100
commit190b77463d40db0278919992912d48d1e96e4796 (patch)
tree995fb829020824d55a45dc2083ce1590a79a62e0 /examples/qtconcurrent/imagescaling/imagescaling.cpp
parentcc3f693029d6fcc65a0153b658061bd121a6af66 (diff)
Improve QtConcurrent ImageScaling example to demo new features
In order to demonstrate the new functionality, changed the example to download the images from the network, scale and show them by attaching different continuations to QFuture. Because QtConcurrent::map is not used anymore, removed the suspension functionality (supporting suspension of download would complicate the logic). Task-number: QTBUG-87205 Change-Id: I5a48b63195d28025ae8c5de28bc6d6178dad03db Reviewed-by: Paul Wicking <paul.wicking@qt.io> Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
Diffstat (limited to 'examples/qtconcurrent/imagescaling/imagescaling.cpp')
-rw-r--r--examples/qtconcurrent/imagescaling/imagescaling.cpp221
1 files changed, 167 insertions, 54 deletions
diff --git a/examples/qtconcurrent/imagescaling/imagescaling.cpp b/examples/qtconcurrent/imagescaling/imagescaling.cpp
index 65ec16e383..59664f8a58 100644
--- a/examples/qtconcurrent/imagescaling/imagescaling.cpp
+++ b/examples/qtconcurrent/imagescaling/imagescaling.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
@@ -48,106 +48,219 @@
**
****************************************************************************/
#include "imagescaling.h"
+#include "downloaddialog.h"
+
+#include <QNetworkReply>
#include <qmath.h>
#include <functional>
-Images::Images(QWidget *parent)
- : QWidget(parent)
+Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDialog())
{
- setWindowTitle(tr("Image loading and scaling example"));
+ setWindowTitle(tr("Image downloading and scaling example"));
resize(800, 600);
- imageScaling = new QFutureWatcher<QImage>(this);
- connect(imageScaling, &QFutureWatcher<QImage>::resultReadyAt, this, &Images::showImage);
- connect(imageScaling, &QFutureWatcher<QImage>::finished, this, &Images::finished);
-
- openButton = new QPushButton(tr("Open Images"));
- connect(openButton, &QPushButton::clicked, this, &Images::open);
+ addUrlsButton = new QPushButton(tr("Add URLs"));
+//! [1]
+ connect(addUrlsButton, &QPushButton::clicked, this, &Images::process);
+//! [1]
cancelButton = new QPushButton(tr("Cancel"));
cancelButton->setEnabled(false);
- connect(cancelButton, &QPushButton::clicked, imageScaling, &QFutureWatcher<QImage>::cancel);
-
- pauseButton = new QPushButton(tr("Pause/Resume"));
- pauseButton->setEnabled(false);
- connect(pauseButton, &QPushButton::clicked, imageScaling, &QFutureWatcher<QImage>::toggleSuspended);
+//! [2]
+ connect(cancelButton, &QPushButton::clicked, this, &Images::cancel);
+//! [2]
QHBoxLayout *buttonLayout = new QHBoxLayout();
- buttonLayout->addWidget(openButton);
+ buttonLayout->addWidget(addUrlsButton);
buttonLayout->addWidget(cancelButton);
- buttonLayout->addWidget(pauseButton);
buttonLayout->addStretch();
+ statusBar = new QStatusBar();
+
imagesLayout = new QGridLayout();
mainLayout = new QVBoxLayout();
mainLayout->addLayout(buttonLayout);
mainLayout->addLayout(imagesLayout);
mainLayout->addStretch();
+ mainLayout->addWidget(statusBar);
setLayout(mainLayout);
}
Images::~Images()
{
- imageScaling->cancel();
- imageScaling->waitForFinished();
+ cancel();
+}
+
+//! [3]
+void Images::process()
+{
+ // Clean previous state
+ replies.clear();
+
+ 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); })
+ .then(QtFuture::Launch::Async,
+ [this] {
+ updateStatus(tr("Scaling..."));
+ return scaled();
+ })
+//! [4]
+//! [5]
+ .then([this](const QList<QImage> &scaled) {
+ QMetaObject::invokeMethod(this, [this, scaled] { showImages(scaled); });
+ updateStatus(tr("Finished"));
+ })
+//! [5]
+//! [6]
+ .onCanceled([this] { updateStatus(tr("Download has been canceled.")); })
+ .onFailed([this](QNetworkReply::NetworkError error) {
+ const auto msg = QString("Download finished with error: %1").arg(error);
+ updateStatus(tr(msg.toStdString().c_str()));
+
+ // Abort all pending requests
+ QMetaObject::invokeMethod(this, &Images::abortDownload);
+ })
+ .onFailed([this](const std::exception& ex) {
+ updateStatus(tr(ex.what()));
+ });
+//! [6]
+ }
+}
+
+//! [7]
+void Images::cancel()
+{
+ statusBar->showMessage(tr("Canceling..."));
+
+ downloadFuture.cancel();
+ abortDownload();
}
+//! [7]
-void Images::open()
+//! [8]
+QFuture<QByteArray> Images::download(const QList<QUrl> &urls)
+//! [8]
{
- // Cancel and wait if we are already loading images.
- if (imageScaling->isRunning()) {
- imageScaling->cancel();
- imageScaling->waitForFinished();
+//! [9]
+ QSharedPointer<QPromise<QByteArray>> promise(new QPromise<QByteArray>());
+ promise->start();
+//! [9]
+
+//! [10]
+ for (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([=] (QNetworkReply::NetworkError error) {
+ promise->setException(std::make_exception_ptr(error));
+ promise->finish();
+ }).onFailed([=] {
+ 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]
- // Show a file open dialog at QStandardPaths::PicturesLocation.
- QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Images"),
- QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),
- "*.jpg *.png");
+//! [14]
+QList<QImage> Images::scaled() const
+{
+ QList<QImage> scaled;
+ const auto data = downloadFuture.results();
+ for (auto imgData : data) {
+ QImage image;
+ image.loadFromData(imgData);
+ if (image.isNull())
+ throw std::runtime_error("Failed to load image.");
- if (files.isEmpty())
- return;
+ scaled.push_back(image.scaled(100, 100, Qt::KeepAspectRatio));
+ }
- const int imageSize = 100;
+ return scaled;
+}
+//! [14]
- // Do a simple layout.
- qDeleteAll(labels);
+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;
+ }
labels.clear();
- int dim = qSqrt(qreal(files.count())) + 1;
+ // 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(imageSize,imageSize);
- imagesLayout->addWidget(imageLabel,i,j);
+ imageLabel->setFixedSize(100, 100);
+ imagesLayout->addWidget(imageLabel, i, j);
labels.append(imageLabel);
}
}
-
- std::function<QImage(const QString&)> scale = [&](const QString &imageFileName) {
- QImage image(imageFileName);
- return image.scaled(QSize(imageSize, imageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- };
-
- // Use mapped to run the thread safe scale function on the files.
- imageScaling->setFuture(QtConcurrent::mapped(files, scale));
-
- openButton->setEnabled(false);
- cancelButton->setEnabled(true);
- pauseButton->setEnabled(true);
}
-void Images::showImage(int num)
+void Images::updateStatus(const QString &msg)
{
- labels[num]->setPixmap(QPixmap::fromImage(imageScaling->resultAt(num)));
+ QMetaObject::invokeMethod(this, [this, msg] { statusBar->showMessage(msg); });
}
-void Images::finished()
+void Images::abortDownload()
{
- openButton->setEnabled(true);
- cancelButton->setEnabled(false);
- pauseButton->setEnabled(false);
+ for (auto reply : replies)
+ reply->abort();
}