QAbstractItemModel:为啥在向模型中插入大量项目时发出 dataChanged 和插入行信号这么慢?

Posted

技术标签:

【中文标题】QAbstractItemModel:为啥在向模型中插入大量项目时发出 dataChanged 和插入行信号这么慢?【英文标题】:QAbstractItemModel: Why emitting dataChanged and insert rows signals when inserting numerous items into model so slow?QAbstractItemModel:为什么在向模型中插入大量项目时发出 dataChanged 和插入行信号这么慢? 【发布时间】:2012-02-07 10:21:50 【问题描述】:

向 *** 中的所有 Qt 模型/视图编程大师致敬。我正在进行的项目需要我为 XML 数据定义一个 QAbstractItemModel 派生模型。我之前在此模型的实现过程中遇到问题时曾在这里问过一个问题: QSortFilterProxyModel crashes when deleting a row from the source model via the source model function

除了一些我不知道如何解决的性能问题外,该模型目前在应用程序中的工作做得很好。此中的功能之一 应用程序是通过设置 Max Num 和 Rand 数来添加 Frame 元素 xml 节点。每个 Frame 元素将具有从 0 到 MaxNum - 1 和 0 到 MaxRand-1 的 Num 和 Rand 数。此外,我需要为 Rand=0 th 和 Rand=MaxRand-1 th 元素添加一个子 Parameter 元素。略图如下:

For Max Num=100, Max Rand=50

<Frame Num="0" Rand="0">
  <Parameter Value="false"/>
 </Frame>
 <Frame Num="0" Rand="1"/>
 <Frame Num="0" Rand="2"/>
 ...
 <Frame Num="0" Rand="48"/>
 <Frame Num="0" Rand="49">
  <Parameter Value="true"/>
 </Frame>
 <Frame Num="1" Rand="0">
  <Parameter Value="false"/>
 </Frame>
 <Frame Num="1" Rand="1"/>
 <Frame Num="1" Rand="2"/>
 ...
 <Frame Num="1" Rand="48"/>
 <Frame Num="1" Rand="49">
  <Parameter Value="true"/>
 </Frame> 
...
<Frame Num="99" Rand="0">
  <Parameter Value="false"/>
 </Frame>
 <Frame Num="99" Rand="1"/>
 <Frame Num="99" Rand="2"/>
 ...
 <Frame Num="99" Rand="48"/>
 <Frame Num="99" Rand="49">
  <Parameter Value="true"/>
 </Frame>

当 MaxNum 和 MaxRand 都很小时,即 Frame 元素的总数约为 50 时,应用程序运行良好。但是,当元素数超过 500 左右时,通过 QAbstractItemModel 函数添加 Frame 元素变得痛苦且呈指数级缓慢。 GUI 冻结了很长一段时间,以至于我大部分时间都放弃了等待。

重大更新:

我发现在我的模型中插入大量(例如 1000 个)项目时导致速度变慢的原因。我对插入的每个项目都调用了一次 insertRow(row, parent) 和 setData(),由于这些函数内部发出的信号,这变得非常昂贵。

我已将代码修改为 insertRows(row, 1000, parent),并定义了一个不发出 dataChanged(currIndex, currIndex) 的新silentSetData()。我还包含了一个名为 notifyDataChanges(startRow, endRow, parentIndex) 的新公共函数,它依次为给定的数据范围发出一次 dataChanged 信号。一切都很好,但现在调用者类需要在插入整个数据集后显式调用 notifyDataChanges。

现在我想知道为什么发出 dataChanged 信号如此昂贵?当一次在模型中插入/更改大量项目时,有没有人尝试过其他方法来解决这个问题?

【问题讨论】:

【参考方案1】:

另一种方法是显式重置模型数据结构。例如,在我的一个模型中,数据用QStringList 表示。我已经实现了一个重置​​模型中数据的函数。例如:

void MyModel:setNewData(const QStringList newList) 

  beginResetModel();

  m_dataList = newList;

  endResetModel();

我想这种方法也适用于您的情况。

【讨论】:

我知道 beginResetModel。我已经使用它来将 XML 文件初始加载到模型中。我的印象是不应该频繁使用模型重置,因为它会重置所有附加视图并可能减慢 GUI,但似乎我错了。 根据您的建议,我添加了不发出任何 dataChanged()、ratbi()、ratbr() 或其他 layoutChanged 信号的 insertRows、setData 和 removeRows 版本。然后我让调用类在开始和结束重置模型之间调用这些(我称之为)无声版本的 remove、insert 和 setdata 来执行批量插入操作。速度明显提高!稍后我将在我的问题的答案帖子中将详细信息发布到我的解决方案中以供参考。无论如何,您的答案将被标记为已接受。谢谢! 谢谢。我想每次在大型数据集上调用 setData 时,事件系统都会被信号淹没。

以上是关于QAbstractItemModel:为啥在向模型中插入大量项目时发出 dataChanged 和插入行信号这么慢?的主要内容,如果未能解决你的问题,请参考以下文章

Qt入门教程数据模型篇QAbstractItemModel抽象模型基类

QAbstractItemModel Class

覆盖 QAbstractItemModel::index 并访问 std::map

QMetaObject 的 QAbstractItemModel,我必须自己写吗?

QAbstractItemModel data() 永远不会被调用

QTreeView 的 QAbstractItemModel:我做错了啥?