简体   繁体   English

QAbstractItemModel 和 QModelIndex 之间的相互作用

[英]Interplay between QAbstractItemModel and QModelIndex

The following questions are about the design of the QAbstractItemModel and QModelIndex classes and the interplay between them, as highlighted by the sample of code below:以下问题是关于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. How should a QModelIndex be created. QModelIndex应该如何创建。 From my reading of the docs the answer is QAbstractItemModel::createIndex() but it seems incomplete because this function does not supply any information about the offset of the ModelIndex from its parent.根据我对文档的阅读,答案是QAbstractItemModel::createIndex()但它似乎不完整,因为此函数不提供有关 ModelIndex 与其父项的偏移量的任何信息。 Rather this is done by QAbstractItemModel::index() .相反,这是由QAbstractItemModel::index() 完成的 Is there a way to make both functions play together?有没有办法让这两个功能一起玩?
  2. How should data be stored in or the model index and what is the difference between the data stored by, for, or in (unsure of the terminology) the model index and the internal pointer ?数据应该如何存储在模型索引中,模型索引和内部指针存储的数据之间有什么区别? And where does the model index get the data that it returns when there it has no setData function?当模型索引没有 setData 函数时,它从哪里获取它返回的数据 Is the internal pointer ever the data?内部指针是数据吗? Can it ever be the data?它可以是数据吗?
  3. What is the difference between the data returned by the ModelIndex and the Model? ModelIndex和Model返回的数据有什么区别? ie QModelIndex::data() and QAbstractItemModel::data( QModelIndex , int ) ?QModelIndex::data()QAbstractItemModel::data( QModelIndex , int ) And why is the setter QAbstractItemModel::setData( QModelIndex , ... ) merely virtual but the getter QAbstractItemModel::data( QModelIndex , ... ) pure virtual.为什么 setter QAbstractItemModel::setData( QModelIndex , ... )只是虚拟的,而 getter QAbstractItemModel::data( QModelIndex , ... ) 是纯虚拟的。 Surely the API should be able to return the data that it stored.当然,API 应该能够返回它存储的数据。

I am aware that my question links to the C++ API whereas the snippet is in PySide.我知道我的问题链接到 C++ API,而代码片段在 PySide 中。 I have done so because this question cuts across both APIs.我这样做是因为这个问题跨越了两个 API。

Considering that you have many questions that are related to QAbstractItemModel and QModelIndex that involve issues of implementation and other design I will take the time to respond one by one so I will edit this answer giving more details.考虑到您有许多与 QAbstractItemModel 和 QModelIndex 相关的问题,涉及实现和其他设计问题,我将花时间一一回复,因此我将编辑此答案以提供更多详细信息。

1. 1.

QModelIndex does not have the information of who its parent is, nor of the offset that indicates, it only keeps the information of the row, column, pointer to the data and the model to which it belongs(see here ). QModelIndex 没有其父节点是谁的信息,也没有表示偏移量的信息,它只保留行、列、指向数据的指针和它所属的模型的信息(参见此处)。 So as you know who your parent is, that is your task, you must override the parent() method of the model and return that.因此,当您知道您的父母是谁时,这是您的任务,您必须覆盖模型的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)

So in the parent() method of the QModelIndex this one gets the parent through the model(see here ): def parent(self) : return self.model.parent(self) .因此,在 QModelIndex 的parent()方法中,此方法通过模型获取父级(参见此处): def parent(self) : return self.model.parent(self)

About the relationship between the index() and createIndex() methods, the second is called by the first, but also must have a relationship with your data structure.关于index()createIndex()方法之间的关系,第二个被第一个调用,但也必须与您的数据结构有关系。 A generic implementation is:一个通用的实现是:

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()

In the case of createIndex() only creates a QModelIndex with the information of the row, the column, the model and the pointer to childItem, that constructor is not in the docs because it is a private constructor(see here ).在 createIndex() 仅使用行、列、模型和指向 childItem 的指针的信息创建 QModelIndex 的情况下,该构造函数不在文档中,因为它是私有构造函数(请参阅此处)。

2. 2.

The internalPointer is a variable that stores the memory position of the data, that is, the QModelIndex does not have the data but knows where it is, so the data must be stored separately, so when the data is obtained using the method data() of the model you must obtain the internalPointer() that returns the item where the information is stored, and according to that the role obtain the data. internalPointer是一个变量,存储数据的内存位置,即QModelIndex没有数据但是知道在哪里,所以必须单独存储数据,所以在获取数据时使用data()方法模型的必须获取internalPointer(),返回存储信息的item,角色根据这个获取数据。

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. 3.

