このページでは

TaskTree による画像のスケーリング

TaskTree を使用して、For ループの反復処理を並列に実行し、画像を非同期でダウンロードしてリサイズする方法を示します。

この例では、QtのTaskTreeクラスを使用して、ネットワークから一連の画像をダウンロードし、UIをブロックすることなくそれらの画像をリサイズする方法を示します。また、For 要素を使用してQList の項目に対して並列ループでサブタスクを実行する方法や、Storage 要素を使用してタスク間でデータを共有する方法についても説明します。

この例は、 Qt Concurrent。

「完了」ステータスのダウンロード済み画像のグリッド

アプリケーションのワークフロー

ユーザーがAdd URLs ボタンを押すと、次のメソッドが実行されます:

    DownloadDialog dialog(this);
    if (dialog.exec() != QDialog::Accepted)
        return;

    const QList<QUrl> urls = dialog.getUrls();
    initLayout(urls.size());

このメソッドは `DownloadDialog` を実行します。ユーザーがダイアログを承認すると、このメソッドは選択された URL のリストを取得し、表示されている画像を消去し、`initLayout(urls.size())` を呼び出して新しい画像用のグリッドを準備します。

urls リスト内の各項目について、アプリは以下の処理を行います:

  • QNetworkReplyWrapper を使用して指定されたアドレスの画像をダウンロードし、受信したQByteArray データをローカルに保存します。
  • QThreadFunction を使用して、QByteArray データをQImage に変換し、別のスレッドでサムネイルサイズにスケーリングします。
  • 結果として生成されたサムネイルをグリッドに表示します。

urls リストを反復処理するために、アプリはListIterator 型のiterator 要素を使用します。ダウンロード後に受信したQByteArray データは、Storage<QByteArray> 型のstorage 要素に保存され、このデータがスケーリング関数に渡されます。

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

ワークフローを記述するrecipe は、以下のようになります:

    const Group recipe = For (iterator) >> Do {
        finishAllAndSuccess,
        parallel,
        onGroupSetup(onRootSetup),
        Group {
            storage,
            QNetworkReplyWrapperTask(onDownloadSetup, onDownloadDone),
            QThreadFunctionTask<QImage>(onScaleSetup, onScaleDone)
        },
        onGroupDone(onRootDone)
    };
    taskTreeRunner.start(recipe);

レシピで使用されているすべての要素の概要は以下の通りです:

要素目的
Forurls のリストを反復処理する最上位のループ。
Do各反復の本体を記述します。
finishAllAndSuccessエラーが発生してもタスクツリーに実行を継続させ、完了時に成功を報告するよう指示します。
parallelDo 本体のすべての子要素(トップレベルのFor 要素のすべての反復を含む)を並列で実行します。

つまり、すべての反復が同時に開始されます。

onGroupSetup(onRootSetup)最初の反復が実行されようとしたときに、onRootSetup ハンドラを呼び出します。
onGroupDone(onRootDone)すべての反復処理が終了したときに、xml-ph-0000@deepl.internalハンドラを呼び出します。すべての反復処理が終了したときに、onRootDone ハンドラを呼び出します。
Group各反復ごとに実行されるサブグループ。デフォルトでは、そのすべての子が順次実行されます。
storageサブグループに入る際に、実行中のタスクツリーに対して、基になる `QByteArray ` インスタンスをインスタンス化するように指示します。

つまり、実行中のタスクツリーは、反復ごとに個別のQByteArray インスタンスをインスタンス化します。

QNetworkReplyWrapperTask各反復ごとに順次実行される最初の非同期タスク。

指定されたurl に対するQByteArray データのダウンロードを開始します。ダウンロードが開始される直前に、onDownloadSetup が呼び出され、ダウンロード用の入力url がセットアップされます。

ダウンロードが完了すると、onDownloadDone が呼び出されます。実行が成功した場合、ダウンロードされたQByteArray データはstorage 要素内に格納されます。

QThreadFunctionTask<QImage>各反復ごとに順次実行される2番目の非同期タスクです。これは、最初のタスクが正常に実行された後にのみ実行されます。

ダウンロードされた画像の保存済みQByteArray データを引数として渡し、別のスレッドで関数を実行します。関数の実行開始直前には、onScaleSetup が呼び出され、スケーリング用のQByteArray データが設定されます。

処理が完了すると、onScaleDone が呼び出されます。実行が成功した場合、サムネイルがグリッド形式で表示されます。

ステータスバーを更新する

以下に、すべてのハンドラの実施方法を示します:

    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);
    };

最初の反復処理が開始されると、onRootSetup ハンドラがステータスバーのメッセージを更新し、Cancel ボタンを有効にします。最後の反復処理が終了すると、onRootDone ハンドラがステータスバーのメッセージを更新し、Cancel ボタンを無効にします。

画像のダウンロードと拡大縮小

各反復処理は他の反復処理と並行して実行され、2つの連続したタスクで構成されています。各反復処理の最初のタスクはQNetworkReplyWrapperTask です。そのsetupハンドラとdoneハンドラは次のように実装されています:

    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."));
    };

onDownloadSetup ハンドラは、QNetworkReplyWrapper が開始されようとする際に実行されます。このハンドラは、ネットワークアクセスマネージャとネットワークリクエストを設定します。また、このハンドラはiterator を取得し、これによりurls リストから現在のアドレスにアクセスできるようになります。

onDownloadDone ハンドラは、QNetworkReplyWrapper が終了した際に実行されます。このハンドラは、storage およびiterator を取得します。タスクが成功した場合、受信したQByteArray データをstorage に格納します。タスクツリーは、各反復ごとに個別のQByteArray インスタンスを作成します。storage を参照解除すると、現在の反復に対応するQByteArray インスタンスにアクセスできます。タスクが失敗またはキャンセルされた場合、ハンドラは直ちにラベルに対するエラーメッセージを表示します。 現在の反復のインデックスは、iterator.iteration() によって返されます。

各反復における 2 番目のタスクは、QThreadFunctionTask<QImage> です。その setup ハンドラと done ハンドラは以下の通りです:

    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."));
    };

onScaleSetup 内で、ハンドラはscale 関数への呼び出しをスケジューリングし、最初のタスクから受け取ったQByteArray データを引数として渡します。これは、キャプチャされたstorage の参照を解除することで行われます。scale 関数は別のスレッドで実行されます。

onScaleDone ハンドラは、scale 関数が終了したときに実行されます。onScaleDone はメインスレッドから呼び出されます。拡大縮小が成功した場合、ハンドラは拡大縮小された画像を適切なラベルに割り当てます。そうでない場合、ハンドラはエラーメッセージを表示します。

以下に、scale 関数の実装例を示します。

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));
}

この関数は、結果の型へのQPromise を引数として受け取ります。promise は、最終結果を返すため、あるいはプロミスのフューチャーをキャンセルしてエラーを発生させるために使用されます。

例の動作

このサンプルは、以下の手順で実行できます:

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.