This polygon is drawn by the QGraphicsPolygonItem and the QGraphicsPathItem. Now I want to add vertices by clicking on the polygon edge with the mouse, but I added the code to add vertices, which causes the polygon to appear deformed.
please help me.
the code as follow.
class GripItem(QGraphicsPathItem):
editing = False
circle = QPainterPath()
circle.addEllipse(QRectF(-6, -6, 12, 12))
square = QPainterPath()
square.addRect(QRectF(-10, -10, 20, 20))
def __init__(self, annotation_item, index):
super(GripItem, self).__init__()
self.m_annotation_item = annotation_item
self.m_index = index
self.mySignal = SignalManager()
#self.editing = True
self.setPath(GripItem.circle)
self.setBrush(QColor("green"))
self.setPen(QPen(QColor("green"), 2))
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setAcceptHoverEvents(True)
self.setZValue(11)
self.setCursor(QCursor(Qt.PointingHandCursor))
def hoverEnterEvent(self, event):
self.setPath(GripItem.square)
self.setBrush(QColor("red"))
super(GripItem, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setPath(GripItem.circle)
self.setBrush(QColor("green"))
super(GripItem, self).hoverLeaveEvent(event)
def mouseReleaseEvent(self, event):
print("release")
self.setSelected(False)
super(GripItem, self).mouseReleaseEvent(event)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionChange and self.isEnabled():
self.m_annotation_item.movePoint(self.m_index, value)
return super(GripItem, self).itemChange(change, value)
def mousePressEvent(self, event):
if event.button() == Qt.RightButton:
if self.editing:
if self.m_annotation_item is not None:
print("parent")
#self.m_annotation_item.scene().removeItem(self)
it = self.m_annotation_item.m_items[self.m_index]
self.scene().removeItem(it)
del it
self.remove_point()
#del self.m_annotation_item.m_points[self.m_index]
self.m_annotation_item.setPolygon(QPolygonF(self.m_annotation_item.m_points))
super(GripItem, self).mousePressEvent(event)
def remove_point(self):
print(self.m_annotation_item.dict_points[self.m_index])
self.m_annotation_item.m_points.clear()
self.m_annotation_item.dict_points[self.m_index] = 0
for key, value in self.m_annotation_item.dict_points.items():
if not isinstance(value, int):
self.m_annotation_item.m_points.append(value)
class PolygonAnnotation(QGraphicsPolygonItem):
editing = False
def __init__(self, scene):
super(PolygonAnnotation, self).__init__()
self.parent_scene = scene
self.m_points = []
self.center_x = 0
self.center_y = 0
self.setZValue(10)
self.setPen(QPen(QColor("green"), 2))
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setCursor(QCursor(Qt.PointingHandCursor))
self.setAcceptHoverEvents(True)
self.m_items = []
self.dict_points = dict()
def number_of_points(self):
return len(self.m_items)
def calculate_center(self, p):
self.center_x += p.x()
self.center_y += p.y()
def addPoint(self, p):
self.m_points.append(p)
#print(len(self.dict_points))
self.dict_points[len(self.dict_points)] = p
self.setPolygon(QPolygonF(self.m_points))
item = GripItem(self, len(self.m_points)-1)
self.scene().addItem(item)
self.m_items.append(item)
item.setPos(p)
def removeLastPoint(self):
if self.m_points:
self.m_points.pop()
self.setPolygon(QPolygonF(self.m_points))
it = self.m_items.pop()
if it is not None and self.scene() is not None:
self.scene().removeItem(it)
del it
if self.editing and self.dict_points:
last_key = list(self.dict_points.keys())[-1]
self.dict_points.pop(last_key)
def movePoint(self, i, p):
if 0 <= i < len(self.m_points):
self.m_points[i] = self.mapFromScene(p)
self.setPolygon(QPolygonF(self.m_points))
def move_item(self, index, pos):
if 0 <= index < len(self.m_items):
item = self.m_items[index]
item.setEnabled(False)
item.setPos(pos)
item.setEnabled(True)
def itemChange(self, change, value):
if change == QGraphicsItem.ItemPositionHasChanged:
for i, point in enumerate(self.m_points):
self.move_item(i, self.mapToScene(point))
return super(PolygonAnnotation, self).itemChange(change, value)
def delete_polygon(self):
for i in range(len(self.m_items)):
it = self.m_items.pop()
self.scene().removeItem(it)
self.scene().removeItem(self)
self.m_points.clear()
del self
def hoverEnterEvent(self, event):
self.setBrush(QColor(255, 0, 0, 100))
super(PolygonAnnotation, self).hoverEnterEvent(event)
def hoverLeaveEvent(self, event):
self.setBrush(QBrush(Qt.NoBrush))
super(PolygonAnnotation, self).hoverLeaveEvent(event)
def getQPointFromDict(self, dict):
self.m_points.clear()
for key, value in dict.items():
if type(value) is not int:
self.m_points.append(value)
# insert a vertex to the polygon.
def mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
if self.parent_scene.current_instruction != Instructions.No_Instruction:
self.parent_scene.current_instruction = Instructions.No_Instruction
self.parent_scene.start_point = None
if event.button() == Qt.LeftButton:
mouse_position = event.pos()
index = self.isEdgeClick(mouse_position)
if index != -1:
# Add a vertex at the mouse click position
self.insert_point(index, self.mapToScene(mouse_position))
return super().mousePressEvent(event)
def insert_point(self, index, p):
self.m_points.insert(index, p)
#print(len(self.dict_points))
self.dict_points[len(self.dict_points)] = p
self.setPolygon(QPolygonF(self.m_points))
item = GripItem(self, len(self.m_points)-1)
self.scene().addItem(item)
self.m_items.insert(index, item)
item.setPos(p)
def isEdgeClick(self, mouse_position):
# Check if the mouse click is on the edge of the polygon
if len(self.m_points) < 2:
return False # Need at least 2 points to have an edge
polygon = QPolygonF(self.m_points)
# Iterate through the edges of the polygon and check if the click is close to any edge
for i in range(len(self.m_points)):
p1 = polygon.at(i)
p2 = polygon.at((i + 1) % len(self.m_points))
# Calculate the distance from the point to the edge
dist = distance_point_to_line(p1, p2, mouse_position)
# You can adjust this threshold as needed
if dist < 1.0:
return i+1
return -1
please provide the right code, thanks!

Assuming that your
distance_point_to_linecomputation (which you never provided, by the way) is valid, your have two problems:GripItem(self, len(self.m_points)-1)), instead of their real index;Even if you fix the above, your implementation has conceptual flaws.
First of all, you're fundamentally storing vertex data in four different "places":
m_points;dict_points;m_items(which are placed at the same points);Then, since the grip items are clearly closely related to the polygon, they should not be "external" items, but children of the polygon.
By using a more appropriate hierarchy between those items allows better management of every aspect, avoiding unnecessary complications (such as your attempt to move grip items when the polygon is moved).
I decided to completely rewrite the whole implementation (based on some previous code of mine), because fixing the above points would have been even more complex.
The idea is that every grip item is always a child of the
PolygonAnnotation, which completely manages their creation, removal and position change.There is only one list of vertexes, which is the list of grip items, only used when they are moved or when new vertexes are added or any vertex is removed. This makes it simpler to find the index of a grip item whenever it's moved (or removed) and you don't need to manually update the indexes of each item every time the vertex count changes.
Note I changed the insertion/removal behavior by using keyboard modifiers (Shift to add, Ctrl for remove), since your code didn't explain about the "editing mode", nor what that
current_instructionmeans (you should useself.scene(), by the way).You can test the above with the following example code: