Drees
Drees

Reputation: 720

How to filter sub-items, QTreeView using QSortFilterProxyModel

I want to be able to filter every item under the 'Parameter' column. I am using a QSortFilterProxyModel and a QLineEdit to try and filter out specific levels from the QTreeView, however I unable to filter out any level/sublevel besides the 'Rootlevel'.

How can I fix this implementation to be able to filter any of the levels and not just the 'RootLevel'?

from PyQt5 import QtGui,QtCore,QtWidgets
import sys

class MainFrame(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)

        tree = {'RootLevel':{
                    "Level1": {"Level1_item1":14, "Level1_item2":12, "Level1_item3":3.55},
                    "Level2": {
                        "Level2_SubLevel1": {"Level2_SubLevel1_item1":3.52, "Level2_SubLevel1_item2":2.55, "Level2_SubLevel1_item3":13},
                        "Level2_SubLevel2": {"Level2_SubLevel2_item1":2, "Level2_SubLevel2_item2":4, "Level2_SubLevel2_item3":3.11}
                        },
                    "Level3": {"Level3_item1":12, "Level3_item2":13.55, "Level3_item3":122}}
        }

        self.tree = QtWidgets.QTreeView(self)
        self.filterEdit = QtWidgets.QLineEdit()
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.filterEdit)
        layout.addWidget(self.tree)

        self.root_model = QtGui.QStandardItemModel()
        self.root_model.setHorizontalHeaderLabels(['Level','Values'])

        self.fill_model_from_json(self.root_model.invisibleRootItem(), tree)

        self.proxyModel = QtCore.QSortFilterProxyModel(self)
        self.proxyModel.setSourceModel(self.root_model)

        self.tree.setModel(self.proxyModel)
        self.tree.header().setMinimumSectionSize(300)
        self.filterEdit.textChanged.connect(self.onTextChanged)

    @QtCore.pyqtSlot(str)
    def onTextChanged(self, text):
        self.proxyModel.setFilterRegExp(text)

    def fill_model_from_json(self, parent, d):
        if isinstance(d, dict):
            for key, value in d.items():
                it = QtGui.QStandardItem(str(key))
                if isinstance(value, dict):
                    parent.appendRow(it)
                    self.fill_model_from_json(it, value)
                else:
                    it2 = QtGui.QStandardItem(str(value))
                    parent.appendRow([it, it2])

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main = MainFrame()
    main.show()
    sys.exit(app.exec_())

Upvotes: 1

Views: 1744

Answers (1)

eyllanesc
eyllanesc

Reputation: 243907

If you want to filter the second column then you must set it using the filterKeyColumn property, if you want to filter at lower levels then you must enable the recursiveFilteringEnabled property, and it is recommended that you expand all the items using expandAll() so that the filtering is visible.

from PyQt5 import QtGui, QtCore, QtWidgets


class MainFrame(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        tree = {
            "RootLevel": {
                "Level1": {
                    "Level1_item1": 14,
                    "Level1_item2": 12,
                    "Level1_item3": 3.55,
                },
                "Level2": {
                    "Level2_SubLevel1": {
                        "Level2_SubLevel1_item1": 3.52,
                        "Level2_SubLevel1_item2": 2.55,
                        "Level2_SubLevel1_item3": 13,
                    },
                    "Level2_SubLevel2": {
                        "Level2_SubLevel2_item1": 2,
                        "Level2_SubLevel2_item2": 4,
                        "Level2_SubLevel2_item3": 3.11,
                    },
                },
                "Level3": {
                    "Level3_item1": 12,
                    "Level3_item2": 13.55,
                    "Level3_item3": 122,
                },
            }
        }

        self.tree = QtWidgets.QTreeView()
        self.filterEdit = QtWidgets.QLineEdit(textChanged=self.onTextChanged)
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(self.filterEdit)
        layout.addWidget(self.tree)

        self.root_model = QtGui.QStandardItemModel()
        self.root_model.setHorizontalHeaderLabels(["Level", "Values"])

        self.fill_model_from_json(self.root_model.invisibleRootItem(), tree)

        self.proxyModel = QtCore.QSortFilterProxyModel(
            self, filterKeyColumn=1, recursiveFilteringEnabled=True
        )
        self.proxyModel.setSourceModel(self.root_model)

        self.tree.setModel(self.proxyModel)
        self.tree.header().setMinimumSectionSize(300)

        self.tree.expandAll()
        self.resize(640, 480)

    @QtCore.pyqtSlot(str)
    def onTextChanged(self, text):
        self.proxyModel.setFilterRegExp(text)

    def fill_model_from_json(self, parent, d):
        if isinstance(d, dict):
            for key, value in d.items():
                it = QtGui.QStandardItem(str(key))
                if isinstance(value, dict):
                    parent.appendRow(it)
                    self.fill_model_from_json(it, value)
                else:
                    it2 = QtGui.QStandardItem(str(value))
                    parent.appendRow([it, it2])


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    main = MainFrame()
    main.show()
    sys.exit(app.exec_())

enter image description here

Upvotes: 4

Related Questions