이 페이지에서

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)모든 반복이 완료되면 onRootDone 핸들러를 호출합니다.
Group각 반복마다 실행되는 하위 그룹입니다. 기본적으로 이 그룹의 모든 자식 노드는 순차적으로 실행됩니다.
storage실행 중인 작업 트리가 하위 그룹에 진입할 때 기본 QByteArray 인스턴스를 인스턴스화하도록 지시합니다.

즉, 실행 중인 작업 트리는 각 반복마다 별도의 QByteArray 인스턴스를 생성합니다.

QNetworkReplyWrapperTask각 반복마다 순차적으로 실행되는 첫 번째 비동기 작업입니다.

주어진 url 에 대한 QByteArray 데이터 다운로드를 시작합니다. 다운로드가 시작될 때, onDownloadSetup 가 호출되어 다운로드를 위한 입력 url 을 설정합니다.

다운로드가 완료되면 onDownloadDone 가 호출됩니다. 실행이 성공하면 다운로드된 QByteArray 데이터가 storage 요소 내에 저장됩니다.

QThreadFunctionTask<QImage>각 반복마다 순차적으로 실행되는 두 번째 비동기 작업입니다. 이 작업은 첫 번째 작업이 성공적으로 완료된 후에만 실행됩니다.

다운로드된 이미지의 저장된 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 ` 버튼을 비활성화합니다.

이미지 다운로드 및 크기 조정

각 반복은 다른 반복과 병렬로 실행되며 두 개의 순차적인 작업으로 구성됩니다. 각 반복의 첫 번째 작업은 ` 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() 을 통해 반환됩니다.

각 반복의 두 번째 작업은 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 내부에서 핸들러는 첫 번째 태스크로부터 수신한 QByteArray 데이터를 인수로 전달하여 scale 함수의 호출을 예약합니다. 이는 캡처된 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.