Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Multiple Proxymodels on one Sourcemodel
QtWS25 Last Chance

Multiple Proxymodels on one Sourcemodel

Scheduled Pinned Locked Moved QML and Qt Quick
3 Posts 2 Posters 1.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    HennsWoerst
    wrote on last edited by HennsWoerst
    #1

    Hello!

    I'm trying to implement an editable treeview in qml that includes an undofunction. I have one source model (QAbstractItemModel) and two QSortFilterProxyModels. One model is filtering out the leaves and one is displaying just the leaves. My problem is that removing an item from the treeview that has leaves as children leads to crashes. Before the crashes the warning QSortFilterProxyModel: inconsistent changes reported by source model appears. It seems to me that due to the structure change in the source model (I remove leaves, so other items will be the new leaves) there are some inconsistencies in the proxymodel's indexes, but after trying for a long time to find a solution I just cannot come up with new ideas. Help would be greatly appreciated.

    I think its best if I show some code:

    • The first step in the whole process is pressing the delete button in the treeview. This creates a QUndoCommand
    undoManager.createDeleteKeyCommand(treeModel, noLeavesProxyModel.mapToSource(treeView.currentIndex))
    
    • The undomanager pushes the command on the stack
    void UndoManager::createDeleteKeyCommand(TreeModel* model, const QModelIndex &index)
    {
    	if(index.isValid())
    		m_undoStack->push(new DeleteKeyCommand(model, index));
    }
    
    • Pushing the command on the stack calls the redo() function. I've experienced myself that it is not possible to store QPersistentModelIndexes in a case like this, so the location of the item is stored in the form of a Path
    void DeleteKeyCommand::redo()
    {
    	QModelIndex index = m_model->pathToIndex(m_path);
    
    	if (index.isValid())
    	{
    		m_model->removeRow(m_row, index);
    	}
    }
    
    • removeRow() calls the removeRows() function in the treemodel. In case the item is a leave after I removed its children (like here) I call invalidateFilter() so the views update accordingly (they do not if I miss this)
    bool TreeModel::removeRows(int row, int count, const QModelIndex &parent)
    {
    	TreeItem *parentItem = getItem(parent);
    	Q_ASSERT(parentItem);
    	bool success = true;
    	bool noChildren = parentItem->childCount() == count;
    
    	beginRemoveRows(parent, row, row + count - 1);
    	success = parentItem->removeChildren(row, count);
    	endRemoveRows();
    
    	if(noChildren){
    		emit invalidateFilter();
    	}
    
    	return success;
    }
    
    • Finally the item gets removed from the QList. Since I do not delete the item I change its parent so that it is detached from the tree.
    bool TreeItem::removeChildren(int row, int count)
    {
    	if(row < 0 || row + count > m_children.count())
    		return false;
    
    	for(int i = 0; i < count; i++)
    	{
    		m_children.at(row)->setParent(TreeItemPtr());
    		m_children.removeAt(row);
    	}
    
    	return true;
    }
    
    • To undo the deletion undo() is called (when creating the QUndoCommand I stored a pointer to the item m_item(qvariant_cast<TreeItemPtr>(model->data(index, TreeModel::ItemRole))))...
    void DeleteKeyCommand::undo()
    {
    	QModelIndex index = m_model->pathToIndex(m_path);
    
    	if (index.isValid())
    	{
    		QList<TreeItemPtr> items;
    		Q_ASSERT(m_item);
    		items.append(m_item);
    		m_model->setItemsToInsert(items);
    
    		m_model->insertRows(m_row, items.count(), index);
    	}
    }
    
    • ... which calls the insertRows() function ...
    bool TreeModel::insertRows(int row, int count, const QModelIndex &parent)
    {
    	TreeItem *parentItem = getItem(parent);
    	Q_ASSERT(parentItem);
    	bool success = true;
    	bool noChildren = parentItem->childCount() == 0;
    
    	QList<TreeItemPtr> items = getItemsToInsert();
    
    	if(items.isEmpty())
    	{
    		qDebug() << "No items to insert";
    		return false;
    	}
    
    	Q_ASSERT(count == items.count());
    
    	beginInsertRows(parent, row, row + count - 1);
    	foreach (TreeItemPtr item, items) {
    		TreeItemPtr p = getItemPtr(parent);
    		Q_ASSERT(p);
    		item->setParent(p);
    	}
    	success = parentItem->insertChildren(row, items);
    	endInsertRows();
    
    	if(noChildren){
    		emit invalidateFilter();
    	}
    
    	return success;
    }
    
    
    • ... and inserts the item back into the QList (after resetting the parentitem)
    bool TreeItem::insertChildren(int index, QList<QSharedPointer<TreeItem> > items)
    {
    	if (index < 0 || index > m_children.count())
    		return false;
    
    	foreach(TreeItemPtr item, items)
    	{
    		m_children.insert(index, item);
    	}
    
    	return true;
    }
    

    The whole process works fine except when I remove an item that has leaves as children. Pressing undo and redo multiple times will lead to a crash. Anybody got an idea what the problem might be?

    1 Reply Last reply
    0
    • J Offline
      J Offline
      Jagh
      wrote on last edited by
      #2

      I guess using a temporary object as parent

      m_children.at(row)->setParent(TreeItemPtr());
      

      might have something to do with this

      H 1 Reply Last reply
      0
      • J Jagh

        I guess using a temporary object as parent

        m_children.at(row)->setParent(TreeItemPtr());
        

        might have something to do with this

        H Offline
        H Offline
        HennsWoerst
        wrote on last edited by
        #3

        @Jagh

        Thank you for your answer. For testing purposes I created an item in the treemodel that definitely exists for the whole time the application is running and added it to the removeChildren(row, count, m_tmpParent) signature so it can be set as temporary parent but that did not change anything (as does not setting the parent at all)

        ratbr QModelIndex(0,0,0x1d60a80,NoLeavesProxyModel(0x7fffffffe070)) 0 0
        rr QModelIndex(0,0,0x1d60a80,NoLeavesProxyModel(0x7fffffffe070)) 0 0
        ratbr QModelIndex(0,0,0x8f7940,TreeModel(0x7fffffffe0f0)) 0 0
        QSortFilterProxyModel: inconsistent changes reported by source model
        rr QModelIndex(0,0,0x8f7940,TreeModel(0x7fffffffe0f0)) 0 0
        ratbr QModelIndex(0,0,0x1d61510,NoLeavesProxyModel(0x7fffffffe070)) 0 0
        rr QModelIndex(0,0,0x1d61510,NoLeavesProxyModel(0x7fffffffe070)) 0 0
        
        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt Extensions
        • Unsolved