| 2713 | # ── view ───────────────────────────────────────────────────────────────────── |
| 2714 | |
| 2715 | class SchematicView(QGraphicsView): |
| 2716 | def __init__(self, scene: SchematicScene): |
| 2717 | super().__init__(scene) |
| 2718 | self.setRenderHint(QPainter.Antialiasing) |
| 2719 | self.setDragMode(QGraphicsView.RubberBandDrag) |
| 2720 | self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) |
| 2721 | self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) |
| 2722 | self.setMouseTracking(True) |
| 2723 | |
| 2724 | scene.placing_started.connect(self._on_active_mode) |
| 2725 | scene.placing_cancelled.connect(self._on_normal_mode) |
| 2726 | scene.wire_mode_started.connect(self._on_active_mode) |
| 2727 | scene.wire_mode_ended.connect(self._on_normal_mode) |
| 2728 | scene.group_move_started.connect(self._on_active_mode) |
| 2729 | scene.group_move_ended.connect(self._on_normal_mode) |
| 2730 | |
| 2731 | self._panning = False |
| 2732 | self._pan_start = None |
| 2733 | self._rb_start = None # viewport pos where left-drag started |
| 2734 | |
| 2735 | QTimer.singleShot(0, self._init_view) |
| 2736 | |
| 2737 | def _init_view(self): |
| 2738 | self.scale(DEFAULT_ZOOM, DEFAULT_ZOOM) |
| 2739 | self.centerOn(0, 0) |
| 2740 | |
| 2741 | def _on_active_mode(self): |
| 2742 | self.setDragMode(QGraphicsView.NoDrag) |
| 2743 | self.setCursor(Qt.CrossCursor) |
| 2744 | |
| 2745 | def _on_normal_mode(self): |
| 2746 | self.setDragMode(QGraphicsView.RubberBandDrag) |
| 2747 | self.unsetCursor() |
| 2748 | |
| 2749 | # ── zoom ───────────────────────────────────────────────────────────────── |
| 2750 | |
| 2751 | def wheelEvent(self, event): |
| 2752 | factor = 1.15 if event.angleDelta().y() > 0 else 1 / 1.15 |
| 2753 | self.scale(factor, factor) |
| 2754 | |
| 2755 | def zoom_in(self): |
| 2756 | self.scale(1.25, 1.25) |
| 2757 | |
| 2758 | def zoom_out(self): |
| 2759 | self.scale(1 / 1.25, 1 / 1.25) |
| 2760 | |
| 2761 | def zoom_reset(self): |
| 2762 | self.resetTransform() |
| 2763 | self.scale(DEFAULT_ZOOM, DEFAULT_ZOOM) |
| 2764 | r = self.scene().itemsBoundingRect() |
| 2765 | self.centerOn(r.center() if not r.isNull() else QPointF(0, 0)) |
| 2766 | |
| 2767 | def zoom_fit(self): |
| 2768 | """Fit all scene items in the viewport with a small margin.""" |
| 2769 | r = self.scene().itemsBoundingRect() |
| 2770 | if r.isNull(): |
| 2771 | self.zoom_reset() |
| 2772 | return |