24. Model-View Programming with QAbstractItemModel
Of the three abstract models we have seen so far, QAbstractItemModel is the most inconvenient to subclass - not because it is more difficult but because QAbstractListModel and QAbstractTableModel, being specialized for lists and tables, provide default implementations for some methods that you have to implement yourself for QAbstractItemModels. At the same time, this makes QAbstractItemModel the most flexible of the three, and suitable for representing more complex data structures such as trees.
In this chapter, we implement a series of models suitable for representing hierarchical data structures to be viewed in a tree view.
24.1 Basic Read-Only Single-Column Tree Model
In addition to data() and rowCount() which are required for a QAbstractListModel subclass and columnCount() which is additionally required for a QAbstractTableModel subclass, creating a read-only QAbstractItemModel subclass requires reimplementing two more methods:
index(row, column, parent), which returns aQModelIndexfor the item at the givenrowandcolumnunder the givenparentindex.parent(index), which returns theQModelIndexof the parent of the item with the givenindex.
These two methods are roughly inverse to each other: index() navigates down the tree (from parent to child), while parent() navigates up (from child to parent). In both cases there are a few edge cases you need to take into account.
![]() |
You are given a hierarchical JSON file (data.json) contains company employee records. Each record includes an employee id, first name, last name, profession and a list of their direct subordinates. Your task is to represent this data in a tree view by implementing a |
To create a basic read-only tree model:
- Create a Python class to represent a node in the tree. Python does not provide a built-in tree data structure so we make our own. Each
TreeItemholds avalue, a reference to itsparent, and a list ofchildren. When a parent is provided, the item automatically appends itself to the parent’schildren. The class methodbuild_tree()reads the JSON file and builds the full tree.
Subclass
QAbstractItemModeland implementrowCount(),columnCount(), anddata(). These methods are already covered in the two previous chapters with the difference thatrowCount()has to account for the tree structure: if the parent index is valid, the row count is the number of children of the item it points to and the number of the root item’s children otherwise.-
Implement
index(). We first callhasIndex()to check whether the requested position is out of bounds, returning an invalidQModelIndexif so. We then determine the parent item: ifparentis valid, we get it viainternalPointer(); otherwise the parent item is the root item. Finally, we callcreateIndex()with the row, column and a reference to the child item, and return the result.hasIndex()determines whether therow,columnandparentcombination would return a valid index, but it does not tell us if the parent itself is valid. A valid position can exist under either a valid parent item or the invisible root, so we still need to distinguish between the two. Implement
parent(). This method returns the parent of the item with the givenindex. Ifindexis invalid we return an invalidQModelIndeximmediately. Otherwise we retrieve the item viainternalPointer()and get its parent. If the parent is the root, we again return an invalidQModelIndex. Otherwise, we determine the parent’s row within its own parent’s children list and return aQModelIndexcreated withcreateIndex().
In the main window, create a model and view instances and bind them. When you run the code a read-only tree view with a single column is shown:

24.2 Adding Multiple Columns to the Tree Model
![]() |
You successfully created a |
To create a read-only tree model with multiple columns:
-
Implement the tree node class. The implementation is similar to the previous example but we add several helper methods and store item data as a list to support multiple columns:
child(row)- returns the child item at given row.child_count()- returns the number of children.column_count()- returns the number of columns.data(column)- returns the data for the given columnrow()- returns the item’s own row index within its parent’s children list.
We also extract item creation into a separate
create_item()static method.
-
Subclass QAbstractItemModel and implement
rowCount(),columnCount(),data()andheaderData(). We make several changes compared to the previous example:rowCount()usesTreeItem.child_count()instead oflen(children)directly. Note that it also returns zero for any column other than the first one - it is a Qt convention for tree models that only items in the first column have children.columnCount()usesTreeItem.column_count()data()usesTreeItem.data(), passing the index column to get the correct field.headerData()returns the appropriate label from theheaderlist.
-
Implement
index()andparent(). The logic is the same as in the previous example, with two changes:index()usesTreeItem.child()to retrieve the child item instead of accessingchildrendirectly.parent()usesTreeItem.row()to determine the parent’s row instead of callingparent.children.index()directly.
The main window code remains unchanged. When you run it a multi-column tree view is shown:

24.3 Making the Tree Model Editable
![]() |
You have implemented a |
To make a QAbstractItemModel subclass editable:
- Add the
setData()method to the tree node class. This method sets the value at the given column in the item’s data.
In the model, update
data()to also handleEditRolein addition toDisplayRole.Implement
flags(). As in previous chapters, return the base flags plusQt.ItemFlag.ItemIsEditableto signal to the view that items can be edited. Return an emptyQt.ItemFlags()for invalid indices.Implement
setData(). Check that the role isEditRoleand that the new value is different from the current one. If so, calltree_item.setData()to update the data, emitdataChanged()and returnTrue. ReturnFalseotherwise.
Now, if you double-click a cell in the tree view, an editor opens and you can edit its value. Note that changes are only saved in memory.

24.4 Resizable Tree Model (Inserting and Removing Nodes)
![]() |
You have an editable |
To create a resizable tree model:
Add a class-level
countertoTreeItemto assign unique IDs to newly inserted nodes.-
Add two new methods to
TreeItem:insert_child(row)creates a newTreeItemwith a unique ID and empty fields and inserts it at the given row in the parent’s children list.remove_child(row)removes the child at the given row using slice assignment. Note that unlike previous examples,__init__()no longer auto-appends the item to its parent. This is done ininsert_child().
Implement
insertRows()in the model. Check that the requested row is within bounds usingrowCount(parent), then guard the insertion withbeginInsertRows()andendInsertRows().Implement
removeRows(). Guard the removal withbeginRemoveRows()andendRemoveRows().
In the main window, add three buttons: Insert sibling, Insert child, and Remove current:
- Insert sibling inserts a new row at
row + 1under the curent index’s parent, placing the new node below the selected one at the same level. - Insert child inserts a new row under the current index itself, making it a child of the selected node.
- Remove current removes the selected row from its parent.
When you run the code you’ll see a tree view with buttons to insert and remove nodes:

