何时从QAbstractItemModel发出dataChanged [英] When to emit dataChanged from a QAbstractItemModel
问题描述
在Qt中,我有一个模型子类
In Qt, I have a model subclassing QAbstractItemModel
- it's a tree displayed in a QTreeView.
模型支持各种形式的更改,这些更改都可以正常运行.相关的两个是:
The model supports various forms of change which all work OK. The two of relevance are:
1)少数相关行中的某些数据发生了变化
1) Some data in a small number of related rows changes
2)可视化更改意味着大多数行应更改其格式-尤其是其背景突出显示的更改.他们的DisplayRole
数据不变.
2) A visualisation change means that the majority of rows should change their formatting - in particular they have a change of background highlighting. Their DisplayRole
data does not change.
当前的设计以相同的方式处理这两个问题:每行有任何更改的模型都会发出dataChanged(start_of_row_index,end_of_row_index)
.我会为所有更改的父行及其所有已更改的子级发出信号.
The current design deals with both of these in the same way: for every row that has any change the model emits dataChanged(start_of_row_index,end_of_row_index)
. I emit the signal for both parent rows that change and for any of their children that have changed.
但是,由于模型变大,这种情况在情况2中表现不佳:发出了大量dataChanged
信号.
However, this performs badly in case 2 as the model gets big: a very large number of dataChanged
signals are emitted.
我更改了代码,以便在情况2中,模型仅针对作为整个树的父级的(单)行发出dataChanged
.
I have changed the code so that in case 2 the model emits dataChanged
only for the (single) row that is the parent of the entire tree.
这似乎仍然可以正常工作,但是不符合我对模型职责的理解.但是我怀疑我可能是错的.
This still appears to work correctly but does not accord with my understanding of the responsibilities of the model. But I suspect I may be wrong.
也许我误会了dataChanged
信号?它是否实际上导致视图更新所有子级以及指定范围?还是在DisplayRole
不变的情况下避免发出dataChanged
?
Perhaps I am misunderstanding the dataChanged
signal? Does it actually cause the view to update all children as well as the specified range? Or can I avoid emitting dataChanged
when it is not the DisplayRole
that is changing?
正如Jan所指出的,在情况2中,我应该为大多数或所有行发出dataChanged
.
As Jan points out, I ought to emit dataChanged
either for most or all of the rows in case 2.
我的代码最初是通过为每个更改的行发出dataChanged
来完成此操作的,但这太昂贵了-视图花费太长时间来处理所有这些信号.
My code originally did this by emitting dataChanged
for every changed row but this is too expensive - the view takes too long to process all these signals.
一种可能的解决方案是为更改行的任何连续块聚合dataChanged
信号,但是例如在每隔一行更改后,它仍然不能很好地工作-它仍然会发出太多信号.
A possible solution could be to aggregate the dataChanged
signal for any contiguous blocks of changed rows but this will still not perform well when, for example, every other row has changed - it would still emit too many signals.
理想情况下,我只想告诉视图将所有数据视为可能已更改(但所有索引仍然有效-布局不变).用单个信号似乎不可能做到这一点.
Ideally I would like to just tell the view to consider all data as potentially changed (but all indexes still valid - the layout unchanged). This does not seem to be possible with a single signal.
由于QTreeView
类的怪癖,可能会(尽管根据规范是不正确的)仅发出一个dataChanged(tl,br
),只要发出tl != br
的时间即可.我进行了这项工作,它通过了我们的测试,但让我感到紧张.
Because of a quirk of the QTreeView
class, it is possible (though incorrect according to the spec) to emit only one dataChanged(tl,br
) as long as tl != br
. I had this working and it passed our testing but left me nervous.
我现在已经确定了一个版本,该版本可以遍历树并为每个父对象发出一个单独的dataChanged(tl,br)
(tl,br跨越该父对象的所有子对象).这符合模型/视图协议,对于我们的模型,通常会将信号数量减少大约10倍.
I have settled for now on a version which traverses the tree and emits a single dataChanged(tl,br)
for every parent (with tl,br spanning all the children of that parent). This conforms to the model/view protocol and for our models it typically reduces the number of signals by about a factor of 10.
但是,这似乎并不理想.还有其他建议吗?
It does not seem ideal however. Any other suggestions anyone?
推荐答案
每当更改任何数据时,您都应该告知您的观点.这种告知"可以通过多种方式发生.当索引的结构未更改时,发出dataChanged
是最常见的一种.其他的是严重"的,例如modelReset
或layoutChanged
.巧合的是,即使在没有dataChanged
的情况下,Qt的某些视图也能够获取更改.鼠标悬停,但您不应该依赖它.这是一个实现细节,并且有待更改.
You are expected to let your views know whenever any data gets changed. This "letting know" can happen through multiple ways; emitting dataChanged
is the most common one when the structure of the indexes has not changed; others are the "serious" ones like modelReset
or layoutChanged
. By a coincidence, some of the Qt's views are able to pick up changes even without dataChanged
on e.g. a mouseover, but you aren't supposed to rely on that. It's an implementation detail and a subject to change.
要回答您问题的最后一部分,是的,只要从QAIM::data()
返回的任何数据发生更改,就必须发出dataChanged
,即使它只是"Qt::DisplayRole
"以外的其他角色.
To answer the final bit of your question, yes, dataChanged
must be emitted whenever any data returned from the QAIM::data()
changes, even if it's "just" some other role than Qt::DisplayRole
.
您在引用性能问题.困难的数字是什么-您实际上是否有任何可衡量的减速,还是只是过早担心这可能会在以后成为问题?您是否知道可以同时使用dataChanged
的两个参数来表示对大型索引矩阵进行更改的事实?
You're citing performance problems. What are the hard numbers -- are you actually getting any measurable slowdown, or are you just prematurely worried that this might be a problem later on? Are you aware of the fact that you can use both arguments to the dataChanged
to signal a change over a big matrix of indexes?
还有更多尝试的方法:
-
确保您的视图不请求其他数据.例如,除非您设置
QTreeView
的uniformRowHeights
(IIRC),否则视图将必须为每个dataChanged
信号执行O(n)调用,从而导致O(n ^ 2)复杂性.不好.
Make sure that your view does not request extra data. For example, unless you set the
QTreeView
'suniformRowHeights
(IIRC), the view will have to execute O(n) calls for eachdataChanged
signal, leading to O(n^2) complexity. That's bad.
如果您真的确定没有办法解决此问题,则可以通过组合layoutAboutToBeChanged
,updatePersistentIndexes
和layoutChanged
来可能.由于您实际上并未更改索引的结构,因此这可能会很便宜.但是,上一点中的优化机会仍然值得一试.
If you are really sure that there's no way around this, you might get away by combining the layoutAboutToBeChanged
, updatePersistentIndexes
and layoutChanged
. As you are not actually changing the structure of your indexes, this might be rather cheap. However, the optimization opportunity in the previous point is still worthwhile taking.
这篇关于何时从QAbstractItemModel发出dataChanged的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!