我想在某些自定义窗口小部件中启用拖放功能,以允许在布局中重新排序和重新排列窗口小部件。基本功能正在运行,但是我想防止将小部件放置在其自身或自身的子级上。在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_() )
[最简单的方法可能是始终接受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()