Qt:防止在子小部件上拖放并显示禁止的光标

问题描述 投票:0回答:1

我想在某些自定义窗口小部件中启用拖放功能,以允许在布局中重新排序和重新排列窗口小部件。基本功能正在运行,但是我想防止将小部件放置在其自身或自身的子级上。在dropEvent方法中这很简单,但是我找不到一种在拖动时也显示“禁止”光标的方法,从而使用户意识到不允许放置。

下面的示例显示了一个测试实现,可以将小部件“ One”到“ Five”拖放到其中(不会进行重新排列,终端上只会显示一条消息,您可能需要按Ctrl或启动阻力)。问题行是ev.accept()方法中的第一个dragEnterEvent。通过接受事件,光标将显示为“允许”状态(对我来说是抓手)。例如,尝试拖动“一个”并将其拖放到“三个”上,尽管没有任何反应。但是,忽略事件会导致事件传播到父对象,因此在这种情况下,将“三”拖动到“三”上会导致“一个”代替。设置WA_NoMousePropagation属性似乎没有任何区别。

因此,我需要的是一种“接受”事件的方法,这样它就不会传播到父级,而是仍然显示“禁止”光标,就像没有人接受该事件一样。有什么想法吗?

#!/usr/bin/env python3

import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *


class WidgetMimeData(QtCore.QMimeData):
    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      self.itemObject = None

    def hasFormat(self, mime):
        if (self.itemObject and (mime == 'widgetitem')):
            return True
        return super().hasFormat(mime)

    def setItem(self, obj):
        self.itemObject = obj

    def item(self):
        return self.itemObject


class DraggableWidget(QGroupBox):
    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.setAcceptDrops(True)

    def addWidget(self, widget):
        return self.layout().addWidget(widget)

    def mouseMoveEvent(self, ev):
        pixmap = QPixmap(self.size())
        pixmap.fill(QtCore.Qt.transparent)
        painter = QPainter()
        painter.begin(pixmap)
        painter.setOpacity(0.8)
        painter.drawPixmap(0, 0, self.grab())
        painter.end()
        drag = QDrag(self)
        mimedata = WidgetMimeData()
        mimedata.setItem(self)
        drag.setMimeData(mimedata)
        drag.setPixmap(pixmap)
        drag.setHotSpot(ev.pos())
        drag.exec_(QtCore.Qt.MoveAction)

    def dragEnterEvent(self, ev):
        item = ev.mimeData().item()
        if item.isAncestorOf(self):
          #ev.ignore()
          ev.accept()
        else:
          ev.accept()

    def dropEvent(self, ev):
        item = ev.mimeData().item()
        if not item.isAncestorOf(self):
          print('dropped on', self.layout().itemAt(0).widget().text())
        ev.accept()


class HelloWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        w1 = DraggableWidget()
        w1.addWidget(QLabel('One'))
        w2 = DraggableWidget()
        w2.addWidget(QLabel('Two'))
        w3 = DraggableWidget()
        w3.addWidget(QLabel('Three'))
        w4 = DraggableWidget()
        w4.addWidget(QLabel('Four'))
        w5 = DraggableWidget()
        w5.addWidget(QLabel('Five'))

        w1.addWidget(w3)
        w1.addWidget(w4)
        w2.addWidget(w5)

        layout = QVBoxLayout()
        layout.addWidget(w1)
        layout.addWidget(w2)
        layout.addStretch(1)

        centralWidget = QWidget(self)          
        centralWidget.setLayout(layout)
        self.setCentralWidget(centralWidget)   


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = HelloWindow()
    mainWin.show()
    sys.exit( app.exec_() )
python pyqt drag-and-drop pyqt5
1个回答
0
投票

[最简单的方法可能是始终接受dragEnterEvent中的事件,而当事件的源是dragMoveEvent的祖先,即self时忽略它

def dragEnterEvent(self, ev):
    ev.accept()

def dragMoveEvent(self, ev):
    item = ev.source()
    if item.isAncestorOf(self):
        ev.ignore()

通过忽略dragMoveEvent中的事件,您也不需要dropEvent中的检查,只需执行

def dropEvent(self, ev):
    print('Dropped of', self.layout().itemAt(0).widget().text())
    ev.accept()
© www.soinside.com 2019 - 2024. All rights reserved.