Auf dieser Seite

TaskTree-Bildskalierung

Zeigt, wie man For-Schleifen-Iterationen parallel ausführt, um Bilder mit TaskTree asynchron herunterzuladen und zu skalieren.

Dieses Beispiel zeigt, wie man Qt TaskTree-Klassen verwendet, um eine Sammlung von Bildern aus dem Netzwerk herunterzuladen und sie zu skalieren, ohne die Benutzeroberfläche zu blockieren. Es wird gezeigt, wie das Element For verwendet wird, um Unteraufgaben in einer parallelen Schleife über QList Elemente auszuführen, und wie das Element Storage verwendet wird, um Daten zwischen Aufgaben auszutauschen.

Dieses Beispiel bietet eine alternative Implementierung für das Bildskalierungsbeispiel in Qt Concurrent.

Raster der heruntergeladenen Bilder mit dem Status Beendet

Arbeitsablauf der Anwendung

Wenn der Benutzer auf die Schaltfläche Add URLs drückt, 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 akzeptiert, ruft die Methode die Liste der ausgewählten URLs ab, löscht die angezeigten Bilder und bereitet ein Raster für neue Bilder vor, indem sie initLayout(urls.size()) aufruft.

Für jeden Eintrag in der Liste urls lädt die App:

  • Lädt das Bild für eine bestimmte Adresse mit QNetworkReplyWrapper herunter und speichert die empfangenen QByteArray Daten lokal.
  • Verwendet QThreadFunction, um die QByteArray Daten in ein QImage zu konvertieren und skaliert sie in einem separaten Thread zu einer Miniaturansicht.
  • Zeigt das resultierende Thumbnail im Grid an.

Um die Liste urls zu durchlaufen, verwendet die Anwendung ein Element iterator vom Typ ListIterator. Die nach dem Herunterladen empfangenen QByteArray Daten werden im storage Element vom Typ Storage<QByteArray> gespeichert, das zur Übergabe der Daten an die Skalierungsfunktion verwendet wird.

    const ListIterator iterator(urls);
    const Storage<QByteArray> storage;

Die recipe, die den Arbeitsablauf 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 Zusammenfassung aller im Rezept verwendeten Elemente:

ElementZweck
ForSchleife auf oberster Ebene, die über eine Liste von urls iteriert.
DoBeschreibt den Körper jeder Iteration.
finishAllAndSuccessWeist den Aufgabenbaum an, trotz Fehlern weiterzulaufen und nach Abschluss einen Erfolg zu melden.
parallelBewirkt, dass alle direkten untergeordneten Elemente des Do Körpers parallel ausgeführt werden, einschließlich aller Iterationen des For Elements der obersten Ebene.

Dies bedeutet, dass alle Iterationen gleichzeitig beginnen.

onGroupSetup(onRootSetup)Ruft den onRootSetup Handler auf, wenn die erste Iteration ausgeführt werden soll.
onGroupDone(onRootDone)Ruft den onRootDone Handler auf, wenn alle Iterationen abgeschlossen sind.
GroupEine Untergruppe, die für jede Iteration ausgeführt wird. Standardmäßig werden alle ihre Untergruppen nacheinander ausgeführt.
storageWeist den laufenden Aufgabenbaum an, die zugrundeliegende QByteArray Instanz zu instanziieren, wenn er die Untergruppe betritt.

Das bedeutet, dass der laufende Aufgabenbaum für jede Iteration eine eigene QByteArray Kopie instanziiert.

QNetworkReplyWrapperTaskDie erste asynchrone Aufgabe, die bei jeder Iteration nacheinander ausgeführt wird.

Beginnt mit dem Herunterladen der QByteArray Daten für eine bestimmte url. Wenn der Download kurz vor dem Start steht, wird onDownloadSetup aufgerufen, um die Eingabe url für den Download vorzubereiten.

Wenn er fertig ist, wird onDownloadDone aufgerufen. Bei erfolgreicher Ausführung werden die heruntergeladenen QByteArray Daten im storage Element gespeichert.

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 die gespeicherten QByteArray Daten des heruntergeladenen Bildes. Wenn die Funktion kurz vor dem Start steht, wird onScaleSetup aufgerufen, um die QByteArray Daten für die Skalierung vorzubereiten.

Wenn sie fertig ist, wird die onScaleDone aufgerufen. Bei erfolgreicher Ausführung wird die Miniaturansicht in einem Raster angezeigt.

Aktualisieren 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 onRootDone Handler die Meldung in der Statusleiste und deaktiviert die Schaltfläche Cancel.

Bilder herunterladen und skalieren

Jede Iteration läuft parallel zu anderen und besteht aus zwei aufeinander folgenden Aufgaben. Die erste Aufgabe in jeder Iteration ist QNetworkReplyWrapperTask. Im Folgenden wird beschrieben, wie die Handler setup und done implementiert werden:

    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 Handler onDownloadSetup wird ausgeführt, wenn QNetworkReplyWrapper kurz vor dem Start steht. Er richtet den Netzzugangsmanager und die Netzanforderung ein. Der Handler erfasst die iterator, die den Zugriff auf die aktuelle Adresse aus der urls Liste ermöglicht.

Der onDownloadDone Handler wird ausgeführt, wenn QNetworkReplyWrapper beendet ist. Er erfasst die storage und iterator. Wenn die Aufgabe erfolgreich ist, speichert er die empfangenen QByteArray Daten in storage. Der Aufgabenbaum erstellt für jede Iteration eine eigene QByteArray -Instanz. Die Dereferenzierung von storage ermöglicht den Zugriff auf die Instanz QByteArray für die aktuelle Iteration. Wenn die Aufgabe fehlschlägt oder abgebrochen wird, zeigt der Handler sofort die Fehlermeldung für das Label an. Der Index der aktuellen Iteration wird von iterator.iteration() zurückgegeben.

Die zweite Aufgabe in jeder Iteration ist QThreadFunctionTask<QImage>. Die Handler für das Einrichten und Beenden sind 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 und übergibt die von der ersten Aufgabe erhaltenen Daten QByteArray als Argument. Dies geschieht durch Dereferenzierung der aufgezeichneten storage. Die Funktion scale läuft in einem separaten Thread.

Der onScaleDone Handler wird ausgeführt, wenn die Funktion scale beendet ist. onScaleDone wird vom Hauptthread aus aufgerufen. Wenn die Skalierung erfolgreich war, 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 zum Ergebnistyp als Argument. Die promise wird verwendet, um das Endergebnis zurückzugeben oder einen Fehler zu melden, indem die Zukunft des Versprechens abgebrochen wird.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorauszuführen, öffnen Sie den Modus Welcome und wählen Sie das Beispiel aus Examples aus. Für weitere Informationen siehe Qt Creator: Tutorial: Erstellen und Ausführen.

Beispielprojekt @ code.qt.io

© 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.