From 31cd1ec6fe72e6f9410e655c632a784429dd680a Mon Sep 17 00:00:00 2001
From: Johannes Kung <johku144@student.liu.se>
Date: Fri, 2 Aug 2024 10:35:49 +0200
Subject: [PATCH] Fixed SignalGraphicsItem drawing

---
 src/simudator/gui/signal_graphics_item.py | 195 +++++++++-------------
 1 file changed, 79 insertions(+), 116 deletions(-)

diff --git a/src/simudator/gui/signal_graphics_item.py b/src/simudator/gui/signal_graphics_item.py
index a0a4320..d2f7569 100644
--- a/src/simudator/gui/signal_graphics_item.py
+++ b/src/simudator/gui/signal_graphics_item.py
@@ -1,14 +1,16 @@
 import itertools
 import math
+import typing
 
 from qtpy import QtCore
 from qtpy.QtCore import QPoint, QPointF, QRectF, Qt
-from qtpy.QtGui import QCursor, QPainter, QPen
+from qtpy.QtGui import QCursor, QPainter, QPainterPath, QPen
 from qtpy.QtWidgets import (
     QGraphicsItem,
     QGraphicsLineItem,
     QGraphicsSceneMouseEvent,
     QGraphicsWidget,
+    QWidget,
 )
 
 from simudator.gui.color_scheme import ColorScheme
@@ -63,30 +65,28 @@ class SignalGraphicsItem(QGraphicsWidget):
         #           |
         # port -----|
 
-        start_point = self.start_port.getPoint()
-        end_point = self.end_port.getPoint()
-        self._init_line_points(start_point, end_point)
-
-        # local_start = self.mapFromItem(start_port, start_point)
-        # local_end = self.mapFromItem(end_port, end_point)
-        # local_start = self.mapFromScene(start_point)
-        # local_end = self.mapFromScene(end_point)
-        local_start = start_point - self.start_port.scenePos()
-        local_end = end_point - self.end_port.scenePos()
-
+        start_point = self.mapFromScene(self.start_port.getPoint())
+        end_point = self.mapFromScene(self.end_port.getPoint())
         self._init_line_points(start_point, end_point)
 
-        self.setPos(0, 0)
-
         # Used to lock layout
         self.is_locked = False
 
     def _init_line_points(self, start_point: QPointF, end_point: QPointF) -> None:
-        """ """
-
-        #           |---- port
-        #           |
-        # port -----|
+        """Initialise the visual representation of the signal as a zig-zag
+        between the ports of the signal.
+
+                  |---- port
+                  |
+        port -----|
+
+        Parameters
+        ----------
+        start_point : QPointF
+            Point of the end of one port.
+        end_point : QPointF
+            Point of the end of the other port.
+        """
 
         half_x = (start_point.x() + end_point.x()) / 2
 
@@ -109,27 +109,17 @@ class SignalGraphicsItem(QGraphicsWidget):
         self.last_line_start_i = None
         self.last_line_end_i = None
 
-        # Initialise the visual representation of the signal as a zig-zag
-        # between the ports of the signal
-        #           |---- port
-        #           |
-        # port -----|
-
         start_point = self.start_port.getPoint()
         end_point = self.end_port.getPoint()
-        self.points.append(start_point)
-        half_x = (start_point.x() + end_point.x()) / 2
-        self.points.append(QPointF(half_x, start_point.y()))
-        self.points.append(QPointF(half_x, end_point.y()))
-        self.points.append(end_point)
+        self._init_line_points(start_point, end_point)
 
         self.setVisible(True)
-        self.drawSignal()
+        self.update()
 
     def splitLine(self, start_point_index: int, end_point_index: int) -> None:
         """
         Splits an existing line of two segments into a new line of four line
-        segments by adding three new points and removing the old corer.
+        segments by adding three new points and removing the old corner.
 
             |   ->    __|
         ____|   -> __|
@@ -153,7 +143,7 @@ class SignalGraphicsItem(QGraphicsWidget):
         else:
             bot_corner = QPointF(half_point.x(), next_half_point.y())
 
-        # Remove the old corner and insteart new points in the correct288.5 order
+        # Remove the old corner and insert new points in the correct order
         self.points.pop(end_point_index)
         self.points.insert(end_point_index, next_half_point)
         self.points.insert(end_point_index, bot_corner)
@@ -167,10 +157,9 @@ class SignalGraphicsItem(QGraphicsWidget):
         no line segment will be chosen as pressed and the mouse press event
         is ignored.
         """
