`QSortFilterProxyModel` incorrect mapping indexes from source model
-
Hi,
I have a
QTableView
where I setQSortFilterProxyModel
and the source model isQStandardItemModel
.Let's suppose I have 10 rows in the table and then I do the resort of items based on some column (like sort items in alphabetical order by file names).
Then user adds new items in the table and there we have some code for that:
d->model->insertRow(d->model->rowCount()); int row = d->model->rowCount() - 1; int proxy_row = d->proxy->mapFromSource( d->model->index(row, 0)).row(); // proxy_row is incorrect!!! QStandardItem *readFileItem = new QStandardItem(name); readFileItem->setFlags(readFileItem->flags() & ~Qt::ItemIsEditable); d->model->setItem(row, d->tableHdrNames.indexOf("read file"), readFileItem);
The problem here is
proxy_row
will be incorrect probably 0 even if new row is the last row of the table (I mean the last one that is displayed in the table GUI).This seems to be fixed and tested when I don't insert/append new row but simply do
d->model->setItem(...)
which automatically adds new row if needed:QStandardItem *readFileItem = new QStandardItem(name); readFileItem->setFlags(readFileItem->flags() & ~Qt::ItemIsEditable); d->model->setItem(d->model->rowCount(), d->tableHdrNames.indexOf("read file"), readFileItem); int row = d->model->rowCount() - 1; int proxy_row = d->proxy->mapFromSource( d->model->index(row, 0)).row(); // proxy_row is correct
I don't understand why this happens and it would be enough for me to use the second approach but my application is complicated enough so the PythonQt is adjusted as well so python scripts maybe run from C++ side and the second approach doesn't fix the issue in this case. I mean
d->proxy->mapFromSource(...)
gives incorrect row even when I used->model->setItem(...)
from Python script (even though this helps from C++ side).Maybe somebody have ideas what are the possible reasons of that?
And this is how these objects are tight to each other:
proxy = new QSortFilterProxyModel(); model = new QStandardItemModel(); proxy->setSourceModel(model); tableView->setModel(proxy);
Qt 5.15.2
Windows 11, msvc 2019 -
@Please_Help_me_D said in `QSortFilterProxyModel` incorrect mapping indexes from source model:
This seems to be fixed and tested when I don't insert/append new row
Is this related to https://doc.qt.io/qt-6/qsortfilterproxymodel.html#dynamicSortFilter-prop
Note that you should not update the source model through the proxy model when dynamicSortFilter is true.
?
-
@Please_Help_me_D You are supposed to modify it off default (
true
) if you are doing what it says. At any rate try setting itfalse
and see if that modifies behaviour? -
@JonB I've tried setting
dynamicSortFilter
toTrue
andFalse
but unfortunately that didn't help...I have a feeling that source model either doesn't send some signal when new row is added or the connection type between proxy model and source model is not the default so it there is no time to proxy receives and process the signal. I will try to look into the source code of these models.
-
@Please_Help_me_D
"Timing" cannot/should not be an issue.
If it's your own source model make sure you do the right thing forinsertRows
per the docs. There is a also a model tester per https://doc.qt.io/qt-6/qabstractitemmodeltester.html. Oh if you are using baseQStandardItemModel
then obviously it behaves correctly.Otherwise please show a complete but minimal example of what goes wrong.
Just glancing and thinking:
d->model->insertRow(d->model->rowCount()); int proxy_row = d->proxy->mapFromSource( d->model->index(row, 0)).row(); // proxy_row is incorrect!!!
First line inserts a new blank row. Meaning all columns are empty/
QVariant()
. So if you are sorting (ascending) by anything it will likely come first, so proxy row will be 0. So that seems correct to me. I think you need to clarify exactly what you think is wrong. -
@JonB I successfully created minimal project that shows the
issue.link to the minimal example project to Download
Steps to reproduce:
- build and run the app
- click on
Add file
button - add several arbitrary files
- manually resort the table view by cicking on the
File
column name - click
Add file
and add few more files - look at the resulted table
Here is the result. As you can see there is blank cells because
proxy_row
is incorrect. -
Because the sorting happens during insertion the row is wrong afterwards.
-
You mean the blank row is inserted and it becomes the first row in the proxy model? But in this case this first proxy row is not filled anyway in the example.
Can't understand the logic...
-
When you want the correct (source) index then use QStandardItemModel::indexFromItem()
-
@Christian-Ehrlicher thank you, this works fine with C++ only:
void MainWindow::onAddBtnClicked(){ QStringList fileNames = QFileDialog::getOpenFileNames( nullptr, QObject::tr("Select one or more files to open"), "", QObject::tr("all (*)")); for (const auto &name : fileNames) { // set item directly to the model (works fine) QStandardItem *readFileItem = new QStandardItem(name); readFileItem->setFlags(readFileItem->flags() & ~Qt::ItemIsEditable); model->setItem(model->rowCount(), 0, readFileItem); QModelIndex index = model->indexFromItem(readFileItem); int row = index.row(); int proxy_row = proxy->mapFromSource( model->index(row, 0)).row(); // set data to the proxy (gives blank cells) proxy->setData( proxy->index(proxy_row, 1), QString::number(row)); proxy->setData( proxy->index(proxy_row, 2), QString::number(proxy_row)); } }
The problem now is that approach doesn't work with PythonQt.
As I mentionned my application includes both C++ and python so that it is posible to run python scripts and new GUI appears with PythonQt force.I tried your approach in python script and it didn't work. For example after manual column resorting I get:
row: 4, proxy_row: 0 row: 5, proxy_row: 0 row: 6, proxy_row: 0
And this is how it looks in Python:
readFileItem = QtGui.QStandardItem(name) readFileItem.setFlags(readFileItem.flags() & ~Qt.Qt.ItemIsEditable) self.wellModel.setItem(self.wellModel.rowCount(), self.wellTableHdrNames.index("read file"), readFileItem) index = self.wellModel.indexFromItem(readFileItem) row = index.row() proxy_row = self.wellProxy.mapFromSource(index).row();
-
If it works in c++ then it also works the same way in python except you don't use python correctly (which i can't justify) so you must be doing something wrong.
Why do you need the index at all when using a QStandardItemModel? -
@Christian-Ehrlicher absolutely agree it shpuld work with python too.
@Christian-Ehrlicher said in `QSortFilterProxyModel` incorrect mapping indexes from source model:
Why do you need the index at all when using a QStandardItemModel?
The idea is that user adds files to the table and then the information about every chosen file is filled in table with
proxyModel.setData(w_proxy_row, ...)
:self.wellProxy.setData(self.wellProxy.index(w_proxy_row, 1), well_name)
I know I can set data directly to the source model but I'm trying to avoid that though I don't remeber exactly why and use
proxy_model
. -
Then either use the proxy_index or - since you're using a QStandardItemModel, set it through the QStandardItemModel. Don't see a problem here.
-
@Christian-Ehrlicher
proxy_index
you mean the one I get with:index = self.wellModel.indexFromItem(readFileItem) row = index.row() proxy_index = self.wellProxy.mapFromSource(index)
?
-
Yes, that's the proxy index.
-
@Christian-Ehrlicher alright I just decided to forbid table view sorting for the python side.
It is strange that C++ side works and Python fails. But there are some chances that the reason of that is my personal additions so I will take some time to investigate it.