diff options
| author | Christian Kolset <christian.kolset@gmail.com> | 2026-06-09 22:27:44 -0600 |
|---|---|---|
| committer | Christian Kolset <christian.kolset@gmail.com> | 2026-06-09 22:27:44 -0600 |
| commit | f8b5e47ffe9a52bd28ad5d953a551ccb92cbbb09 (patch) | |
| tree | f22a6743ea7b9ac2a4c476bd85abd462ea445f78 /ui | |
| parent | 4d62e4c03227500984a2770ef9615c603a5b71aa (diff) | |
Channels: inject variable names as comment in code editor, fix derivative wrt
- Variable names now appear as a live '# Variables: A0 = x[0] · ...'
comment at the top of the code editor (function, custom_script, and
built-in read-only templates); updates on every source add/remove/change
- Derivative 'With Respect To' channel combo now included in variable list
as x[1] when channel mode is selected
- _apply() strips the auto-generated comment before saving the script so
it doesn't pollute the stored expression
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/windows/channels_window.py | 64 |
1 files changed, 59 insertions, 5 deletions
diff --git a/ui/windows/channels_window.py b/ui/windows/channels_window.py index 3730b12..266432b 100644 --- a/ui/windows/channels_window.py +++ b/ui/windows/channels_window.py @@ -723,6 +723,7 @@ class DerivedBlock(QFrame): _cur_wrt = self.dc.params.get("deriv_wrt", "time") self._wrt_cb.setCurrentIndex(1 if _cur_wrt == "channel" else 0) self._wrt_cb.currentIndexChanged.connect(self._on_wrt_changed) + self._wrt_cb.currentIndexChanged.connect(lambda _: self._update_vars_hint()) wrt_top.addWidget(self._wrt_cb); wrt_top.addStretch() wrt_lay.addLayout(wrt_top) self._wrt_x_row = QWidget() @@ -736,6 +737,7 @@ class DerivedBlock(QFrame): for i in range(self._wrt_x_cb.count()): if self._wrt_x_cb.itemData(i) == x_src: self._wrt_x_cb.setCurrentIndex(i); break + self._wrt_x_cb.currentIndexChanged.connect(lambda _: self._update_vars_hint()) wx_lay.addWidget(self._wrt_x_cb, 1) wrt_lay.addWidget(self._wrt_x_row) bl.addWidget(self._wrt_grp) @@ -814,17 +816,63 @@ class DerivedBlock(QFrame): self._update_vars_hint() def _update_vars_hint(self): - """Refresh the variable-name hint above the code editor.""" + """Refresh the variable-name hint and code comment whenever sources change.""" if not hasattr(self, "_vars_lbl"): return from core.signal_processor import _make_src_names + from ui.code_templates import BUILTIN_TEMPLATES + + # Build source list — for derivatives in channel mode, include wrt channel sources = [cb.currentData() for cb in self._src_combos if cb.currentData()] + is_deriv = self.dc.kind in ("derivative", "second_derivative") + if (is_deriv and hasattr(self, "_wrt_cb") + and self._wrt_cb.currentIndex() == 1 + and hasattr(self, "_wrt_x_cb")): + wrt_src = self._wrt_x_cb.currentData() + if wrt_src and wrt_src not in sources: + sources.append(wrt_src) + if not sources: self._vars_lbl.setText("No sources — add sources above to use named variables") + self._inject_code_comment("") return + names = _make_src_names(sources) parts = [f"{name} = x[{i}]" for i, name in enumerate(names)] - self._vars_lbl.setText("Variables: " + " · ".join(parts)) + comment = "# Variables: " + " · ".join(parts) + self._vars_lbl.setText(comment) + self._inject_code_comment(comment) + + def _inject_code_comment(self, comment: str): + """Insert or update a '# Variables:' first line in the code editor.""" + if not hasattr(self, "_code"): + return + from ui.code_templates import BUILTIN_TEMPLATES + + if self.dc.kind in _BUILTIN: + # Read-only template — prepend comment to base template each time + template = BUILTIN_TEMPLATES.get(self.dc.kind, "") + new_text = (comment + "\n" + template) if comment else template + if self._code.toPlainText() != new_text: + self._code.setPlainText(new_text) + + elif self.dc.kind in ("function", "custom_script"): + current = self._code.toPlainText() + lines = current.splitlines() + if lines and lines[0].startswith("# Variables:"): + # Replace existing auto-comment + rest = "\n".join(lines[1:]).lstrip("\n") + new_text = (comment + "\n" + rest) if comment else rest + else: + new_text = (comment + "\n" + current) if comment else current + if self._code.toPlainText() != new_text: + # Preserve cursor position + cursor = self._code.textCursor() + pos = cursor.position() + len(comment) + 1 + self._code.setPlainText(new_text) + cursor.setPosition(min(pos, len(new_text))) + self._code.setTextCursor(cursor) + # expression kind: label-only, no code injection (single-line expr) def _on_kind(self, idx: int): from ui.code_templates import BUILTIN_TEMPLATES @@ -834,9 +882,9 @@ class DerivedBlock(QFrame): if k in ("derivative", "second_derivative") and not self._src_combos: self._add_src() if k in _BUILTIN: - # Show the template as a read-only preview + # Reset to bare template first; _update_vars_hint will prepend comment self._code.setPlainText(BUILTIN_TEMPLATES.get(k, "")) - self._update_vars_hint() + self._update_vars_hint() # also injects variable comment into code self._update_visibility() def _on_wrt_changed(self, idx: int): @@ -855,6 +903,7 @@ class DerivedBlock(QFrame): self._help.setText(_KIND_HELP.get("custom_script", "")) self.dc.script = template self._code.setPlainText(template) + self._update_vars_hint() # prepend variable comment to template self._update_visibility() def _update_visibility(self): @@ -884,7 +933,12 @@ class DerivedBlock(QFrame): if self.dc.kind == "expression": self.dc.expression = self._code.toPlainText().strip() elif self.dc.kind in ("function", "custom_script"): - self.dc.script = self._code.toPlainText() + # Strip auto-generated Variables comment before saving + raw = self._code.toPlainText() + lines = raw.splitlines() + if lines and lines[0].startswith("# Variables:"): + raw = "\n".join(lines[1:]).lstrip("\n") + self.dc.script = raw err = self.processor.add_derived(self.dc) if err: self._st.setText(f"⚠ {err[:100]}") |
