我正在尝试从这个答案扩展出色的实现,因此将通过用户在图形场景中的点击创建路径,并使用双击关闭路径。
这很好用,但我唯一无法解决的问题是路径中第一个节点的更改。显然我希望它会通过该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_()