"Index out of range" when removing last item in QAbstractTableModel - qt

I implemented the removeRows() method according to the documentation. My data is stored in a QList. I can remove items just fine using:
bool MeasurementManager::removeRows(int row, int count, const QModelIndex &m) {
if(count > 1) {
qDebug() << "MeasurementManager: cannot remove more than one measurement";
return false;
}
beginRemoveRows(QModelIndex(), row, row+count-1);
list.removeAt(row);
endRemoveRows();
return true
}
However, when removing the last item I get the following error message, when executing beginRemoveRows():
ASSERT failure in QList<T>::at: "index out of range"
When removing the last item (leading to the crash) it obviously has to be in row 0, but as long as there are other items in the model I can remove the item in row 0 without any problems.
If I comment out the actual removal of my data like this
beginRemoveRows(QModelIndex(), row, row+count-1);
//list.removeAt(row);
endRemoveRows();
no crash occurs, so my assumption it, that something tries to access one of list's elements after the removal. However when stepping through the function the beginRemoveRows() method clearly is the culprit.
Any help where to start debugging would be appreciated!

I found the solution, my bad. I had connected to the ´selectionChanged()´ signal to a custom slot. This tried to access the recently deleted item in the table model.
I overlooked, that deselection a table item emits an selectionChanged() signal, too.

What you see is probably an artifact of code reordering introduced by optimizing the code.
Compile the code again with all optimizations disabled to avoid confusing the debugger.

Just had the same problem. Simply call reset() and remove the rows afterwards.
void QItemSelectionModel::reset() [virtual slot]
Clears the selection model. Does not emit any signals.

Related

Processing signal of QTreeWidget that has QTreeWidgetItem derivitives as items

I didn't find a proper solution to this problem, so I hope somebody can give me an answer to my problem:
I am using a normal QTreeWidget, but as items I use an own subclass of QTreeWidgetItem (because I needed to store some other information in the item). Now I want to use the itemClicked() signal by the QTreeWidget, but I think my slot doesn't get any signal and I think it has to do with the signature of itemClicked(), since it sends a QTreeWidgetItem and of course not my own subclass.
Is it possible that QTreeWidget doesn't detect a click on my own subclass items?
Here is my connection:
connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *)), this, SLOT(displayTransformData(QTreeWidgetItem*)));
And here is my slot:
void GUI::displayTransformData(QTreeWidgetItem* item) {
cout << "test" endl;
Q_actorTreeWidgetItem* actor_item = dynamic_cast<Q_actorTreeWidgetItem*>(item);
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor = actor_item->getActorReference();
double* position = new double[3];
position = actor->GetOrigin();
x_loc->setText(QString(std::to_string( position[0]).c_str() ));
}
I'm already trying to cast the item that I could get from the signal into my own subclass, but the slot function is never called, because the test from my cout line doesn't appear in the console.
I'm very grateful for every help!
The problem is your incorrect SIGNAL specification,
SIGNAL(itemClicked(QTreeWidgetItem *))
You should probably see a warning message at the console along the lines of:
QObject::connect: No such signal
tree_widget::itemClicked(QTreeWidgetItem *) in ...
From the documentation the actual signal spec is
void QTreeWidget::itemClicked(QTreeWidgetItem *item, int column)
So, using the old Qt4 syntax you need
connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
this, SLOT(displayTransformData(QTreeWidgetItem*)));
If possible, however, you should make use of the newer Qt5 signal/slot syntax
connect(treeWidget, &QTreeWidget::itemClicked, this, &GUI::displayTransformData);

QTreeView with QAbstractItemModel: tree items collapse and expend when adding/updating new children

I have a QTreeView with my own model. When adding new items into the tree, some items expand or collapse. How can I preserve the expand state when modifying the tree?
Thank you, Martin.
It's quite late for the question's author but I had a similar problem and ended up here, so maybe it is worth posting a solution I came up with.
My understanding is that updating nodes is not a problem - indices are not invalidated and expansion is conserved. When you add a new node, however, the default seems to be to make the node collapsed. The fallowing small hack changes the default to expand all newly added indices:
// This is done at the point where the model is set to be used by the view
connect(&model, &QAbstractItemModel::rowsInserted,
[&](const QModelIndex &parent, int first, int last) {
for (; first <= last; ++first) {
tree_view->expand(
model.index(first, 0, parent));
}
});
In case you want to replace a node with a new version (remove it and add a new one in its place) you can use a similar approach: remember expansion by connecting to QAbstractItemModel::rowsAboutToBeRemoved and using QTreeView::isExpanded(). The state can be resored in a function/slot connected to QAbstractItemModel::rowsInserted.
I would like to share some code, but it is too long. I will explain where my problem was instead.
This is my tree structure
It is necessary to use following functions when inserting/deleting rows.
void QAbstractItemModel::beginInsertRows(const QModelIndex & parent, int first, int last);
void QAbstractItemModel::endInsertRows()
void QAbstractItemModel::beginRemoveRows(const QModelIndex & parent, int first, int last)
void QAbstractItemModel::endRemoveRows()
I found out that when inserting/deleting items A and C, it is required to use invalid model index as a parent index. An invalid model index is QModelIndex() without any parameters. At least it is what help in my case.
A simple tree model example is available here:
http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html

