diff options
author | Sona Kurazyan <sona.kurazyan@qt.io> | 2020-11-09 16:44:26 +0100 |
---|---|---|
committer | Sona Kurazyan <sona.kurazyan@qt.io> | 2020-11-12 08:56:14 +0100 |
commit | 190b77463d40db0278919992912d48d1e96e4796 (patch) | |
tree | 995fb829020824d55a45dc2083ce1590a79a62e0 /examples/qtconcurrent/imagescaling/imagescaling.cpp | |
parent | cc3f693029d6fcc65a0153b658061bd121a6af66 (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.cpp | 221 |
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(); } |