python - 如何使用 QFileSystemModel 和 QListView 将文件项拖放到文件夹项中?
问题描述
我正在创建一个小部件来探索和管理我的 qt 应用程序中的文件。为了构建它,我在视图模式下使用 aQFileSystemModel
和 a 。QListView
IconMode
它应该允许将文件(项目)移动到带有QListView
.
我的问题是如何实现这个?
首先,我试图覆盖supportedDragActions
andsupportedDropActions
函数ContentFileSystemModel
以允许移动和复制操作。另外,我重写了flags
启用拖放功能。最后,我覆盖canDropMimeData
anddropMimeData
来检查它们是否正在运行,但看起来它们没有。
第一个问题是模型不允许将项目文件拖放到该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)
其他重要的实现在模型中(如果目标是可写目录,则canDropMimeData()
必须返回)和(实际上将移动文件)。True
dropMimeData()
最后一步是覆盖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()
推荐阅读
- apache-kafka - Kafka regex-topics / 听多个主题的实际限制是什么
- computer-vision - Pytorch 加载保存的重量但不工作
- java - Play Framework 上的 Java Socket 打印在 Windows 中变慢
- javascript - 将剃刀页面显示为模式弹出窗口
- javascript - 获取触发事件的元素(节点)
- github - 将项目从github等导入到自己的IDE需要大量工作(有时根本不起作用)是否正常
- gtk3 - 如何使用 GtkSourceView 库应用样式
- c# - Binding generic data to DataGridView
- django - 如何从模型创建数据框(Django)
- pagination - Azure CosmosDB 意外分页行为