如何过滤Qtableview中的多列?

How to filter Multiple column in Qtableview?

我正在使用 QtableView 来显示我的日志并按列过滤它们,使用了 QSortFilterProxyModel。如果我使用特定值过滤一列,并使用过滤后的数据,如果我尝试过滤第二列,则最后一个过滤器将重置,并且数据将显示为与第二列上的过滤器相对应。我想在Qtableview 上实现多列过滤。

代码片段:

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)

您必须创建一个继承自 QSortFilterProxyModel 的 class,并覆盖 filterAcceptsRow 方法,如果至少有一项不满足则返回 False,如果全部满足则返回 True .

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

示例:

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_())

这里的实现有点高级,清算更方便,支持不同的列组合方式:AND(和原来一样,所有指定的列必须匹配),OR。

PySide2,Python3.

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)