diff options
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(); } |