Sur cette page

Modèles et vues : modèle de liste utilisant un thread de travail pour la récupération des données

Cet exemple montre comment implémenter un modèle de liste avec une interface utilisateur réactive en utilisant un thread de travail pour récupérer les données.

Capture d'écran de l'application montrant une liste de titres avec les pochettes d'album, les titres des morceaux, les noms des artistes et les titres des albums

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 travail situé dans un autre ` QThread`, qui récupère les données depuis une source de données lente.

Présentation de l’exemple « Liste de morceaux avec threads »

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 3 600 chansons prendrait 6 minutes, ce qui rendrait l’ouverture de l’application impraticable. Ce délai est atténué en ne récupérant que les données correspondant à la zone visible de la vue, à l’aide d’un objet « QObject » placé dans un thread de travail.

L’objet « worker » est soumis à une limite quant au nombre de requêtes de récupération qu’il peut contenir dans sa file d’attente. Cela garantit que seuls les éléments de la partie actuellement visible de la liste des morceaux sont récupérés, ce qui évite d’avoir à attendre le chargement de la partie non visible de la liste lorsque l’utilisateur a déjà fait défiler une partie de celle-ci.

Cet exemple met l’accent sur le modèle source de la vue. La vue elle-même est une ListView QML non modifiée dotée d’un simple délégué. L’utilisation du thread est masquée derrière l’implémentation de la gestion des données du modèle et l’ ListView n’a pas besoin d’être personnalisée pour s’adapter au modèle basé sur les threads.

De plus, comme l’accent est mis sur le modèle, l’ Qt Quick Controls est configurée pour utiliser le style « Universal » sur toutes les plateformes afin de garantir un comportement identique de l’interface utilisateur.

import QtQuick
import QtQuick.Controls.Universal

Fonctionnement

La logique métier consistant à fournir les données de la liste des morceaux est séparée dans une classe d’ DataStorage qui offre une interface simple basée sur des identifiants 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 à DataStorage, ce dernier vérifie d’abord s’il dispose déjà de ces données. Si c’est le cas, les données sont renvoyées instantanément, comme ce serait le cas dans un modèle sans threads. Si les données ne sont pas trouvées, DataStorage émet un signal « dataFetchNeeded() » vers l’objet worker et ajoute un élément vide à la liste des données déjà existantes. L’ajout de cet élément vide garantit qu’aucun autre signal ne sera envoyé au worker 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 du worker traite les signaux `dataFetchNeeded()` qu’il a reçus en s’envoyant un signal à lui-même, ce qui lui permet de recevoir tous les signaux déjà présents dans `QEventQueue` avant de lancer l’opération de lecture de données, qui est lente.

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()` et ` QAbstractItemModel::rowsInserted()`, ainsi que deux signaux pour déclencher les méthodes ` QAbstractItemModel::beginRemoveRows()` et ` QAbstractItemModel::endRemoveRows()` au sein de `ThreadedListModel`.

Pour l’insertion et le déplacement, le ThreadedListModel peut simplement appeler QAbstractItemModel::beginInsertRows(), puis ajouter les nouveaux ID à sa liste d’ID et appeler QAbstractItemModel::endInsertRows(). Comme ThreadedListModel conserve 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 la même manière, ThreadedListModel peut appeler QAbstractItemModel::beginMoveRows(), déplacer des identifiants dans sa liste d’identifiants, puis appeler QAbstractItemModel::endMoveRows().

La suppression est un cas légèrement plus complexe. La vue doit pouvoir demander les données qui vont être supprimées avant qu’elles ne le soient effectivement. 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 directement connecté revient à DataStorage, celui-ci peut supprimer l’élément, puis signaler à nouveau le modèle à l’aide d’un autre signal qui déclenche l’appel de ` QAbstractItemModel::endRemoveRows()` par le modèle.

Exécution de l’exemple

Vous pouvez exécuter l’exemple à partir de :

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