QTableView: how to edit non-editable cells in the program?

How should this be done by using the model->setData() method call?
I have derived a class called "MyStandardItemModel" from QStandardItemModel. I have made my third and fourth columns non-editable by overriding the protected virtual flags method. This is how it goes:
#define TX_PACKET_COLUMN (4u)
#define RX_PACKET_COLUMN (5u)
Qt::ItemFlags MyStandardItemModel::flags(const QModelIndex& index) const
{
if (index.column() == TX_PACKET_COLUMN || index.column() == RX_PACKET_COLUMN)
{
return (QStandardItemModel::flags(index) & ~Qt::ItemIsEditable);
}
else
{
return QStandardItemModel::flags(index);
}
}
...
//Set model
ui->testCaseTableView->setModel(model);
Having this done, I am not able to edit the cells in the third and fourth column.
Now, I want that when I double click on these cells, a pop-up dialog comes up. I will modify some data in the editable field of that dialog, and then copy it back to the non editable cells inside the code.
I tried to just write a doubleclick() handler for the QTreeView and just copy some data to the cells just to see if it is possible to copy data to the non-editable cells.
This operation is failing, and the data is not written into the non-editable cells.
Here you can find the double click handler:
void MainWindow::on_testCaseTableView_doubleClicked(const QModelIndex &index)
{
QVariant variant;
variant.toString() = "AA";
if((index.column() == TX_PACKET_COLUMN)||(index.column() == RX_PACKET_COLUMN))
{
model->setData(index, variant); // set new value
}
}
The setData(..) operation is clearing the already written data in the cells, but string "AA" is not getting written. Please suggest how to copy some data to non-editable cells inside the code.
QVariant set is empty. Nothing needs to be wrong in your model. Error is on this line:
variant.toString() = "AA";
change to:
QVariant variant("AA"); // just for testing anyway
As I indicated in my comment, you have to fix this first issue:
instead of:
QVariant variant;
variant.toString() = "AA";
you should write
QVariant variant = QLatin1String("AA");
In general, you would look into the setData(...) implementation for such cases whether you emit the data changed signal properly and so forth, but here you are entering a prior issue which can lead to problems, so let us fix that.
Note, you should use QLatin1String to avoid the unnecessary explicit conversion from raw char* to QString. This is a good practice in general, and this is available with Qt 4 as well as Qt 5.
Although, you could also use the QStringLiteral macro for creating a QString very efficiently with template magic out of your raw literal, but that requires C++11.

How to maintain vertical scroll position in a QTableWidget

This a very simple problem to which I can find no solution:
This is my code:
qint32 pos = ui->twShow->verticalScrollBar()->value();
ui->twShow->blockSignals(true);
//Code for updating the contents QTableWidget twShow, this is done by erasing all cells and adding them again, in case it matters.
ui->twShow->blockSignals(false);
if (pos > 0){
ui->twShow->verticalScrollBar()->setValue(pos);
}
What I want to accomplish is simply to maintain the vertical scroll position. However the setValue function ignores the value pos (I've checked by printing the value before and after the instruction and both times its cero).
I have also tried:
QScrollBar *bar = ui->twShow->verticalScrollBar();
// Same code as before
ui->twShow->setVerticalScrollBar(bar); //This line crashes de program
However the last line crashes the program (which I've checked by commenting it, and it works fine).
Any advice would be greatly appreciated...
Thank you very much
QTableWidget * tw;
int desiredRow;
// before update
desiredRow = tw->row(tw->itemAt(1,1));
...
// update code
...
tw->scrollToItem( tw->item( desiredRow, 0),
QAbstractItemView::EnsureVisible | QAbstractItemView::PositionAtTop );
QAbstractItemView::EnsureVisible = 0.
The 'or' flag converts the result to an integer which is not allowed as parameter of the scrollToItem method. On the other hand enums are not intended to be used as combined flags.

Why doesn't QTableView row count update?

I created a QAbstractTableModel called PresetTableModel, and connected it to a QTableView. I implemented the rowCount, columnCount, and data functions. Everything works if I have rowCount return a fixed number, but as soon as I get it to return a variable value, the list view doesn't show any rows. The debug statement in the code below shows the size value starting at 0, but changing to 9 once the list gets populated.
int PresetTableModel::rowCount(const QModelIndex & /*parent*/) const
{
qDebug() << preset_list.count();
return preset_list.size();
}
Is there something else I need to do to force it to update the number of rows?
When modifying the underlying data, you must use the model's notification mechanism to notify the views. E.g, when appending data:
beginInsertRows(QModelIndex(), preset_list.size(), preset_list.size()+1); //notify that two rows will be appended (rows size() and size() + 1)
preset_list.append(something);
preset_list.append(somethingelse);
endInsertRows(); //notify views that you're done with modifying the underlying data
Accordingly, you have to call beginRemoveRows() and endRemoveRows() when removing rows, and emit dataChanged() when existing entries are updated.
On a side note, your rowCount() method should read
if (!parent.isValid())
return preset_list.size(); //top-level: return list size
else
return 0; //list item: no further children (flat list)
to limit the depth. Otherwise each item in the list has again preset_list.size() entries.
i use:
void refresh() {
emit dataChanged(index(0, 0),
index(rowCount(), columnCount())); // update whole view
emit layoutChanged();
}

Resources