QTreeView + custom model issue with sorting
-
I need to display data in a single level tree structure (so they are appear like a simple list of items). The data come as list of dicts, like this
data = [{"name": 1, "author": "user2", "created":"2023-10-31T12:40:22.083315Z", "attr":...}, {"name": 2, "author": "user1", "created":"2023-10-31T12:40:22.083315Z", "attr":...}...]To show this data in a tree view I have created a custom model
class Node: def __init__(self, data={}): self.parent = None self.children = list() self.data = data def add_child_node(self, node): if node is None: return node.parent = self self.children.append(node) def delete_children(self): self.children.clear() def version(self): if self.data: return self.data.get("name") return None class MyModel(QAbstractItemModel): VERSION = Qt.UserRole + 1 def __init__(self, parent=None): super().__init__(parent) self.root_node = Node() def clear(self): self.beginResetModel() self.root_node.delete_children() self.endResetModel() def add_data(self, data): previous_row_count = self.rowCount() self.beginInsertRows(QModelIndex(), previous_row_count, previous_row_count) for i in data: node = Node(i) self.root_node.add_child_node(node) self.endInsertRows() def index2node(self, index): if not index.isValid(): return self.root_node return index.internalPointer() def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None node = self.index2node(index) if role == Qt.DisplayRole: if index.column() == 0: return node.data["name"] elif index.column() == 1: return node.data["author"] elif index.column() == 2: return node.data["created"] else: return None elif role == Qt.ToolTipRole: if index.column() == 2: return node.data["created"] elif role == MyModel.VERSION: return node.data["name"] else: return None def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: if section == 0: return "Version" elif section == 1: return "Author" elif section == 2: return "Created" else: return None return None def rowCount(self, parent=QModelIndex()): node = self.index2node(parent) if node is None: return 0 return len(node.children) def columnCount(self, parent=QModelIndex()): return 3 def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() node = self.index2node(parent) if node is None: return QModelIndex() return self.createIndex(row, column, node.children[row]) def parent(self, index): if not index.isValid(): return QModelIndex() node = self.index2node(index) if node is not None: return self.index_of_parent_tree_node(node.parent) else: return QModelIndex() def index_of_parent_tree_node(self, parent_node): grand_parent_node = parent_node.parent if grand_parent_node is None: return QModelIndex() row = grand_parent_node.children.index(parent_node) return self.createIndex(row, 0, parent_node) def item_from_index(self, index): return self.index2node(index)The model is created and assigned to tree view, then populated by calling
add_data()method whenever new portion of data arrived.model = MyModel() tree.setModel(model) ... model.add_data(data)This works fine and I have a tree view with three columns filled with information.
But when I try to add sorting by using
QSortFilterProxyModelonly the first item is shown in the tree, even though the source model is populated correctly.Am I missing something in my model implementation or doing something wrong?
-
I need to display data in a single level tree structure (so they are appear like a simple list of items). The data come as list of dicts, like this
data = [{"name": 1, "author": "user2", "created":"2023-10-31T12:40:22.083315Z", "attr":...}, {"name": 2, "author": "user1", "created":"2023-10-31T12:40:22.083315Z", "attr":...}...]To show this data in a tree view I have created a custom model
class Node: def __init__(self, data={}): self.parent = None self.children = list() self.data = data def add_child_node(self, node): if node is None: return node.parent = self self.children.append(node) def delete_children(self): self.children.clear() def version(self): if self.data: return self.data.get("name") return None class MyModel(QAbstractItemModel): VERSION = Qt.UserRole + 1 def __init__(self, parent=None): super().__init__(parent) self.root_node = Node() def clear(self): self.beginResetModel() self.root_node.delete_children() self.endResetModel() def add_data(self, data): previous_row_count = self.rowCount() self.beginInsertRows(QModelIndex(), previous_row_count, previous_row_count) for i in data: node = Node(i) self.root_node.add_child_node(node) self.endInsertRows() def index2node(self, index): if not index.isValid(): return self.root_node return index.internalPointer() def data(self, index, role=Qt.DisplayRole): if not index.isValid(): return None node = self.index2node(index) if role == Qt.DisplayRole: if index.column() == 0: return node.data["name"] elif index.column() == 1: return node.data["author"] elif index.column() == 2: return node.data["created"] else: return None elif role == Qt.ToolTipRole: if index.column() == 2: return node.data["created"] elif role == MyModel.VERSION: return node.data["name"] else: return None def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: if section == 0: return "Version" elif section == 1: return "Author" elif section == 2: return "Created" else: return None return None def rowCount(self, parent=QModelIndex()): node = self.index2node(parent) if node is None: return 0 return len(node.children) def columnCount(self, parent=QModelIndex()): return 3 def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() node = self.index2node(parent) if node is None: return QModelIndex() return self.createIndex(row, column, node.children[row]) def parent(self, index): if not index.isValid(): return QModelIndex() node = self.index2node(index) if node is not None: return self.index_of_parent_tree_node(node.parent) else: return QModelIndex() def index_of_parent_tree_node(self, parent_node): grand_parent_node = parent_node.parent if grand_parent_node is None: return QModelIndex() row = grand_parent_node.children.index(parent_node) return self.createIndex(row, 0, parent_node) def item_from_index(self, index): return self.index2node(index)The model is created and assigned to tree view, then populated by calling
add_data()method whenever new portion of data arrived.model = MyModel() tree.setModel(model) ... model.add_data(data)This works fine and I have a tree view with three columns filled with information.
But when I try to add sorting by using
QSortFilterProxyModelonly the first item is shown in the tree, even though the source model is populated correctly.Am I missing something in my model implementation or doing something wrong?
-
@voltron Start by checking how many items you have and what their parentage is when accessed via the
QSortFilterProxyModelindexes. Also since you do not say I presume your QSFPM actually does neither filtering nor sorting currently?@JonB beginInsertRows() is called with wrong parameters.
-
@voltron Start by checking how many items you have and what their parentage is when accessed via the
QSortFilterProxyModelindexes. Also since you do not say I presume your QSFPM actually does neither filtering nor sorting currently? -
@JonB beginInsertRows() is called with wrong parameters.
@Christian-Ehrlicher thanks for the pointer, changing call to
self.beginInsertRows(QModelIndex(), previous_row_count, len(data))fixes it.
-
V voltron has marked this topic as solved on