我有一个带有 QGraphicsView/Scene 的测试 PyQt 应用程序,以及一个带有按钮的工具栏,允许用户将形状拖到视口中。
视口中间还有另一个按钮(在我的实际应用程序中,这是许多图标和标签,想象一下类似覆盖在虚幻引擎视口 UI 顶部的图标)。
将形状从工具栏拖到视口中时,如果有人将鼠标悬停在视口中嵌入的按钮上,则会触发“dragLeaveEvent”方法,我实际上想完全忽略此按钮,让我继续在视口周围拖动形状视口。
我尝试了各种方法,包括处理从场景、视图中的拖放,以及在不同位置的默认拖/放方法上设置 event.ignore() 。如果我忽略 QGraphicsView 上的事件,那么我也无法在场景上拖/放。似乎顶部的内容将决定拖放功能。我真的希望能够忽略顶级小部件(视口按钮)。
这是解决该问题的最少代码。如果将“圆圈”从工具栏拖到视口上,并将鼠标悬停在视口按钮上,该圆圈将落入场景中,并创建一个新圆圈。这不是我正在寻找的行为。我希望能够忽略视口按钮并继续拖动对象。
如有任何建议,我们将不胜感激!
干杯, 艾卡
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsItem, QToolBar, QPushButton, QVBoxLayout, QWidget, QLabel, QGraphicsEllipseItem
from PyQt5.QtGui import QDrag
from PyQt5.QtCore import Qt, QMimeData, QRectF, QPointF
class CircleItem(QGraphicsItem):
def __init__(self, x, y, radius, parent=None):
super().__init__(parent)
# create a simple shape we can move around the scene
self.shape = QGraphicsEllipseItem(x, y, radius, radius)
def boundingRect(self):
return QRectF(self.shape.boundingRect())
def paint(self, painter, option, widget=None):
self.shape.paint(painter, option, widget)
# for toolbar icons
class DragDropWidget(QLabel):
def __init__(self, shape_type):
super().__init__(shape_type)
self.shape_type = shape_type
def mousePressEvent(self, event):
drag = QDrag(self)
drag.setHotSpot(event.pos())
mime_data = QMimeData()
mime_data.setText(self.shape_type)
drag.setMimeData(mime_data)
drag.exec_(Qt.MoveAction)
class CustomView(QGraphicsView):
def __init__(self, scene):
super(CustomView, self).__init__(scene)
# def dragEnterEvent(self, event):
# event.ignore()
# def dragMoveEvent(self, event):
# event.ignore()
# def dropEvent(self, event):
# event.ignore()
# def dragLeaveEvent(self, event):
# event.ignore()
class CustomScene(QGraphicsScene):
def __init__(self):
super(CustomScene, self).__init__()
self.shape = None
def dragEnterEvent(self, event):
if event.mimeData().hasText():
# Create the shape and add to scene
shape_type = event.mimeData().text()
self.shape = CircleItem(0, 0, 100)
self.addItem(self.shape)
event.acceptProposedAction()
def dragMoveEvent(self, event):
if self.shape:
# Move the shape around the scene with mouse cursor
self.shape.setPos(event.scenePos())
event.acceptProposedAction()
def dropEvent(self, event):
# Drop the shape to the scene
self.shape = None
event.acceptProposedAction()
def dragLeaveEvent(self, event):
# I don't want this event to be tiggered when mousing over button in viewport
print("LEAVING!")
#event.acceptProposedAction()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.scene = CustomScene()
self.view = CustomView(self.scene)
self.setCentralWidget(self.view)
toolbar = QToolBar("Shapes Toolbar")
self.addToolBar(toolbar)
circle_button = DragDropWidget('Circle')
toolbar.addWidget(circle_button)
layout = QVBoxLayout()
layout.addWidget(self.view)
main_widget = QWidget()
main_widget.setLayout(layout)
self.setCentralWidget(main_widget)
# UI for the view
view_button = QPushButton('I want to ignore this button when drag/dropping')
view_layout = QVBoxLayout()
view_layout.addWidget(view_button)
self.view.setLayout(view_layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 800, 600)
mainWindow.show()
sys.exit(app.exec_())
正如musicamante所提到的,拼图中缺少的部分是在视口上设置布局(
self.view.viewport().setLayout(view_layout)
),而不是在self.view本身上设置布局。
这里更新的代码似乎适合我的情况:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsItem, QToolBar, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QGraphicsEllipseItem, QSpacerItem, QSizePolicy
from PyQt5.QtGui import QDrag
from PyQt5.QtCore import Qt, QMimeData, QRectF, QPointF
class ViewportControls(QWidget):
def __init__(self):
super(ViewportControls, self).__init__()
self.layout = QHBoxLayout(self)
# Create whatever buttons and labels we need
self.button = QPushButton('Do something')
self.button2 = QPushButton('Do something else')
self.layout.addWidget(self.button)
self.layout.addWidget(self.button2)
class CircleItem(QGraphicsItem):
def __init__(self, x, y, radius, parent=None):
super().__init__(parent)
# create a simple shape we can move around the scene
self.shape = QGraphicsEllipseItem(x, y, radius, radius)
def boundingRect(self):
return QRectF(self.shape.boundingRect())
def paint(self, painter, option, widget=None):
self.shape.paint(painter, option, widget)
# for toolbar icons
class DragDropWidget(QLabel):
def __init__(self, shape_type):
super().__init__(shape_type)
self.shape_type = shape_type
def mousePressEvent(self, event):
drag = QDrag(self)
drag.setHotSpot(event.pos())
mime_data = QMimeData()
mime_data.setText(self.shape_type)
drag.setMimeData(mime_data)
drag.exec_(Qt.MoveAction)
class CustomScene(QGraphicsScene):
def __init__(self):
super(CustomScene, self).__init__()
self.shape = None
def dragEnterEvent(self, event):
if event.mimeData().hasText():
# Create the shape and add to scene
shape_type = event.mimeData().text()
self.shape = CircleItem(0, 0, 100)
self.addItem(self.shape)
event.acceptProposedAction()
def dragMoveEvent(self, event):
if self.shape:
# Move the shape around the scene with mouse cursor
self.shape.setPos(event.scenePos())
event.acceptProposedAction()
def dropEvent(self, event):
# Drop the shape to the scene
self.shape = None
event.acceptProposedAction()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.scene = CustomScene()
self.view = QGraphicsView(self.scene)
self.setCentralWidget(self.view)
toolbar = QToolBar("Shapes Toolbar")
self.addToolBar(toolbar)
circle_button = DragDropWidget('Circle')
toolbar.addWidget(circle_button)
layout = QVBoxLayout()
layout.addWidget(self.view)
main_widget = QWidget()
main_widget.setLayout(layout)
self.setCentralWidget(main_widget)
# UI overlay for the viewport
view_layout = QVBoxLayout()
view_layout.addWidget(ViewportControls())
spacer_item = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
view_layout.addItem(spacer_item)
# add the layout to the viewport
self.view.viewport().setLayout(view_layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 800, 600)
mainWindow.show()
sys.exit(app.exec_())