The data() method of the QModelIndex uses the data() method of the model(see here ), so conceptually they are the same, like: def data(self, role): return self.model.data(self, role) .数据()的QModelIndex的方法使用数据()模型(见的方法在这里),所以在概念上它们是相同的,如: def data(self, role): return self.model.data(self, role) .

Not every model is editable, for example QStringListModel is not, so it is not necessary that the setData() method be overwritten, so Qt by default makes the models not editable, that is, they do nothing and return false(see here ), So that means that it must be virtual , that is, this method will only be modified if it is not overridden and if you do not do it, it will be called the parent's method, that is, it will not do anything.并非每个模型都是可编辑的,例如 QStringListModel 不是,因此没有必要覆盖setData()方法,因此 Qt 默认情况下使模型不可编辑,即它们什么都不做并返回 false(请参阅此处),所以这意味着它必须是virtual ,也就是说,这个方法只有在没有被覆盖的情况下才会被修改,如果你不这样做,它将被称为父的方法,即它不会做任何事情。 Unlike the data() method since every model must return the information so the developer must be forced to overwrite that class when it inherits from QAbstractItemModel, so it is declared pure virtual .data()方法不同,因为每个模型都必须返回信息,因此当它从 QAbstractItemModel 继承时,开发人员必须被迫覆盖该类,因此它被声明为纯 virtual I recommend you read so that you know in more detail the differences: C++ Virtual/Pure Virtual Explained我建议您阅读以便更详细地了解差异: C++ Virtual/Pure Virtual Explained

While @eyllanesc's answer is correct I struggled to understand it until I stared long and hard at this article before a pattern emerged.虽然@eyllanesc 的答案是正确的,但我一直在努力理解它,直到在出现模式之前我长时间而努力地盯着这篇文章 Therefore, I contribute to his answer in what I feel is a more logical way than the order I asked my questions.因此,我以我认为比我提出问题的顺序更合乎逻辑的方式为他的回答做出贡献。

  1. In spite of what its name suggests, QAbstractItemModel is better understood as an interface to the model data.尽管它的名字暗示,QAbstractItemModel 更好地理解为模型数据的接口。 Typically the root to the model data is a member of the QAbstractItemModel object, which acts as a wrapper of sorts for the model data.通常,模型数据的根是 QAbstractItemModel 对象的成员,该对象充当模型数据的各种包装。 (A different approach would be required, for example, if the data were stored in a SQL database.) QAbstractItemModel also: (例如,如果数据存储在 SQL 数据库中,则需要不同的方法。) QAbstractItemModel 还:

    • Defines the (hierarchical) relationship between the data components.定义数据组件之间的(分层)关系。

    • Provides functions for adding and removing rows and columns of data from the model.提供用于从模型中添加和删除数据行和列的函数。 (This fact is key to understanding how QModelIndex is used.) (这个事实是理解如何使用 QModelIndex 的关键。)

  2. QModelIndex is many things, but most importantly it contains the internal pointer to each data component, in addition to some information about current the position of the data component in the data hierarchy (the position can change). QModelIndex 有很多东西,但最重要的是它包含了每个数据组件的内部指针,此外还有一些关于当前数据组件在数据层次结构中的位置(位置可以改变)的信息。 It should now be clear why the the docs state that:现在应该清楚为什么文档说明:

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.在调用更改模型结构或删除项目的模型函数后,您不应依赖索引来保持有效。

It is for this reason also why member functions of QAbstractItemModel that return a QModelIndex (eg QModelIndex::index() and QModelIndex::parent() ) have to create a fresh one everytime , using QAbstractItemModel::createIndex() .这也是为什么返回 QModelIndex 的 QAbstractItemModel 的成员函数(例如QModelIndex::index()QModelIndex::parent() )必须每次都使用QAbstractItemModel::createIndex()创建一个新的。

Finally, as @eyllanesc said, no implementation of the setter QAbstractItemModel::setData( QModelIndex , value, role ) is mandated unless the data is editable.最后,正如@eyllanesc 所说,除非数据可编辑,否则不会强制执行 setter QAbstractItemModel::setData( QModelIndex , value, role )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM