En esta página

Escalado de Imágenes TaskTree

Demuestra cómo ejecutar iteraciones del bucle For en paralelo para descargar y escalar imágenes de forma asíncrona utilizando TaskTree.

Este ejemplo muestra cómo utilizar las clases Qt TaskTree para descargar una colección de imágenes de la red y escalarlas sin bloquear la interfaz de usuario. Demuestra cómo utilizar el elemento For para ejecutar subtareas en un bucle paralelo sobre elementos de QList, y cómo utilizar el elemento Storage para compartir datos entre tareas.

Este ejemplo proporciona una implementación alternativa para el ejemplo de escalado de imágenes en Qt Concurrent.

Cuadrícula de imágenes descargadas con estado Finalizado

Flujo de trabajo de la aplicación

Cuando el usuario pulsa el botón Add URLs, se ejecuta el siguiente método:

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

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

El método se ejecuta DownloadDialog. Cuando el usuario acepta el diálogo, el método obtiene la lista de URL seleccionadas, borra las imágenes mostradas y prepara una rejilla para nuevas imágenes llamando a initLayout(urls.size()).

Para cada elemento de la lista urls, la aplicación:

  • Descarga la imagen para una dirección dada utilizando QNetworkReplyWrapper y almacena los datos recibidos de QByteArray localmente.
  • Utiliza QThreadFunction para convertir los datos de QByteArray en QImage y los escala a una miniatura en un subproceso separado.
  • Muestra la miniatura resultante en la cuadrícula.

Para iterar sobre la lista urls, la aplicación utiliza un elemento iterator de tipo ListIterator. Los datos de QByteArray recibidos tras la descarga se almacenan en el elemento storage de tipo Storage<QByteArray>, que se utiliza para pasar los datos a la función de escala.

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

El recipe que describe el flujo de trabajo tiene este aspecto:

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

He aquí un resumen de todos los elementos utilizados en la receta:

ElementoPropósito
ForBucle de nivel superior que itera sobre una lista de urls.
DoDescribe el cuerpo de cada iteración.
finishAllAndSuccessOrdena al árbol de tareas que continúe ejecutándose a pesar de los errores e informe del éxito cuando haya terminado.
parallelHace que todos los hijos directos del cuerpo de Do se ejecuten en paralelo, incluidas todas las iteraciones del elemento de nivel superior For.

Esto significa que todas las iteraciones comenzarán simultáneamente.

onGroupSetup(onRootSetup)Invoca al manejador onRootSetup cuando la primera iteración está a punto de ejecutarse.
onGroupDone(onRootDone)Invoca al manejador onRootDone cuando todas las iteraciones han terminado.
GroupUn subgrupo, ejecutado para cada iteración. Por defecto, todos sus hijos se ejecutan en secuencia.
storageIndica al árbol de tareas en ejecución que instancie la instancia QByteArray subyacente al entrar en el subgrupo.

Esto significa que el árbol de tareas en ejecución instanciará una copia separada de QByteArray para cada iteración.

QNetworkReplyWrapperTaskLa primera tarea asíncrona que se ejecuta en secuencia para cada iteración.

Inicia la descarga de los datos de QByteArray para un url dado. Cuando la descarga está a punto de comenzar, se invoca a onDownloadSetup para configurar la entrada url para la descarga.

Cuando termina, se invoca a onDownloadDone. En caso de ejecución satisfactoria, los datos QByteArray descargados se almacenan dentro del elemento storage.

QThreadFunctionTask<QImage>La segunda tarea asíncrona ejecutada en secuencia para cada iteración. Se ejecuta sólo después de la ejecución exitosa de la primera tarea.

Ejecuta una función en un hilo separado, pasando los datos almacenados QByteArray de la imagen descargada. Cuando la función está a punto de comenzar, se invoca a onScaleSetup para configurar los datos de QByteArray para el escalado.

Cuando termina, se invoca a onScaleDone. En caso de ejecución exitosa muestra la miniatura en una cuadrícula.

Actualizar la barra de estado

A continuación se muestra cómo se implementan todos los manejadores:

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

Cuando se inicia la primera iteración, el manejador onRootSetup actualiza el mensaje de la barra de estado y habilita el botón Cancel. Cuando finaliza la última iteración, el controlador onRootDone actualiza el mensaje de la barra de estado y desactiva el botón Cancel.

Descargar y escalar imágenes

Cada iteración se ejecuta en paralelo con otras y consta de dos tareas secuenciales. La primera tarea de cada iteración es QNetworkReplyWrapperTask. A continuación se muestra cómo se implementan sus manejadores de configuración y finalización:

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

El gestor onDownloadSetup se ejecuta cuando QNetworkReplyWrapper está a punto de comenzar. Configura el gestor de acceso a la red y la petición de red. El manejador captura el iterator, que da acceso a la dirección actual de la lista urls.

El manejador onDownloadDone se ejecuta cuando QNetworkReplyWrapper termina. Captura storage y iterator. Si la tarea tiene éxito, almacena los datos recibidos de QByteArray en storage. El árbol de tareas crea una instancia de QByteArray distinta para cada iteración. La desreferencia a storage da acceso a la instancia QByteArray para la iteración actual. Si la tarea falla o se cancela, el manejador muestra inmediatamente el mensaje de error de la etiqueta. El índice de la iteración actual es devuelto por iterator.iteration().

La segunda tarea en cada iteración es QThreadFunctionTask<QImage>. Sus handlers de setup y done son los siguientes:

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

Dentro de onScaleSetup, el manejador programa una llamada a la función scale, pasando los datos QByteArray recibidos de la primera tarea como argumento. Esto se hace desreferenciando el storage capturado. La función scale se ejecuta en un hilo independiente.

El manejador onScaleDone se ejecuta cuando finaliza la función scale. onScaleDone se llama desde el hilo principal. Si el escalado tiene éxito, el manejador asigna la imagen escalada a la etiqueta apropiada. En caso contrario, muestra un mensaje de error.

A continuación se muestra cómo se implementa la función 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));
}

La función toma como argumento un QPromise al tipo de resultado. El promise se utiliza para devolver el resultado final o para emitir un error cancelando el futuro de la promesa.

Ejecución del ejemplo

Para ejecutar el ejemplo desde Qt Creator, abra el modo Welcome y seleccione el ejemplo de Examples. Para más información, ver Qt Creator: Tutorial: Construir y ejecutar.

Proyecto de ejemplo @ 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.