在 QGraphicsPixmapItem 中打开上下文菜单时,如何删除此 QGraphicsLineItem?

How can I delete this QGraphicsLineItem when context menu is open in a QGraphicsPixmapItem?

我正在开发一个 GUI,除少数特殊情况外,您可以在其中连接节点。这个实现在大多数情况下工作得很好,但经过一些测试我发现,当我通过 QGraphicsLineItem 将一个 QGraphicsPixmapItem 与另一个连接时,用户在完成 link 之前打开上下文菜单,该行卡住了,而且无法删除。

到link两个元素的过程是先按下该元素,然后在移动线条的同时按住,当指针悬停在另一个元素上时松开。这是分别使用 mousePressEvent、mouseMoveEvent 和 mouseReleaseEvent 实现的。

此代码是示例:

#!/usr/bin/env python3

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

import sys


class Ellipse(QGraphicsEllipseItem):
    def __init__(self, x, y):
        super(Ellipse, self).__init__(x, y, 30, 30)

        self.setBrush(QBrush(Qt.darkBlue))
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setZValue(100)

    def contextMenuEvent(self, event):
        menu = QMenu()

        first_action = QAction("First action")
        second_action = QAction("Second action")

        menu.addAction(first_action)
        menu.addAction(second_action)

        action = menu.exec(event.screenPos())


class Link(QGraphicsLineItem):
    def __init__(self, x, y):
        super(Link, self).__init__(x, y, x, y)

        self.pen_ = QPen()
        self.pen_.setWidth(2)
        self.pen_.setColor(Qt.red)

        self.setPen(self.pen_)

    def updateEndPoint(self, x2, y2):
        line = self.line()
        self.setLine(line.x1(), line.y1(), x2, y2)


class Scene(QGraphicsScene):
    def __init__(self):
        super(Scene, self).__init__()

        self.link = None
        self.link_original_node = None

        self.addItem(Ellipse(200, 400))
        self.addItem(Ellipse(400, 400))

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            item = self.itemAt(event.scenePos(), QTransform())
            if item is not None:
                self.link_original_node = item
                offset = item.boundingRect().center()
                self.link = Link(item.scenePos().x() + offset.x(), item.scenePos().y() + offset.y())
                self.addItem(self.link)

    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        if self.link is not None:
            self.link.updateEndPoint(event.scenePos().x(), event.scenePos().y())

    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        if self.link is not None:
            item = self.itemAt(event.scenePos(), QTransform())
            if isinstance(item, (Ellipse, Link)):
                self.removeItem(self.link)
                self.link_original_node = None
                self.link = None


class MainWindow(QMainWindow):
    def __init__(self):
        super(QMainWindow, self).__init__()

        self.scene = Scene()
        self.canvas = QGraphicsView()

        self.canvas.setScene(self.scene)
        self.setCentralWidget(self.canvas)

        self.setGeometry(500, 200, 1000, 600)
        self.setContextMenuPolicy(Qt.NoContextMenu)


app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec())

如何摆脱行before/after上下文菜单事件?我试图阻止他们,但我不知道如何。

假设菜单仅通过按下鼠标按钮触发,解决方案是也删除 mouseButtonPress 中任何现有的 link 项。

    def mousePressEvent(self, event):
        if self.link is not None:
            self.removeItem(self.link)
            self.link_original_node = None
            self.link = None
        # ...

请注意,对于非常小的项目,itemAt 并不总是可靠的,因为项目的 shape() 可能会稍微偏离映射的鼠标位置。由于 link 在任何情况下都会被删除,因此只需在 mouseReleaseEvent():

中执行相同的操作
    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        if self.link is not None:
            item = self.itemAt(event.scenePos(), QTransform())
            if isinstance(item, Ellipse):
                # do what you need with the linked ellipses

            # note the indentation level
            self.removeItem(self.link)
            self.link_original_node = None
            self.link = None