Sur cette page

Modèles et vues : Modèle de liste utilisant un fil d'exécution pour la récupération des données

Démontre comment mettre en œuvre un modèle de liste avec une interface utilisateur réactive en utilisant un fil d'exécution pour récupérer les données.

Capture d'écran de l'application où une liste de chansons avec les pochettes d'albums, les noms de chansons, les noms d'artistes et les noms d'albums sont visibles.

Cet exemple présente un modèle d'élément personnalisé, héritant de QAbstractListModel. Le modèle obtient ses données à partir d'un objet de travailleur qui est dans un QThread séparé, récupérant des données à partir d'une source de données lente.

Vue d'ensemble de l'exemple de la liste de chansons en file d'attente

La source de données simule une source de données lente en ajoutant un délai de 100 millisecondes par chanson récupérée. Cela signifie que le chargement de la liste complète de 3600 chansons prendrait 6 minutes, ce qui rendrait l'ouverture de l'application impraticable. Ce délai est atténué par la récupération des données uniquement pour la zone visible de la vue, à l'aide d'un QObject placé dans un thread de travailleur.

L'objet travailleur a une limite pour le nombre de demandes de récupération qu'il conserve dans la file d'attente. Cela garantit que seuls les éléments de la partie visible de la liste de chansons sont récupérés, ce qui évite d'attendre le chargement de la partie non visible de la liste, lorsque l'utilisateur a déjà fait défiler une partie de la liste.

Cet exemple se concentre sur le modèle source de la vue. La vue elle-même est un QML ListView non modifié avec un simple délégué. L'utilisation d'un fil conducteur est cachée derrière la mise en œuvre de la gestion des données du modèle et le site ListView n'a pas besoin d'être personnalisé pour pouvoir s'adapter au modèle basé sur un fil conducteur.

De plus, comme l'accent est mis sur le modèle, les contrôles Qt Quick sont configurés pour utiliser le style universel sur toutes les plateformes afin de garantir un comportement identique de l'interface utilisateur.

import QtQuick
import QtQuick.Controls.Universal

Fonctionnement

La logique commerciale de fourniture des données de la liste des chansons est séparée dans la classe DataStorage qui fournit une interface simple basée sur l'ID pour le modèle.

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

Lorsque le modèle demande des données au DataStorage, celui-ci vérifie d'abord s'il dispose déjà des données. Si c'est le cas, les données sont renvoyées instantanément, comme c'est le cas dans un modèle non filé. Si les données ne sont pas trouvées, le DataStorage émet un signal dataFetchNeeded() à l'intention de l'objet worker et ajoute un élément vide à la liste des données déjà existantes. L'ajout d'un élément vide garantit qu'aucun autre signal ne sera envoyé à l'agent pour le même élément de la liste.

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

QueueWorker - l'objet thread worker - traite les signaux dataFetchNeeded() qu'il a reçus en s'envoyant un signal à lui-même, ce qui permet de recevoir tous les signaux déjà présents dans QEventQueueue avant de lancer l'opération de lecture lente des données.

Application de l'approche aux modèles dynamiques

Si l'on souhaite étendre la solution à un cas où des éléments peuvent être ajoutés, déplacés ou supprimés de la source de données (dans ce cas, RemoteMedia), DataStorage doit être mis à jour avec des signaux correspondant à QAbstractItemModel::rowsMoved(), QAbstractItemModel::rowsInserted() et deux signaux pour déclencher QAbstractItemModel::beginRemoveRows() et QAbstractItemModel::endRemoveRows() à l'intérieur de ThreadedListModel.

Pour l'insertion et le déplacement, le ThreadedListModel peut simplement appeler QAbstractItemModel::beginInsertRows(), puis ajouter de nouveaux ID à sa liste d'ID et appeler QAbstractItemModel::endInsertRows(). Comme ThreadedListModel détient une copie de la liste d'identifiants et accède au stockage par identifiant, il n'est pas nécessaire de signaler le début et la fin du stockage. De même, ThreadedListModel peut appeler QAbstractItemModel::beginMoveRows(), déplacer des ID dans sa liste d'ID, puis appeler QAbstractItemModel::endMoveRows().

La suppression est un cas légèrement plus complexe. La vue doit avoir la possibilité de demander les données qui vont être supprimées avant qu'elles ne le soient réellement. DataStorage doit donc signaler un avertissement de suppression, ce qui amène le modèle à appeler QAbstractItemModel::beginRemoveRows(). À ce stade, ThreadedListModel peut recevoir un ou plusieurs appels data(). Une fois que l'appel au signal connecté direct revient à DataStorage, il est normal que DataStorage supprime l'élément et signale à nouveau au modèle un autre signal qui déclenche l'appel du modèle à QAbstractItemModel::endRemoveRows().

Exécution de l'exemple

Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.

Exemple de projet @ 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.