タスクツリーの画像スケーリング
タスクツリー(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 | トップレベルFor 要素のすべての反復を含め、Do 本体のすべての直接の子要素を並列に実行させる。 これは、すべての反復が同時に開始されることを意味する。 |
| onGroupSetup(onRootSetup) | 最初の反復が実行されそうになると、onRootSetup ハンドラを呼び出します。 |
| onGroupDone(onRootDone) | すべての反復が終了すると、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 です。以下は、そのセットアップと完了ハンドラの実装方法です:
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 リストから現在のアドレスにアクセスできるようにする。
QNetworkReplyWrapper が終了すると、onDownloadDone ハンドラが実行される。storage iteratorタスクが成功すると、受信したQByteArray データをstorage に格納します。タスク・ツリーは、各反復ごとに個別のQByteArray インスタンスを作成する。storage を再参照すると、現在の反復のQByteArray インスタンスにアクセスできる。タスクが失敗またはキャンセルされた場合、ハンドラは直ちにラベルのエラー・メッセージを表示する。現在の反復のインデックスはiterator.iteration() によって返される。
各反復の2番目のタスクはQThreadFunctionTask<QImage>である。そのセットアップと完了ハンドラは以下の通りである:
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 関数は別のスレッドで実行される。
scale 関数が終了すると、onScaleDone ハンドラーが実行される。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:チュートリアルを参照してください:ビルドと実行を参照してください。
© 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.