PyQt5 - 拖放时忽略 QGraphicsView 中的小部件

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

我有一个带有 QGraphicsView/Scene 的测试 PyQt 应用程序,以及一个带有按钮的工具栏,允许用户将形状拖到视口中。

视口中间还有另一个按钮(在我的实际应用程序中,这是许多图标和标签,想象一下类似覆盖在虚幻引擎视口 UI 顶部的图标)。

923e2b9e-3b15-4dca-b1ec-9904e10d9bba-image.png

将形状从工具栏拖到视口中时,如果有人将鼠标悬停在视口中嵌入的按钮上,则会触发“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_())


python pyqt5 drag-and-drop qgraphicsview
1个回答
0
投票

正如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_())
© www.soinside.com 2019 - 2024. All rights reserved.