[Qt-interest] Item moves in tree models

Pavel Lebedev cygnus at michiru.ru
Sat Oct 9 04:23:38 CEST 2010


Hi, I've been trying to implement item moving in a tree item model with
QAbstractItemModel::beginMoveRows()/endMoveRows(), but haven't been able to
figure out why a rather simple case is failing. Here's a minimal example:

//-------------------------------------------------------------------------

#include <QtCore/QAbstractItemModel>
#include <QtGui/QApplication>
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
#include <QtGui/QTreeView>

class Model : public QAbstractItemModel
{
    Q_OBJECT
private:
    struct Node
    {
        QString name;
        Node* parent;
        QList<Node*> children;
    };

    Node* root;

    Node* nodeFromIndex(const QModelIndex& index) const
    {
        if(!index.isValid())
            return root;
        Node* parent = static_cast<Node*>(index.internalPointer());
        int row = index.row();
        return row>=0&&row<parent->children.count()?parent->children[index.row()]:0;
    }
public:
    Model(QObject* parent = 0)
        : QAbstractItemModel(parent)
    {
        root = new Node;
        root->parent = 0;
        for(int i=1;i<=2;++i){
            Node* node = new Node;
            node->name = tr("Node %1").arg(i);
            node->parent = root;
            root->children.append(node);
        }
    }

    void recurseDeleteChildren(Node* node)
    {
        for(int i=node->children.count()-1;i>=0;--i)
            recurseDeleteChildren(node->children[i]);
        delete node;
    }

    ~Model()
    {
        recurseDeleteChildren(root);
    }

    QModelIndex index(int row,int column,const QModelIndex& parent) const
    {
        Node* node = nodeFromIndex(parent);
        if(node&&row>=0&&row<node->children.count())
            return createIndex(row,column,node);
        return QModelIndex();
    }

    QModelIndex parent(const QModelIndex& index) const
    {
        Node* node = nodeFromIndex(index);
        if(node&&node!=root){
            Node* parent = node->parent;
            if(parent!=root){
                Node* grandParent = parent->parent;
                return createIndex(grandParent->children.indexOf(parent),0,grandParent);
            }
        }
        return QModelIndex();
    }

    int rowCount(const QModelIndex& index) const
    {
        Node* node = nodeFromIndex(index);
        return node?node->children.count():0;
    }

    int columnCount(const QModelIndex& index) const
    {
        return 1;
    }

    QVariant data(const QModelIndex& index,int role) const
    {
        if(role==Qt::DisplayRole){
            Node* node = nodeFromIndex(index);
            if(node)
                return node->name;
        }
        return QVariant();
    }

    void moveItem()
    {
        // Make first item a child of second item
        bool ok = beginMoveRows(
            QModelIndex(),            // Moving child of root
            0,                        // range from first child
            0,                        //                        to (same) first child, inclusive
            index(1,0,QModelIndex()), // destination is second child of root
            0                         // destination index is first child
        );
        Q_ASSERT(ok);
        Node* newParent = root->children[1];
        Node* movedNode = root->children.takeAt(0);
        movedNode->parent = newParent;
        newParent->children.append(movedNode);
        endMoveRows();
    }
};

class MainWindow : public QWidget
{
    Q_OBJECT
private:
    Model* model;
private slots:
    void buttonClicked()
    {
        model->moveItem();    
    }
public:
    MainWindow()
    {
        QVBoxLayout* layout = new QVBoxLayout(this);
        QTreeView* view = new QTreeView;
        model = new Model(this);
        view->setModel(model);
        view->setHeaderHidden(true);
        layout->addWidget(view);
        QPushButton* button = new QPushButton(tr("Move item"));
        connect(button,SIGNAL(clicked(bool)),SLOT(buttonClicked()));
        layout->addWidget(button);
    }
};

int main(int argc,char* argv[])
{
    QApplication a(argc,argv);
    MainWindow wnd;
    wnd.show();
    return a.exec();
}

//-------------------------------------------------------------------------

That is, in a model with two elements being children of the root, I'm trying
to move the first child to become a child of second element.
beginMoveRows() returns true, but inside endMoveRows(), when it seems to be
fixing up persistent model indexes, it calls back
Model::index(row = 0,column = 0,index = { row = 1, column = 0, pointer = root node }),
where index appears to refer to the second child, where it was before move took place,
but since this index is now invalid, an invalid QModelIndex is returned and a warning
message is printed:
QAbstractItemModel::endMoveRows:  Invalid index ( 0 , 0 ) in model Model(0xcba8e0)
While the view displays changed model correctly, application crashes upon termination.

Thanks in advance.

Pavel





More information about the Qt-interest-old mailing list