0

我正在开发一个 GUI,您可以在其中连接它们之间的节点,除了少数特殊情况。此实现在大多数情况下都可以正常工作,但是经过一些测试后,我发现,当我通过 QGraphicsLineItem 将一个 QGraphicsPixmapItem 与另一个连接时,并且用户在完成链接之前打开上下文菜单时,该行会卡住,并且不能删除。

链接两个元素的过程是首先按下元素,然后在移动线时按住并在指针移到另一个元素上时释放。这是分别使用 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())

如何在上下文菜单事件之前/之后摆脱掉线?我试图阻止他们,但我不知道怎么做。

4

1 回答 1

0

假设菜单仅由鼠标按键触发,解决方案是删除其中的任何现有链接项mouseButtonPress

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

请注意,itemAt对于非常小的项目并不总是可靠的,因为该项目shape()可能会稍微偏离映射的鼠标位置。由于在任何情况下都会删除链接,因此只需在以下内容中执行相同操作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
于 2021-11-12T19:50:59.520 回答