Search code examples
pythonpython-2.7pyqtpyqt4qtableview

How to filter Multiple column in Qtableview?


I'm using QtableView to show my logs and to filter them by column, QSortFilterProxyModel is used. If i filter one column using certain value, and with the filtered data, if i try to filter second column, last filter gets reset and data are displayed corresponding to filter on second column. I want to achieve multiple column filter on Qtableview.

Code snippet:

self.tableView = QTableView()
self.model = QtGui.QStandardItemModel(self)
self.proxy = QtGui.QSortFilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)

def updateTable(self):
    self.model.invisibleRootItem().appendRow(,,,,)

def filterTable(self, stringAction, filterColumn):
    filterString = QtCore.QRegExp(  stringAction,
                                    QtCore.Qt.CaseSensitive,
                                    QtCore.QRegExp.FixedString
                                    )

    self.proxy.setFilterRegExp(filterString)
    self.proxy.setFilterKeyColumn(filterColumn)

Solution

  • You must create a class that inherits from QSortFilterProxyModel, and overwrite the filterAcceptsRow method where False is returned if at least one item is not satisfied and True if all are satisfied.

    class SortFilterProxyModel(QSortFilterProxyModel):
        def __init__(self, *args, **kwargs):
            QSortFilterProxyModel.__init__(self, *args, **kwargs)
            self.filters = {}
    
        def setFilterByColumn(self, regex, column):
            self.filters[column] = regex
            self.invalidateFilter()
    
        def filterAcceptsRow(self, source_row, source_parent):
            for key, regex in self.filters.items():
                ix = self.sourceModel().index(source_row, key, source_parent)
                if ix.isValid():
                    text = self.sourceModel().data(ix).toString()
                    if not text.contains(regex):
                        return False
            return True
    

    Example:

    def random_word():
        letters = "abcdedfg"
        word = "".join([choice(letters) for _ in range(randint(4, 7))])
        return word
    
    
    class Widget(QWidget):
        def __init__(self, *args, **kwargs):
            QWidget.__init__(self, *args, **kwargs)
            self.setLayout(QVBoxLayout())
    
            tv1 = QTableView(self)
            tv2 = QTableView(self)
            model = QStandardItemModel(8, 4, self)
            proxy = SortFilterProxyModel(self)
            proxy.setSourceModel(model)
            tv1.setModel(model)
            tv2.setModel(proxy)
            self.layout().addWidget(tv1)
            self.layout().addWidget(tv2)
    
            for i in range(model.rowCount()):
                for j in range(model.columnCount()):
                    item = QStandardItem()
                    item.setData(random_word(), Qt.DisplayRole)
                    model.setItem(i, j, item)
    
            flayout = QFormLayout()
            self.layout().addLayout(flayout)
            for i in range(model.columnCount()):
                le = QLineEdit(self)
                flayout.addRow("column: {}".format(i), le)
                le.textChanged.connect(lambda text, col=i:
                                       proxy.setFilterByColumn(QRegExp(text, Qt.CaseSensitive, QRegExp.FixedString),
                                                               col))
    
    
    if __name__ == '__main__':
        import sys
    
        app = QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())