在“QTreeView”中删除项目时实现复制和移动

Implementing copy and move when dropping an Item in `QTreeView`

我修改了整个问题,因为我想要的行为很难实现和实际使用。

我正在尝试模仿文件资源管理器中的行为,当我在拖动时按 Shift 时,文件将被移动而不是被复制。

这是我要模仿的行为:

行为: 是我正在使用 LeftClick 进行选择和拖动。


关于行为本身:

我覆盖了 mousePressEventmouseMoveEvent 以开始拖动。创建拖动后,它使用 QTimer 检测我是否按下了 ControlShift 修饰符。一旦检测到修改器,它就会使用 setDefaultDropAction 设置默认的放置操作。 (我想我应该使用 setDropAction 但它仅在 dragMoveEvent 中可用,我在 QDrag Class 中使用它)


问题: 部分行为现在有效,但仍然存在一些问题。

  1. 即使我按下 Shift,DropIndicator 也没有从 + 变为 ->
  2. 与上述问题相关,即使我按下 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()