Revision control

++ // 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
++ */