我正在尝试创建一个允许您以交互方式创建线条的应用程序。左键单击以创建新点,右键单击以完成线的创建。由于某些原因,当我单击鼠标右键时,程序将关闭,没有任何错误。
试图删除尽可能多的额外代码。所有发现的结果是该程序位于标有“ CRUSH ON this Line”的行上
UPD: 有趣的是,如果类MVGraphicsLinkTest没有从QGraphicsObject继承,而是从update_point_positions(self)删除了装饰器@pyqtSlot(),那么一切都会正常进行。
我不明白为什么
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import (QGraphicsPathItem, QGraphicsObject, QApplication,
QGraphicsView, QGraphicsScene, QGraphicsItem)
from PyQt5.QtCore import Qt, pyqtSignal, QMarginsF, QRectF, QPointF, pyqtSlot
from PyQt5.QtGui import (QPainter, QPainterPath, QPainterPathStroker, QColor,
QPen, QBrush)
class MVGraphicsLinkTest(QGraphicsPathItem, QGraphicsObject):
def __init__(self, scene):
super().__init__()
scene.addItem(self)
self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.setAcceptHoverEvents(True)
# lines width
self._width = 10
self._color = QColor(0, 100, 0, 255)
self._brush = QBrush(self._color)
self.setBrush(self._brush)
self.setPen(QPen(Qt.NoPen))
# stroker
self._path_stroker = QPainterPathStroker()
self._path_stroker.setWidth(self._width)
self._path_stroker.setCapStyle(Qt.RoundCap)
self._path_stroker.setJoinStyle(Qt.RoundJoin)
self._path_stroker.setDashPattern(Qt.SolidLine)
# init first point
self.nodes = [MVGraphicsLinkNode(QPointF(10,10))]
self.points = [QPointF(10,10)]
# init dummy point
self._dummy_mode = True
self._dummy_node = MVGraphicsLinkNode(QPointF(10,10), parent=self)
self._dummy_point = QPointF(10,10)
self.update_path()
self.grabMouse()
def color(self):
return self._color
def update_path(self):
self.prepareGeometryChange()
for i, point in enumerate(self.points):
if i==0:
self._path = QPainterPath(point)
else:
self._path.lineTo(point)
if self._dummy_mode:
self._path.lineTo(self._dummy_point)
self.setPath(self._path_stroker.createStroke(self._path))
def mousePressEvent(self, e):
if e.button() == Qt.LeftButton:
# builds line
if self._dummy_mode:
self.points.append(e.pos())
self.nodes.append(self._dummy_node)
self._dummy_node = MVGraphicsLinkNode(e.pos(), parent=self)
if e.button() == Qt.RightButton:
# exit dummy mode and saving points
if self._dummy_mode:
self._dummy_mode = False
self._dummy_point = None
self.scene().removeItem(self._dummy_node)
self._dummy_node = None
self.connect_nodes()
self.update_path()
self.ungrabMouse()
if len(self.points)<2:
self.delete_self()
super().mousePressEvent(e)
e.accept()
def mouseMoveEvent(self, e):
if self._dummy_mode:
self._dummy_point = e.pos()
self._dummy_node.setPos(self._dummy_point)
self.update_path()
super().mouseMoveEvent(e)
def connect_nodes(self):
# connecting node signals
for i, node in enumerate(self.nodes):
node.set_index(i)
###############################################################
# CRUSH ON THIS LINE ##########################################
###############################################################
node.change_position_signal.connect(self.update_point_positions)
node.delete_signal.connect(self.delete_node)
def disconnect_nodes(self):
# disconnecting node signals
for node in self.nodes:
node.change_position_signal.disconnect()
node.delete_signal.disconnect()
def delete_node(self, index):
if len(self.points)>2:
self.disconnect_nodes()
self.points.pop(index)
del_node = self.nodes.pop(index)
self.scene().removeItem(del_node)
self.connect_nodes()
self.update_path()
print('scene items: {0}'.format(len(self.scene().items())))
@pyqtSlot()
def update_point_positions(self):
for i, node in enumerate(self.nodes):
self.points[i] = node.scenePos()
self.update_path()
class MVGraphicsLinkNode(QGraphicsObject):
change_position_signal = pyqtSignal()
delete_signal = pyqtSignal(int)
def __init__(self, pos, movable=True, parent=None):
super().__init__(parent=parent)
self.setPos(pos)
self._movable = movable
self._index = -1
self.setFlag(QGraphicsItem.ItemIsMovable, self._movable)
self.setFlag(QGraphicsItem.ItemIsSelectable, self._movable)
self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True)
self.setAcceptHoverEvents(True)
self.setEnabled(self._movable)
self._hover = False
self._size = 12
self._color = QColor(200, 200, 50, 200)
if parent:
self._color = parent.color()
self._color_sel = QColor(self._color)
self._color_sel.setAlpha(100)
self._brush1 = QBrush(self._color_sel)
self._brush2 = QBrush(self._color)
self._pen = QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)
def set_index(self, index):
self._index = index
self.setToolTip('index = {0}'.format(self._index))
def itemChange(self, change, value):
if change == QGraphicsItem.ItemScenePositionHasChanged and self.scene():
self.change_position_signal.emit()
return super().itemChange(change, value)
def size(self):
if self._hover:
return 2*self._size
else:
return self._size
def boundingRect(self):
return QRectF(-self._size, -self._size,
2*self._size, 2*self._size)
def shape(self):
path = QPainterPath()
path.addEllipse(QPointF(), self.size()/2, self.size()/2)
return path
def paint(self, painter, option, widget=None):
painter.setBrush(self._brush1)
painter.setPen(self._pen)
if self._hover or self.isSelected():
painter.drawEllipse(QPointF(), self._size, self._size)
painter.setBrush(self._brush2)
painter.drawEllipse(QPointF(), self._size/2, self._size/2)
def hoverEnterEvent(self, e):
self._hover = self._movable
super().hoverLeaveEvent(e)
def hoverLeaveEvent(self, e):
self._hover = False
super().hoverLeaveEvent(e)
def mouseDoubleClickEvent(self, e):
print('delete sender: {0}'.format(self._index))
self.delete_signal.emit(self._index)
super().mouseDoubleClickEvent(e)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
view = QGraphicsView()
view.setDragMode(QGraphicsView.RubberBandDrag)
scene = QGraphicsScene()
m = QMarginsF(30, 30, 30, 30)
scene_rect = QRectF(0, 0, 600, 600)
scene.setSceneRect(scene_rect.marginsAdded(m))
view.setScene(scene)
view.setRenderHint(QPainter.Antialiasing)
view.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
MVGraphicsLinkTest(scene)
view.show()
sys.exit(app.exec_())