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