9

It is required to implement QAbstractItemModel.parent() method or else get this nasty error:

NotImplementedError: QAbstractItemModel.parent() is abstract and must be overridden

Aside from .parent() the index() method also needs to be overridden or face:

NotImplementedError: QAbstractItemModel.index() is abstract and must be overridden

QUESTION: What are the purpose of both methods and what is the difference in the way they work?

EDITED LATER:

Example of .parent() method:

def getNodeFromIndex(self, index):    
    if index.isValid():
        node = index.internalPointer()
        if node:
            return node            
    return self.items


def parent(self, index):
    node = self.getNodeFromIndex(index)
    parentNode = node.getParent()
    if parentNode == self.items:
        return QtCore.QModelIndex()
    return self.createIndex(parentNode.row(), 0, parentNode)

Example on .index() method:

def index(self, row, column, parentIndex):
    parentNode = self.getNodeFromIndex(parentIndex)
    childNode = parentNode.getChildren(row)
    if childNode:            
        newIndex=self.createIndex(row, column, childNode)
        return newIndex
    else:
        return QtCore.QModelIndex()

From endless testing I do see that .parent() method is called only on top level QTableView items. While .index() is called for all the items: top-level, second-level children items, third-level grand-children items and etc. I also do see that both return QModelIndex with row, column and data variable "linked" to it. It looks like the QModelIndexes returned by both methods should be in sync.

The .parent() returns parent of model item with given index. If item has no parent, an invalid QModelIndex is returned. A common convention used in models that expose tree data structures is that only items in first column have children. For that case, when reimplementing this function in a subclass the column of the returned QModelIndex would be 0. When reimplementing this function in a subclass, be careful to avoid calling QModelIndex member functions, such as QModelIndex::parent(), since indexes belonging to your model will simply call your implementation leading to infinite recursion.

The .index() returns the index of the item in the model specified by the given row, column and parent index. When reimplementing this function in a subclass, call createIndex() to generate model indexes that other components can use to refer to items in your model.

It is worth to mention that both methods use self.createIndex(row, column, dataVariable) method. So they both do the same thing: they create QModelIndexes. I just don't understand why we need two methods to do the same thing! And it is hard to debug it since it seems they run in infinite loop....

Nejat
  • 31,784
  • 12
  • 106
  • 138
alphanumeric
  • 17,967
  • 64
  • 244
  • 392
  • 1
    What is stopping you from reading the [Qt documentation on the subject](https://qt-project.org/doc/qt-4.8/model-view-programming.html#parents-and-children)? – ekhumoro Jan 10 '15 at 04:25
  • Hi Ekhumoro! Qt docs examples are mostly in C (its MVC tutorial at least). – alphanumeric Jan 10 '15 at 04:29
  • From endless testing I do see that `.parent()` method is called only on top level QTableView items. While `.index()` is called for all the items: top-level, second-level children items, third-level grand-children items and etc. I also do see that both return QModelIndex with row, column and data variable "linked" to it. It looks like the QModelIndexes returned by both methods should be in sync. – alphanumeric Jan 10 '15 at 04:32
  • `.parent()` returns parent of model item with given index. If item has no parent, an invalid QModelIndex is returned. A common convention used in models that expose tree data structures is that only items in first column have children. For that case, when reimplementing this function in a subclass the column of the returned QModelIndex would be 0. When reimplementing this function in a subclass, be careful to avoid calling QModelIndex member functions, such as QModelIndex::parent(), since indexes belonging to your model will simply call your implementation leading to infinite recursion. – alphanumeric Jan 10 '15 at 04:34
  • `.index()` returns the index of the item in the model specified by the given row, column and parent index. When reimplementing this function in a subclass, call createIndex() to generate model indexes that other components can use to refer to items in your model. – alphanumeric Jan 10 '15 at 04:35
  • It is worth to mention that both methods use `self.createIndex(row, column, dataVariable)` method. So they both do the same thing: they create QModelIndexes. I just don't understand why we need two methods to do the same thing! And it is hard to debug it since it seems they run in infinite loop..... – alphanumeric Jan 10 '15 at 04:38
  • Please keep comments for asking posters questions or providing small clarifications. I have copied your comments to your post, where you should have put them in the first place. Feel free to clean up and delete your now-redundant comments. – Oliver Jan 12 '15 at 06:24

1 Answers1

5

These methods are defined abstract to enforce the user to implement them when subclassing. Your model would not work without implementing them that's because they are necessary to define the structure of your model.

Usually when you want to create a hierarchical model you should implement index() and parent() methods. For table and list models, in many cases it's enough to subclass QAbstractListModel and QAbstractTableModel which have their default implementations of the two methods.

In simple words QAbstractItemModel.parent() returns the parent QModelIndex from a child and QAbstractItemModel.index() is called whenever the model or the view needs to create a QModelIndex for a particular child item (or a top-level item if parent is an invalid QModelIndex).

Nejat
  • 31,784
  • 12
  • 106
  • 138
  • Thanks! I try to design a model to be expandable for QTreeView if a such need arises. – alphanumeric Jan 10 '15 at 04:41
  • Can we say that `.parent()` methods is used to "create" QModelIndex for top-level items. And `.index()` methods is used to create QModelIndex for any other from top-level items? Or a such statement would be wrong? – alphanumeric Jan 10 '15 at 04:43
  • @Sputnix No that's completely wrong. `index()` is actually used to create `QModelIndex` of every item in a model. It can be top-level or a child item. `parent()` is used to return the parent of any item. In hierarchical models many items in different levels have parents and just the top-level items do not have a parent in which an invalid `QModelIndex` in `parent()` should be returned for them. – Nejat Jan 10 '15 at 04:52
  • Inside of `.parent()` method I do see (in my original post under #EDITED LATER): if it is top-level index received as an argument this method simply returns `QtCore.QModelIndex()`. But if it is not top-level item this `.parent()` method goes ahead and create a new `QModelIndex` by using: `self.createIndex(parentNode.row(), 0, parentNode)` where `parentNode` is a data variable that is "linked" to a new created `QModelIndex`. So... it does create the `QModelIndex`es... I am really getting lost with the terms we use... such as "it returns".... returns from where? I really would like to understand – alphanumeric Jan 10 '15 at 05:00
  • Yes it creates the `QModelIndex` of the parent for an item and returns. If the item is top-level one, it creates an invalid `QModelIndex` and returns it. – Nejat Jan 10 '15 at 05:14
  • Thanks! Last question... how does `node = index.internalPointer()` work? What exactly happens when `.internalPointer()` called on index? – alphanumeric Jan 10 '15 at 05:27
  • 1
    `internalPointer()` returns the data structure associated with a given index. That's the pointer to the data which you pass as an argument when calling `createIndex`. – Nejat Jan 10 '15 at 07:40