From 81ed78c2936d569daa85bf5dcface076a36d6f2b Mon Sep 17 00:00:00 2001 From: Sona Kurazyan Date: Tue, 8 Dec 2020 17:42:12 +0100 Subject: [PATCH] Improve the QtConcurrent ImageScaling example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide execution context to QFuture continuations, instead of using QMetaObject::invokeMethod calls for running in the main thread. Change-Id: Ica7de19494065d677ffc94224781bfbe292b4f21 Reviewed-by: Leena Miettinen Reviewed-by: MÃ¥rten Nordheim --- .../doc/src/qtconcurrent-imagescaling.qdoc | 16 +++++++++++----- .../qtconcurrent/imagescaling/imagescaling.cpp | 14 +++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/qtconcurrent/imagescaling/doc/src/qtconcurrent-imagescaling.qdoc b/examples/qtconcurrent/imagescaling/doc/src/qtconcurrent-imagescaling.qdoc index a919b7e2ed..8814c826ed 100644 --- a/examples/qtconcurrent/imagescaling/doc/src/qtconcurrent-imagescaling.qdoc +++ b/examples/qtconcurrent/imagescaling/doc/src/qtconcurrent-imagescaling.qdoc @@ -128,23 +128,29 @@ Since the scaling may be computationally heavy, and we don't want to block the main thread, we pass the \c QtFuture::Launch::Async option, to launch the scaling step in - a new thread. + a new thread. The \c scaled() method returns a list of the scaled images to the next + step, which takes care of showing images in the layout. - The \c scaled() method returns a list of the scaled images to the next step, which - takes care of showing images in the layout: + Note that \c updateStatus() is called through QMetaObject::invokeMethod(), + because it updates the UI and needs to be invoked from the main thread. \dots \snippet imagescaling/imagescaling.cpp 5 \dots - Note that showImages() needs to be invoked from the main thread, so we call it through - QMetaObject::invokeMethod(). + For the same reason \c showImages() also needs to be invoked from the main thread, so + we pass \c this as a context to \c .then(). By default, \c .then() is launched in the + parent's thread, but if a context object is specified, it is launched in the context + object's thread. Then we add cancellation and failure handlers: \dots \snippet imagescaling/imagescaling.cpp 6 + We don't need to specify the context anymore, because \c .onCanceled() and the next + handlers will be launched in their parent's context. + The handler attached via the \c .onCanceled() method will be called if the user has pressed the \e "Cancel" button: diff --git a/examples/qtconcurrent/imagescaling/imagescaling.cpp b/examples/qtconcurrent/imagescaling/imagescaling.cpp index 59664f8a58..66bb0686c3 100644 --- a/examples/qtconcurrent/imagescaling/imagescaling.cpp +++ b/examples/qtconcurrent/imagescaling/imagescaling.cpp @@ -118,24 +118,24 @@ void Images::process() downloadFuture.then([this](auto) { cancelButton->setEnabled(false); }) .then(QtFuture::Launch::Async, [this] { - updateStatus(tr("Scaling...")); + QMetaObject::invokeMethod(this, + [this] { updateStatus(tr("Scaling...")); }); return scaled(); }) //! [4] //! [5] - .then([this](const QList &scaled) { - QMetaObject::invokeMethod(this, [this, scaled] { showImages(scaled); }); + .then(this, [this](const QList &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())); + updateStatus(tr("Download finished with error: %1").arg(error)); // Abort all pending requests - QMetaObject::invokeMethod(this, &Images::abortDownload); + abortDownload(); }) .onFailed([this](const std::exception& ex) { updateStatus(tr(ex.what())); @@ -256,7 +256,7 @@ void Images::initLayout(qsizetype count) void Images::updateStatus(const QString &msg) { - QMetaObject::invokeMethod(this, [this, msg] { statusBar->showMessage(msg); }); + statusBar->showMessage(msg); } void Images::abortDownload()