Search code examples
pythonpysideqabstractitemmodelqmodelindex

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:

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. 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. Rather this is done by 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? 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? i.e. QModelIndex::data() and QAbstractItemModel::data( QModelIndex , int )? And why is the setter QAbstractItemModel::setData( QModelIndex , ... ) merely virtual but the getter QAbstractItemModel::data( QModelIndex , ... ) pure virtual. Surely the API should be able to return the data that it stored.

I am aware that my question links to the C++ API whereas the snippet is in PySide. I have done so because this question cuts across both APIs.


Solution

  • While @eyllanesc's answer is correct I struggled to understand it until I stared long and hard at this article before a pattern emerged. 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. 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. (A different approach would be required, for example, if the data were stored in a SQL database.) QAbstractItemModel also:

      • 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.)

    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). 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 (e.g. QModelIndex::index() and QModelIndex::parent() ) have to create a fresh one everytime, using QAbstractItemModel::createIndex().

    Finally, as @eyllanesc said, no implementation of the setter QAbstractItemModel::setData( QModelIndex , value, role ) is mandated unless the data is editable.