拖放在“QFileModelSystem”中不起作用
Drag and Drop not working in `QFileModelSystem`
我正在尝试在 QFileSystemModel
中进行拖放操作,但由于我之前没有进行拖放操作的经验,所以我首先在 QTreeView
中进行了尝试。 (我附上了行为的视频)
现在我对我想要的行为没意见了,然后我只是将模型更改为 QFileSystemModel
但遗憾的是它不起作用。所以我尝试阅读 QFileSystemModel
, QTreeView
, and Drag and Drop from Qt
,最后得到以下代码:
我得到的代码:
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class MQTreeView(QTreeView):
def __init__(self, model):
super().__init__()
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
# self.setDragDropMode(QAbstractItemView.InternalMove)
self.setModel(model)
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setRootIndex(model.index(os.path.dirname(os.path.abspath("__file__"))))
self.setDefaultDropAction(Qt.MoveAction)
self.viewport().setAcceptDrops(True)
def dragEnterEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
return
event.ignore()
# return super().dragEnterEvent(event)
def dropEvent(self, event):
print("[drop event] - dropped")
if event.source():
QTreeView.dropEvent(self, event)
else:
ix = self.indexAt(event.pos())
model = self.model()
if ix.isValid():
if not model.isDir(ix):
ix = ix.parent() # In case of folder/Dir
pathDir = model.filePath(ix)
else:
# for empty drag and drop
pathDir = model.rootPath()
m = event.mimeData()
if m.hasUrls():
urlLocals = [url for url in m.urls() if url.isLocalFile()]
accepted = False
for urlLocal in urlLocals:
path = urlLocal.toLocalFile()
info = QFileInfo(path)
n_path = QDir(pathDir).filePath(info.fileName())
o_path = info.absoluteFilePath()
if n_path == o_path:
continue
if info.isDir():
QDir().rename(o_path, n_path)
else:
qfile = QFile(o_path)
if QFile(n_path).exists():
n_path += "(copy)"
qfile.rename(n_path)
print(f"added -> {info.fileName()}")
accepted = True
if accepted:
event.acceptProposedAction()
# return super().dropEvent(event)
class AppDemo(QWidget):
def __init__(self):
super().__init__()
# -- right -- #
self.model1 = QFileSystemModel()
self.model1.setRootPath(os.path.dirname(os.path.abspath("__file__")))
self.view1 = MQTreeView(self.model1)
# -- left -- #
self.model2 = QFileSystemModel()
self.model2.setRootPath(os.path.dirname(os.path.abspath("__file__")))
self.view2 = MQTreeView(self.model2)
# -- layout -- #
layout = QHBoxLayout(self)
layout.addWidget(self.view1)
layout.addWidget(self.view2)
app = QApplication(sys.argv)
main = AppDemo()
main.show()
app.exec_()
上面的代码仍然没有执行我想要的行为,但我很确定其他地方是错误的,它不是覆盖函数(dragEnterEvent
和 dropEvent
)。我最好的猜测是我没有正确设置 QTreeView
接受掉落的正确方式,尽管我不太确定。
我的问题:我的实现有什么问题?是我接受掉落的方式还是其他原因?
发现问题了!我没有覆盖 dragMoveEvent
方法。我需要覆盖 dragMoveEvent
以确保不会禁止拖动。
我需要接受 dragEnterEvent
中的所有拖动事件:
def dragEnterEvent(self, event):
event.accept()
然后我需要过滤dragMoveEvent
中的事件:
def dragMoveEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
print("[dropEnterEvent] - event accepted")
return
event.ignore()
我在下面附上了工作行为的视频和代码。
最终实现:
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class MQTreeView(QTreeView):
def __init__(self, model, path):
super().__init__()
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setModel(model)
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setRootIndex(model.index(path))
self.setDefaultDropAction(Qt.MoveAction)
self.viewport().setAcceptDrops(True)
def dragEnterEvent(self, event):
event.accept()
def dragMoveEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
print("[dropEnterEvent] - event accepted")
return
event.ignore()
def dropEvent(self, event):
print("[drop event] - dropped")
if event.source():
ix = self.indexAt(event.pos())
model = self.model()
if ix.isValid():
if not model.isDir(ix):
ix = ix.parent()
pathDir = model.filePath(ix)
else:
# for empty drag and drop
pathDir = model.rootPath()
m = event.mimeData()
if m.hasUrls():
urlLocals = [url for url in m.urls() if url.isLocalFile()]
accepted = False
for urlLocal in urlLocals:
path = urlLocal.toLocalFile()
info = QFileInfo(path)
destination = QDir(pathDir).filePath(info.fileName())
source = info.absoluteFilePath()
if destination == source:
continue # means they are in the same folder
if info.isDir():
QDir().rename(source, destination)
else:
qfile = QFile(source)
if QFile(destination).exists():
n_info = QFileInfo(destination)
destination = n_info.canonicalPath() + QDir.separator() + n_info.completeBaseName() + " (copy)"
if n_info.completeSuffix(): # for moving files without suffix
destination += "." + n_info.completeSuffix()
qfile.rename(destination)
print(f"added -> {info.fileName()}") # for debugging
accepted = True
if accepted:
event.acceptProposedAction()
class AppDemo(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
cwd = "test/"
nw = "test copy/"
# -- right -- #
self.model1 = QFileSystemModel()
self.model1.setRootPath(os.path.dirname(cwd))
self.view1 = MQTreeView(self.model1, cwd)
# -- left -- #
self.model2 = QFileSystemModel()
self.model2.setRootPath(os.path.dirname(nw))
self.view2 = MQTreeView(self.model2, nw)
# -- layout -- #
layout = QHBoxLayout(self)
layout.addWidget(self.view1)
layout.addWidget(self.view2)
app = QApplication(sys.argv)
main = AppDemo()
main.show()
app.exec_()
我正在尝试在 QFileSystemModel
中进行拖放操作,但由于我之前没有进行拖放操作的经验,所以我首先在 QTreeView
中进行了尝试。 (我附上了行为的视频)
现在我对我想要的行为没意见了,然后我只是将模型更改为 QFileSystemModel
但遗憾的是它不起作用。所以我尝试阅读 QFileSystemModel
, QTreeView
, and Drag and Drop from Qt
,最后得到以下代码:
我得到的代码:
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class MQTreeView(QTreeView):
def __init__(self, model):
super().__init__()
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
# self.setDragDropMode(QAbstractItemView.InternalMove)
self.setModel(model)
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setRootIndex(model.index(os.path.dirname(os.path.abspath("__file__"))))
self.setDefaultDropAction(Qt.MoveAction)
self.viewport().setAcceptDrops(True)
def dragEnterEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
return
event.ignore()
# return super().dragEnterEvent(event)
def dropEvent(self, event):
print("[drop event] - dropped")
if event.source():
QTreeView.dropEvent(self, event)
else:
ix = self.indexAt(event.pos())
model = self.model()
if ix.isValid():
if not model.isDir(ix):
ix = ix.parent() # In case of folder/Dir
pathDir = model.filePath(ix)
else:
# for empty drag and drop
pathDir = model.rootPath()
m = event.mimeData()
if m.hasUrls():
urlLocals = [url for url in m.urls() if url.isLocalFile()]
accepted = False
for urlLocal in urlLocals:
path = urlLocal.toLocalFile()
info = QFileInfo(path)
n_path = QDir(pathDir).filePath(info.fileName())
o_path = info.absoluteFilePath()
if n_path == o_path:
continue
if info.isDir():
QDir().rename(o_path, n_path)
else:
qfile = QFile(o_path)
if QFile(n_path).exists():
n_path += "(copy)"
qfile.rename(n_path)
print(f"added -> {info.fileName()}")
accepted = True
if accepted:
event.acceptProposedAction()
# return super().dropEvent(event)
class AppDemo(QWidget):
def __init__(self):
super().__init__()
# -- right -- #
self.model1 = QFileSystemModel()
self.model1.setRootPath(os.path.dirname(os.path.abspath("__file__")))
self.view1 = MQTreeView(self.model1)
# -- left -- #
self.model2 = QFileSystemModel()
self.model2.setRootPath(os.path.dirname(os.path.abspath("__file__")))
self.view2 = MQTreeView(self.model2)
# -- layout -- #
layout = QHBoxLayout(self)
layout.addWidget(self.view1)
layout.addWidget(self.view2)
app = QApplication(sys.argv)
main = AppDemo()
main.show()
app.exec_()
上面的代码仍然没有执行我想要的行为,但我很确定其他地方是错误的,它不是覆盖函数(dragEnterEvent
和 dropEvent
)。我最好的猜测是我没有正确设置 QTreeView
接受掉落的正确方式,尽管我不太确定。
我的问题:我的实现有什么问题?是我接受掉落的方式还是其他原因?
发现问题了!我没有覆盖 dragMoveEvent
方法。我需要覆盖 dragMoveEvent
以确保不会禁止拖动。
我需要接受 dragEnterEvent
中的所有拖动事件:
def dragEnterEvent(self, event):
event.accept()
然后我需要过滤dragMoveEvent
中的事件:
def dragMoveEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
print("[dropEnterEvent] - event accepted")
return
event.ignore()
我在下面附上了工作行为的视频和代码。
最终实现:
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class MQTreeView(QTreeView):
def __init__(self, model, path):
super().__init__()
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setModel(model)
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setRootIndex(model.index(path))
self.setDefaultDropAction(Qt.MoveAction)
self.viewport().setAcceptDrops(True)
def dragEnterEvent(self, event):
event.accept()
def dragMoveEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
print("[dropEnterEvent] - event accepted")
return
event.ignore()
def dropEvent(self, event):
print("[drop event] - dropped")
if event.source():
ix = self.indexAt(event.pos())
model = self.model()
if ix.isValid():
if not model.isDir(ix):
ix = ix.parent()
pathDir = model.filePath(ix)
else:
# for empty drag and drop
pathDir = model.rootPath()
m = event.mimeData()
if m.hasUrls():
urlLocals = [url for url in m.urls() if url.isLocalFile()]
accepted = False
for urlLocal in urlLocals:
path = urlLocal.toLocalFile()
info = QFileInfo(path)
destination = QDir(pathDir).filePath(info.fileName())
source = info.absoluteFilePath()
if destination == source:
continue # means they are in the same folder
if info.isDir():
QDir().rename(source, destination)
else:
qfile = QFile(source)
if QFile(destination).exists():
n_info = QFileInfo(destination)
destination = n_info.canonicalPath() + QDir.separator() + n_info.completeBaseName() + " (copy)"
if n_info.completeSuffix(): # for moving files without suffix
destination += "." + n_info.completeSuffix()
qfile.rename(destination)
print(f"added -> {info.fileName()}") # for debugging
accepted = True
if accepted:
event.acceptProposedAction()
class AppDemo(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
cwd = "test/"
nw = "test copy/"
# -- right -- #
self.model1 = QFileSystemModel()
self.model1.setRootPath(os.path.dirname(cwd))
self.view1 = MQTreeView(self.model1, cwd)
# -- left -- #
self.model2 = QFileSystemModel()
self.model2.setRootPath(os.path.dirname(nw))
self.view2 = MQTreeView(self.model2, nw)
# -- layout -- #
layout = QHBoxLayout(self)
layout.addWidget(self.view1)
layout.addWidget(self.view2)
app = QApplication(sys.argv)
main = AppDemo()
main.show()
app.exec_()