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