Я пытаюсь создать приложение, которое позволяет вам интерактивно создавать линии.Щелкните левой кнопкой мыши, чтобы создать новую точку, щелкните правой кнопкой мыши, чтобы завершить создание линии.По какой-то причине, когда я нажимаю правую кнопку мыши, программа закрывается без ошибок.
Пытался удалить как можно больше лишнего кода.Все, что было обнаружено, это то, что программа попадает на строку, помеченную "CRUSH ON THIS LINE"
UPD: Интересно, что если класс MVGraphicsLinkTest не наследуется от QGraphicsObject, а удалить decorator @pyqtSlot () из update_point_positions (сам), то все работает без ошибок.
Я не понимаю, почему
# -*- 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_())