【发布时间】:2021-11-12 18:42:35
【问题描述】:
我正在开发一个 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())
如何在上下文菜单事件之前/之后摆脱掉线?我试图阻止他们,但我不知道怎么做。
【问题讨论】:
-
似乎只有在椭圆项目上释放链接项目时才会删除链接项目。在任何情况下都不应该将其移除,尤其是当鼠标没有在任何项目上释放时?
-
@musicamante 感谢您的观看!我在代码中犯了一个错误,现在它是正确的。目标是在任何情况下都不将它们之间的元素链接起来,因此如果该行脱离任何元素,则必须将其删除,就像更新的代码一样。该错误仅在上下文菜单打开时发生。
-
由于该菜单只能用鼠标打开,我想当用户也右键单击而保持左键时会出现问题按下,对吗?在任何情况下,考虑到在某些情况下订单项可能不恰好在光标位置(这是由于位置映射的复杂工作方式),因此,您应该始终在鼠标释放中检查
if self.link is not None,并最终基于此将其删除,并且仅依靠itemAt才能成功链接。 -
@musicamante 没错,在按住左键的同时按右键会出现问题。您提出的解决方案的问题是当用户访问上下文菜单时 mouseReleaseEvent 不会激活。就像链接进入“空白”一样。
-
这是另一个点:当上下文菜单事件打开菜单时,鼠标会被隐式释放。事实仍然存在,如果该项目存在,则应始终在鼠标释放时将其删除,不仅是在检查鼠标 over 之后:因为它假定它是在鼠标按下时创建的,所以链接项目的位置是无关的。看我的回答。另请注意,虽然您的方法“有效”,但它存在一些问题:例如,根本不调用基本实现
mousePressEvent可能会导致意外行为(尝试双击)。
标签: python-3.x pyqt5 qgraphicslineitem