How to access source model methods from Proxy model
-
Hello,
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:self.MyTableView.model().set_headers(self.headers)
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()
Thanks,
-
@hachbani
All I think you are missing is thatQAbstractProxyModel
has a methodQAbstractItemModel *QAbstractProxyModel::sourceModel() const
. That matches with theProxyModel.setSourceModel(Model)
which you do.So to implement your
self.MyTableView.model().set_headers(self.headers)
you will now want:self.MyTableView.model().sourceModel().set_headers(self.headers)
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() == Model
because that is no longer the case. Write yourself a method inMyTableView
like:def tableModel() -> TableModel: return self.MyTableView.model().sourceModel()
and use that instead of your
self.MyTableView.model()
. Or, you could make yourModel = TableModel()
a member variable and use that directly, depending on your programming preferences. -
@hachbani @hachbani
I had just augmented my previous post with a suggested convenience method (or member variable) for this.You do not need to implement
QAbstractItemModel
methods on theQAbstractProxyModel
wrapper, 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. -
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[0], sourceParent) if sourceModel.data(id) == self.filter[1]: 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 ? -
@hachbani said in How to access source model methods from Proxy model:
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
This is because of your (erroneous) overrides ofdef mapFromSource
/mapToSource
back in yourProxyModel
.Those methods need to map between the different row numbers in your proxy versus source model. Because you are using a
QSortFilterProxyModel
the 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.QSortFilterProxyModel
knows how to do all of this mapping for you, but you have undone all its work in your definitions, which are actually like aQIdentityProxyModel
. 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
QSortFilterProxyModel
.