调用connect for signal时我的应用程序崩溃

时间:2019-05-09 13:34:50

标签: python pyqt pyqt5 qt-signals

我正在尝试创建一个允许您以交互方式创建线条的应用程序。左键单击以创建新点,右键单击以完成线的创建。由于某些原因,当我单击鼠标右键时,程序将关闭,没有任何错误。

试图删除尽可能多的额外代码。所有发现的结果是该程序位于标有“ 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_())

0 个答案:

没有答案