I created a custom model for QTreeView. The complete minimal code to show the problem is below. If I add a new node to the root node, by clicking “Add Level 1”, it shows up. But if I add a new node to the second level by clicking “Add Level 2”, it does not show up. That node only shows up if I collapse the parent node and then expand it again. What part of my MyTreeModel
is wrong?
I am adding the QT tag, even though my code is PySide6, because the fault is probably in my understanding of the methods of QAbstractItemModel, which is not something specific to Python or to PySide.
Full Code
from __future__ import annotations
from typing import Optional
from PySide6.QtCore import QAbstractItemModel, QModelIndex
from PySide6.QtGui import Qt
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QHBoxLayout, QTreeView
class TreeNode:
def __init__(self, name: str, parent_node):
self.name = name
self.parent_node = parent_node
self.children: list[TreeNode] = []
def get_child_by_name(self, name) -> Optional[TreeNode]:
for child in self.children:
if child.name == name:
return child
return None
class MyTreeModel(QAbstractItemModel):
def __init__(self):
super().__init__()
self.root_node = TreeNode("root", None)
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return "Name"
def rowCount(self, parentIndex):
if not parentIndex.isValid():
parentNode = self.root_node
else:
parentNode = parentIndex.internalPointer()
return len(parentNode.children)
def columnCount(self, parent):
return 1
def data(self, index, role):
if not index.isValid():
return None
if role == Qt.DisplayRole:
node: TreeNode = index.internalPointer()
column = index.column()
match column:
case 0:
return node.name
case _:
return None
else:
return None
def parent(self, index):
if not index.isValid():
return QModelIndex()
childNode: TreeNode = index.internalPointer()
parentNode = childNode.parent_node
if parentNode == self.root_node:
return QModelIndex()
row_within_parent = parentNode.children.index(childNode)
return self.createIndex(row_within_parent, 0, parentNode);
def index(self, row, column, parentIndex):
if not self.hasIndex(row, column, parentIndex):
return QModelIndex()
if not parentIndex.isValid():
parentNode = self.root_node
else:
parentNode = parentIndex.internalPointer()
child_node = parentNode.children[row]
if child_node:
return self.createIndex(row, column, child_node)
else:
return QModelIndex()
def set_data(self, data: []):
self.beginResetModel()
self.apply_data(data)
self.endResetModel()
def update_data(self, data: []):
self.apply_data(data, True)
def apply_data(self, data, notify=False):
for item in data:
parent_node = self.root_node;
for part in item.split("/"):
existing = parent_node.get_child_by_name(part)
if existing:
parent_node = existing
else:
if notify:
parent_index = self.get_index(parent_node)
count = len(parent_node.children)
self.beginInsertRows(parent_index, count, count)
new_node = TreeNode(part, parent_node)
parent_node.children.append(new_node)
parent_node = new_node
if notify:
self.endInsertRows()
def get_index(self, node: TreeNode):
if not node.parent_node:
return QModelIndex()
row = node.parent_node.children.index(node)
return self.createIndex(row, 0, node.parent_node)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(600, 400)
button1 = QPushButton("Add Level 1")
button1.clicked.connect(self.add1)
button2 = QPushButton("Add Level 2")
button2.clicked.connect(self.add2)
row1 = QHBoxLayout()
row1.setAlignment(Qt.AlignLeft)
row1.addWidget(button1)
row1.addWidget(button2)
self.tree_view = QTreeView()
layout = QVBoxLayout()
layout.addLayout(row1)
layout.addWidget(self.tree_view, stretch=1)
self.central_widget = QWidget()
self.central_widget.setLayout(layout)
self.setCentralWidget(self.central_widget)
def showEvent(self, event):
data = ["mammals", "birds", "mammals/dog", "birds/eagle", "mammals/cat"]
my_model = MyTreeModel()
my_model.set_data(data)
self.tree_view.setModel(my_model)
self.tree_view.expandAll()
def add1(self):
data = ["reptiles"]
m = self.tree_view.model()
m.update_data(data)
def add2(self):
data = ["mammals/rat"]
m = self.tree_view.model()
m.update_data(data)
app = QApplication([])
win = MainWindow()
win.show()
app.exec()