Revision control

Copy as Markdown

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tasktree/imagescaling
\meta tags {widgets, threads, network}
\examplecategory {Data Processing & I/O}
\examplecategory {Networking}
\title TaskTree Image Scaling
\ingroup tasktree_examples
\brief Demonstrates how to run For loop iterations in parallel
to asynchronously download and scale images using TaskTree.
This example shows how to use Qt TaskTree classes to download a collection
of images from the network and scale them without blocking the UI. It
demonstrates how to use the \l {QtTaskTree::}{For} element to run subtasks
in a parallel loop over QList items, and how to use the
\l {QtTaskTree::}{Storage} element to share data between tasks.
This example provides an alternative implementation for the image scaling
example in \l {Image Scaling}{Qt Concurrent}.
\image tasktree-imagescaling-example.png
{Grid of downloaded images with Finished status}
\section1 Application workflow
When the user presses the \uicontrol {Add URLs} button, the following method
runs:
\snippet tasktree/imagescaling/imagescaling.cpp 0
The method runs \c DownloadDialog. When the user accepts the dialog, the
method gets the list of selected URLs, clears the displayed images, and
prepares a grid for new images by calling \c initLayout(urls.size()).
For each item in the \c urls list, the app:
\list
\li Downloads the image for a given address using
\l {QtTaskTree::} {QNetworkReplyWrapper} and stores the received
\l QByteArray data locally.
\li Uses \l {QtTaskTree::} {QThreadFunction} to convert the
\l QByteArray data to a \l QImage and scales it to a thumbnail
in a separate thread.
\li Displays the resulting thumbnail in the grid.
\endlist
To iterate over the \c urls list, the app uses an \c iterator element of
\l {QtTaskTree::}{ListIterator} type. The \l QByteArray data received
after downloading is stored in the \c storage element of
\l {QtTaskTree::}{Storage}<QByteArray> type, which is used to
pass the data to the scale function.
\snippet tasktree/imagescaling/imagescaling.cpp 1
The \c recipe that describes the workflow looks like this:
\snippet tasktree/imagescaling/imagescaling.cpp 2
Here is a summary of all elements used in the recipe:
\table
\header
\li Element
\li Purpose
\row
\li \l {QtTaskTree::} {For}
\li Top level loop iterating over a list of \c urls.
\row
\li \l {QtTaskTree::} {Do}
\li Describes the body of each iteration.
\row
\li \l {QtTaskTree::} {finishAllAndSuccess}
\li Instructs the task tree to continue running
despite errors and report success when finished.
\row
\li \l {QtTaskTree::} {parallel}
\li Causes all direct children of the \l{QtTaskTree::}{Do} body
to run in parallel, including all iterations of the
top-level \l{QtTaskTree::}{For} element.
This means that all iterations will start simultaneously.
\row
\li \l {QtTaskTree::} {onGroupSetup}(onRootSetup)
\li Invokes \c onRootSetup handler when the first iteration
is about to be executed.
\row
\li \l {QtTaskTree::} {onGroupDone}(onRootDone)
\li Invokes \c onRootDone handler when all the iterations are finished.
\row
\li \l {QtTaskTree::} {Group}
\li A subgroup, executed for each iteration.
By default, all its children execute in sequence.
\row
\li \c storage
\li Instructs the running task tree to instantiate the underlying
QByteArray instance when entering the subgroup.
This means the running task tree instantiates
a separate QByteArray copy for each iteration.
\row
\li QNetworkReplyWrapperTask
\li The first asynchronous task that runs in sequence for each iteration.
Starts downloading the QByteArray data for a given \c url.
When the download is about to start, the \c onDownloadSetup
is invoked to setup the input \c url for download.
When it's finished, the \c onDownloadDone is invoked.
In case of successful execution, the downloaded QByteArray data
is stored inside \c storage element.
\row
\li QThreadFunctionTask<QImage>
\li The second asynchronous task executed in sequence for each iteration.
It's executed only after successful execution of the first task.
Executes a function in a separate thread, passing the stored QByteArray
data of the downloaded image. When the function is about to start,
the \c onScaleSetup is invoked to setup the QByteArray data for scaling.
When it's finished, the \c onScaleDone is invoked.
In case of successful execution it displays the thumbnail in a grid.
\endtable
\section1 Update the status bar
The following shows how all handlers are implemented:
\snippet tasktree/imagescaling/imagescaling.cpp 3
When the first iteration starts, the \c onRootSetup handler updates the
status bar message and enables the \uicontrol Cancel button. When the last
iteration finishes, the \c onRootDone handler updates the status bar
message and disables the \uicontrol Cancel button.
\section1 Download and scale images
Each iteration runs in parallel with others and consists of two sequential
tasks. The first task in each iteration is
\l {QtTaskTree::} {QNetworkReplyWrapperTask}.
Here is how its setup and done handlers are implemented:
\snippet tasktree/imagescaling/imagescaling.cpp 4
The \c onDownloadSetup handler runs when
\l {QtTaskTree::} {QNetworkReplyWrapper} is about to
start. It sets up the network access manager and network request. The
handler captures the \c iterator, which gives access to the current address
from the \c urls list.
The \c onDownloadDone handler runs when
\l {QtTaskTree::} {QNetworkReplyWrapper} finishes. It
captures the \c storage and \c iterator. If the task succeeds, it stores
the received \l QByteArray data in \c storage. The task tree creates a
separate \l QByteArray instance for each iteration. Dereferencing \c storage
gives access to the \l QByteArray instance for the current iteration. If the
task fails or is canceled, the handler shows the error message for the
label immediately. The index of the current iteration is returned by
\c {iterator.iteration()}.
The second task in each iteration is
\l {QtTaskTree::} {QThreadFunctionTask}<QImage>. Its
setup and done handlers are as follows:
\snippet tasktree/imagescaling/imagescaling.cpp 5
Inside \c onScaleSetup, the handler schedules a call to the
\c scale function, passing the \l QByteArray data received from the first
task as an argument. This is done by dereferencing the captured \c storage.
The \c scale function runs in a separate thread.
The \c onScaleDone handler runs when the \c scale function finishes.
\c onScaleDone is called from the main thread. If scaling succeeds, the
handler assigns the scaled image to the appropriate label. Otherwise, the
handler shows an error message.
The following shows how the \c scale function is implemented:
\snippet tasktree/imagescaling/imagescaling.cpp 6
The function takes a \l QPromise to the result type as an argument. The
\c promise is used to return the final result or to issue an error by
canceling the promise's future.
\include examples-run.qdocinc
*/