Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct
How to access source model methods from Proxy model
I'm using Pyside2 with python 3.8.
I'm trying to figure out how to set up a proxy model to my qtableview. At a first glance, I'm just trying to set up the proxy model without overriding the filterAcceptsRow() method. Just to get a first understanding of how the sortfilterproxymodel works.
Here's my Proxy model class:
class ProxyModel(QtCore.QSortFilterProxyModel): def __init__(self,parent=None): super(ProxyModel, self).__init__() self.filter = (0,0) def mapFromSource(self, index): return self.createIndex(index.row(), index.column()) def mapToSource(self, index): return self.sourceModel().index(index.row(), index.column(), QtCore.QModelIndex()) def Filtre(self, column, valeur): self.filter = (column, valeur)
in the MainWindow class, I have did the folliwing lines of code:
ProxyModel = ProxyModel() Model = TableModel() ProxyModel.setSourceModel(Model) self.MyTableView.setModel(ProxyModel)
I have several methods in my TableModel() class, which subclass from
QAbstractTableModel. Let's say now I want to set up my headers, Before Implementing the proxymodel, the following line would work:
Where set_headers() is my method and self.headers is a list of column's headers.
After Implementing the ProxyModel() class, This doesn't work: it says that
ProxyModel object has no attribute set_headers.Which makes sense because I think now, the algorithm goes in PorxyModel and search for a method called set_headers. I thought that since It's a proxy model, It kind of works automatically that the code would go to the source model looking for the method.
Definitely, I'm doing it wrong, and all my other source model methods that I'm calling are not going to work. Unless I fix it.
setting up Proxy models is all new to me, and I'm still missing on several basics even though after reading tons of documentation.
Can any one give me any more insight on this please ?
Here's a glimpse of my TableModel class, if ever you need it:
class TableModel(QtCore.QAbstractTableModel): def __init__(self, mlist=None): super(TableModel, self).__init__() self._items =  if mlist == None else mlist self._header =  def rowCount(self, parent = QtCore.QModelIndex): return len(self._items) def columnCount(self, parent = QtCore.QModelIndex): return len(self._header) def data(self, index, role = QtCore.Qt.DisplayRole): if not index.isValid(): return None if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole: return self._items[index.row()][index.column()] return None def setData(self, index, value, role = QtCore.Qt.EditRole): if value is not None and role == QtCore.Qt.EditRole: self._items[index.row()-1][index.column()] = value self.dataChanged.emit(index, index) return True return False def addRow(self, rowObject): row = self.rowCount() self.beginInsertRows(QtCore.QModelIndex(), row, row) self._items.append(rowObject) self.endInsertRows() self.layoutChanged.emit()
All I think you are missing is that
QAbstractProxyModelhas a method
QAbstractItemModel *QAbstractProxyModel::sourceModel() const. That matches with the
ProxyModel.setSourceModel(Model)which you do.
So to implement your
self.MyTableView.model().set_headers(self.headers)you will now want:
For more advanced scenarios you could use Python's
isinstance(self.MyTableView.model(), ProxyModel)if you need to check whether the view's model is a proxy model rather than a direct model.
Yes, you will have to change other existing code which currently assumes that
self.MyTableView.model() == Modelbecause that is no longer the case. Write yourself a method in
def tableModel() -> TableModel: return self.MyTableView.model().sourceModel()
and use that instead of your
self.MyTableView.model(). Or, you could make your
Model = TableModel()a member variable and use that directly, depending on your programming preferences.
Thanks for the reply, so for everytime I'm calling a sourceModel, I have to call it from .sourceModel().method, I don't need to reimplement those methods into the proxy model right ?
JonB last edited by
You do not need to implement
QAbstractItemModelmethods on the
QAbstractProxyModelwrapper, although you could do. I tend not to, it gets to be a potentially large number of methods/signals/slots etc. I think it's cleaner to go through the suggested method which returns the source model from the proxy model and continue to use that as you did before.
This post is deleted!
Hi, Thanks a lot, it worked ! I did as you sad and called the methods using model().sourceModel().
I'm trying to reimplement my FilterAcceptRows(). here's what I did.
class ProxyModel(QtCore.QSortFilterProxyModel): def __init__(self,parent=None): super(ProxyModel, self).__init__() self.filter = (3,20) def filterAcceptsRow(self, sourceRow, sourceParent): print('im here') sourceModel = self.sourceModel() id = sourceModel.index(sourceRow, self.filter, sourceParent) if sourceModel.data(id) == self.filter: return True return False
What I'm trying to do is filter on the 4th column, the rows where the cells(row, 4) is equal to 20.
The output is a bit weird, it does filter but I don't know what it does filter on. Reading my code, how do you think this is working ?
The output is a bit weird,
I don't know what this means. Your code looks alright to me, you are picking out column #3 in the source row and seeing if that (
Qt.DisplayRole) equals 20. Assuming those numbers are correct, it should work, are you saying it does not?
hachbani last edited by hachbani
@JonB , Here's my Table without the filterAcceptsRow() reimplementation
My Table after filtering on 13 as a value in the 4th column (Num It)
I don't understand.. It's like all what it did is keeping the first 6 lines
This is because of your (erroneous) overrides of
mapToSourceback in your
Those methods need to map between the different row numbers in your proxy versus source model. Because you are using a
QSortFilterProxyModelthe row numbers will not match: when filtering the proxy's fist row might be the source's fifth row, and if you add sorting they can all come in a different order anyway.
QSortFilterProxyModelknows how to do all of this mapping for you, but you have undone all its work in your definitions, which are actually like a
QIdentityProxyModel. Hence it is showing the right number of rows (presumably) but the wrong ones --- there are 6 accepted rows, so it picks numbers 0--5 from the source model!
Get rid of those two methods. You never needed to override them in a
Man, thanks again really, the newbie I'm on this sort of things, you're saving me. I've looking all over the documentations for the past 3 days to do this. Thanks. It works.