Important: Please read the Qt Code of Conduct -

QTreeView, QAbstractItemModel and internal move

  • Hi everyone,

    I try to implement an internal move of items within a QTreeView with the mouse.

    My model inherits from QAbstractItemModel with 2 columns. Each items of my model have a boolean, a name, the pointer to the parent and list of children. In the future, I intend to add other data inside my items.

    In my model, I set the flags for each valid QModelIndex to :Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;

    I implemented moveRows and moveColumns methods inside my custom Model.

    Unfortunately those methods are never called by the view.

    For information, I enable the drag and drop on the QTreeView with setDragEnable(true) and setDragDropMode(Qt::InternalMove).

    Do I need to implement all the mimeData functions just to move some data?

    I can understand the reason of mimeData for drag and drop between widgets but in my case I just want to change the parent. I don't find any documentation/example that explains type of drag and drop move.


    Edit: I forgot to mention my environment: Windows 10/Mac OS X High Sierra, Qt 5.10.1, Visual studio 17, Clang

  • Hi,

    I think you need to implement dropMineData in the model too.
    And call moveRow in it.

  • Moderators

    moveRows() and moveColumns() are not called by QAbstractItemModel. Honestly i do not know what they are used for.

    Nevertheless the item view calls dropMimeData() on the model. The default implementation then calls decodeData().
    Check the implementation of decodeData() to see what functions are called. Those are the ones you need to reimplement (like insertRows(), ...). Then setData() is called on them. So by default the moving is "faked".

  • Thanks for your answers.

    @mpergand I try to call moveRow in it but there is no change. Worse: my data are not shown anymore ^^

    @raven-worx I also try to use the default implementation of dropMimeData but when the moved item has some children, then only the item is shown without his children (the data are not corrupted, only the view).

    I already re-implement insertRows but it didn't work.

    I finally succeed by using QStandardItemModel and a custom Item that inherits from QStandardItem. There is some methods to re-implement: QStandardItem *clone() const; void read(QDataStream &in); void write(QDataStream &out) const;

    I found on the Internet a pretty convenient way to put some custom data as pointer inside a QDataStream:

    QDataStream & operator << (QDataStream & s, const Data *& data)
    	qulonglong ptrval(*reinterpret_cast<qulonglong *>(&data));
    	return s << ptrval;
    QDataStream & operator >> (QDataStream & s, Data & data)
    	qulonglong ptrval;
    	s >> ptrval;
    	data = **reinterpret_cast<Data **>(&ptrval);
    	return s;

    I don't know if it's the best way but it's working.

  • @Athius
    Sure it works, here my code for a bookmark tree:

    bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
    { Q_UNUSED(column)
    	if(action EQ Qt::IgnoreAction) return false;
             if(action EQ Qt::MoveAction)
                        QByteArray bytes=data->data(MIME_Bookmark);
                        QDataStream stream(&bytes,QIODevice::QIODevice::ReadOnly);
                        qintptr i; int r,c; 
                        QModelIndex index=createIndex(r,c,i);
    	return true;

    Of course you need to create and return your custom data in mimeData () as well.