通过图形场景中的鼠标操作和鼠标事件使 GraphicsItems 可编辑
Make GraphicsItems editable by mouse action and mouse events in a Graphicscene
我目前正在编写一个交互式 GUI,下面的代码通过鼠标事件以连续的方式插入节点和相应的边,通过双击事件开始和结束绘制会话(基于此 post ).我想让节点和边可编辑,因为在绘制之后用户可以通过鼠标事件在节点位置周围移动,如下例所示。
,
代码
class WindowClass(QMainWindow):
def __init__(self, parent=None):
super(WindowClass, self).__init__(parent)
view = QGraphicsView()
view.setMouseTracking(True)
view.setRenderHint(QPainter.Antialiasing)
scene = SceneClass(self)
view.setScene(scene)
self.setCentralWidget(view)
self.resize(640, 480)
class SceneClass(QGraphicsScene):
def __init__(self, parent=None):
super(SceneClass, self).__init__(QRectF(-1000, -1000, 2000, 2000), parent)
self._edge_item = None
self.pos = None
self.pos_end = None
self.node_start = None
self.node_end = None
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton:
"Agregar Nodo"
self.node_start = Node()
self.node_start.setPos(event.scenePos())
self.addItem(self.node_start)
"Agregar Edge"
self._edge_item = Edge()
self._edge_item.src = self.node_start
self.addItem(self._edge_item)
if self.node_end:
self.node_end = None
self.node_start = None
self.node_end = self.node_start
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.node_end:
"Agregar Nodo"
self.node_start = Node()
self.node_start.setPos(event.scenePos())
self.addItem(self.node_start)
self.node_end = self.node_start
"Agregar Edge"
self._edge_item = Edge()
self._edge_item.src = self.node_start
self.addItem(self._edge_item)
def mouseMoveEvent(self, event):
if self._edge_item and self.node_end:
self._edge_item.p1 = event.scenePos()
super(SceneClass, self).mouseMoveEvent(event)
class Node(QGraphicsEllipseItem):
def __init__(self, rect=QRectF(-10, -10, 20, 20), parent=None):
super(Node, self).__init__(rect, parent)
self.edges = []
self.setZValue(1)
self.setBrush(Qt.darkGray)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
def addEdge(self, edge):
self.edges.append(edge)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
self.setBrush(Qt.green if value else Qt.darkGray)
if change == QGraphicsItem.ItemPositionHasChanged:
for edge in self.edges:
edge.adjust()
return super(Node, self).itemChange(change, value)
class Edge(QGraphicsLineItem):
def __init__(self, parent=None):
super(Edge, self).__init__(parent)
self.setPen(QPen(Qt.red, 3))
self._src = None
self._dst = None
@property
def src(self):
return self._src
@src.setter
def src(self, node):
self._src = node
self._src.addEdge(self)
self.adjust()
@property
def dst(self):
return self._dst
@dst.setter
def dst(self, node):
self._dst = node
self._dst.addEdge(self)
self.adjust()
@property
def p1(self):
return self.line().p1()
@p1.setter
def p1(self, p):
line = self.line()
line.setP1(p)
self.setLine(line)
@property
def p2(self):
return self.line().p2()
@p2.setter
def p2(self, p):
line = self.line()
line.setP2(p)
self.setLine(line)
def adjust(self):
self.prepareGeometryChange()
if self.src:
self.p1 = self.src.pos()
self.p2 = self.src.pos()
if self.dst:
self.p2 = self.dst.pos()
我尝试通过在边缘添加一个悬停事件来启动class,但是在设置接受悬停事件并创建一个函数来指示对该事件的操作后,该函数不会触发。
OP 的逻辑不正确,所以我不得不稍微修改一下,以便它正确地遵循顺序。
另一方面,默认情况下已经实现了移动项目的行为,但必须将鼠标事件发送到项目,为此必须调用 mouseXEvent 的 super() 方法方法。
class SceneClass(QGraphicsScene):
def __init__(self, parent=None):
super(SceneClass, self).__init__(QRectF(-1000, -1000, 2000, 2000), parent)
self._edge_item = None
self._last_node = None
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton:
if self._last_node:
self._last_node = None
self._edge_item = None
else:
node = Node()
node.setPos(event.scenePos())
self.addItem(node)
self._edge_item = Edge()
self._edge_item.src = node
self.addItem(self._edge_item)
self._last_node = node
return
super(SceneClass, self).mouseDoubleClickEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self._last_node:
node = Node()
node.setPos(event.scenePos())
self.addItem(node)
self._edge_item.dst = node
self._edge_item = Edge()
self._edge_item.src = node
self.addItem(self._edge_item)
self._last_node = node
return
super(SceneClass, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._edge_item:
self._edge_item.p1 = event.scenePos()
super(SceneClass, self).mouseMoveEvent(event)
class Node(QGraphicsEllipseItem):
def __init__(self, rect=QRectF(-10, -10, 20, 20), parent=None):
super(Node, self).__init__(rect, parent)
self.edges = []
self.setZValue(1)
self.setBrush(Qt.darkGray)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
def addEdge(self, edge):
self.edges.append(edge)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
self.setBrush(Qt.green if value else Qt.darkGray)
if change == QGraphicsItem.ItemPositionHasChanged:
for edge in self.edges:
edge.adjust()
return super(Node, self).itemChange(change, value)
class Edge(QGraphicsLineItem):
def __init__(self, parent=None):
super(Edge, self).__init__(parent)
self.setPen(QPen(Qt.red, 3))
self._src = None
self._dst = None
@property
def src(self):
return self._src
@src.setter
def src(self, node):
self._src = node
self._src.addEdge(self)
self.adjust()
@property
def dst(self):
return self._dst
@dst.setter
def dst(self, node):
self._dst = node
self._dst.addEdge(self)
self.adjust()
@property
def p1(self):
return self.line().p1()
@p1.setter
def p1(self, p):
line = self.line()
line.setP1(p)
self.setLine(line)
@property
def p2(self):
return self.line().p2()
@p2.setter
def p2(self, p):
line = self.line()
line.setP2(p)
self.setLine(line)
def adjust(self):
self.prepareGeometryChange()
if self.src:
self.p1 = self.src.pos()
self.p2 = self.src.pos()
if self.dst:
self.p2 = self.dst.pos()
我目前正在编写一个交互式 GUI,下面的代码通过鼠标事件以连续的方式插入节点和相应的边,通过双击事件开始和结束绘制会话(基于此 post
代码
class WindowClass(QMainWindow):
def __init__(self, parent=None):
super(WindowClass, self).__init__(parent)
view = QGraphicsView()
view.setMouseTracking(True)
view.setRenderHint(QPainter.Antialiasing)
scene = SceneClass(self)
view.setScene(scene)
self.setCentralWidget(view)
self.resize(640, 480)
class SceneClass(QGraphicsScene):
def __init__(self, parent=None):
super(SceneClass, self).__init__(QRectF(-1000, -1000, 2000, 2000), parent)
self._edge_item = None
self.pos = None
self.pos_end = None
self.node_start = None
self.node_end = None
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton:
"Agregar Nodo"
self.node_start = Node()
self.node_start.setPos(event.scenePos())
self.addItem(self.node_start)
"Agregar Edge"
self._edge_item = Edge()
self._edge_item.src = self.node_start
self.addItem(self._edge_item)
if self.node_end:
self.node_end = None
self.node_start = None
self.node_end = self.node_start
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.node_end:
"Agregar Nodo"
self.node_start = Node()
self.node_start.setPos(event.scenePos())
self.addItem(self.node_start)
self.node_end = self.node_start
"Agregar Edge"
self._edge_item = Edge()
self._edge_item.src = self.node_start
self.addItem(self._edge_item)
def mouseMoveEvent(self, event):
if self._edge_item and self.node_end:
self._edge_item.p1 = event.scenePos()
super(SceneClass, self).mouseMoveEvent(event)
class Node(QGraphicsEllipseItem):
def __init__(self, rect=QRectF(-10, -10, 20, 20), parent=None):
super(Node, self).__init__(rect, parent)
self.edges = []
self.setZValue(1)
self.setBrush(Qt.darkGray)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
def addEdge(self, edge):
self.edges.append(edge)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
self.setBrush(Qt.green if value else Qt.darkGray)
if change == QGraphicsItem.ItemPositionHasChanged:
for edge in self.edges:
edge.adjust()
return super(Node, self).itemChange(change, value)
class Edge(QGraphicsLineItem):
def __init__(self, parent=None):
super(Edge, self).__init__(parent)
self.setPen(QPen(Qt.red, 3))
self._src = None
self._dst = None
@property
def src(self):
return self._src
@src.setter
def src(self, node):
self._src = node
self._src.addEdge(self)
self.adjust()
@property
def dst(self):
return self._dst
@dst.setter
def dst(self, node):
self._dst = node
self._dst.addEdge(self)
self.adjust()
@property
def p1(self):
return self.line().p1()
@p1.setter
def p1(self, p):
line = self.line()
line.setP1(p)
self.setLine(line)
@property
def p2(self):
return self.line().p2()
@p2.setter
def p2(self, p):
line = self.line()
line.setP2(p)
self.setLine(line)
def adjust(self):
self.prepareGeometryChange()
if self.src:
self.p1 = self.src.pos()
self.p2 = self.src.pos()
if self.dst:
self.p2 = self.dst.pos()
我尝试通过在边缘添加一个悬停事件来启动class,但是在设置接受悬停事件并创建一个函数来指示对该事件的操作后,该函数不会触发。
OP 的逻辑不正确,所以我不得不稍微修改一下,以便它正确地遵循顺序。
另一方面,默认情况下已经实现了移动项目的行为,但必须将鼠标事件发送到项目,为此必须调用 mouseXEvent 的 super() 方法方法。
class SceneClass(QGraphicsScene):
def __init__(self, parent=None):
super(SceneClass, self).__init__(QRectF(-1000, -1000, 2000, 2000), parent)
self._edge_item = None
self._last_node = None
def mouseDoubleClickEvent(self, event):
if event.button() == Qt.LeftButton:
if self._last_node:
self._last_node = None
self._edge_item = None
else:
node = Node()
node.setPos(event.scenePos())
self.addItem(node)
self._edge_item = Edge()
self._edge_item.src = node
self.addItem(self._edge_item)
self._last_node = node
return
super(SceneClass, self).mouseDoubleClickEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self._last_node:
node = Node()
node.setPos(event.scenePos())
self.addItem(node)
self._edge_item.dst = node
self._edge_item = Edge()
self._edge_item.src = node
self.addItem(self._edge_item)
self._last_node = node
return
super(SceneClass, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self._edge_item:
self._edge_item.p1 = event.scenePos()
super(SceneClass, self).mouseMoveEvent(event)
class Node(QGraphicsEllipseItem):
def __init__(self, rect=QRectF(-10, -10, 20, 20), parent=None):
super(Node, self).__init__(rect, parent)
self.edges = []
self.setZValue(1)
self.setBrush(Qt.darkGray)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
def addEdge(self, edge):
self.edges.append(edge)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemSelectedChange:
self.setBrush(Qt.green if value else Qt.darkGray)
if change == QGraphicsItem.ItemPositionHasChanged:
for edge in self.edges:
edge.adjust()
return super(Node, self).itemChange(change, value)
class Edge(QGraphicsLineItem):
def __init__(self, parent=None):
super(Edge, self).__init__(parent)
self.setPen(QPen(Qt.red, 3))
self._src = None
self._dst = None
@property
def src(self):
return self._src
@src.setter
def src(self, node):
self._src = node
self._src.addEdge(self)
self.adjust()
@property
def dst(self):
return self._dst
@dst.setter
def dst(self, node):
self._dst = node
self._dst.addEdge(self)
self.adjust()
@property
def p1(self):
return self.line().p1()
@p1.setter
def p1(self, p):
line = self.line()
line.setP1(p)
self.setLine(line)
@property
def p2(self):
return self.line().p2()
@p2.setter
def p2(self, p):
line = self.line()
line.setP2(p)
self.setLine(line)
def adjust(self):
self.prepareGeometryChange()
if self.src:
self.p1 = self.src.pos()
self.p2 = self.src.pos()
if self.dst:
self.p2 = self.dst.pos()