在“QTreeView”中删除项目时实现复制和移动
Implementing copy and move when dropping an Item in `QTreeView`
我修改了整个问题,因为我想要的行为很难实现和实际使用。
我正在尝试模仿文件资源管理器中的行为,当我在拖动时按 Shift
时,文件将被移动而不是被复制。
这是我要模仿的行为:
行为: 是我正在使用 LeftClick
进行选择和拖动。
关于行为本身:
我覆盖了 mousePressEvent
和 mouseMoveEvent
以开始拖动。创建拖动后,它使用 QTimer 检测我是否按下了 Control
和 Shift
修饰符。一旦检测到修改器,它就会使用 setDefaultDropAction
设置默认的放置操作。 (我想我应该使用 setDropAction
但它仅在 dragMoveEvent
中可用,我在 QDrag Class 中使用它)
问题:
部分行为现在有效,但仍然存在一些问题。
- 即使我按下
Shift
,DropIndicator 也没有从 +
变为 ->
- 与上述问题相关,即使我按下
Shift
键,dropAction 也只是 copyAction
而不是 moveAction
。
我的问题: 是什么导致了这些问题?我的直觉告诉我,我应该使用 setDropAction
而不是 setDefaultDropAction
,但同样它仅在 dragMoveEvent
中可用
我的测试代码:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ModifiedQDrag(QDrag):
def __init__(self, source):
super().__init__(source)
self.timer = QTimer(self)
self.timer.timeout.connect(self.process_event)
self.timer.setInterval(100)
self.timer.start()
def process_event(self):
if qApp.keyboardModifiers() & Qt.ControlModifier:
self.source().setDefaultDropAction(Qt.CopyAction)
elif qApp.keyboardModifiers() & Qt.ShiftModifier:
print("shift pressed")
self.source().setDefaultDropAction(Qt.MoveAction)
class Tree(QTreeView):
def __init__(self):
super().__init__()
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setDropIndicatorShown(True)
self.viewport().setAcceptDrops(True)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# -- mouse dragging -- #
def mousePressEvent(self, event):
if event.button() == Qt.RightButton:
self.dragStartPosition = event.pos()
return super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() != Qt.RightButton:
return
if ((event.pos() - self.dragStartPosition).manhattanLength() < QApplication.startDragDistance()):
return
drag = ModifiedQDrag(self)
mimeData = QMimeData()
mimeData = self.model().mimeData([self.indexAt(event.pos())])
drag.setMimeData(mimeData)
dragAction = drag.exec(Qt.MoveAction | Qt.CopyAction, Qt.CopyAction)
return super().mouseMoveEvent(event)
def dragMoveEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
return
event.ignore()
def dropEvent(self, event):
print("[drop event] - dropped")
class FileSystemView(QWidget):
def __init__(self):
super().__init__()
# -- left side -- #
left_side_dir = r"<Dir>"
self.model = QFileSystemModel()
self.model.setRootPath(left_side_dir)
self.tree = Tree()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(left_side_dir))
# -- right side -- #
right_side_dir = r"<Dir>"
self.model2 = QFileSystemModel()
self.model2.setRootPath(right_side_dir)
self.tree2 = Tree()
self.tree2.setModel(self.model2)
self.tree2.setRootIndex(self.model2.index(right_side_dir))
# -- layout -- #
self.tree_layout = QHBoxLayout()
self.tree_layout.addWidget(self.tree)
self.tree_layout.addWidget(self.tree2)
self.setLayout(self.tree_layout)
app = QApplication(sys.argv)
demo = FileSystemView()
demo.show()
sys.exit(app.exec_())
Qt只能对鼠标移动作出反应,以触发拖放动作的变化:顾名思义,dragMoveEvent()
只能通过鼠标调用move .
考虑到这一点,一个可能的解决方案是在键盘修改器更改时手动强制移动鼠标。这样你甚至不需要创建一个 QDrag 子类,你可以保持默认行为。
请注意,要正确获取修饰符,您不应使用 keyboardModifiers()
,而应使用 queryKeyboardModifiers()
,因为第一个仅在直接处理键盘事件时才可靠,并且可能不会使用实际更新键盘的当前状态。
class Tree(QTreeView):
# ...
def checkDrag(self):
modifiers = qApp.queryKeyboardModifiers()
if self.modifiers != modifiers:
self.modifiers = modifiers
pos = QCursor.pos()
# slightly move the mouse to trigger dragMoveEvent
QCursor.setPos(pos + QPoint(1, 1))
# restore the previous position
QCursor.setPos(pos)
def mouseMoveEvent(self, event):
if event.buttons() != Qt.RightButton:
return
if ((event.pos() - self.dragStartPosition).manhattanLength() < QApplication.startDragDistance()):
return
self.modifiers = qApp.queryKeyboardModifiers()
# a local timer, it will be deleted when the function returns
dragTimer = QTimer(interval=100, timeout=self.checkDrag)
dragTimer.start()
self.startDrag(Qt.MoveAction|Qt.CopyAction)
def dragMoveEvent(self, event):
if not event.mimeData().hasUrls():
event.ignore()
return
if qApp.queryKeyboardModifiers() & Qt.ShiftModifier:
event.setDropAction(Qt.MoveAction)
else:
event.setDropAction(Qt.CopyAction)
event.accept()
我修改了整个问题,因为我想要的行为很难实现和实际使用。
我正在尝试模仿文件资源管理器中的行为,当我在拖动时按 Shift
时,文件将被移动而不是被复制。
这是我要模仿的行为:
行为: 是我正在使用 LeftClick
进行选择和拖动。
关于行为本身:
我覆盖了 mousePressEvent
和 mouseMoveEvent
以开始拖动。创建拖动后,它使用 QTimer 检测我是否按下了 Control
和 Shift
修饰符。一旦检测到修改器,它就会使用 setDefaultDropAction
设置默认的放置操作。 (我想我应该使用 setDropAction
但它仅在 dragMoveEvent
中可用,我在 QDrag Class 中使用它)
问题: 部分行为现在有效,但仍然存在一些问题。
- 即使我按下
Shift
,DropIndicator 也没有从+
变为->
- 与上述问题相关,即使我按下
Shift
键,dropAction 也只是copyAction
而不是moveAction
。
我的问题: 是什么导致了这些问题?我的直觉告诉我,我应该使用 setDropAction
而不是 setDefaultDropAction
,但同样它仅在 dragMoveEvent
我的测试代码:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class ModifiedQDrag(QDrag):
def __init__(self, source):
super().__init__(source)
self.timer = QTimer(self)
self.timer.timeout.connect(self.process_event)
self.timer.setInterval(100)
self.timer.start()
def process_event(self):
if qApp.keyboardModifiers() & Qt.ControlModifier:
self.source().setDefaultDropAction(Qt.CopyAction)
elif qApp.keyboardModifiers() & Qt.ShiftModifier:
print("shift pressed")
self.source().setDefaultDropAction(Qt.MoveAction)
class Tree(QTreeView):
def __init__(self):
super().__init__()
self.setDragDropMode(QAbstractItemView.DragDrop)
self.setDropIndicatorShown(True)
self.viewport().setAcceptDrops(True)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# -- mouse dragging -- #
def mousePressEvent(self, event):
if event.button() == Qt.RightButton:
self.dragStartPosition = event.pos()
return super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() != Qt.RightButton:
return
if ((event.pos() - self.dragStartPosition).manhattanLength() < QApplication.startDragDistance()):
return
drag = ModifiedQDrag(self)
mimeData = QMimeData()
mimeData = self.model().mimeData([self.indexAt(event.pos())])
drag.setMimeData(mimeData)
dragAction = drag.exec(Qt.MoveAction | Qt.CopyAction, Qt.CopyAction)
return super().mouseMoveEvent(event)
def dragMoveEvent(self, event):
m = event.mimeData()
if m.hasUrls():
event.accept()
return
event.ignore()
def dropEvent(self, event):
print("[drop event] - dropped")
class FileSystemView(QWidget):
def __init__(self):
super().__init__()
# -- left side -- #
left_side_dir = r"<Dir>"
self.model = QFileSystemModel()
self.model.setRootPath(left_side_dir)
self.tree = Tree()
self.tree.setModel(self.model)
self.tree.setRootIndex(self.model.index(left_side_dir))
# -- right side -- #
right_side_dir = r"<Dir>"
self.model2 = QFileSystemModel()
self.model2.setRootPath(right_side_dir)
self.tree2 = Tree()
self.tree2.setModel(self.model2)
self.tree2.setRootIndex(self.model2.index(right_side_dir))
# -- layout -- #
self.tree_layout = QHBoxLayout()
self.tree_layout.addWidget(self.tree)
self.tree_layout.addWidget(self.tree2)
self.setLayout(self.tree_layout)
app = QApplication(sys.argv)
demo = FileSystemView()
demo.show()
sys.exit(app.exec_())
Qt只能对鼠标移动作出反应,以触发拖放动作的变化:顾名思义,dragMoveEvent()
只能通过鼠标调用move .
考虑到这一点,一个可能的解决方案是在键盘修改器更改时手动强制移动鼠标。这样你甚至不需要创建一个 QDrag 子类,你可以保持默认行为。
请注意,要正确获取修饰符,您不应使用 keyboardModifiers()
,而应使用 queryKeyboardModifiers()
,因为第一个仅在直接处理键盘事件时才可靠,并且可能不会使用实际更新键盘的当前状态。
class Tree(QTreeView):
# ...
def checkDrag(self):
modifiers = qApp.queryKeyboardModifiers()
if self.modifiers != modifiers:
self.modifiers = modifiers
pos = QCursor.pos()
# slightly move the mouse to trigger dragMoveEvent
QCursor.setPos(pos + QPoint(1, 1))
# restore the previous position
QCursor.setPos(pos)
def mouseMoveEvent(self, event):
if event.buttons() != Qt.RightButton:
return
if ((event.pos() - self.dragStartPosition).manhattanLength() < QApplication.startDragDistance()):
return
self.modifiers = qApp.queryKeyboardModifiers()
# a local timer, it will be deleted when the function returns
dragTimer = QTimer(interval=100, timeout=self.checkDrag)
dragTimer.start()
self.startDrag(Qt.MoveAction|Qt.CopyAction)
def dragMoveEvent(self, event):
if not event.mimeData().hasUrls():
event.ignore()
return
if qApp.queryKeyboardModifiers() & Qt.ShiftModifier:
event.setDropAction(Qt.MoveAction)
else:
event.setDropAction(Qt.CopyAction)
event.accept()