Chained QSortFilterProxyModels

2024/9/20 14:43:18

Let's say I have a list variable datalist storing 10,000 string entities. The QTableView needs to display only some of these entities. That's is why QTableView was assigned QSortFilterProxyModel that does all the filtering. After all Proxy work is completed the QTableView "receives" 25 entities to display (so remaining 9,975 entities were "filtered out".

Now, I create a QLineEdit to be used as a search field where the user can type a keyword to narrow down the list of the displayed 25 entities (items) even further. For this purpose I link QLineEdit's textChanged signal to assigned to QTableView Proxy's filterAcceptsRow() method.

The problem I see here is that Proxy model needs to get back to the original 10,000 entities long list to re-filter once again. And again. And again.

I wonder if it is possible to create a second Proxy that would pickup what the first Proxy has already filtered out: 25 entities instead of 10,000.

So the resulting schema would look like this:

datalist > QAbstractTableModel > QSortFilterProxyModel > QSortFilterProxyModel > QTableView

Where:

datalist is 10,000 entities long list variable.

QAbstractTableModel is a base data model

QSortFilterProxyModel is a first Proxy Model doing the dirtiest and slowest filtering job

QSortFilterProxyModel is a second Proxy Model working on a pre-filtered by first Proxy data (it is used to filter by the user's keyword).

QTableView is a QTableView itself used to display the entity items.

So, the question is: is it a valid idea?

Answer

The code below uses two ProxyModels filtering 10,000 items. It works... enter image description here

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sysclass MyTableModel(QAbstractTableModel):def __init__(self, parent=None, *args):QAbstractTableModel.__init__(self, parent, *args)self.items = [i for i in range(10000)]def rowCount(self, parent):return len(self.items)       def columnCount(self, parent):return 1def data(self, index, role):if not index.isValid():return QVariant()elif role != Qt.DisplayRole:return QVariant()row=index.row()column=index.column()if row<len(self.items):return QVariant(self.items[row])else:return QVariant()class Proxy01(QSortFilterProxyModel):def __init__(self):super(Proxy01, self).__init__()def filterAcceptsRow(self, row, parent):        sourceModel=self.sourceModel()index=sourceModel.index(row, 0, parent)name=sourceModel.data(index, Qt.DisplayRole).toString()if name and not int(name)%10:return Truereturn Falseclass Proxy02(QSortFilterProxyModel):def __init__(self):super(Proxy02, self).__init__()self.keyword=Nonedef setKeyword(self, arg):if arg: self.keyword=str(arg)self.reset()    def filterAcceptsRow(self, row, parent):sourceModel=self.sourceModel().sourceModel()index=sourceModel.index(row, 0, parent)name=sourceModel.data(index, Qt.DisplayRole).toString()if self.keyword and name and not self.keyword.lower() in str(name).lower():return False        return Trueclass MyWindow(QWidget):def __init__(self, *args):QWidget.__init__(self, *args)self.tablemodel=MyTableModel(self)               self.proxy1=Proxy01()self.proxy1.setSourceModel(self.tablemodel)self.proxy2=Proxy02()self.proxy2.setSourceModel(self.proxy1)tableviewA=QTableView() tableviewA.setModel(self.proxy2)searchEdit=QLineEdit()searchEdit.textChanged.connect(self.proxy2.setKeyword)layout = QVBoxLayout(self)layout.addWidget(tableviewA)layout.addWidget(searchEdit)self.setLayout(layout)def test(self, arg):print argif __name__ == "__main__":app = QApplication(sys.argv)w = MyWindow()w.show()sys.exit(app.exec_())

Here is a single Proxy approach. Searching by keyword is noticably slower vs two proxys implementation:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sysclass MyTableModel(QAbstractTableModel):def __init__(self, parent=None, *args):QAbstractTableModel.__init__(self, parent, *args)self.items = [i for i in range(10000)]def rowCount(self, parent):return len(self.items)       def columnCount(self, parent):return 1def data(self, index, role):if not index.isValid():return QVariant()elif role != Qt.DisplayRole:return QVariant()row=index.row()column=index.column()if row<len(self.items):return QVariant(self.items[row])else:return QVariant()class Proxy01(QSortFilterProxyModel):def __init__(self):super(Proxy01, self).__init__()self.keyword=Nonedef setKeyword(self, arg):if arg: self.keyword=str(arg)self.reset()    def filterAcceptsRow(self, row, parent):sourceModel=self.sourceModel()index=sourceModel.index(row, 0, parent)name=sourceModel.data(index, Qt.DisplayRole).toString()if self.keyword and name and not self.keyword.lower() in str(name).lower():return False        return Trueclass MyWindow(QWidget):def __init__(self, *args):QWidget.__init__(self, *args)self.tablemodel=MyTableModel(self)               self.proxy1=Proxy01()self.proxy1.setSourceModel(self.tablemodel)tableviewA=QTableView() tableviewA.setModel(self.proxy1)searchEdit=QLineEdit()searchEdit.textChanged.connect(self.proxy1.setKeyword)layout = QVBoxLayout(self)layout.addWidget(tableviewA)layout.addWidget(searchEdit)self.setLayout(layout)def test(self, arg):print argif __name__ == "__main__":app = QApplication(sys.argv)w = MyWindow()w.show()sys.exit(app.exec_()) 
https://en.xdnf.cn/q/72159.html

Related Q&A

Python subprocess.popen() without waiting

Im using Python 3.4.2 on Windows. In script1.py Im doing this:myProc = subprocess.Popen([sys.executable, "script2.py", "argument"]) myProc.communicate()it works and call script2.py …

Python27.dll File Missing - Exception

I have downloaded my code from bit-bucket which was made by my group member. It contain all the frameworks and python script folder. But when I run this code on my system it generates the following err…

Download an Image Using Selenium Webdriver in Python

I am trying to download an image from a URL using Selenium Webdriver in Python. The site is protected by a login page, so cant just save the URL contents using requests. I am able to get text from the …

Can I turn off implicit Python unicode conversions to find my mixed-strings bugs?

When profiling our code I was surprised to find millions of calls toC:\Python26\lib\encodings\utf_8.py:15(decode)I started debugging and found that across our code base there are many small bugs, usual…

jupyter: how to stop execution on errors?

The common way to defensively abort execution in python is to simply do something like: if something_went_wrong:print("Error message: goodbye cruel world")exit(1)However, this is not good pra…

Python 2.7 on Google App Engine, cannot use lxml.etree

Ive been trying to use html5lib with lxml on python 2.7 in google app engine. But when I run the following code, it gives me an error saying "NameError: global name etree is not defined". Is …

Pandas split name column into first and last name if contains one space

Lets say I have a pandas DataFrame containing names like so:name_df = pd.DataFrame({name:[Jack Fine,Kim Q. Danger,Jane Smith, Juan de la Cruz]})name 0 Jack Fine 1 Kim Q. Danger 2 Jane Smith 3 J…

Docker. No such file or directory

I have some files which I want to move them to a docker container. But at the end docker cant find a file..The folder with the files on local machine are at /home/katalonne/flask4File Structure if it m…

How to recover original values after a model predict in keras?

This is a more conceptual question, but I have to confess I have been dealing with it for a while. Suppose you want to train a neural network (NN), using for instance keras. As it is recommended you pe…

Find closest line to each point on big dataset, possibly using shapely and rtree

I have a simplified map of a city that has streets in it as linestrings and addresses as points. I need to find closest path from each point to any street line. I have a working script that does this, …