QAbstractItemModel 和 QModelIndex 之间的相互作用



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

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(),但它似乎不完整,因为此函数没有提供有关ModelIndex与其父级偏移量的任何信息。相反,这是由QAbstractItemModel::index()完成的。有没有办法让这两个功能一起玩?
  2. 数据应如何存储在模型索引中或模型索引中,以及模型索引和内部指针
  3. 存储的数据、为模型索引存储的数据或存储在模型索引和内部指针中(不确定术语)之间有什么区别?当模型索引没有 setData 函数时,它从哪里获得它返回的数据?内部指针是否曾经是数据?它能成为数据吗?
  4. ModelIndex 和 Model 返回的数据有什么区别?即 QModelIndex::d ata() 和 QAbstractItemModel::d ata( QModelIndex , int )?为什么 setter QAbstractItemModel::setData( QModelIndex , ... ) 只是虚拟的,而 getter QAbstractItemModel::d ata( QModelIndex , ... ) 纯虚拟的。当然,API 应该能够返回它存储的数据。

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

考虑到您有许多与QAbstractItemModel和QModelIndex相关的问题,这些问题涉及实现和其他设计问题,我将花时间一一回答,因此我将编辑此答案以提供更多细节。

1.

QModelIndex 没有其父级是谁的信息,也没有指示的偏移量的信息,它只保留行、列、指向数据的指针和它所属的模型的信息(见这里)。因此,当您知道您的父级是谁时,这是您的任务,您必须覆盖模型的 parent() 方法并返回它。

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)

所以在 QModelIndex 的 parent() 方法中,这个方法通过模型获取父级(见这里):def parent(self) : return self.model.parent(self)

关于 index() 和 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()的情况下,只创建一个QModelIndex,其中包含行,列,模型和指向childItem的指针的信息,该构造函数不在文档中,因为它是一个私有构造函数(见这里)。

阿拉伯数字。

internalPointer 是存储数据内存位置的变量,也就是说 QModelIndex 没有数据但知道数据在哪里,所以数据必须单独存储,所以当使用模型的方法 data() 获取数据时必须获取返回存储信息的项的 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默认让模型不可编辑,也就是说它们什么都不做,返回false(见这里),所以这意味着它必须是虚拟的,也就是说,这个方法只有在不被覆盖的情况下才会被修改,如果你不这样做, 它将被称为父方法,也就是说,它不会做任何事情。与 data() 方法不同,因为每个模型都必须返回信息,因此开发人员在继承自 QAbstractItemModel 时必须强制覆盖该类,因此它被声明为纯虚拟。我建议您阅读,以便您更详细地了解差异: C++虚拟/纯虚拟解释

虽然@eyllanesc的回答是正确的,但我很难理解它,直到我盯着这篇文章看了很长时间,才出现了一个模式。因此,我以我认为比我提问的顺序更合乎逻辑的方式为他的回答做出贡献。

  1. 不管它的名字是什么,QAbstractItemModel最好被理解为模型数据的接口。通常,模型数据的根是 QAbstractItemModel 对象的成员,该对象充当模型数据的包装器。(例如,如果数据存储在 SQL 数据库中,则需要使用不同的方法。QAbstractItemModel 还:

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

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

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

模型索引应立即使用,然后丢弃。你 不应依赖索引在调用模型后保持有效 更改模型结构或删除项的函数。

这也是为什么返回 QModelIndex 的 QAbstractItemModel 成员函数(例如 QModelIndex::index() 和 QModelIndex::p arent() )每次都必须使用 QAbstractItemModel::createIndex() 创建一个新的函数

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

最新更新