【问题标题】:How to filter Multiple column in Qtableview?如何过滤 Qtableview 中的多列?
【发布时间】:2018-04-22 10:17:37
【问题描述】:

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

代码sn-p:

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)

【问题讨论】:

    标签: python python-2.7 pyqt pyqt4 qtableview


    【解决方案1】:

    您必须创建一个继承自QSortFilterProxyModel 的类,并覆盖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_())
    

    【讨论】:

    • 嘿,如何一次清除所有列过滤器?有没有过滤器重置或清除过滤器之类的选项? @eyllanesc
    • 和个别情况一样,要传递给过滤空字符串的列
    • text.contains(regex) 看起来很可疑,你确定它工作正常吗(除非regex 只是字符串,而不是QRegExp)?顺便说一句,这是我的更高级的版本stackoverflow.com/a/57845903/964478
    • 您的解决方案没有完全发挥作用。 “包含”在 Python 中不是一个东西,不是吗?此外,您不能只检查正则表达式中的文本(在 Python 中等效),因为正则表达式是 PySide2.QtCore.QRegExp 对象并且它不可迭代 - 请修改您的答案,因为它不完整。
    • @masky007 如果我的解决方案不符合您的要求或者您认为它不正确,请给它一个 DV 并发布您的正确答案。
    【解决方案2】:

    这里是高级一点的实现,清空更方便,支持不同的列组合方式:AND(和原来一样,所有指定的列必须匹配),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)
    

    【讨论】:

    • 你能举个例子吗,我正在努力研究如何使用它。谢谢
    • 当我尝试像这样调用 setFilterByColumn 时,AttributeError: 'PySide2.QtCore.QRegExp' 对象没有属性 'match':self.lineEdit.textChanged.connect(lambda text, col =i: self.proxy.setFilterByColumn(col, QRegExp(text, Qt.CaseInsensitive, QRegExp.FixedString))))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 2018-01-25
    相关资源
    最近更新 更多