Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QFileSystemModel drop items issue



  • Hello! I have a QTreeView and QListView which uses QFileSystemModels and I want to implement drag and drop between those views. I already implemented dropMimeData between those views only for QClipboard operations. I have some questions.

    1. Any ideas how to implement it for drag and drops (what exactly should be reimplemented, some example)?
    2. 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 for QFile::copy and CopyFile (WinAPI) methods. How to fix it?
    3. Coping big files using dropMimeData freezes main GUI, the same for deleting big files using model->remove. Is there any option to enable threading or other techniques?
    4. dropMimeData return false for Qt::CopyAction the dirs. How to fix it?

    Thanks in advance for your help.


  • Lifetime Qt Champion

    Hi,

    There's a whole chapter of that here



  • @SGaist

    Ok. I will check it. Thanks.



  • Hello!

    I have reimplemented QFileSystemModel class to add the drag and drop feature between QTreeView and QListView but it still does not work. It displays the following dialog:

    image1

    #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 the ListView.

    ("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:
    2021-02-11_005901.png



  • 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:

    invalid_filename.gif

    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:
    example_1.gif

    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.


Log in to reply