I have a TreeView which is displaying items from an AbstractItemModel.. Now I wanted to add extra Filter functionality to my application, but somehow, the data is not visible in the TreeView (after calling newData()). How does the interaction between the QAbstractItemModel and the QSortFilterProxyModel happens? what should the QSortFilterProxyModel knows more the the setSource(QAbstractItemModel)
Here my code (copied from: https://stackoverflow.com/a/60910989/298487)
import logging
import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import QSortFilterProxyModel
class DBObject:
def __init__(self, name, parent, children=None):
self.name = name
self.parent = parent
self.children = children or list()
def __repr__(self):
return f"name: {self.name}, parent: {self.parent.name if self.parent is not None else '-'}"
class Model(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._root = DBObject("root", None)
def newData(self):
items = ["foo", "bar", "baz"]
for x in items:
child = DBObject(x + "0", self._root)
self._root.children.append(child)
for y in items:
child.children.append(DBObject(y + "1", child))
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
if not parent.isValid():
return 1
parentItem = parent.internalPointer()
rowCount = len(parentItem.children)
logging.info(f"rowCount({parentItem}): rowCount={rowCount}")
return rowCount
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
item = index.internalPointer()
parentItem = item.parent
logging.info(f"parent({item}): parent={parentItem}")
if parentItem is None:
return QtCore.QModelIndex()
else:
if parentItem.parent is None:
return self.createIndex(0, 0, parentItem)
else:
return self.createIndex(parentItem.parent.children.index(parentItem), 0, parentItem)
def index(self, row, column, parent=QtCore.QModelIndex()):
if not parent.isValid():
if row != 0 or column != 0:
return QtCore.QModelIndex()
else:
logging.info(f"index({row}, {column}, None): index={self._root}")
return self.createIndex(0, 0, self._root)
parentItem = parent.internalPointer()
if 0 <= row < len(parentItem.children):
logging.info(f"index({row}, {column}, {parentItem}): index={parentItem.children[row]}")
return self.createIndex(row, column, parentItem.children[row])
else:
logging.info(f"index({row}, {column}, {parentItem}): index=None")
return QtCore.QModelIndex()
def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
if not index.isValid():
return None
item = index.internalPointer()
if role == QtCore.Qt.ItemDataRole.DisplayRole:
return item.name
else:
return None
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemFlag.NoItemFlags
return (
QtCore.Qt.ItemFlag.ItemIsEnabled
| QtCore.Qt.ItemFlag.ItemIsSelectable)
class ProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self.setFilterKeyColumn(0)
self.setRecursiveFilteringEnabled(True)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setMinimumSize(640, 480)
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
layout = QtWidgets.QVBoxLayout(centralWidget)
self._treeView = QtWidgets.QTreeView(self)
layout.addWidget(self._treeView)
self._model = Model()
self._proxyModel = ProxyModel()
self._proxyModel.setSourceModel(self._model)
# this line will not work
self._treeView.setModel(self._proxyModel)
# if i replace it with this line, it is working
# but the filtering will not work
self._treeView.setModel(self._model)
self._proxyModel.setFilterFixedString("bar1")
button = QtWidgets.QPushButton("Add")
layout.addWidget(button)
button.clicked.connect(self._Clicked)
def _Clicked(self):
self._model.newData()
self._treeView.expandAll()
def main():
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec()
if __name__ == "__main__":
main()