En esta página

Modelos y vistas: Modelo de lista utilizando un subproceso de trabajo para la obtención de datos

Demuestra cómo implementar un modelo de lista con una interfaz de usuario responsiva utilizando un subproceso de trabajo para obtener datos.

Captura de pantalla de la aplicación en la que aparece una lista de canciones con las carátulas de los álbumes, los nombres de las canciones, los nombres de los artistas y los nombres de los álbumes

Este ejemplo presenta un modelo de elementos personalizado que hereda de QAbstractListModel. El modelo obtiene sus datos de un objeto trabajador que está en QThread, obteniendo datos de una fuente de datos lenta.

Visión general del ejemplo de lista de canciones enhebradas

La fuente de datos simula una fuente de datos lenta añadiendo un retardo de 100 milisegundos por canción obtenida de ella. Esto significa que la carga de la lista completa de 3600 canciones tardaría 6 minutos, lo que haría poco práctica la apertura de la aplicación. Este retraso se mitiga obteniendo los datos sólo para el área visible de la vista, utilizando un QObject colocado en un hilo trabajador.

El objeto trabajador tiene un límite para el número de solicitudes de obtención que mantiene en cola. Esto garantiza que sólo se obtengan los elementos de la parte visible de la lista de canciones, eliminando la necesidad de esperar a que se cargue la parte no visible de la lista, cuando el usuario ya se ha desplazado más allá de alguna parte de la lista.

Este ejemplo se centra en el modelo fuente de la vista. La vista en sí es un QML ListView sin modificar con un simple delegado. El uso de hilos está oculto tras la implementación del manejo de datos del modelo y el ListView no necesita ninguna personalización para poder adaptarse al modelo basado en hilos.

Además, como el foco está en el modelo, el Qt Quick Controls está configurado para usar el estilo Universal en todas las plataformas para asegurar un comportamiento idéntico de la UI.

import QtQuick
import QtQuick.Controls.Universal

Cómo funciona

La lógica de negocio de proporcionar los datos de la lista de canciones está separada en la clase DataStorage que proporciona una interfaz simple basada en ID para el modelo.

QList<int> idList();
MediaElement item(int id) const;
std::optional<int> currentlyFetchedId() const;

Cuando el modelo solicita datos del DataStorage, el almacenamiento comprueba primero si ya tiene los datos disponibles. En caso afirmativo, los datos se devuelven al instante, como ocurriría en un modelo sin hilos. En caso de que no se encuentren los datos, el DataStorage emitirá una señal dataFetchNeeded() al objeto worker y añadirá un elemento vacío a la lista de datos ya existentes. La adición del elemento vacío garantiza que no se envíen más señales al trabajador para el mismo elemento de la lista.

if (!m_items.contains(id)) {
    m_items.insert(id, MediaElement{});
    emit dataFetchNeeded(m_idList.indexOf(id));
}
return m_items.value(id);

QueueWorker - el objeto worker thread - procesa las señales dataFetchNeeded() que ha recibido enviándose una señal a sí mismo, lo que permite recibir todas las señales ya existentes en QEventQueue antes de iniciar la operación de lectura lenta de datos.

Aplicación del enfoque a modelos dinámicos

Si se desea ampliar la solución a un caso en el que se puedan añadir, mover o eliminar elementos de la fuente de datos (en este caso RemoteMedia), es necesario actualizar DataStorage con señales que coincidan con QAbstractItemModel::rowsMoved(), QAbstractItemModel::rowsInserted() y dos señales que activen QAbstractItemModel::beginRemoveRows() y QAbstractItemModel::endRemoveRows() dentro de ThreadedListModel.

Para la inserción y movimiento el ThreadedListModel puede simplemente llamar a QAbstractItemModel::beginInsertRows(), luego añadir nuevos IDs a su lista de IDs y llamar a QAbstractItemModel::endInsertRows(). Como ThreadedListModel tiene una copia de la lista de ID y accede al almacenamiento por ID, no hay necesidad de señalar el inicio y el final desde el almacenamiento. Igualmente ThreadedListModel puede llamar a QAbstractItemModel::beginMoveRows(), mover IDs en su lista de IDs y luego llamar a QAbstractItemModel::endMoveRows().

La eliminación es un caso un poco más complejo. La vista necesita tener la posibilidad de solicitar los datos que van a ser eliminados antes de que sean realmente eliminados. Así que DataStorage necesita avisar de la eliminación, haciendo que el Modelo llame a QAbstractItemModel::beginRemoveRows(). En esta etapa ThreadedListModel puede recibir una o más llamadas a data(). Una vez que la llamada a la señal directa conectada vuelve a DataStorage, está bien que DataStorage elimine el elemento y entonces vuelva a señalar al modelo con otra señal que provoque que el modelo llame a QAbstractItemModel::endRemoveRows().

Ejecución del ejemplo

Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para obtener más información, consulte 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.