From 04b86eb3db9a42b2e3bf0e5c23f2bcb261002dbb Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 01:34:32 +0200 Subject: ImageScaling: Implement the example using TaskTree Change-Id: Iac54157955d5dffe12a7fdeed904fbcf62a2b667 Reviewed-by: Marcus Tillmanns Reviewed-by: Qt CI Bot Reviewed-by: --- tests/manual/tasking/imagescaling/imagescaling.cpp | 205 +++++++-------------- tests/manual/tasking/imagescaling/imagescaling.h | 29 +-- 2 files changed, 73 insertions(+), 161 deletions(-) diff --git a/tests/manual/tasking/imagescaling/imagescaling.cpp b/tests/manual/tasking/imagescaling/imagescaling.cpp index 362a30287e..e3acda749b 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.cpp +++ b/tests/manual/tasking/imagescaling/imagescaling.cpp @@ -3,19 +3,24 @@ #include "imagescaling.h" #include "downloaddialog.h" +#include +#include -#include +using namespace Tasking; Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDialog(this)) { resize(800, 600); - addUrlsButton = new QPushButton(tr("Add URLs")); + QPushButton *addUrlsButton = new QPushButton(tr("Add URLs")); connect(addUrlsButton, &QPushButton::clicked, this, &Images::process); cancelButton = new QPushButton(tr("Cancel")); cancelButton->setEnabled(false); - connect(cancelButton, &QPushButton::clicked, this, &Images::cancel); + connect(cancelButton, &QPushButton::clicked, this, [this] { + statusBar->showMessage(tr("Canceled.")); + taskTree.reset(); + }); QHBoxLayout *buttonLayout = new QHBoxLayout(); buttonLayout->addWidget(addUrlsButton); @@ -32,141 +37,77 @@ Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDi mainLayout->addStretch(); mainLayout->addWidget(statusBar); setLayout(mainLayout); - - connect(&scalingWatcher, &QFutureWatcher>::finished, - this, &Images::scaleFinished); } -Images::~Images() +static void scale(QPromise &promise, const QByteArray &data) { - cancel(); + const auto image = QImage::fromData(data); + if (image.isNull()) + promise.future().cancel(); + else + promise.addResult(image.scaled(100, 100, Qt::KeepAspectRatio)); } void Images::process() { - // Clean previous state - replies.clear(); - addUrlsButton->setEnabled(false); - - if (downloadDialog->exec() == QDialog::Accepted) { + if (downloadDialog->exec() != QDialog::Accepted) + return; - const auto urls = downloadDialog->getUrls(); - if (urls.empty()) - return; + const auto urls = downloadDialog->getUrls(); + initLayout(urls.size()); + const auto onRootSetup = [this] { + statusBar->showMessage(tr("Downloading and Scaling...")); cancelButton->setEnabled(true); - - initLayout(urls.size()); - - downloadFuture = download(urls); - statusBar->showMessage(tr("Downloading...")); - - downloadFuture - .then([this](auto) { - cancelButton->setEnabled(false); - updateStatus(tr("Scaling...")); - scalingWatcher.setFuture(QtConcurrent::run(Images::scaled, - downloadFuture.results())); - }) - .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())); - }) - .then([this]() { - cancelButton->setEnabled(false); - addUrlsButton->setEnabled(true); - }); - } -} - -void Images::cancel() -{ - statusBar->showMessage(tr("Canceling...")); - - downloadFuture.cancel(); - abortDownload(); -} - -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); -} - -QFuture Images::download(const QList &urls) -{ - QSharedPointer> promise(new QPromise()); - promise->start(); - - for (const auto &url : urls) { - QSharedPointer reply(qnam.get(QNetworkRequest(url))); - replies.push_back(reply); - - 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(); - } - promise->addResult(reply->readAll()); - - // Report finished on the last download - if (promise->future().resultCount() == urls.size()) - promise->finish(); - }).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(); - }); + }; + const auto onRootDone = [this] { + statusBar->showMessage(tr("Finished.")); + cancelButton->setEnabled(false); + }; + QList tasks { + finishAllAndDone, + parallel, + onGroupSetup(onRootSetup), + onGroupDone(onRootDone) + }; + + int i = 0; + for (const QUrl &url : urls) { + TreeStorage storage; + + const auto onDownloadSetup = [this, url](NetworkQuery &query) { + query.setNetworkAccessManager(&qnam); + query.setRequest(QNetworkRequest(url)); + }; + const auto onDownloadDone = [storage](const NetworkQuery &query) { + *storage = query.reply()->readAll(); + }; + const auto onDownloadError = [this, i](const NetworkQuery &query) { + labels[i]->setText(tr("Download\nError.\nCode: %1.").arg(query.reply()->error())); + }; + + const auto onScalingSetup = [storage](ConcurrentCall &data) { + data.setConcurrentCallData(&scale, *storage); + }; + const auto onScalingDone = [this, i](const ConcurrentCall &data) { + labels[i]->setPixmap(QPixmap::fromImage(data.result())); + }; + const auto onScalingError = [this, i](const ConcurrentCall &) { + labels[i]->setText(tr("Image\nData\nError.")); + }; + + const Group group { + Storage(storage), + NetworkQueryTask(onDownloadSetup, onDownloadDone, onDownloadError), + ConcurrentCallTask(onScalingSetup, onScalingDone, onScalingError) + }; + tasks.append(group); + ++i; } - return promise->future(); -} - -Images::OptionalImages Images::scaled(const QList &data) -{ - QList 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; -} - -void Images::showImages(const QList &images) -{ - for (int i = 0; i < images.size(); ++i) { - labels[i]->setAlignment(Qt::AlignCenter); - labels[i]->setPixmap(QPixmap::fromImage(images[i])); - } + taskTree.reset(new TaskTree(tasks)); + connect(taskTree.get(), &TaskTree::done, this, [this] { taskTree.release()->deleteLater(); }); + taskTree->start(); } void Images::initLayout(qsizetype count) @@ -186,19 +127,9 @@ void Images::initLayout(qsizetype count) for (int j = 0; j < dim; ++j) { QLabel *imageLabel = new QLabel; imageLabel->setFixedSize(100, 100); + imageLabel->setAlignment(Qt::AlignCenter); 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(); -} diff --git a/tests/manual/tasking/imagescaling/imagescaling.h b/tests/manual/tasking/imagescaling/imagescaling.h index d3409e60a0..91e89c13b2 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.h +++ b/tests/manual/tasking/imagescaling/imagescaling.h @@ -4,10 +4,9 @@ #ifndef IMAGESCALING_H #define IMAGESCALING_H -#include -#include #include -#include +#include +#include class DownloadDialog; class Images : public QWidget @@ -15,27 +14,11 @@ class Images : public QWidget Q_OBJECT public: Images(QWidget *parent = nullptr); - ~Images(); - - void initLayout(qsizetype count); - - QFuture download(const QList &urls); - void updateStatus(const QString &msg); - void showImages(const QList &images); - void abortDownload(); - -public slots: - void process(); - void cancel(); - -private slots: - void scaleFinished(); private: - using OptionalImages = std::optional>; - static OptionalImages scaled(const QList &data); + void process(); + void initLayout(qsizetype count); - QPushButton *addUrlsButton; QPushButton *cancelButton; QVBoxLayout *mainLayout; QList labels; @@ -44,9 +27,7 @@ private: DownloadDialog *downloadDialog; QNetworkAccessManager qnam; - QList> replies; - QFuture downloadFuture; - QFutureWatcher scalingWatcher; + std::unique_ptr taskTree; }; #endif // IMAGESCALING_H -- cgit v1.2.3