"parent" 的概念在 QAbstractListModel 中有何意义?

How is the concept of a "parent" meaningful in a QAbstractListModel?

我有一个 QAbstractListModel 的子类,它是 QML ListView 的模型(使用 PySide6)。列表的每一行都有一个复选框,当用户 checks/unchecks 一个框时,它会使用我对 setData() 的覆盖更新列表模型该行中的布尔值,这按预期工作。我还有 Button 应该 select/clear 列表中的所有复选框。

我的模型子类为 select all 提供了以下方法,当用户点击 Button 或应用程序中发生其他事情时可以调用该方法:

def select_all(self):
    for i in range(self.rowCount()):
        row = self._rows[i]
        row['selected'] = True

        # Emitting this for each row works...
        self.dataChanged.emit(self.index(i), self.index(i), [])
        
    # ... whereas emitting just one signal for ALL rows does NOT work
    # self.dataChanged.emit(self.index(0), self.index(self.rowCount()), [])

正如您从评论中看到的那样,我需要为每一行发出 dataChanged 以便更新 ListView 中的复选框。发出一次信号并使用 topLeftbottomRight 参数 不会 更新 ListView(但模型数据正确更新)。

根据文档,QAbstractItemModel 提供的 dataChanged 信号(并由 QAbstractListModel 继承)具有 this caveat:

If the items are of the same parent, the affected ones are those between topLeft and bottomRight inclusive. If the items do not have the same parent, the behavior is undefined.

我似乎 运行 遇到了模型中的行不具有相同 parent 的场景,因此,“行为未定义”。我想这是有道理的,因为我从来没有做任何事情来为我的任何行建立 parent/child 关系。我还看到 这意味着当 topLeftbottomRight 索引相同时 Qt 的行为不同。所以,我想更好地理解这一点,我有几个问题:

  1. 我是否更正了这个 parent 概念是 在为每一行发出时 起作用的原因 不是对所有行都有效吗?
  2. parent/child 关系的概念是否仅对扩展 QAbstractItemModel 而不是 QAbstractListModel 的 tree-like 模型有意义?它对列表有意义吗?
  3. 如果它对列表有意义,那么“parent”和“child”是什么?我将如何配置 QAbstractListModel 的子类,以便可以发出一次 dataChanged 来更新多行?

谢谢!

虽然 Qt 项视图的基本实现只是在 topLeftbottomRight 索引不匹配时不加区别地更新视图(因此您可以只提供两个“随机”,但仍然 sibling 索引),索引不仅必须共享一个共同的父级(对于一维和二维模型始终是无效索引),而且必须 valid.

加上这一行,第二个索引有效:

self.dataChanged.emit(self.index(0), self.index(self.rowCount()), [])

这是因为索引总是从 0 开始,最后一行的索引实际上是 rowCount - 1。虽然理论上它们是兄弟姐妹,但由于它们共享相同的父级(根索引的父级无效,因为它是无效索引的父级),它们不是 both 有效。

所以,正确的语法是:

self.dataChanged.emit(self.index(0), self.index(self.rowCount() - 1), [])
                                                               ^^^^

请注意,roles 参数是可选的,并且已经默认为一个空列表(C++ 中的 QVector),因此除非您真的想指定已更改的角色,否则您不需要提供它。