How to filter Multiple column in Qtableview?

后端 未结 2 519
悲哀的现实
悲哀的现实 2020-12-18 08:52

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, i

相关标签:
2条回答
  • 2020-12-18 08:58

    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_())
    
    0 讨论(0)
  • 2020-12-18 09:21

    Here is a bit more advanced implementation, it has more convenient clearing and supports different column combination modes: AND (the same as the original, all specified columns must match), OR.

    PySide2, Python 3.

    import re
    
    from PySide2.QtCore import Qt, QSortFilterProxyModel
    
    
    class MultiFilterMode:
        AND = 0
        OR = 1
    
    
    class MultiFilterProxyModel(QSortFilterProxyModel):    
        def __init__(self, *args, **kwargs):
            QSortFilterProxyModel.__init__(self, *args, **kwargs)
            self.filters = {}
            self.multi_filter_mode = MultiFilterMode.AND
    
        def setFilterByColumn(self, column, regex):
            if isinstance(regex, str):
                regex = re.compile(regex)
            self.filters[column] = regex
            self.invalidateFilter()
    
        def clearFilter(self, column):
            del self.filters[column]
            self.invalidateFilter()
    
        def clearFilters(self):
            self.filters = {}
            self.invalidateFilter()
    
        def filterAcceptsRow(self, source_row, source_parent):
            if not self.filters:
                return True
    
            results = []
            for key, regex in self.filters.items():
                text = ''
                index = self.sourceModel().index(source_row, key, source_parent)
                if index.isValid():
                    text = self.sourceModel().data(index, Qt.DisplayRole)
                    if text is None:
                        text = ''
                results.append(regex.match(text))
    
            if self.multi_filter_mode == MultiFilterMode.OR:
                return any(results)
            return all(results)
    
    0 讨论(0)
提交回复
热议问题