May-28-2022, 06:23 PM
Thank Alex,
That was it! I had initially tried to do the filtering in filterAcceptsRow, and put in the return True as a way of disabling it. I think I should have been using this as follows then:
You're still using QStandardItemModel
I used that in the non-subclassed implementation because that is what the example at https://raw.githubusercontent.com/PyQt5/...ermodel.py used. I understood deanhystad's comments to mean that if subclassing you use QSortFilterProxyModel, but I am not sure how relevant that is to this example because once I fixed the filterAcceptsRow function the subclassed model also works with QStandardItemModel. I have included the working subclassed version for reference.
My understanding is that the source model should (or can be) QStandardItemModel, but the proxy model must be QSortFilterProxyModel.
That was it! I had initially tried to do the filtering in filterAcceptsRow, and put in the return True as a way of disabling it. I think I should have been using this as follows then:
class SortFilterProxyModel(QSortFilterProxyModel): def __init__(self): super().__init__() def filterAcceptsRow(self, sourceRow, sourceParent): result = False if self.filterKeyColumn() == 0: # if we are interested in checking the first column's (0) data for validity index = self.sourceModel().index(sourceRow, 0, sourceParent) data = self.sourceModel().data(index) if data: print("Additional Validity Checking for: " + data) # we could additionally filter here on the data and return true or false explicitly # for example: # if my additional check is not met: # return False # else: # allow the proxy filter we have set up with self.proxyModel.setFilterRegExp(regExp) decide if this should be included. # return super(SortFilterProxyModel, self).filterAcceptsRow(sourceRow, sourceParent) # Otherwise ignore return super(SortFilterProxyModel, self).filterAcceptsRow(sourceRow, sourceParent)Regarding:
You're still using QStandardItemModel
I used that in the non-subclassed implementation because that is what the example at https://raw.githubusercontent.com/PyQt5/...ermodel.py used. I understood deanhystad's comments to mean that if subclassing you use QSortFilterProxyModel, but I am not sure how relevant that is to this example because once I fixed the filterAcceptsRow function the subclassed model also works with QStandardItemModel. I have included the working subclassed version for reference.
My understanding is that the source model should (or can be) QStandardItemModel, but the proxy model must be QSortFilterProxyModel.
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QMenu, QAction, QDialog, QInputDialog, QTableView, QHeaderView, QLineEdit, QLabel, QVBoxLayout) from PyQt5.QtGui import QIcon, QStandardItemModel import sys from PyQt5.QtCore import Qt, QSortFilterProxyModel, QModelIndex, QRegExp headers = ["Word", "Meaning", ""] NUMBER_OF_COLUMNS = 2 NUMBER_OF_ROWS = 6 class TableView(QTableView): def __init__(self, title, rows, columns): QTableView.__init__(self) self.proxyModel = SortFilterProxyModel() # This property holds whether the proxy model is dynamically sorted and filtered whenever the contents of the source model change self.proxyModel.setDynamicSortFilter(True) self.model = QStandardItemModel( rows, columns, self) self.model.setHeaderData(0, Qt.Horizontal, "Word") self.model.setHeaderData(1, Qt.Horizontal, "Meaning") self.setWindowTitle(title) self.setSampleData() self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) self.setAlternatingRowColors(True) self.setSortingEnabled(True) self.verticalHeader().hide() self.setSelectionMode(QTableView.SingleSelection) self.setSelectionBehavior(QTableView.SelectRows) self.clicked.connect(self.getItem) self.filterSTring = "" def setSampleData(self): self.model.setData(self.model.index( 0, 0, QModelIndex()), "Abundance", Qt.DisplayRole) self.model.setData(self.model.index( 0, 1, QModelIndex()), "A very large quantity of something", Qt.DisplayRole) self.model.setData(self.model.index( 1, 0, QModelIndex()), "Belonging", Qt.DisplayRole) self.model.setData(self.model.index( 1, 1, QModelIndex()), "An affinity for a place or situation.", Qt.DisplayRole) self.model.setData(self.model.index( 2, 0, QModelIndex()), "Candor", Qt.DisplayRole) self.model.setData(self.model.index( 2, 1, QModelIndex()), "The quality of being open and honest in expression; frankness", Qt.DisplayRole) self.proxyModel.setSourceModel(self.model) self.setModel(self.proxyModel) def setFilterString(self, string): self.filterString = "^" + string def getItem(self, index): mapped_index = self.proxyModel.mapToSource(index) item = self.model.itemFromIndex(mapped_index) print(item.text() + " ") item = self.model.data(mapped_index) row = mapped_index.row() column = mapped_index.column() data = mapped_index.data() item = self.model.itemFromIndex(mapped_index) print(item.text() + " " + str(row) + " " + str(column) + " " + data) def filterRegExpChanged(self): syntax = QRegExp.RegExp # can be one of QRegExp.RegExp2, QRegExp.WildCard, QRegExp.RegExp2 etc, see https://doc.qt.io/qt-5/qregexp.html#PatternSyntax-enum caseSensitivity = Qt.CaseInsensitive regExp = QRegExp(self.filterString, caseSensitivity, syntax) # This property holds the QRegExp used to filter the contents of the source model self.proxyModel.setFilterRegExp(regExp) class SortFilterProxyModel(QSortFilterProxyModel): def __init__(self): super().__init__() def filterAcceptsRow(self, sourceRow, sourceParent): result = False if self.filterKeyColumn() == 0: # only interested in the first column index = self.sourceModel().index(sourceRow, 0, sourceParent) data = self.sourceModel().data(index) # we could additionally filter here on the data # return True # Otherwise ignore return super(SortFilterProxyModel, self).filterAcceptsRow(sourceRow, sourceParent) class WordSelector(QDialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.parent = parent self.lastStart = 0 self.initUI() def initUI(self): self.table = TableView("Words", NUMBER_OF_ROWS, NUMBER_OF_COLUMNS) layout = QVBoxLayout() self.filterLabel = QLabel(" Filter") layout.addWidget(self.filterLabel) self.filter = QLineEdit(self) self.filter.setStyleSheet( "background-color: #FFFFFF; padding:1px 1px 1px 1px") self.filter.setFixedWidth(120) self.filter.returnPressed.connect(self.lookupWord) layout.addWidget(self.filter) layout.addWidget(self.table) self.setGeometry(300, 300, 500, 300) self.setWindowTitle("Find and Replace") self.setLayout(layout) def lookupWord(self): self.table.setFilterString(self.filter.text()) self.table.filterRegExpChanged() def main(args): app = QApplication(args) wordSelector = WordSelector() wordSelector.show() sys.exit(app.exec_()) if __name__ == "__main__": main(sys.argv)Thank you for all of your help. I really appreciate it.