1

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

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

4

2 回答 2

3

考虑到您有许多与 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() 的情况下,仅使用行、列、模型和指向 childItem 的指针的信息创建 QModelIndex,该构造函数不在文档中,因为它是私有构造函数(参见此处)。

2.

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(参见此处),所以这意味着它必须是virtual,也就是说,这个方法只有在它没有被覆盖的情况下才会被修改,如果你不这样做,它将被称为父方法,即它不会做任何事情。与data()方法不同,因为每个模型都必须返回信息,因此当它继承自 QAbstractItemModel 时,开发人员必须强制覆盖该类,因此它被声明为纯虚拟。我建议您阅读,以便您更详细地了解差异: C++ 虚拟/纯虚拟解释

于 2019-07-21T00:11:02.873 回答
0

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

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

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

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

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

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

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

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

于 2019-07-24T21:00:25.057 回答