如何过滤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)
我正在使用 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)