Mise à l'échelle des images avec TaskTree
Démontre comment exécuter des itérations de la boucle For en parallèle pour télécharger et mettre à l'échelle des images de manière asynchrone à l'aide de TaskTree.
Cet exemple montre comment utiliser les classes TaskTree de Qt pour télécharger une collection d'images depuis le réseau et les mettre à l'échelle sans bloquer l'interface utilisateur. Il montre comment utiliser l'élément For pour exécuter des sous-tâches dans une boucle parallèle sur les éléments QList, et comment utiliser l'élément Storage pour partager des données entre les tâches.
Cet exemple fournit une implémentation alternative pour l'exemple de mise à l'échelle d'une image dans Qt Concurrent.

Flux de travail de l'application
Lorsque l'utilisateur appuie sur le bouton Add URLs, la méthode suivante est exécutée :
DownloadDialog dialog(this); if (dialog.exec() != QDialog::Accepted) return; const QList<QUrl> urls = dialog.getUrls(); initLayout(urls.size());
La méthode s'exécute DownloadDialog. Lorsque l'utilisateur accepte la boîte de dialogue, la méthode récupère la liste des URL sélectionnés, efface les images affichées et prépare une grille pour les nouvelles images en appelant initLayout(urls.size()).
Pour chaque élément de la liste urls, l'application :
- télécharge l'image correspondant à une adresse donnée à l'aide de QNetworkReplyWrapper et stocke localement les données reçues de QByteArray.
- Utilise QThreadFunction pour convertir les données QByteArray en QImage et les mettre à l'échelle d'une vignette dans un thread séparé.
- Affiche la vignette résultante dans la grille.
Pour itérer sur la liste urls, l'application utilise un élément iterator de type ListIterator. Les données QByteArray reçues après le téléchargement sont stockées dans l'élément storage de type Storage<QByteArray>, qui est utilisé pour transmettre les données à la fonction de mise à l'échelle.
const ListIterator iterator(urls); const Storage<QByteArray> storage;
Le site recipe qui décrit le flux de travail se présente comme suit :
const Group recipe = For (iterator) >> Do { finishAllAndSuccess, parallel, onGroupSetup(onRootSetup), Group { storage, QNetworkReplyWrapperTask(onDownloadSetup, onDownloadDone), QThreadFunctionTask<QImage>(onScaleSetup, onScaleDone) }, onGroupDone(onRootDone) }; taskTreeRunner.start(recipe);
Voici un résumé de tous les éléments utilisés dans la recette :
| Élément | Objectif |
|---|---|
| For | Boucle de premier niveau itérant sur une liste de urls. |
| Do | Décrit le corps de chaque itération. |
| finishAllAndSuccess | Demande à l'arbre des tâches de continuer à s'exécuter malgré les erreurs et de signaler le succès lorsqu'il est terminé. |
| parallel | Fait en sorte que tous les enfants directs du corps Do s'exécutent en parallèle, y compris toutes les itérations de l'élément de premier niveau For. Cela signifie que toutes les itérations commenceront simultanément. |
| onGroupSetup(onRootSetup) | Invoque le gestionnaire onRootSetup lorsque la première itération est sur le point d'être exécutée. |
| onGroupDone(onRootDone) | Invoque le gestionnaire onRootDone lorsque toutes les itérations sont terminées. |
| Group | Un sous-groupe, exécuté pour chaque itération. Par défaut, tous ses enfants s'exécutent en séquence. |
storage | Indique à l'arbre des tâches en cours d'exécution d'instancier l'instance QByteArray sous-jacente lorsqu'il entre dans le sous-groupe. Cela signifie que l'arbre des tâches en cours d'exécution instancie une copie distincte de QByteArray pour chaque itération. |
| QNetworkReplyWrapperTask | La première tâche asynchrone qui s'exécute en séquence pour chaque itération. Commence à télécharger les données QByteArray pour une Lorsqu'il est terminé, |
| QThreadFunctionTask<QImage> | La deuxième tâche asynchrone est exécutée en séquence pour chaque itération. Elle n'est exécutée qu'après l'exécution réussie de la première tâche. Exécute une fonction dans un thread séparé, en passant les données QByteArray stockées de l'image téléchargée. Lorsque la fonction est sur le point de démarrer, la tâche Lorsqu'elle est terminée, la fonction |
Mise à jour de la barre d'état
Ce qui suit montre comment tous les gestionnaires sont mis en œuvre :
const auto onRootSetup = [this] { statusBar->showMessage(tr("Downloading and Scaling...")); cancelButton->setEnabled(true); }; const auto onRootDone = [this](DoneWith result) { const QString message = result == DoneWith::Cancel ? tr("Canceled.") : tr("Finished."); statusBar->showMessage(message); cancelButton->setEnabled(false); };
Lorsque la première itération commence, le gestionnaire onRootSetup met à jour le message de la barre d'état et active le bouton Cancel. Lorsque la dernière itération se termine, le gestionnaire onRootDone met à jour le message de la barre d'état et désactive le bouton Cancel.
Téléchargement et mise à l'échelle des images
Chaque itération s'exécute en parallèle avec les autres et consiste en deux tâches séquentielles. La première tâche de chaque itération est QNetworkReplyWrapperTask. Voici comment ses gestionnaires de configuration et d'exécution sont mis en œuvre :
const auto onDownloadSetup = [this, iterator](QNetworkReplyWrapper &task) { task.setNetworkAccessManager(&qnam); task.setRequest(QNetworkRequest(*iterator)); }; const auto onDownloadDone = [this, storage, iterator](const QNetworkReplyWrapper &task, DoneWith result) { const int it = iterator.iteration(); if (result == DoneWith::Success) *storage = task.reply()->readAll(); else if (result == DoneWith::Error) labels[it]->setText(tr("Download\nError.\nCode: %1.").arg(task.reply()->error())); else labels[it]->setText(tr("Canceled.")); };
Le gestionnaire onDownloadSetup s'exécute lorsque QNetworkReplyWrapper est sur le point de démarrer. Il configure le gestionnaire d'accès au réseau et la demande de réseau. Le gestionnaire capture l'adresse iterator, qui donne accès à l'adresse actuelle de la liste urls.
Le gestionnaire onDownloadDone s'exécute lorsque QNetworkReplyWrapper se termine. Il capture les adresses storage et iterator. Si la tâche réussit, il stocke les données QByteArray reçues dans storage. L'arbre des tâches crée une instance QByteArray distincte pour chaque itération. Le déréférencement de storage permet d'accéder à l'instance QByteArray pour l'itération en cours. Si la tâche échoue ou est annulée, le gestionnaire affiche immédiatement le message d'erreur correspondant à l'étiquette. L'index de l'itération en cours est renvoyé par iterator.iteration().
La deuxième tâche de chaque itération est QThreadFunctionTask<QImage>. Ses gestionnaires de configuration et d'exécution sont les suivants :
const auto onScaleSetup = [storage](QThreadFunction<QImage> &task) { task.setThreadFunctionData(&scale, *storage); }; const auto onScaleDone = [this, iterator](const QThreadFunction<QImage> &task, DoneWith result) { const int it = iterator.iteration(); if (result == DoneWith::Success) labels[it]->setPixmap(QPixmap::fromImage(task.result())); else if (result == DoneWith::Error) labels[it]->setText(tr("Image\nData\nError.")); else labels[it]->setText(tr("Canceled.")); };
À l'intérieur de onScaleSetup, le gestionnaire programme un appel à la fonction scale, en passant les données QByteArray reçues de la première tâche comme argument. Pour ce faire, il déréalise la capture storage. La fonction scale s'exécute dans un thread séparé.
Le gestionnaire onScaleDone s'exécute lorsque la fonction scale se termine. onScaleDone est appelé à partir du fil d'exécution principal. Si la mise à l'échelle réussit, le gestionnaire attribue l'image mise à l'échelle à l'étiquette appropriée. Dans le cas contraire, le gestionnaire affiche un message d'erreur.
Ce qui suit montre comment la fonction scale est mise en œuvre :
static void scale(QPromise<QImage> &promise, const QByteArray &data) { const auto image = QImage::fromData(data); if (image.isNull()) promise.future().cancel(); else promise.addResult(image.scaled(100, 100, Qt::KeepAspectRatio)); }
La fonction prend comme argument un QPromise pour le type de résultat. Le promise est utilisé pour renvoyer le résultat final ou pour émettre une erreur en annulant le futur de la promesse.
Exécution de l'exemple
Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.