From ad7f1c39ef473a327ca3b467b579be0099377f55 Mon Sep 17 00:00:00 2001 From: Christian Kolset Date: Wed, 29 Jul 2026 13:17:00 -0600 Subject: Make Run the master stop switch; add Log button disabled/recording styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run/Stop previously only paused acquisition — control outputs (motor speed, switches, PWM duty) kept whatever value was last written, so stopping the run loop didn't stop a running motor. Adds ControlWidget.safe_stop() (per-widget override, default zeros the output) and ControlPanel.safe_stop_all(), called from _toggle_run's Stop branch before engine.stop(). Per-widget behavior is deliberately not uniform: - OnOffSwitch/MotorControl/PwmControl have a latched running/enabled state, so safe_stop() drives their own toggle handler (consistent UI + write in one path) and, for Motor/PWM, zeroes the slider too. - SetpointControl/AnalogOutputControl only write on an explicit user action and have no universally safe forced value (e.g. 0 isn't necessarily "off" for an arbitrary process setpoint or analog output) — Stop leaves them untouched rather than guessing. Log button: added a :disabled QSS rule so "can't log yet" reads as clearly inert rather than a duller version of the enabled look, and a 600ms blink (toggling a "recording" dynamic property the QSS keys off) while a recording is active, so it reads as live/recording rather than a static pressed button. Master Stop now calls _toggle_log(False) explicitly when forcing the button off, since QPushButton.setChecked() doesn't emit clicked — without this the blink would keep running after a master Stop even though logging itself already halted via engine.stop()'s internal stop_logging() call. Co-Authored-By: Claude Sonnet 5 --- ui/main_window.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'ui/main_window.py') diff --git a/ui/main_window.py b/ui/main_window.py index d0e0ac4..268a9d3 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -196,6 +196,9 @@ class MainWindow(QMainWindow): self._clock = QTimer(self); self._clock.setInterval(1000) self._clock.timeout.connect(self._tick) + self._rec_blink = QTimer(self); self._rec_blink.setInterval(600) + self._rec_blink.timeout.connect(self._tick_rec_blink) + def _connect_signals(self): self.engine.new_data.connect(self.processor.on_raw_data) self.processor.processed_data.connect(self._chart.on_new_data) @@ -521,9 +524,12 @@ class MainWindow(QMainWindow): self._run_btn.setText("⏹ STOP"); self._log_btn.setEnabled(True) self._clock.start(); self._status.setText("Acquiring…") else: + self._ctrl.safe_stop_all() # master switch — stop outputs before halting acquisition self.engine.stop() self._run_btn.setText("▶ RUN") - if self._log_btn.isChecked(): self._log_btn.setChecked(False) + if self._log_btn.isChecked(): + self._log_btn.setChecked(False) + self._toggle_log(False) # setChecked() alone won't fire clicked — stop blink/logging explicitly self._log_btn.setEnabled(False); self._clock.stop() self._status.setText("Stopped") @@ -539,8 +545,18 @@ class MainWindow(QMainWindow): os.path.join(self._settings.get("log_dir", "logs"), "")) self._log_btn.setText("⏹ LOGGING") self._status.setText(f"Logging → {p}") + self._rec_blink.start() else: self.engine.stop_logging(); self._log_btn.setText("⬤ LOG") + self._rec_blink.stop() + self._log_btn.setProperty("recording", False) + self._log_btn.style().unpolish(self._log_btn); self._log_btn.style().polish(self._log_btn) + + def _tick_rec_blink(self): + """Pulse the Log button's background while a recording is active.""" + on = not self._log_btn.property("recording") + self._log_btn.setProperty("recording", on) + self._log_btn.style().unpolish(self._log_btn); self._log_btn.style().polish(self._log_btn) # ── Theme / settings ────────────────────────────────────────────────── -- cgit v1.2.3