Bildskalierung mit TaskTree
Veranschaulicht, wie man „For“-Schleifeniterationen parallel ausführt, um Bilder mithilfe von TaskTree asynchron herunterzuladen und zu skalieren.
Dieses Beispiel zeigt, wie man mit Qt-TaskTree-Klassen eine Sammlung von Bildern aus dem Netzwerk herunterlädt und skaliert, ohne die Benutzeroberfläche zu blockieren. Es demonstriert, wie man das Element „ For “ verwendet, um Unteraufgaben in einer parallelen Schleife über „ QList “-Elemente auszuführen, und wie man das Element „ Storage “ nutzt, um Daten zwischen Aufgaben auszutauschen.
Dieses Beispiel bietet eine alternative Implementierung für das Beispiel zur Bildskalierung in Qt Concurrent.

Anwendungsablauf
Wenn der Benutzer auf die Schaltfläche „ Add URLs “ klickt, wird die folgende Methode ausgeführt:
DownloadDialog dialog(this);
if (dialog.exec() != QDialog::Accepted)
return;
const QList<QUrl> urls = dialog.getUrls();
initLayout(urls.size());Die Methode „ DownloadDialog “ wird ausgeführt. Wenn der Benutzer den Dialog bestätigt, ruft die Methode die Liste der ausgewählten URLs ab, löscht die angezeigten Bilder und bereitet durch den Aufruf von „ initLayout(urls.size()) “ ein Raster für neue Bilder vor.
Für jedes Element in der Liste „ urls “ führt die App folgende Schritte aus:
- Lädt das Bild für eine bestimmte Adresse mithilfe von ` QNetworkReplyWrapper ` herunter und speichert die empfangenen ` QByteArray `-Daten lokal.
- Verwendet „ QThreadFunction “, um die „ QByteArray “-Daten in ein „ QImage “ zu konvertieren, und skaliert es in einem separaten Thread zu einem Vorschaubild.
- Zeigt das resultierende Miniaturbild im Raster an.
Um die Liste „ urls “ zu durchlaufen, verwendet die App ein „ iterator “-Element vom Typ „ ListIterator “. Die nach dem Herunterladen empfangenen „ QByteArray “-Daten werden im „ storage “-Element vom Typ „ Storage<QByteArray>“ gespeichert, das dazu dient, die Daten an die Skalierungsfunktion zu übergeben.
const ListIterator iterator(urls);
const Storage<QByteArray> storage;Das „ recipe “, das den Workflow beschreibt, sieht wie folgt aus:
const Group recipe = For (iterator) >> Do {
finishAllAndSuccess,
parallel,
onGroupSetup(onRootSetup),
Group {
storage,
QNetworkReplyWrapperTask(onDownloadSetup, onDownloadDone),
QThreadFunctionTask<QImage>(onScaleSetup, onScaleDone)
},
onGroupDone(onRootDone)
};
taskTreeRunner.start(recipe);Hier ist eine Übersicht über alle im Rezept verwendeten Elemente:
| Element | Zweck |
|---|---|
| For | Schleife auf oberster Ebene, die eine Liste von „ urls “ durchläuft. |
| Do | Beschreibt den Hauptteil jeder Iteration. |
| finishAllAndSuccess | Weist den Aufgabenbaum an, trotz Fehlern weiterzulaufen und nach Abschluss den Erfolg zu melden. |
| parallel | Bewirkt, dass alle direkten untergeordneten Elemente des „ Do “-Körpers parallel ausgeführt werden, einschließlich aller Iterationen des obersten „ For “-Elements. Das bedeutet, dass alle Iterationen gleichzeitig starten. |
| onGroupSetup(onRootSetup) | Ruft den ` onRootSetup `-Handler auf, wenn die erste Iteration kurz vor der Ausführung steht. |
| onGroupDone(onRootDone) | Ruft den Handler „ onRootDone “ auf, wenn alle Iterationen abgeschlossen sind. |
| Group | Eine Untergruppe, die bei jeder Iteration ausgeführt wird. Standardmäßig werden alle ihre untergeordneten Elemente nacheinander ausgeführt. |
storage | Weist den laufenden Aufgabenbaum an, beim Eintritt in die Untergruppe die zugrunde liegende „ QByteArray “-Instanz zu instanziieren. Das bedeutet, dass der laufende Aufgabenbaum für jede Iteration eine separate Kopie von „ QByteArray “ instanziiert. |
| QNetworkReplyWrapperTask | Die erste asynchrone Aufgabe, die bei jeder Iteration nacheinander ausgeführt wird. Beginnt mit dem Herunterladen der „ QByteArray “-Daten für eine bestimmte „ Nach Abschluss des Vorgangs wird die Funktion „ |
| QThreadFunctionTask<QImage> | Die zweite asynchrone Aufgabe, die bei jeder Iteration nacheinander ausgeführt wird. Sie wird erst nach erfolgreicher Ausführung der ersten Aufgabe ausgeführt. Führt eine Funktion in einem separaten Thread aus und übergibt dabei die gespeicherten ` QByteArray `-Daten des heruntergeladenen Bildes. Bevor die Funktion gestartet wird, wird ` Nach Abschluss des Vorgangs wird die Funktion „ |
Aktualisierung der Statusleiste
Im Folgenden wird gezeigt, wie alle Handler implementiert sind:
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);
};Wenn die erste Iteration beginnt, aktualisiert der Handler „ onRootSetup “ die Meldung in der Statusleiste und aktiviert die Schaltfläche „ Cancel “. Wenn die letzte Iteration beendet ist, aktualisiert der Handler „ onRootDone “ die Meldung in der Statusleiste und deaktiviert die Schaltfläche „ Cancel “.
Bilder herunterladen und skalieren
Jede Iteration läuft parallel zu den anderen und besteht aus zwei aufeinanderfolgenden Aufgaben. Die erste Aufgabe in jeder Iteration ist „ QNetworkReplyWrapperTask “. So sind die „setup“- und „done“-Handler implementiert:
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."));
};Der „ onDownloadSetup “-Handler wird ausgeführt, wenn „ QNetworkReplyWrapper “ kurz vor dem Start steht. Er richtet den Netzwerkzugriffsmanager und die Netzwerkanfrage ein. Der Handler erfasst den „ iterator “, der Zugriff auf die aktuelle Adresse aus der Liste „ urls “ gewährt.
Der Handler „ onDownloadDone “ wird ausgeführt, wenn „ QNetworkReplyWrapper “ beendet wird. Er erfasst die Werte „ storage “ und „ iterator “. Wenn die Aufgabe erfolgreich ist, speichert er die empfangenen „ QByteArray “-Daten in „ storage “. Der Aufgabenbaum erstellt für jede Iteration eine separate „ QByteArray “-Instanz. Durch Dereferenzierung von „ storage “ erhält man Zugriff auf die „ QByteArray “-Instanz für die aktuelle Iteration. Wenn die Aufgabe fehlschlägt oder abgebrochen wird, zeigt der Handler sofort die Fehlermeldung für die Beschriftung an. Der Index der aktuellen Iteration wird von „ iterator.iteration() “ zurückgegeben.
Die zweite Aufgabe in jeder Iteration ist QThreadFunctionTask<QImage>. Ihre Setup- und Done-Handler lauten wie folgt:
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."));
};Innerhalb von „ onScaleSetup “ plant der Handler einen Aufruf der Funktion „ scale “ ein und übergibt dabei die von der ersten Aufgabe empfangenen „ QByteArray “-Daten als Argument. Dies geschieht durch Dereferenzierung des erfassten „ storage “. Die Funktion „ scale “ läuft in einem separaten Thread.
Der Handler „ onScaleDone “ wird ausgeführt, wenn die Funktion „ scale “ beendet ist. „ onScaleDone “ wird vom Hauptthread aus aufgerufen. Wenn die Skalierung erfolgreich ist, ordnet der Handler das skalierte Bild dem entsprechenden Label zu. Andernfalls zeigt der Handler eine Fehlermeldung an.
Im Folgenden wird gezeigt, wie die Funktion ` scale ` implementiert ist:
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));
}Die Funktion nimmt ein „ QPromise “ vom Ergebnistyp als Argument entgegen. Das „ promise “ wird verwendet, um das Endergebnis zurückzugeben oder einen Fehler auszulösen, indem die Zukunft des Promises abgebrochen wird.
Ausführen des Beispiels
Sie können das Beispiel wie folgt ausführen:
- Qt Creator
Öffnen Sie den „ Welcome “-Modus und wählen Sie das Beispiel unter „ Examples “ aus. Weitere Informationen finden Sie unter Qt Creator: Tutorial: Erstellen und Ausführen.
- Qt Extension for Visual Studio Code
Führen Sie den Befehl „ Qt: Open Qt examples “ aus der Datei „ Command Palette “ aus und wählen Sie das Beispiel aus der Liste aus. Weitere Informationen finden Sie unter Qt Extension for Visual Studio Code: Tutorial: Erstellen und Ausführen.
© 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.