Solved Unable to get selected rows/indexes in QTableView with custom model
-
Hello,
I have a QTableView. I am trying to implement actions like Insert Above, Insert Below, Delete, etc. To implement these actions I need to know the selected rows in the table.Within the action handler, both the lines below return a collection size 0 (even though there are rows selected in the table):
QModelIndexList selections = this->qTableView->selectionModel()->selectedIndexes(); QModelIndexList selections1 = this->qTableView->selectionModel()->selectedRows();
I am using a model that is subclass off of QAbstractTableModel.
On the qTableView, I am also settings these attributes:
this->qTableView->setSelectionBehavior(QAbstractItemView::SelectRows); this->qTableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
What am I doing wrong? How can I get the selected rows and their indexes in the model?
Thanks
-
Hi,
Can you show the implementation of your custom model ?
What action handler is that ?
Does it maybe clear the selection when called ? -
Hi SGaist,
I am overriding the following functions in my custom model (not sure if you need the internals):int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; Qt::DropActions supportedDropActions() const override; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
I am installing a right-click customContextMenuRequested menu on the QTableView. To this QMenu, I add a QAction. The QAction is connected to a function in my class like this:
connect(insertQuoteRowAbove, SIGNAL(triggered()), this, SLOT(insertQuoteRowAbove()));
In the insertQuoteRowAbove function, I get empty collection when I call:
QModelIndexList selections = this->qTableView->selectionModel()->selectedIndexes(); QModelIndexList selections1 = this->qTableView->selectionModel()->selectedRows();
Thanks
-
Then a maybe silly question but are you explicitly selecting a row before right clicking on it ?
That said, you have the position of the mouse pointer given by the
customContextMenuRequested
signal, so you can use that to get the row under the mouse cursor. -
Yes, even I select the row first, then right click on it, I still get empty collections when I execute the selectedRows and selectedIndexes on the selectionModel.
With regards to your suggestion about using the position of the mouse pointer:
I have both these lines:QModelIndex index = this->qTableView->indexAt(pos);
and then
contextMenu->popup(this->qTableView->viewport()->mapToGlobal(pos));
I believe both of those are necessary. There are some inconsistencies in the column resolution, but the row resolution seems to be reliable. The problem I have taking this path is: How do I send the position or the QModelIndex into my insertQuoteRowAbove handler? Since the triggered signal does not have a QModelIndex parameter, I get the signal-slot mismatch error.
connect(insertQuoteRowAbove, SIGNAL(triggered()), this, SLOT(insertQuoteRowAbove()));
QUESTIONS:
Is it cause for concern that my selectionModel is not tracking the selections? Any tips on how I can figure this out?Secondly, how do I get around the signal-slot mismatch if I have to go the route of passing the position/index?. I was able to figure out the answer to this question. signalmapper is the way to pass parameters into the slot. I am focussed on figuring out why my selectionModel is not tracking my selections.
Thanks SGaist.
-
You can make that index a member of your class or use a lambda.
Yes, it's pretty strange and you should investigate that.
-
After setting up the Qt sources, I was able to debug into this problem. QtCreator and gdb are extremely unreliable. Debugging is like a minefield, bcos any one click could cause the debugger to exit.
In any case, after stepping through the code, I could see that the selection was being applied in the selection model. When querying for the selectedRows or selectedIndexes the list was empty. It felt like I was accessing different selectionModels, and that is what the problem turned out to be. My cached selectionModel reference was not for the table that was being shown. Hence the empty selections.
This was my stupid mistake. A stable debugging environment would make it so easy to find my own stupid mistakes. I fear that I will never has a stable debugging environment with Qt.
-
@JohnGa Hi i'm woking on a similar project like your's if possible can u share your source code for right click in contextmenu to add row above , below
-
@Raghul-Sekar My questions above were the result of caching the selectionModel. I looked at the code and can't remember why I was caching the selectionModel. The key is NOT to cache references to the selectionModel. Any operations on the model, invalidates the existing selectionModel and creates a new one. Let's say you start out with a table that has rows 1,2,3,4. If you delete row 3, the selectionModel has to be emptied because the row does NOT exist anymore. The key is to query the underlying model for the selections after every model modification (this is because the indexes will shift after modifications to the underlying model).
Just like in regular data structures, one has to be careful when removing items in a collection. Let's say you have rows 1,2,3,4,5,6,7,8,9. Let's say you want to delete rows 3,5,7. Just like in regular data structures, if you delete row 3 first, then rows 5 & 7 are not at indexes 5 & 7. Therefore for multiple deletes, delete in reverse order. This is the same thing we do to avoid concurrent modification error.
That said, to insert below:
STEP 1: In your custom view:QModelIndexList selections = this->selectionModel()->selectedRows();
STEP 2: Reverse sort the selections (only needed if your table support multiple selections.
STEP 3: Iterate over the indexes and add to model:for(int i = 0; i < selections.count(); i++) { QModelIndex index = selections.at(i); qDebug() << "insertRowBelow row: " << index.row() << " column: " << index.column(); this->model()->insertRows(index.row() + 1, 1, this->model()->index(0, 0)); }
Using this approach you will be able to implement multi insert above/below, delete multiples, etc. Example: For insert above you would modify the params to the insertRows call.