首页 > 解决方案 > 如何使用 QFileSystemModel 和 QListView 将文件项拖放到文件夹项中?

问题描述

我正在创建一个小部件来探索和管理我的 qt 应用程序中的文件。为了构建它,我在视图模式下使用 aQFileSystemModel和 a 。QListViewIconMode

它应该允许将文件(项目)移动到带有QListView.

我的问题是如何实现这个?

首先,我试图覆盖supportedDragActionsandsupportedDropActions函数ContentFileSystemModel以允许移动和复制操作。另外,我重写了flags启用拖放功能。最后,我覆盖canDropMimeDataanddropMimeData来检查它们是否正在运行,但看起来它们没有。

第一个问题是模型不允许将项目文件拖放到该QListView区域中,一旦它在光标中显示一个禁止图标(如下图所示)。

首先,我必须设置模型以允许在文件夹中放置项目。之后,我可以实现将拖动的项目转移到文件夹中的代码。

在此处输入图像描述

准备重现问题的代码:

import sys
import os

from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *


class ContentFileSystemModel(QFileSystemModel):

    def __init__(self):
        super(ContentFileSystemModel, self).__init__()

    def supportedDragActions(self) -> Qt.DropActions:
        print("supportedDragActions")
        return Qt.MoveAction | super(ContentFileSystemModel, self).supportedDragActions() | Qt.CopyAction

    def supportedDropActions(self) -> Qt.DropActions:
        print("supportedDropActions")
        return Qt.MoveAction | super(ContentFileSystemModel, self).supportedDropActions() | Qt.CopyAction

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        defaultFlags = super(ContentFileSystemModel, self).flags(index)
        if not index.isValid():
            return defaultFlags
        fileInfo = self.fileInfo(index)
        # The target
        if fileInfo.isDir():
            # Allowed drop
            return Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled | defaultFlags
        # The source: should be directory( in that case)
        elif fileInfo.isFile():
            # Allowed drag
            return Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled | defaultFlags
        return defaultFlags

    def canDropMimeData(self, data: QMimeData, action: Qt.DropAction,
                        row: int, column: int, parent: QModelIndex) -> bool:
        print("canDropMimeData")
        return True

    def dropMimeData(self, data: QMimeData, action: Qt.DropAction,
                     row: int, column: int, parent: QModelIndex) -> bool:
        print("dropMimeData")
        return True


def main(argv):
    app = QApplication(sys.argv)

    path = "C:\\Users\\Me\\Desktop"
    file_system_model = ContentFileSystemModel()
    file_system_model.setRootPath(path)
    file_system_model.setReadOnly(False)

    lv_file_manager = QListView()
    lv_file_manager.setModel(file_system_model)
    lv_file_manager.setViewMode(QListView.IconMode)
    lv_file_manager.setRootIndex(file_system_model.index(path))
    lv_file_manager.setResizeMode(QListView.Adjust)

    lv_file_manager.setMovement(QListView.Static)
    lv_file_manager.setSelectionMode(QAbstractItemView.ExtendedSelection)
    lv_file_manager.setWrapping(True)
    lv_file_manager.setAcceptDrops(True)
    lv_file_manager.setDragEnabled(True)
    lv_file_manager.setDropIndicatorShown(True)
    lv_file_manager.setUniformItemSizes(True)
    lv_file_manager.setDragDropMode(QAbstractItemView.InternalMove)
    lv_file_manager.setFlow(QListView.LeftToRight)

    lv_file_manager.show()
    app.exec_()


if __name__ == "__main__":
    main(sys.argv)

标签: pythonpyside2qlistviewqfilesystemmodel

解决方案


您正在设置错误的移动属性,因为您正在使用Static

用户不能移动项目。

使用 时IconMode,该属性会自动设置为Free,因此您只需删除以下行:

lv_file_manager.setMovement(QListView.Static)

其他重要的实现在模型中(如果目标是可写目录,则canDropMimeData()必须返回)和(实际上将移动文件)。TruedropMimeData()

最后一步是覆盖dragMoveEvent()以防止在当前视图周围移动图标。

请注意,还进行了以下更改:

  • flags()如果ItemIsDragEnabled目标是文件,则不应返回;
  • setAcceptDrops(True)并且setDragEnabled(True)不是必需的,因为它们会在 Static移动时自动设置(使用上述IconMode说明时就是这种情况);
  • setDragDropMode()也不需要;
class ContentFileSystemModel(QFileSystemModel):
    # ...
    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        defaultFlags = super(ContentFileSystemModel, self).flags(index)
        if not index.isValid():
            return defaultFlags
        fileInfo = self.fileInfo(index)
        if fileInfo.isDir():
            return Qt.ItemIsDropEnabled | Qt.ItemIsDragEnabled | defaultFlags
        elif fileInfo.isFile():
            # files should *not* be drop enabled
            return Qt.ItemIsDragEnabled | defaultFlags
        return defaultFlags

    def canDropMimeData(self, data: QMimeData, action: Qt.DropAction,
                        row: int, column: int, parent: QModelIndex) -> bool:
        if row < 0 and column < 0:
            target = self.fileInfo(parent)
        else:
            target = self.fileInfo(self.index(row, column, parent))
        return target.isDir() and target.isWritable()

    def dropMimeData(self, data: QMimeData, action: Qt.DropAction,
                     row: int, column: int, parent: QModelIndex) -> bool:
        if row < 0 and column < 0:
            targetDir = QDir(self.fileInfo(parent).absoluteFilePath())
        else:
            targetDir = QDir(self.fileInfo(self.index(row, column, parent)).absoluteFilePath())
        dataList = []
        # first check if the source is writable (so that we can move it) 
        # and that it doesn't already exist on the target path
        for url in data.text().splitlines():
            path = QUrl(url).toLocalFile()
            fileObject = QFile(path)
            if not fileObject.permissions() & QFile.WriteUser:
                return False
            targetPath = targetDir.absoluteFilePath(QFileInfo(path).fileName())
            if targetDir.exists(targetPath):
                return False
            dataList.append((fileObject, targetPath))
        # actually move the objects, you might want to add some feedback
        # if movement failed (eg, no space left) and eventually undo the
        # whole operation
        for fileObject, targetPath in dataList:
            if not fileObject.rename(targetPath):
                return False
        return True

class FileView(QListView):
    def dragMoveEvent(self, event):
        # accept drag movements only if the target supports drops
        if self.model().flags(self.indexAt(event.pos())) & Qt.ItemIsDropEnabled:
            super().dragMoveEvent(event)
        else:
            event.ignore()


def main(argv):
    # ...
    lv_file_manager = FileView()

推荐阅读