如何使用 QFileSystemModel 和 QListView 将文件项目拖放到文件夹项目中?
How to drag and drop files items inside a folder item using QFileSystemModel and QListView?
我正在创建一个小部件来浏览和管理我的 qt 应用程序中的文件。为了构建它,我使用 QFileSystemModel
和 QListView
以及 IconMode
视图模式。
它应该允许使用 QListView
.
将文件(项目)移动到文件夹(其他项目)中
我的问题是如何实现它?
首先,我试图覆盖 ContentFileSystemModel
中的 supportedDragActions
和 supportedDropActions
函数以允许移动和复制操作。此外,我重写了 flags
函数以启用拖放功能。最后,我重写 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)
你设置错了movement property, since you're using Static
:
The items cannot be moved by the user.
当使用 IconMode
时,属性 会自动设置为 Free
,因此您只需 删除 以下行:
lv_file_manager.setMovement(QListView.Static)
其他重要的实现在模型的 canDropMimeData()
(如果目标是可写目录,则必须 return True
)和 dropMimeData()
(实际上会移动文件)。
最后一步是覆盖 dragMoveEvent()
以防止在当前视图周围移动图标。
请注意,还进行了以下更改:
flags()
应该 而不是 return ItemIsDragEnabled
如果目标是一个文件;
setAcceptDrops(True)
和 setDragEnabled(True)
不是必需的,因为当移动 not 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()
我正在创建一个小部件来浏览和管理我的 qt 应用程序中的文件。为了构建它,我使用 QFileSystemModel
和 QListView
以及 IconMode
视图模式。
它应该允许使用 QListView
.
我的问题是如何实现它?
首先,我试图覆盖 ContentFileSystemModel
中的 supportedDragActions
和 supportedDropActions
函数以允许移动和复制操作。此外,我重写了 flags
函数以启用拖放功能。最后,我重写 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)
你设置错了movement property, since you're using Static
:
The items cannot be moved by the user.
当使用 IconMode
时,属性 会自动设置为 Free
,因此您只需 删除 以下行:
lv_file_manager.setMovement(QListView.Static)
其他重要的实现在模型的 canDropMimeData()
(如果目标是可写目录,则必须 return True
)和 dropMimeData()
(实际上会移动文件)。
最后一步是覆盖 dragMoveEvent()
以防止在当前视图周围移动图标。
请注意,还进行了以下更改:
flags()
应该 而不是 returnItemIsDragEnabled
如果目标是一个文件;setAcceptDrops(True)
和setDragEnabled(True)
不是必需的,因为当移动 notStatic
时它们会自动设置(当如上所述使用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()