| 26 | |
| 27 | |
| 28 | class ShapeItem(QGraphicsItem): |
| 29 | |
| 30 | def __init__(self, kind: str, |
| 31 | rel_points: list, |
| 32 | stroke_color: str = "#000000", |
| 33 | fill_color: str = "#ffffff", |
| 34 | fill_style: str = "none", # "none" | "solid" |
| 35 | line_style: str = "solid", # "solid"|"dashed"|"dotted"|"dash-dot" |
| 36 | line_end_start: str = "none", # "none"|"arrow"|"dot"|"diamond" |
| 37 | line_end_end: str = "none", |
| 38 | line_width: float = 1.5, |
| 39 | pos: QPointF = QPointF(0, 0)): |
| 40 | super().__init__() |
| 41 | |
| 42 | # Migrate legacy "arrow" kind |
| 43 | if kind == "arrow": |
| 44 | kind = "line" |
| 45 | line_end_end = "arrow" |
| 46 | |
| 47 | self.kind = kind |
| 48 | self.rel_points = [QPointF(p) for p in rel_points] |
| 49 | self.stroke_color = stroke_color |
| 50 | self.fill_color = fill_color |
| 51 | self.fill_style = fill_style |
| 52 | self.line_style = line_style |
| 53 | self.line_end_start = line_end_start |
| 54 | self.line_end_end = line_end_end |
| 55 | self.line_width = line_width |
| 56 | self.setPos(pos) |
| 57 | self.setFlag(QGraphicsItem.ItemIsSelectable) |
| 58 | self.setFlag(QGraphicsItem.ItemIsMovable) |
| 59 | self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) |
| 60 | |
| 61 | # ── geometry ────────────────────────────────────────────────────────────── |
| 62 | |
| 63 | def _content_rect(self) -> QRectF: |
| 64 | pts = self.rel_points |
| 65 | if not pts: |
| 66 | return QRectF() |
| 67 | if self.kind == "line": |
| 68 | xs = [p.x() for p in pts]; ys = [p.y() for p in pts] |
| 69 | return QRectF(min(xs), min(ys), |
| 70 | max(xs) - min(xs), max(ys) - min(ys)) |
| 71 | if self.kind == "rect" and len(pts) >= 2: |
| 72 | x0, y0 = pts[0].x(), pts[0].y() |
| 73 | x1, y1 = pts[1].x(), pts[1].y() |
| 74 | return QRectF(min(x0, x1), min(y0, y1), |
| 75 | abs(x1 - x0), abs(y1 - y0)) |
| 76 | if self.kind == "circle" and len(pts) >= 2: |
| 77 | r = math.hypot(pts[1].x(), pts[1].y()) |
| 78 | return QRectF(-r, -r, 2 * r, 2 * r) |
| 79 | return QRectF() |
| 80 | |
| 81 | def boundingRect(self) -> QRectF: |
| 82 | m = self.line_width / 2 + 18 |
| 83 | return self._content_rect().adjusted(-m, -m, m, m) |
| 84 | |
| 85 | def shape(self) -> QPainterPath: |
no outgoing calls
no test coverage detected