Python 如何使用QFileSystemModel和QListView将文件项拖放到文件夹项中?

Python 如何使用QFileSystemModel和QListView将文件项拖放到文件夹项中?,python,pyside2,qlistview,qfilesystemmodel,Python,Pyside2,Qlistview,Qfilesystemmodel,我正在创建一个小部件来探索和管理qt应用程序中的文件。为了构造它,我使用了QFileSystemModel和QListView以及IconMode视图模式 它应该允许使用QListView将文件(项目)移动到文件夹(其他项目)中 我的问题是如何实现这一点 首先,我试图覆盖ContentFileSystemModel中的supportedDragActions和supportedDropActions函数,以允许移动和复制操作。另外,我重写了标志函数以启用拖放。最后,我覆盖了canDropMime

我正在创建一个小部件来探索和管理qt应用程序中的文件。为了构造它,我使用了
QFileSystemModel
QListView
以及
IconMode
视图模式

它应该允许使用
QListView
将文件(项目)移动到文件夹(其他项目)中

我的问题是如何实现这一点

首先,我试图覆盖
ContentFileSystemModel
中的
supportedDragActions
supportedDropActions
函数,以允许移动和复制操作。另外,我重写了
标志
函数以启用拖放。最后,我覆盖了
canDropMimeData
dropMimeData
,以检查它们是否正在运行,但看起来它们没有运行

第一个问题是,一旦在光标中显示禁止的图标(如下图所示),模型就不允许将项目文件放入
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)
您设置了错误的属性,因为您正在使用:

用户无法移动项目

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

lv_file_manager.setMovement(QListView.Static)
其他重要的实现在模型的
candromimedata()
(如果目标是可写目录,则必须返回
True
)和
dromimedata()
(这将实际移动文件)

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

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

  • flags()
    如果目标是文件,则不应返回
    ItemIsDragEnabled
  • setAcceptDrops(True)
    setDragEnabled(True)
    不是必需的,因为当移动不是
    Static
    时会自动设置它们(如上所述使用
    IconMode
    时就是这种情况)
  • setDragDropMode()
    也不是必需的
类内容文件系统模型(QFileSystemModel):
# ...
def标志(self,索引:QModelIndex)->Qt.ItemFlags:
defaultFlags=super(ContentFileSystemModel,self).flags(index)
如果不是index.isValid():
返回默认标志
fileInfo=self.fileInfo(索引)
如果fileInfo.isDir():
返回Qt.ItemIsDragEnabled | Qt.ItemIsDragEnabled | defaultFlags
elif fileInfo.isFile():
#文件应*不*启用拖放功能
返回Qt.ItemIsDragEnabled | defaultFlags
返回默认标志
def CANDROPMIMEMEDATA(自身,数据:qIMEDATA,动作:Qt.DropAction,
行:int,列:int,父级:QModelIndex)->bool:
如果行<0,列<0:
target=self.fileInfo(父级)
其他:
target=self.fileInfo(self.index(行、列、父级))
返回target.isDir()和target.iswriteable()
def dropMimeData(self,数据:QMimeData,操作:Qt.DropAction,
行:int,列:int,父级:QModelIndex)->bool:
如果行<0,列<0:
targetDir=QDir(self.fileInfo(parent.absoluteFilePath())
其他:
targetDir=QDir(self.fileInfo(self.index(行、列、父级)).absoluteFilePath())
数据列表=[]
#首先检查源是否可写(以便我们可以移动它)
#而且它在目标路径上还不存在
对于data.text().splitlines()中的url:
path=QUrl(url).toLocalFile()
fileObject=QFile(路径)
如果不是fileObject.permissions()&QFile.WriteUser:
返回错误
targetPath=targetDir.absoluteFilePath(QFileInfo(path).fileName())
如果targetDir.存在(targetPath):
返回错误
追加((fileObject,targetPath))
#实际移动对象时,可能需要添加一些反馈
#如果移动失败(例如,没有剩余空间),并最终撤消
#整体运作
对于fileObject,数据列表中的targetPath:
如果不是fileObject.rename(目标路径):
返回错误
返回真值
类文件视图(QListView):
def dragMoveEvent(自身,事件):
#仅当目标支撑下降时才接受拖曳运动
如果self.model().flags(self.indexAt(event.pos())&Qt.itemspropertable:
super().dragmovevent(事件)
其他:
event.ignore()
def总管(argv):
# ...
lv_file_manager=FileView()

谢谢,musicamante。当我删除该行时,项目可以自由移动到
QListView
中的其他位置。然而,一旦我想只允许将项目移动到文件夹中,这是一个不受欢迎的特性。无论如何,一旦模型允许删除项目并调用
candromimedata
函数,这就是进步。我将尝试将此放置限制在文件夹中。我将在这里报告任何进展。如果您想获得对放置目标的更多控制,必须覆盖视图上的
dropEvent
。不久前,我用Qt自己编写了一个小文件管理器,因此,如果您能更好地根据情况阐明程序应该如何运行,我可以给您一些建议,并可能编辑答案。我希望只允许将项目(文件和文件夹)拖到其他文件夹项目中。当用户将其放置在文件夹以外的其他位置时,不应发生任何事情。当用户将文件放入文件夹时,应该将其移动到文件夹中。当我说“移动”时,我的意思是从当前文件夹中删除文件并将其放入t
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()