-        print(892734)
         # Calculate and save which line segment was pressed (choose the one
         # closest to the point pressed)
-        press_point = event.scenePos()
+        press_point = self.mapFromScene(event.scenePos())
         line_start_i, line_end_i = self.getClosestPoints(press_point)
         line_start = self.points[line_start_i]
         line_end = self.points[line_end_i]
@@ -186,34 +175,36 @@ class SignalGraphicsItem(QGraphicsWidget):
         dist_to_line = self.getLineDistance(line_start, line_end, press_point)
         if dist_to_line > LONGEST_CLICK_DIST:
             event.ignore()
+            # event.accept()
 
-        # Save whether the pressed line segment is vertical or horisontal
+        # Save whether the pressed line segment is vertical or horizontal
         # for convenience
         if line_start.x() == line_end.x():
             self.last_line_vertical = True
         elif line_start.y() == line_end.y():
             self.last_line_vertical = False
 
+        event.accept()
+
     def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
         """
         Calls the functions 'handleLeftClick' or 'handleRightClick' if
         the user left or right clicked.
         """
-        print(1111111111111)
         if event.button() == Qt.MouseButton.LeftButton:
             self.handleLeftClick()
 
         elif event.button() == Qt.MouseButton.RightButton:
             self.handleRightClick()
+        event.accept()
 
     def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
         """
         Handles moving a pressed line segment with left click. The segment is
-        moved horisontally of the segment itself is vertical and vice versa.
+        moved horizontally of the segment itself is vertical and vice versa.
         The two line sgments that make up the ends of the signal are not moved
         if pressed.
         """
-        print(22222222222222222222)
         if not self.left_click or self.is_locked:
             return
 
@@ -224,7 +215,7 @@ class SignalGraphicsItem(QGraphicsWidget):
             self.setCursor(QCursor(QtCore.Qt.SizeVerCursor))
 
         # Retrieve which line segment to move and where to move it
-        current_mouse_point = event.scenePos()
+        current_mouse_point = self.mapFromScene(event.scenePos())
         line_start = self.points[self.last_line_start_i]
         line_end = self.points[self.last_line_end_i]
 
@@ -235,30 +226,30 @@ class SignalGraphicsItem(QGraphicsWidget):
         if line_end == self.points[0] or line_end == self.points[-1]:
             return
 
-        # If the line segment is vertical, move it horisontally288.5
+        # If the line segment is vertical, move it horizontally
         if self.last_line_vertical:
             line_start.setX(current_mouse_point.x())
             line_end.setX(current_mouse_point.x())
 
-        # If the line segment is horisontal, move it vertically
+        # If the line segment is horizontal, move it vertically
         if not self.last_line_vertical:
             line_start.setY(current_mouse_point.y())
             line_end.setY(current_mouse_point.y())
 
-        self.drawSignal()
+        self.update()
 
     def handleLeftClick(self) -> None:
         self.align(
             self.last_line_start_i, self.last_line_end_i, self.last_line_vertical
         )
-        self.drawSignal()
 
         # Reset cursor
         self.setCursor(QCursor(QtCore.Qt.ArrowCursor))
+        self.update()
 
     def align_search(self, start_index, ascending=False) -> int:
         """
-        Recursivly searches deeper into the line structure to get the smallest
+        Recursively searches deeper into the line structure to get the smallest
         and largest index of which all points in between should be removed to
         merge small line segments.
         """
@@ -316,22 +307,27 @@ class SignalGraphicsItem(QGraphicsWidget):
             return
 
         self.splitLine(self.last_line_start_i, self.last_line_end_i)
-        self.drawSignal()
+        self.update()
+
+    def shape(self) -> QPainterPath:
+        # Construct the shape as the union of the shapes of the line segments
+        path = QPainterPath()
+        for line_segment in self._line_segments:
+            local_shape = self.mapFromItem(line_segment, line_segment.shape())
+            path.addPath(local_shape)
+        return path
 
     def boundingRect(self):
         return self.childrenBoundingRect()
 
-    def paint(self, painter, *args):
-        # This avoids errors, it is a pure virtual function that
-        # needs an implementation
+    def paint(
+        self,
+        painter: typing.Optional[QPainter],
+        option: typing.Optional['QStyleOptionGraphicsItem'],
+        widget: typing.Optional[QWidget] = None,
+    ) -> None:
         # Remove old lines from the GUI
