0
votes

Why does deleting a QTreeWidgetItem* automatically advance iterators associated with the same QTreeWidget? This behavior does not appear to be documented anywhere that I looked. I would expect that after deleting a QTreeWidget* the iterator would be unaffected (other than now pointing to an invalid value). Instead it seems that the QTreeWidget destructor automatically advances all iterators for the QTreeWidget.

Looking briefly into the qt sources, I think this works by having all iterators register themselves with the model when they are created. Then in QTreeItem::~QTreeItem the call to QTreeModel::beginRemoveItems() calls QTreeWidgetItemIteratorPrivate::ensureValidIterator() which advances all of the iterators.

Other than directly inspecting all of the QT sources, how are you supposed to know this happens? Do all QT iterators act this way? I have more experience with STL iterators than with QT and perhaps that is why I found this so unexpected.

The code below demonstrates this behavior:

    #include <QTreeWidget>
    #include <QDebug>
    #include <QApplication>

    int main(int argc, char **argv)
    {
        QApplication a( argc, argv );
        QTreeWidget * tw_1 = new QTreeWidget();
        QTreeWidget * tw_2 = new QTreeWidget();
        for(int i = 1; i <=5; i++)
        {
                tw_1->addTopLevelItem(new QTreeWidgetItem(QStringList(QString("Item %1").arg(i))));
                tw_2->addTopLevelItem(new QTreeWidgetItem(QStringList(QString("Item %1").arg(i))));
        }

        {
        qDebug() << "Elements in Tree Widget 1";
        QTreeWidgetItemIterator it(tw_1);
        while(*it)
        {
                qDebug() << "\t" << (*it)->text(0);
                ++it;
        }

        //Delete the items while iterating over the tree
        QTreeWidgetItemIterator ii(tw_1);
        while(*ii)
        {
                delete *ii;
        }

        qDebug() << "Elements remaining in Tree Widget 1";
        QTreeWidgetItemIterator itt(tw_1);
        while(*itt)
        {
                qDebug() << (*itt)->text(0);
                ++itt;
        }
        }

        {
        qDebug() << "Elements in Tree Widget 2";
        QTreeWidgetItemIterator it(tw_2);
        while(*it)
        {
                qDebug() << "\t" << (*it)->text(0);
                ++it;
        }

        //Delete the items while iterating over the tree
        QTreeWidgetItemIterator ii(tw_2);
        while(*ii)
        {
                delete *ii;
                ++ii;
        }
           qDebug() << "Elements remaining in Tree Widget 2";
        QTreeWidgetItemIterator itt(tw_2);
        while(*itt)
        {                                                                                                                     
                qDebug() << (*itt)->text(0);                                                                                  
                ++itt;                                                                                                        
        }                                                                                                                     
        }                                                                                                                     
}  

This produces the output:

Elements in Tree Widget 1 
     "Item 1" 
     "Item 2" 
     "Item 3" 
     "Item 4" 
     "Item 5" 
Elements remaining in Tree Widget 1 
Elements in Tree Widget 2 
     "Item 1" 
     "Item 2" 
     "Item 3" 
     "Item 4" 
     "Item 5" 
Elements remaining in Tree Widget 2 
     "Item 2" 
     "Item 4"  
1

1 Answers

0
votes

The iterators are defined as a QList in QTreeModel.

QList is index based. If an item with current index is removed, the next item becomes the current index.

Refer below link. You will see definition for iterators as shown below.

https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidget_p.h.html#QTreeModel

You will see a definition like this

QList<QTreeWidgetItemIterator*> iterators;

Now, when you call delete on iterator, the destructor of QTreeWidgetItemIterator gets called.

https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidgetitemiterator.cpp.html#_ZN23QTreeWidgetItemIteratorD1Ev

QTreeWidgetItemIterator::~QTreeWidgetItemIterator() 
{ 
    d_func()->m_model->iterators.removeAll(this); 
} 

By noticing the above code, the iterator gets deleted using QList function removeAll and the iterator is removed from the list completely.

QList is index based. So if an iterator which has index 3 is deleted, the next iterator index becomes 3.

Now when you do ++,

below function gets called, which brings the next item in the list. So you iterator is moved to next item (index 4).

https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidgetitemiterator.cpp.html#_ZN23QTreeWidgetItemIteratorD1Ev

QTreeWidgetItemIterator &QTreeWidgetItemIterator::operator++() 
{ 

    if (current) 
        do { 
            current = d_func()->next(current); 
        } while (current && !matchesFlags(current)); 
    return *this; 
} 

QTreeWidgetItemIterator of the model are maintained as QList. So when you remove an iterator, the next iterator becomes current index. So you do notice such behavior.