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);レシピで使用されているすべての要素の概要は以下の通りです:
| 要素 | 目的 |
|---|---|
| For | urls のリストを反復処理する最上位のループ。 |
| Do | 各反復の本体を記述します。 |
| finishAllAndSuccess | エラーが発生してもタスクツリーに実行を継続させ、完了時に成功を報告するよう指示します。 |
| parallel | Do 本体のすべての子要素(トップレベルのFor 要素のすべての反復を含む)を並列で実行します。 つまり、すべての反復が同時に開始されます。 |
| onGroupSetup(onRootSetup) | 最初の反復が実行されようとしたときに、onRootSetup ハンドラを呼び出します。 |
| onGroupDone(onRootDone)すべての反復処理が終了したときに、xml-ph-0000@deepl.internalハンドラを呼び出します。 | すべての反復処理が終了したときに、onRootDone ハンドラを呼び出します。 |
| Group | 各反復ごとに実行されるサブグループ。デフォルトでは、そのすべての子が順次実行されます。 |
storage | サブグループに入る際に、実行中のタスクツリーに対して、基になる `QByteArray ` インスタンスをインスタンス化するように指示します。 つまり、実行中のタスクツリーは、反復ごとに個別のQByteArray インスタンスをインスタンス化します。 |
| QNetworkReplyWrapperTask | 各反復ごとに順次実行される最初の非同期タスク。 指定された ダウンロードが完了すると、 |
| QThreadFunctionTask<QImage> | 各反復ごとに順次実行される2番目の非同期タスクです。これは、最初のタスクが正常に実行された後にのみ実行されます。 ダウンロードされた画像の保存済みQByteArray データを引数として渡し、別のスレッドで関数を実行します。関数の実行開始直前には、 処理が完了すると、 |
ステータスバーを更新する
以下に、すべてのハンドラの実施方法を示します:
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 は、最終結果を返すため、あるいはプロミスのフューチャーをキャンセルしてエラーを発生させるために使用されます。
例の動作
このサンプルは、以下の手順で実行できます:
- Qt Creator
Welcome モードを開き、Examples から例を選択します。詳細については、Qt Creator の「チュートリアル: ビルドと実行」を参照してください。
- Qt Extension for Visual Studio Code
Command Palette から `Qt: Open Qt examples ` コマンドを実行し、リストから例を選択します。詳細については、Qt Extension for Visual Studio Code の「チュートリアル: ビルドと実行」を参照してください。
© 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.