Qt View, QAbstractItemModel and Qt::MoveAction
-
Hello, I am using a QTreeView and an inherited QAbstractItemModel that supports only Qt::MoveAction. My problem is that when I attempt to move something, it doesn't really "move". What ends up happening is that I see that removeRows gets called and this is not good, because all of the children of the "moved" object aren't transferred.
Therefore, my question is if it possible to make moveRows get called instead. I can't call it manually, as I don't know which object is being moved where (as mimeData and dropMimeData hold that information but execute separately). I've also tried to store the items that get modified but the compiler doesn't let me store const variables.
-
Hi and welcome to DevNet,
Do you want to do internal moves or moving from one widget to another ?
-
I'm speaking about internal moves in this case. It works so far, but not when I move an item that has children, as the children are deleted. I don't want the original item to get removed, just moved. Is this not possible within a QAbstractItemModel?
It seems as if removeRows is called no matter what. I want it to call moveRows if I'm moving an item instead.
-
Since you have a custom model, it would be a good idea to share your code so it can be analyzed to see what can go wrong. i.e. did you set the view to only do internal moves ? Did you use beginMoveRows/Colums and endMoveRows/Colums etc...
-
My TreeView Creation:
@QTreeView view;
view.setModel(&model);
view.setWindowTitle("Simple Tree Model");
view.setAcceptDrops(true);
view.setDragEnabled(true);
view.setDropIndicatorShown(true);
view.setDragDropOverwriteMode(false);
view.setDefaultDropAction(Qt::MoveAction);
view.setDragDropMode(QTreeView::InternalMove);
view.show();@And in my simple model, I have:
@Qt::DropActions TreeModel::supportedDropActions() const
{
return Qt::MoveAction;
}QStringList TreeModel::mimeTypes() const
{
return QString("text/uri-list|Item|text/plain").split("|");
}QMimeData* TreeModel::mimeData(const QModelIndexList &indexes) const
{
if (indexes.size() != 1)
{
return NULL;
}
QMimeData* mimedata = new QMimeData();
mimedata->setData("Item", "The data.");
return mimedata;
}bool TreeModel::dropMimeData(const QMimeData data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (!parent.isValid())
{
qDebug() << "Dropping into the root is disabled.";
return false;
}
else
{
qDebug() << "Dropping into" << static_cast<TreeItem>(parent.internalPointer())->data();
// I have to do the insert here but don't want to remove and insert. I want to move.
}
return true;
}bool TreeModel::removeRows(int row, int count, const QModelIndex &parent)
{
qDebug() << "Removed.";
return true;
}bool TreeModel::insertRows(int row, int count, const QModelIndex &parent)
{
qDebug() << "Inserted.";
return true;
}bool TreeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
qDebug() << "Moved.";
return true;
}@I built this off of the simple tree model example. Even though my tree only supports moving, it still shows "Removed" after I move something.
I could attempt to make a minimal example showing the problem if requested.
-
I made a single source code to show the problem (divided into 2 posts, because it's long). The application should call moveRows not removeRows/insertRows, since MoveAction is the default action:
@#include <QList>
#include <QVariant>
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QStringList>
#include <QVariant>
#include <QMimeData>
#include <QDebug>
#include <QtWidgets>class TreeItem
{
public:
TreeItem(const QList<QVariant> &data, TreeItem *parent = 0);
~TreeItem();void appendChild(TreeItem *child); TreeItem *child(int row); int childCount() const; int columnCount() const; QVariant data(int column) const; int row() const; TreeItem *parent();
private:
QList<TreeItem*> childItems;
QList<QVariant> itemData;
TreeItem *parentItem;
};TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
{
parentItem = parent;
itemData = data;
}TreeItem::~TreeItem()
{
qDeleteAll(childItems);
}void TreeItem::appendChild(TreeItem *item)
{
childItems.append(item);
}TreeItem *TreeItem::child(int row)
{
return childItems.value(row);
}int TreeItem::childCount() const
{
return childItems.count();
}int TreeItem::columnCount() const
{
return itemData.count();
}QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}TreeItem *TreeItem::parent()
{
return parentItem;
}int TreeItem::row() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));return 0;
}
class TreeItem;
class TreeModel : public QAbstractItemModel
{
Q_OBJECTpublic:
TreeModel(const QString &data, QObject *parent = 0);
~TreeModel();QVariant data(const QModelIndex &index, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QStringList mimeTypes () const; QMimeData* mimeData(const QModelIndexList &indexes) const; bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex &parent); bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild); bool removeRows( int row, int count, const QModelIndex & parent = QModelIndex()); bool insertRows(int row, int count, const QModelIndex &parent); Qt::DropActions supportedDropActions() const;
private:
TreeItem *rootItem;
};TreeModel::TreeModel(const QString &data, QObject *parent) : QAbstractItemModel(parent)
{
QList<QVariant> rootData;
rootData << "Title";
rootItem = new TreeItem(rootData);
QList<QVariant> otherData;
otherData << "Item 2";
rootItem->appendChild(new TreeItem(otherData, rootItem));
otherData.replace(0, "Item 3");
rootItem->appendChild(new TreeItem(otherData, rootItem));
otherData.replace(0, "Item 4");
rootItem->appendChild(new TreeItem(otherData, rootItem));
otherData.replace(0, "Item X");
rootItem->child(2)->appendChild(new TreeItem(otherData, rootItem->child(2)));
otherData.replace(0, "Item A");
rootItem->child(2)->child(0)->appendChild(new TreeItem(otherData, rootItem->child(2)->child(0)));
}
@ -
@TreeModel::~TreeModel()
{
delete rootItem;
}int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();if (role != Qt::DisplayRole) return QVariant(); TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); return item->data(index.column());
}
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
}QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);return QVariant();
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();TreeItem *parentItem; if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); TreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer()); TreeItem *parentItem = childItem->parent(); if (parentItem == rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem);
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
return 0;if (!parent.isValid()) parentItem = rootItem; else parentItem = static_cast<TreeItem*>(parent.internalPointer()); return parentItem->childCount();
}
Qt::DropActions TreeModel::supportedDropActions() const
{
return Qt::MoveAction;
}QStringList TreeModel::mimeTypes() const
{
return QStringList("data");
}QMimeData* TreeModel::mimeData(const QModelIndexList &indexes) const
{
if (indexes.size() != 1)
{
return NULL;
}
QMimeData* mimedata = new QMimeData();
mimedata->setData("data", "testing");
return mimedata;
}bool TreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
qDebug() << "Drop Confirmed";
return true;
}bool TreeModel::removeRows(int row, int count, const QModelIndex &parent)
{
qDebug() << "Removed.";
return true;
}bool TreeModel::insertRows(int row, int count, const QModelIndex &parent)
{
qDebug() << "Inserted.";
return true;
}bool TreeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
qDebug() << "Moved.";
return true;
}int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(simpletreemodel);QApplication app(argc, argv); TreeModel model("Item1"); QTreeView view; view.setModel(&model); view.setWindowTitle("Simple Tree Model"); view.setAcceptDrops(true); view.setDragEnabled(true); view.setDropIndicatorShown(true); view.setDragDropOverwriteMode(false); view.setDefaultDropAction(Qt::MoveAction); view.setDragDropMode(QTreeView::InternalMove); view.show(); return app.exec();
}
@
-
This might be a dumb observation on a somewhat stale thread. But I was looking into a similar issue and realized that the QAbstractItemModel::moveRows virtual function is new in Qt5. So If you're using an older version of Qt I guess (although I haven't actually confirmed yet) that moves are implemented in terms of a remove and an insert. In which case, the behaviour described by the OP would be expected.
-
view.setDragDropOverwriteMode(true);
And raw will not be deleted.