Solved QFileSystemModel drop items issue
-
Hello! I have a
QTreeView
andQListView
which usesQFileSystemModels
and I want to implement drag and drop between those views. I already implementeddropMimeData
between those views only forQClipboard
operations. I have some questions.- Any ideas how to implement it for drag and drops (what exactly should be reimplemented, some example)?
- When move or copy files from one location to another (names are unique)
dropMimeData
returns false (and I think no operation is done), the same forQFile::copy
andCopyFile (WinAPI)
methods. How to fix it? - Coping big files using
dropMimeData
freezes mainGUI
, the same for deleting big files usingmodel->remove
. Is there any option to enable threading or other techniques? dropMimeData
return false forQt::CopyAction
the dirs. How to fix it?
Thanks in advance for your help.
-
Hello!
I have fixed the issue by reimplementing the
QFileSystemModel
and using the nativeWin API
method -SHFileOperationW
. It displays the progress dialog and allows to transfer big files without program freeze.
The issue is resolved. -
Hi,
There's a whole chapter of that here
-
Ok. I will check it. Thanks.
-
Hello!
I have reimplemented
QFileSystemModel
class to add the drag and drop feature betweenQTreeView
andQListView
but it still does not work. It displays the following dialog:#ifndef EXPLORERMODEL_H #define EXPLORERMODEL_H #include <QObject> #include <QFileSystemModel> #include <QMimeData> #include <QBrush> #include <QDebug> class ExplorerModel : public QFileSystemModel { Q_OBJECT public: using QFileSystemModel::QFileSystemModel; Qt::DropActions supportedDragActions() const; Qt::DropActions supportedDropActions() const; Qt::ItemFlags flags(const QModelIndex &index) 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); private: bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); }; #endif // EXPLORERMODEL_H
#include "explorermodel.h" Qt::DropActions ExplorerModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions ExplorerModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::ItemFlags ExplorerModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QFileSystemModel::flags(index); if (index.isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; } else { return Qt::ItemIsDropEnabled | defaultFlags; } } QStringList ExplorerModel::mimeTypes() const { QStringList types; types << "application/octet-stream"; return types; } QMimeData *ExplorerModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const QModelIndex &index : indexes) { if (index.isValid()) { QString text = data(index, Qt::DisplayRole).toString(); stream << text; } } mimeData->setData("application/octet-stream", encodedData); return mimeData; } bool ExplorerModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(action); Q_UNUSED(row); Q_UNUSED(parent); qDebug() << data->formats(); if (!data->hasFormat("application/octet-stream")) { return false; } if (column > 0) { return false; } return true; } bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) { qDebug() << "Test..."; return false; } if (action == Qt::IgnoreAction) { return true; } int beginRow; if (row != -1) { beginRow = row; } else if (parent.isValid()) { beginRow = parent.row(); } else { beginRow = rowCount(QModelIndex()); } QByteArray encodedData = data->data("application/octet-stream"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QStringList newItems; int rows = 0; while (!stream.atEnd()) { QString text; stream >> text; newItems << text; ++rows; } insertRows(beginRow, rows, QModelIndex()); for (const QString &text : qAsConst(newItems)) { QModelIndex idx = index(beginRow, 0, QModelIndex()); setData(idx, text); beginRow++; } return true; }
When droping simple .txt file from
TreeView
to theListView
.("application/octet-stream") QModelIndex(2,0,0x2ad605a4350,ExplorerModel(0x2ad604e4cd0)) | "New Text Document.txt" QModelIndex(3,0,0x2ad605a41c0,ExplorerModel(0x2ad604e4cd0)) | "17 bytes" QModelIndex(-1,-1,0x0,QObject(0x0)) | "txt File" QModelIndex(-1,-1,0x0,QObject(0x0)) | "2/10/2021 10:57 PM"
It does not copy or move items. Any ideas how to fix it? Thanks.
-
I have found out that the problem is in the
dropMimeData
method. So, I have changed it to:bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) { return false; } if (action == Qt::IgnoreAction) { return true; } int beginRow; if (row != -1) { beginRow = row; } else if (parent.isValid()) { beginRow = parent.row(); } else { beginRow = rowCount(QModelIndex()); } removeRow(row, parent); insertRow(beginRow, parent); QModelIndex idx = index(beginRow, 0, QModelIndex()); setData(idx, data); return true; }
Now, it displays:
-
So, now I changed 2 methods:
QMimeData *ExplorerModel::mimeData(const QModelIndexList &indexes) const { QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); if (indexes.first().isValid()) { QString text = data(indexes.first(), Qt::DisplayRole).toString(); qDebug() << text; stream << text; } mimeData->setData("application/octet-stream", encodedData); return mimeData; } bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) { return false; } if (action == Qt::IgnoreAction) { return true; } int beginRow; if (row != -1) { beginRow = row; } else if (parent.isValid()) { beginRow = parent.row(); } else { beginRow = rowCount(QModelIndex()); } QByteArray encodedData = data->data("application/octet-stream"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QString text; while (!stream.atEnd()) { stream >> text; } //removeRow(row, parent); insertRow(beginRow, parent); QModelIndex idx = index(beginRow, 0, QModelIndex()); setData(idx, text); return true; }
It fixed the issue with the wrong indexes which lead to the incorrect name. But it still displays this notification:
Any ideas what could cause such issue? Thanks.
-
I have added the
setData
method:bool ExplorerModel::setData(const QModelIndex &index, const QVariant &value, int role) { qDebug() << "setData...."; emit dataChanged(index, index, {Qt::UserRole}); return QFileSystemModel::setData(index, value, role); }
Now, it does not display any message, but still the file is not copied/moved.
-
I have found the solution. There is no need to override all those methods, but only one.
Code:
Qt::ItemFlags ExplorerModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QFileSystemModel::flags(index); if (!index.isValid()) { return defaultFlags; } const QFileInfo &fileInfo = this->fileInfo(index); if (fileInfo.isDir()) { // The target // allowed drop return Qt::ItemIsDropEnabled | defaultFlags; } else if (fileInfo.isFile()) { // The source: should be directory (in that case) // allowed drag return Qt::ItemIsDragEnabled | defaultFlags; } return defaultFlags; }
Screenshot:
Now, it copies the files successfully. But there is another issue, when the file size is >= 1 GB the program freezes. Is there any way to enable the threading for copy/cut operation or display the progress dialog in the model? Thank you.
-
Hello!
I have fixed the issue by reimplementing the
QFileSystemModel
and using the nativeWin API
method -SHFileOperationW
. It displays the progress dialog and allows to transfer big files without program freeze.
The issue is resolved.