Search code examples
c++qtcrashqabstractitemmodel

Properly remove subtree from QAbstractItemModel


It's not clear for me what is the proper way to remove a branch from a custom model class derived from QAbstractItemModel. I have a method that should refresh a tree node (remove all subbranches and insert the new ones).

void SessionTreeModel::refresh(const QModelIndex &index, const DbObjectI *object)
{
    auto item = getItem(index);

    assert(item != nullptr);

    if (item != nullptr)
    {
        if (item->childCount() > 0)
        {
            beginRemoveRows(index, 0, item->childCount() - 1);

            item->removeAll();

            endRemoveRows();
        }

        if (object != nullptr && object->getChildCount() > 0)
        {
            beginInsertRows(index, 0, static_cast<int>(object->getChildCount()) - 1);

            item->appendChildren(object);

            endInsertRows();
        }
    }
}
void SessionTreeItem::removeAll()
{
    for (auto& child : childItems_)
    {
        child->removeAll();
    }

    childItems_.clear();
}

The problem is that the app often crashes after a few refreshes when 'beginRemoveRows()' is called and according to the call stack the problem is that the SessionTreeModel::parent() is called with an index which holds a dangling internal pointer.

QModelIndex SessionTreeModel::parent(const QModelIndex &child) const
{
    if (child.isValid())
    {
        auto childItem = getItem(child);

        auto parentItem = childItem->parent();

        if (parentItem != nullptr &&
            parentItem != rootObject_.get())
        {
            return createIndex(static_cast<int>(parentItem->childCount()),
                               0, parentItem);
        }
    }

    return QModelIndex{};
}

It looks like the tree view is holding an index for an item that is already removed and is trying to get its parent.

Could you please advise what could be wrong?


Solution

  • First of all, I would like to say thanks to Scheff for his comment. I'm glad I didn't have to go in this direction ;)

    After hours spent digging into my code, I finally found the issue. A stupid one indeed! In the parent() method the index is created using parentItem->childCount() instead of parentItem->row().