-
-        painter.setPen(QPen(QtCore.Qt.red))
-        painter.drawLine(0, 0, 5, 0)
-
-        while self.lines:
-            item = self.lines.pop()
-            item.setParentItem(None)
+        painter.save()
 
         # Remove old lines form the GUI
         while self._line_segments:
@@ -342,19 +338,12 @@ class SignalGraphicsItem(QGraphicsWidget):
         # to the third point and so on
         prev_point = self.points[0]
         for point in self.points[1:]:
-            # Coordinates
-            # Bounded box for clicking
-            x1 = prev_point.x()
-            y1 = prev_point.y()
-            x2 = point.x()
-            y2 = point.y()
-            # line = QGraphicsLineItem(x1, y1, x2, y2, self)
-            # line.setPen(ColorScheme.Signal)
             segment = LineSegment(prev_point, point, self)
             self._line_segments.append(segment)
-            # self.lines.append(line)
             prev_point = point
 
+        painter.restore()
+
     def distance_sq(self, point1, point2):
         dx = point1.x() - point2.x()
         dy = point1.y() - point2.y()
@@ -420,8 +409,8 @@ class SignalGraphicsItem(QGraphicsWidget):
         # Find out whether the start or the end of the signal moved
         old_start = self.points[0]
         old_end = self.points[-1]
-        new_start = self.start_port.getPoint()
-        new_end = self.end_port.getPoint()
+        new_start = self.mapFromScene(self.start_port.getPoint())
+        new_end = self.mapFromScene(self.end_port.getPoint())
 
         # Index the points that should be moved
         if old_start != new_start:
@@ -475,42 +464,19 @@ class LineSegment(QGraphicsWidget):
         super().__init__(parent)
         self.setFlag(QGraphicsItem.ItemIsMovable)
 
-        self._parent = parent
-
         self._margin = 5
 
-        self._calc_area(start_pos, end_pos)
-
-        self.setPos(self._start_pos)
-
-    def _calc_area(self, start_pos: QPointF, end_pos: QPointF):
-
-        start_pos = self.mapFromItem(self._parent, start_pos)
-        end_pos = self.mapFromItem(self._parent, end_pos)
-
-        self._start_pos = start_pos
-        self._end_pos = end_pos
-
-        top_left = QPointF(
-            min(self._start_pos.x(), self._end_pos.x()),
-            min(self._start_pos.y(), self._end_pos.y()),
-        )
-        width = max(self._start_pos.x(), self._end_pos.x()) - top_left.x()
-        height = max(self._start_pos.y(), self._end_pos.y()) - top_left.y()
-        self.setGeometry(
-            top_left.x() - self._margin,
-            top_left.y() - self._margin,
-            width + 2 * self._margin,
-            height + 2 * self._margin,
-        )
-
-    def update_position(self, start_pos: QPointF, end_pos: QPointF):
-        self._calc_area(start_pos, end_pos)
+        self.setPos(start_pos)
+        self._start_pos = QPointF(0, 0)
+        self._end_pos = self.mapFromParent(end_pos)
 
     def paint(self, painter: QPainter, *args):
         painter.drawLine(self._start_pos, self._end_pos)
-        painter.setPen(QPen(QtCore.Qt.red))
-        painter.drawRect(self.boundingRect())
+
+    def shape(self) -> QPainterPath:
+        path = QPainterPath()
+        path.addRect(self.boundingRect())
+        return path
 
     def boundingRect(self):
         top_left = QPointF(
@@ -526,21 +492,18 @@ class LineSegment(QGraphicsWidget):
             height + 2 * self._margin,
         )
 
-    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
-        """
-        Calls the functions 'handleLeftClick' or 'handleRightClick' if
-        the user left or right clicked.
-        """
-        print(123)
-        return
-        if event.button() == Qt.MouseButton.LeftButton:
-            self.handleLeftClick()
-
-        elif event.button() == Qt.MouseButton.RightButton:
-            self.handleRightClick()
-
-    def mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
-        print(321)
-
-    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
-        print(654)
+    def mousePressEvent(
+        self, event: typing.Optional['QGraphicsSceneMouseEvent']
+    ) -> None:
+        # Let the parent SignalGraphicsItem handle mouse events
+        event.ignore()
+
+    def mouseReleaseEvent(
+        self, event: typing.Optional['QGraphicsSceneMouseEvent']
+    ) -> None:
+        # Let the parent SignalGraphicsItem handle mouse events
+        event.ignore()
+
+    def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent | None') -> None:
+        # Let the parent SignalGraphicsItem handle mouse events
+        event.ignore()
-- 
GitLab