diff options
Diffstat (limited to 'ui/control_editor.py')
| -rw-r--r-- | ui/control_editor.py | 58 |
1 files changed, 56 insertions, 2 deletions
diff --git a/ui/control_editor.py b/ui/control_editor.py index c582e6b..48de21d 100644 --- a/ui/control_editor.py +++ b/ui/control_editor.py @@ -18,8 +18,10 @@ from PyQt6.QtWidgets import ( QLabel, QLineEdit, QComboBox, QDoubleSpinBox, QSpinBox, QPushButton, QGroupBox, QWidget, QStackedWidget, QFrame, QCheckBox, QSizePolicy, + QTextEdit, QScrollArea, ) from PyQt6.QtCore import Qt +from PyQt6.QtGui import QFont, QFontMetrics class _DynStack(QStackedWidget): @@ -68,6 +70,8 @@ class ControlSpec: max_val: float = 100.0 step: float = 1.0 max_rpm: int = 3000 + # Optional Python snippet executed on every value change + on_action_script: str = "" def to_dict(self) -> dict: return asdict(self) @@ -177,8 +181,8 @@ class ControlEditorDialog(QDialog): self.result_spec: Optional[ControlSpec] = None self.setWindowTitle("Edit Control" if spec else "Add Control") - self.setMinimumSize(440, 460) - self.resize(460, 500) + self.setMinimumSize(440, 520) + self.resize(460, 580) self._build() self._load_spec() @@ -244,6 +248,40 @@ class ControlEditorDialog(QDialog): self._stack.addWidget(panel) params_lay.addWidget(self._stack) root.addWidget(self._params_grp) + + # ── On-action script ─────────────────────────────────────────── + script_grp = QGroupBox("On Action Script (optional)") + script_lay = QVBoxLayout(script_grp); script_lay.setContentsMargins(10, 12, 10, 10) + + _help = QLabel( + "Runs every time this control changes value.\n" + "Available: value, channel_id, device_id, vars (shared dict), " + "channels (latest values by channel_id), math" + ) + _help.setObjectName("traceSource"); _help.setWordWrap(True) + script_lay.addWidget(_help) + + _mono = QFont("IBM Plex Mono, Consolas, Monospace") + _mono.setStyleHint(QFont.StyleHint.Monospace) + self._script_edit = QTextEdit() + self._script_edit.setFont(_mono) + self._script_edit.setTabStopDistance(QFontMetrics(_mono).horizontalAdvance(" ") * 4) + self._script_edit.setMinimumHeight(80); self._script_edit.setMaximumHeight(160) + self._script_edit.setPlaceholderText( + "# Example: zero a position offset when button is pressed\n" + "# vars['zero_pos'] = channels.get('encoder_0', 0)" + ) + script_lay.addWidget(self._script_edit) + + self._script_status = QLabel("") + self._script_status.setObjectName("traceSource") + check_btn = QPushButton("Check Syntax") + check_btn.clicked.connect(self._check_script) + btn_row = QHBoxLayout() + btn_row.addWidget(self._script_status, 1) + btn_row.addWidget(check_btn) + script_lay.addLayout(btn_row) + root.addWidget(script_grp) root.addStretch() # ── Buttons ──────────────────────────────────────────────────── @@ -258,6 +296,18 @@ class ControlEditorDialog(QDialog): btn_row.addWidget(cancel); btn_row.addWidget(ok) root.addLayout(btn_row) + def _check_script(self): + src = self._script_edit.toPlainText().strip() + if not src: + self._script_status.setText(""); return + try: + compile(src, "<script>", "exec") + self._script_status.setText("✓ OK") + self._script_status.setStyleSheet("color:#22c55e;") + except SyntaxError as e: + self._script_status.setText(f"⚠ {e}") + self._script_status.setStyleSheet("color:#ef4444;") + def _load_spec(self): """Populate fields from self.spec.""" # Type @@ -287,6 +337,9 @@ class ControlEditorDialog(QDialog): for name, panel in self._param_panels.items(): panel.load(self.spec) + # Script + self._script_edit.setPlainText(self.spec.on_action_script) + def _on_type_changed(self, type_name: str): idx = list(_PARAM_PANELS.keys()).index(type_name) self._stack.setCurrentIndex(idx) @@ -340,6 +393,7 @@ class ControlEditorDialog(QDialog): device_id=self._dev_cb.currentData() or "", channel_id=channel_id, active_low=self._active_low_chk.isChecked(), + on_action_script=self._script_edit.toPlainText().strip(), ) # Save type-specific params panel = self._param_panels.get(ctype) |
