(self, symbol_name: str, instance_id: str, svg_bytes: bytes)
| 477 | """ |
| 478 | |
| 479 | def __init__(self, symbol_name: str, instance_id: str, svg_bytes: bytes): |
| 480 | stripped, texts = _split_symbol_text(svg_bytes) |
| 481 | super().__init__(stripped) |
| 482 | self.symbol_name = symbol_name |
| 483 | self.instance_id = instance_id |
| 484 | self._svg_bytes = stripped |
| 485 | # Embedded symbol text, drawn upright in paint()/export (not in the artwork). |
| 486 | self.symbol_texts = texts |
| 487 | self.model: str = SYMBOL_MODEL.get(symbol_name, "") |
| 488 | self.params: dict[str, str] = params_for_symbol(symbol_name) or fixed_params_for_symbol(symbol_name) |
| 489 | _n_refs = refs_for_symbol(symbol_name) |
| 490 | _is_sub = SYMBOL_PREFIX.get(symbol_name) == "X" |
| 491 | self.refs: list[str] = [] if _is_sub else ["?"] * _n_refs |
| 492 | # Ground and port are power symbols — show net name, never refdes |
| 493 | _show_refdes = symbol_name not in ("0", "port") |
| 494 | self.prop_display: dict[str, tuple[bool, bool]] = {"refdes": (_show_refdes, False)} |
| 495 | # Parameter default visibility comes from the symbol's data-params. |
| 496 | _param_disp = SYMBOL_PARAM_DISPLAY.get(symbol_name, {}) |
| 497 | for pname in self.params: |
| 498 | self.prop_display[pname] = _param_disp.get(pname, (False, False)) |
| 499 | # References are shown by default. |
| 500 | for i in range(refs_for_symbol(symbol_name)): |
| 501 | self.prop_display[f"ref {i + 1}"] = (True, False) |
| 502 | # The model is shown per the symbol's data-model flag (subcircuit blocks |
| 503 | # and "?" reminders set it, e.g. data-model="?|1"). |
| 504 | if self.model: |
| 505 | self.prop_display["model"] = (SYMBOL_MODEL_SHOW.get(symbol_name, False), False) |
| 506 | # Power symbols (ground/port) carry a net-name field, shown by default. |
| 507 | if symbol_name in ("0", "port"): |
| 508 | self.prop_display["name"] = (True, False) |
| 509 | self.prop_offsets: dict[str, tuple[float, float]] = {} |
| 510 | self.h_flip: bool = False |
| 511 | self.v_flip: bool = False |
| 512 | self._labels: dict[str, _PropertyLabel] = {} |
| 513 | # Key of the label the user last clicked, or None. Drives the dashed |
| 514 | # leader line: a clicked attribute shows only its own line; selecting the |
| 515 | # component body (no active label) shows lines to all attributes. |
| 516 | self._active_label_key: "str | None" = None |
| 517 | self.setFlag(QGraphicsItem.ItemIsSelectable) |
| 518 | self.setFlag(QGraphicsItem.ItemIsMovable) |
| 519 | self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) |
| 520 | self.setZValue(config.Z_COMPONENT) # above wires so labels stay selectable |
| 521 | self._prev_pos: "QPointF | None" = None |
| 522 | # Wires captured at the start of a drag (as [(wire, point_index), …]) so |
| 523 | # they follow this component's pins. None until the first move of a drag; |
| 524 | # reset to None by the scene at every mouse press. See _rubber_band_wires. |
| 525 | self._drag_wires: "list | None" = None |
| 526 | # Indices of pins with nothing connected to them — drawn as grey markers. |
| 527 | # Starts with all pins unconnected; the scene refreshes this on every |
| 528 | # topology change (via _sync_junctions → _refresh_pin_markers). |
| 529 | self._unconnected_pins: set[int] = set( |
| 530 | range(len(PIN_POSITIONS.get(symbol_name, []))) |
| 531 | ) |
| 532 | self.update_labels() |
| 533 | _live_components.add(self) |
| 534 | |
| 535 | # ── flip / rotation ─────────────────────────────────────────────────────── |
| 536 |
no test coverage detected