2

我正在尝试从这个答案扩展出色的实现,因此将通过用户在图形场景中的点击创建路径,并使用双击关闭路径。

这很好用,但我唯一无法解决的问题是路径中第一个节点的更改。显然我希望它会通过该itemChanged方法更新附加到它的最后一个子路径,但无法实现。

关于如何解决这个问题的任何建议?

我尝试了以下变体,但它不影响最后一个子路径:

def itemChange(self, change, value):
    if change == QGraphicsItem.ItemPositionChange:
        self.path.updateElement(self.index, value.toPoint())
        if self.index == 0:
            last_element_idx = self.path.path.elementCount()
            self.path.updateElement(last_element_idx, value.toPoint())
    return QGraphicsEllipseItem.itemChange(self, change, value)

我的实验的完整代码:

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QPointF, Qt
from PyQt5.QtGui import QPen, QPainterPath, QPainter, QPolygonF
from PyQt5.QtWidgets import QGraphicsEllipseItem, QGraphicsItem, QGraphicsPathItem, QApplication, QGraphicsScene, \
    QGraphicsView, QFrame, QMainWindow

rad = 3

class Node(QGraphicsEllipseItem):
    def __init__(self, path, index):
        super(Node, self).__init__(-rad, -rad, 2*rad, 2*rad)
        self.rad = rad
        self.path = path
        self.index = index
        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
        self.setBrush(Qt.green)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            self.path.updateElement(self.index, value.toPoint())
            if self.index == 0:
                last_element_idx = self.path.path.elementCount()
                self.path.updateElement(last_element_idx, value.toPoint())
        return QGraphicsEllipseItem.itemChange(self, change, value)

class Path(QGraphicsPathItem):
    def __init__(self, pos, scene):
        self.path = QPainterPath()
        self.path.moveTo(*pos)
        super(Path, self).__init__(self.path)
        self.scene = scene
        self.pos = pos
        self.setPen(QPen(Qt.red, 1.75))
        self.scene.addItem(self)

    def addElement(self, pos):
        self.path.lineTo(QPointF(*pos))
        self.setPath(self.path)
        self.scene.update()

    def closePath(self):
        self.path.closeSubpath()
        n = self.path.elementCount()
        for i in range(n-1):
            node = Node(self, i)
            elem = self.path.elementAt(i)
            node.setPos(elem.x, elem.y)
            self.scene.addItem(node)
        self.setPath(self.path)
        self.scene.update()

    def updateElement(self, index, pos):
        self.path.setElementPositionAt(index, pos.x(), pos.y())
        self.setPath(self.path)
        self.scene.update()

class GraphicViewer(QGraphicsView):
    def __init__(self, parent, img=None, masks=None):
        super(GraphicViewer, self).__init__(parent)
        self.img = img
        self._scene = QGraphicsScene(self)
        self._scene.setSceneRect(0, 0, 1920, 900)
        self.setScene(self._scene)
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(0, 0, 0)))
        self.setFrameShape(QFrame.NoFrame)
        self.setRenderHint(QPainter.Antialiasing)
        self.resize(1920, 900)
        self.editable_path = None

    def mousePressEvent(self, event: QtGui.QMouseEvent):
        modifiers = QApplication.keyboardModifiers()
        if modifiers == QtCore.Qt.AltModifier:
            self.pos = (event.x(), event.y())
            if self.editable_path is None:
                self.editable_path = Path(self.pos, self._scene)
            else:
                self.editable_path.addElement(self.pos)
        else:
            super(GraphicViewer, self).mousePressEvent(event)

    def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent):
        if self.editable_path is not None:
            self.editable_path.closePath()
            self.editable_path = None

if __name__ == "__main__":

    app = QApplication([])
    MainWindow = QMainWindow()
    view = GraphicViewer(MainWindow)
    MainWindow.resize(1920, 900)
    MainWindow.show()
    app.exec_()
4

1 回答 1

2

您几乎拥有它,但是您的itemChange方法有一个错误。

我会重新编写您的示例,如下所示:

class Node(QGraphicsEllipseItem):
    ...
    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            self.path.updateElement(self.index, value.toPoint())
        return QGraphicsEllipseItem.itemChange(self, change, value)

class Path(QGraphicsPathItem):
    ...    
    def updateElement(self, index, pos):
        self.path.setElementPositionAt(index, pos.x(), pos.y())
        if index == 0:
            self.path.setElementPositionAt(
                self.path.elementCount() - 1, pos.x(), pos.y())
        self.setPath(self.path)
        self.scene.update()
于 2017-10-25T17:30:28.657 回答