Index for beginInsertRows() with QTreeView



  • Hello,

    i have a question on how to calculate the first and last index when inserting a row (new treeitem) in a treemodel
    documentation of beginInsertRows()

    My approach is as follows (the new item is appended after the existing items):

        QModelIndex index = tree_view->currentIndex();
        TreeItem* item = static_cast<ae_oe::TreeItem*>(index.internalPointer());
        int insert_pos = item->getNrChildren()+1;
        beginInsertRows(index, index.row()+insert_pos, index.row()+insert_pos);
        item.appendChild(childItem);
        endInsertRows();
    
        emit dataChanged();
    

    The dataChanged() signal calls the QWidget::update() of QTreeView.

    However when i insert a new child sometimes it does not update the view (collapsing the tree and expanding it again updates the view)

    What am i doing wrong?



  • First of all a design question: QModelIndex index = tree_view->currentIndex(); in the same method that calls beginInsertRows implies that the model knows (or even owns) the view. This goes against the design of model/view itself. The same model should be usable by different views


    int insert_pos = item->getNrChildren()+1;

    this depends on what getNrChildren actually does but, intuitively, if it returns the number of children of the index then it should just be const int insert_pos = item->getNrChildren();


    index.row()+insert_pos

    if index is the parent, what has its row to do with the new row?! its should just be beginInsertRows(index, insert_pos, insert_pos);


    The dataChanged() signal calls the QWidget::update() of QTreeView.

    No. Just no. Don't hack around the problems, if the view does not update correctly it just means your insert/remove/move implementation is wrong. Remove emit dataChanged();



  • @VRonin Thanks for the quick answer.


    The TreeModel knows the TreeView (there is only one view and one model) but you are right, i have to clean up where things belong here.


    getNrChildren() returns the children count of the item (not including the children's children)


    i have also tried with

    int insert_pos = item->getNrChildren();
    beginInsertRows(index, insert_pos, insert_pos);
    

    still the same behavior


    The emit dataChanged() was unneccesary, however removing it does not change anything.



  • Try running the model test. download modeltest.h and modeltest.cpp. When you create the model add a line with something like new ModelTest(myModel ,myModel); and use the rest of your app as normal. it will assert if it detects something wrong



  • Thanks for the hint with the model test.

    I've tested two versions of the code

    beginInsertRows(index, insert_pos, insert_pos);
    

    as well as some definitely wrong one

    beginInsertRows(index, 23, 42);
    

    The test itself seems to work since it detected an error somewhere else (when removing an item) however
    both version don't cause any errors detected by the test.

    edit:

    i've uncommented the output in ModelTest::rowsInserted() to get some details: This is what i get:

    For the first 3 insertations:

    rowsInserted start= 2 end= 2 oldsize= 2 parent= "TreeItem" current rowcount of parent= 13
    itemWasInserted: 2 QVariant(QString, "TreeItem")
    

    from then on:

    rowsInserted start= 5 end= 5 oldsize= 0 parent= "" current rowcount of parent= 0
    itemWasInserted: 5 QVariant(Invalid)
    

    until i collapse and expand the view. Then it works again for some items

    rowsInserted start= 2 end= 2 oldsize= 2 parent= "TreeItem" current rowcount of parent= 3
    itemWasInserted: 2 QVariant(QString, "TreeItem")
    rowsInserted start= 3 end= 3 oldsize= 3 parent= "TreeItem" current rowcount of parent= 4
    itemWasInserted: 3 QVariant(QString, "TreeItem")
    rowsInserted start= 4 end= 4 oldsize= 4 parent= "TreeItem" current rowcount of parent= 5
    itemWasInserted: 4 QVariant(QString, "TreeItem")
    rowsInserted start= 5 end= 5 oldsize= 0 parent= "" current rowcount of parent= 0
    itemWasInserted: 5 QVariant(Invalid)
    rowsInserted start= 6 end= 6 oldsize= 0 parent= "" current rowcount of parent= 0
    itemWasInserted: 6 QVariant(Invalid)
    rowsInserted start= 7 end= 7 oldsize= 0 parent= "" current rowcount of parent= 0
    itemWasInserted: 7 QVariant(Invalid)
    


  • What's childItem? how do you create it? What does TreeItem::appendChild() do? But most importantly, do you really need to waste your time in a custom model or could you just use QStandardItemModel instead?



  • appendChild() just appends the item to the parent item

    void TreeItem::appendChild(TreeItem *item)
    {
        m_child_items.push_back(item);
    }
    

    childItem is another TreeItem* it is crated in the method that is called to add a new TreeItem to the model.

        TreeItem *childItem = new TreeItem(&item);
        ...
        ...
        item.appendChild(childItem);
    

    My Model inherits from QAbstractItemModel. The reason i want to implement the insertion myself is as follows:

    • i have an eventFilter with a right-klick-menu that offers the possibility to insert several types of items to the model which all inherit from TreeItem
    • depending on the chosen action a signal is send to one of the "insert_item_slots" of the model
    • each of the slots then creates the demanded item and appends it to the parent


  • @gde23 said in Index for beginInsertRows() with QTreeView:

    (&item);
    item.

    This looks like item is on the stack. Is it the case?

    i have an eventFilter with a right-klick-menu

    Normally this is just achieved with setContextMenuPolicy(Qt::CustomContextMenu); and a slot connected to QWidget::customContextMenuRequested



  • Ok, i finally got it working.

    The solution looks like this:

    void TreeModel::addItem()
    {
        QModelIndex index = m_tree_view->currentIndex();
        TreeItem* parent_item = static_cast<TreeItem*>(index.internalPointer());
        if(parent_item == NULL)
        {
            return;
        }
        int c = parent_item->getChildCount();    
        beginInsertRows(QModelIndex(), index.row()+c, index.row()+c);
    
        TreeItem* child_item = new TreeIrem(parent_item);
        parent_item->appenChild(child_item);
        endInsertRows();
    }
    

    However I wonder why this is correct with the dummy QModelIndex() and not the real one?

    @VRonin thanks a lot for the help



  • This can't be working. This tries to append rows on the root item without checking if index is root.
    This, should work:

    void TreeModel::addItem()
    {
        QModelIndex index = m_tree_view->currentIndex();
        TreeItem* parent_item = static_cast<TreeItem*>(index.internalPointer());
        if(!parent_item)
            return;
        const int c = parent_item->getChildCount();    
        beginInsertRows(index.parent(), c, c);
        TreeItem* child_item = new TreeIrem(parent_item);
        parent_item->appenChild(child_item);
        endInsertRows();
    }
    


  • I have tested both versions and i don't really get it either, but with the version I posted everything behaves like it should.
    With the version you posted, after appending an item the tree (sometimes) collapses in some region.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.