Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QModelIndex becomes invalid when removing rows

I'm subclassing QAbstractItemModel to display items in a QTreeView, and within this subclass (projectModel), I have a function to delete the currently-selected index in the tree view. Component is the class used to represent all the members of the model:

void
projectModel::deleteComponent()
{
    QModelIndex child_index = _treeview->selectionModel()->currentIndex();
    Component* child = static_cast<Component*>(child_index.internalPointer());

    Component* parent = child->Parent();
    QModelIndex parent_index = createIndex(parent->row(), 0, parent);

    int row = child->row();

    beginRemoveRows(parent_index, row, row);
    parent->delete_child(child);
    endRemoveRows();
}

The parent and child indicies and raw pointers are good just before the call to beginRemoveRows; debugger shows that they point to the correct item and its parent. However, the program crashes after calling beginRemoveRows. Specifically, it crashes in projectModel::parent():

QModelIndex
projectModel::parent(const QModelIndex &index) const
{    
    if (!index.isValid())
        return QModelIndex();

    Component* item = getItem(index);        //Fails to cast index internal pointer to a valid Component*
    Component* parentItem = item->Parent();

    if (parentItem == _rootnode)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

When I break on the crash and examine the debugger output, it shows that the variable item in the parent() function is garbage. Somehow it looks like my QModelIndex gets corrupted between the call to deleteComponent and the call to parent. Is there anything blatantly wrong with what I'm doing, or is the problem perhaps more subtle?

like image 537
Carlton Avatar asked Feb 10 '23 12:02

Carlton


1 Answers

This is fully expected.

Non-persistent indices are valid until you change the structure of the model. A structural change is any change that is signaled other than by emitting dataChanged.

Structural changes must be considered barriers to index lifetime: you must discard any non-persistent indices held from before a structural change.

Depending on where deleteComponent is called, likely what happens is that the caller holds some indices, the deleteComponent invalidates them all, and you're in undefined behavior territory from there onwards.

You need to use persistent indices if you want them to stay valid over structural changes, and even then they'll only be valid if the given item still exists. If you're using your own model, you need to explicitly support persistent indices, and the users of your model must use them.

like image 192
Kuba hasn't forgotten Monica Avatar answered Feb 13 '23 04:02

Kuba hasn't forgotten Monica