模型和视图:使用工作线程获取数据的列表模型

演示如何使用工作线程获取数据来实现具有响应式用户界面的列表模型。

带有专辑封面、歌曲名称、艺术家姓名和专辑名称的歌曲列表截图

本示例介绍了一个自定义项目模型,该模型继承于QAbstractListModel 。该模型从工作线程对象获取数据,工作线程对象位于单独的QThread 中,从慢速数据源获取数据。

线程歌曲列表示例概述

数据源模拟慢速数据源,每从慢速数据源获取一首歌曲会增加 100 毫秒的延迟。这意味着加载整个 3600 首歌曲列表需要 6 分钟,使应用程序的打开变得不切实际。通过使用放置在工作线程中的QObject ,只获取视图可见区域的数据,可以减少这种延迟。

工作线程对象对其队列中的获取请求数量有限制。这样可以确保只获取歌曲列表当前可见部分的元素,而无需在用户已滚动过列表的某些部分时等待列表的非可见部分加载。

本例的重点在于视图的源模型。视图本身是一个未修改的 QMLListView ,带有一个简单的委托。线程的使用隐藏在模型数据处理的实现之后,ListView 不需要任何定制就能适应基于线程的模型。

此外,由于重点是模型,Qt Quick Controls 在所有平台上都设置为使用通用样式,以确保相同的用户界面行为。

import QtQuick
import QtQuick.Controls.Universal

工作原理

提供歌曲列表数据的业务逻辑被分离到DataStorage 类中,该类为模型提供了一个基于 ID 的简单接口。

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

当模型向 DataStorage 请求数据时,DataStorage 会首先检查其是否拥有可用数据。如果有,就会立即返回数据,就像在非线程模型中一样。如果找不到数据,DataStorage 会向 Worker 对象发出dataFetchNeeded() 信号,并在已有数据列表中添加一个空项。添加空项可确保不会再为同一列表项向 Worker 发送信号。

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

QueueWorker - 工作线程对象--通过向自身发送一个信号来处理已收到的 dataFetchNeeded() 信号,这样就可以在开始慢速数据读取操作前接收 QEventQueue 中已存在的所有信号。

将该方法应用于动态模型

如果希望将解决方案扩展到从数据源(本例中为 RemoteMedia)中添加、移动或删除项目的情况,则需要用信号更新 DataStorage,以匹配QAbstractItemModel::rowsMoved() 和QAbstractItemModel::rowsInserted() 以及两个信号来触发 ThreadedListModel 内部的QAbstractItemModel::beginRemoveRows() 和QAbstractItemModel::endRemoveRows() 。

对于插入和移动,ThreadedListModel 只需调用QAbstractItemModel::beginInsertRows() ,然后在其 ID 列表中添加新 ID 并调用QAbstractItemModel::endInsertRows() 即可。由于 ThreadedListModel 拥有 ID 列表的副本,并通过 ID 访问存储空间,因此无需从存储空间发出开始和结束信号。同样,ThreadedListModel 可以调用QAbstractItemModel::beginMoveRows() ,移动 ID 列表中的 ID,然后调用QAbstractItemModel::endMoveRows() 。

移除的情况稍微复杂一些。视图需要在实际移除数据之前请求将被移除的数据。因此,DataStorage 需要发出删除警告信号,导致模型调用QAbstractItemModel::beginRemoveRows() 。在此阶段,ThreadedListModel 可能会收到一个或多个data() 调用。一旦 DataStorage 返回直接连接信号的调用,DataStorage 就可以移除项目,然后再次向模型发出信号,触发模型调用QAbstractItemModel::endRemoveRows() 。

运行示例

运行示例 Qt Creator,打开Welcome 模式并从Examples 中选择示例。更多信息,请参阅Qt Creator: 教程:构建并运行

示例项目 @ code.qt.io

© 2025 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.