QAbstractItemModel 和 QModelIndex 之间的相互作用

Interplay between QAbstractItemModel and QModelIndex

以下问题是关于 QAbstractItemModelQModelIndex 类 的设计以及它们之间的相互作用,如下面的代码示例所强调:

class Data:
    def __init__(self):
        self.value = 42

class Model( QAbstractItemModel ):
    def __init__( self ):
        QAbstractItemModel.__init__(self)
        data = Data()

        modelIndex = self.createIndex( 1 , 2 , data ) ### 1
        self.index( 1 , 2 , QModelIndex() ) ### 2
        self.setData( modelIndex , data.value ) ### 3
        self.dataChanged.emit( modelIndex , modelIndex )

        modelIndex.data() ###4
        self.data( modelIndex ) ### 5
  1. 应该如何创建QModelIndex。根据我对文档的阅读,答案是 QAbstractItemModel::createIndex() but it seems incomplete because this function does not supply any information about the offset of the ModelIndex from its parent. Rather this is done by QAbstractItemModel::index()。有没有办法让这两个功能一起玩?
  2. 数据应该如何存储在模型索引中,以及由模型索引、为模型索引或在模型索引中(不确定术语)存储的数据与 internal pointer? And where does the model index get the data 它 returns 什么时候没有 setData 函数?内部指针曾经是数据吗?它可以成为数据吗?
  3. ModelIndex 和 Model 编辑的数据 return 有什么区别?即 QModelIndex::data() and QAbstractItemModel::data( QModelIndex , int )? And why is the setter QAbstractItemModel::setData( QModelIndex , ... ) merely virtual but the getter QAbstractItemModel::data( QModelIndex , ... ) 纯虚拟。当然 API 应该能够 return 它存储的数据。

我知道我的问题链接到 C++ API 而片段在 PySide 中。我这样做是因为这个问题跨越了 APIs.

考虑到您有很多与 QAbstractItemModel 和 QModelIndex 相关的问题,涉及实施和其他设计问题,我将花时间一一回复,因此我将编辑此答案以提供更多详细信息。

1.

QModelIndex没有其父级是谁的信息,也没有指示的偏移量的信息,它只保留行、列、指向数据的指针和它所属的模型的信息(见here). So as you know who your parent is, that is your task, you must override the parent() 模型的方法和 return 那.

def parent(self, index):
    if not index.isValid():
        return QtCore.QModelIndex()
    childItem = index.internalPointer()
    parentItem = childItem.parentItem()
    if parentItem == self.rootItem:
        return QtCore.QModelIndex()
    return self.createIndex(..., ..., parentItem)

所以在parent() method of the QModelIndex this one gets the parent through the model(see here): def parent(self) : return self.model.parent(self).

关于index() and createIndex()方法之间的关系,第二个被第一个调用,也必须和你的数据结构有关系。通用实现是:

def index(self, row, column, parent):
    if not self.hasIndex(row, column, parent):
        return QtCore.QModelIndex()
    parentItem = None
    if not parent.isValid():
        parentItem = self.rootItem 
    else:
        parentItem = parent.internalPointer()
    childItem = parentItem.child(...)
    if childItem is not None:
        return self.createIndex(row, column, childItem)
    return QtCore.QModelIndex()

在 createIndex() 的情况下,仅使用行、列、模型和指向 childItem 的指针的信息创建 QModelIndex,该构造函数不在文档中,因为它是私有构造函数(参见 here).

2.

internalPointer是存储数据内存位置的变量,即QModelIndex没有数据但知道在哪,所以数据必须单独存储,所以在获取数据时使用模型的方法 data() 您必须获取 return 存储信息的项目的 internalPointer(),并根据该角色获取数据。

def data(self, index, role=QtCore.Qt.DisplayRole):
    if not index.isValid():
        return
    item = index.internalPointer()
    value = item.data(..., role) # like item.value
    return value

3.

QModelIndex的data()方法使用模型的data()方法(见这里),所以从概念上讲,它们是相同的,例如:def data(self, role): return self.model.data(self, role).

并非每个模型都是可编辑的,例如 QStringListModel 不是,因此没有必要覆盖 setData() 方法,因此 Qt 默认情况下使模型不可编辑,也就是说,它们什么都不做,并且 return false(参见 这里 ),所以这意味着它必须是 virtual,即,这个方法只有在没有被覆盖的情况下才会被修改,如果你不这样做,它会调用父类的方法,也就是什么都不做。与 data() 方法不同,因为每个模型都必须 return 信息,因此当它从 QAbstractItemModel 继承时,开发人员必须强制覆盖 class,因此它被声明为 纯虚拟。我建议您阅读以便更详细地了解差异:C++ Virtual/Pure Virtual Explained

虽然@eyllanesc 的回答是正确的,但我一直在努力理解它,直到我长时间盯着 this article 才出现一种模式。因此,我以我认为比我提问的顺序更合乎逻辑的方式为他的回答做出贡献。

  1. 尽管顾名思义,QAbstractItemModel 最好理解为模型数据的接口。通常,模型数据的根是 QAbstractItemModel 对象的成员,它充当模型数据的各种包装器。 (例如,如果数据存储在 SQL 数据库中,则需要采用不同的方法。)QAbstractItemModel 还:

    • 定义数据组件之间的(分层)关系。

    • 提供用于在模型中添加和删除数据行和列的函数。 (这一事实是理解 QModelIndex 的使用方式的关键。)

  2. QModelIndex 有很多东西,但最重要的是它包含 指向每个数据组件的内部指针,除了一些 关于 current 数据组件在 数据层次结构(位置可以改变)。现在应该清楚为什么文档声明:

Model indexes should be used immediately and then discarded. You should not rely on indexes to remain valid after calling model functions that change the structure of the model or delete items.

这也是为什么 QAbstractItemModel 的成员函数 return QModelIndex(例如 QModelIndex::index() and QModelIndex::parent() ) have to create a fresh one everytime, using QAbstractItemModel::createIndex().

最后,正如@eyllanesc 所说,除非数据是可编辑的,否则不会强制执行 setter QAbstractItemModel::setData( QModelIndex , value, role )