Solved 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 callsbeginInsertRows
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 beconst 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 bebeginInsertRows(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
andmodeltest.cpp
. When you create the model add a line with something likenew 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 doesTreeItem::appendChild()
do? But most importantly, do you really need to waste your time in a custom model or could you just useQStandardItemModel
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 toQWidget::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. -
@VRonin Ok, so, finally your version was right. It didn't make a lot of sense that it worked with the one i posted.
The reason why it did was, that my method to get the current index was broken and leaded to some strange behavior.
Thanks again for the help.