From afc3bb783a648a3f7a35ffc9c1c6c84858e276c6 Mon Sep 17 00:00:00 2001 From: x01dc Date: Fri, 14 Aug 2026 10:17:52 +0200 Subject: [PATCH] fix(tomo_params): debounce beamline-busy banner to stop flicker _refresh_busy_banner() toggled the banner's visibility synchronously on every poll tick and scan-queue push message, so brief blips in the underlying queue status made it flicker on/off. Show busy immediately, but hold the banner up for 5s of continuous idle before clearing it, mirroring the debounce pattern already used for XRayEye's queue-guarded toggles (_queue_idle_timer). Co-Authored-By: Claude Sonnet 5 --- .../widgets/tomo_params/tomo_params.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py b/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py index 3f4f33e6..0e457fd6 100644 --- a/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py +++ b/csaxs_bec/bec_widgets/widgets/tomo_params/tomo_params.py @@ -288,6 +288,11 @@ class TomoParamsWidget(BECWidget, QWidget): self._poll_timer = QTimer(self) self._poll_timer.setInterval(self._POLL_INTERVAL_MS) self._poll_timer.timeout.connect(self._on_poll) + self._banner_busy = False + self._banner_idle_timer = QTimer(self) + self._banner_idle_timer.setSingleShot(True) + self._banner_idle_timer.setInterval(5000) + self._banner_idle_timer.timeout.connect(self._hide_busy_banner) if self._profile is not None: self._poll_timer.start() self.refresh() @@ -1013,20 +1018,33 @@ class TomoParamsWidget(BECWidget, QWidget): self._refresh_busy_banner() def _refresh_busy_banner(self) -> None: - """Show/hide the advisory busy banner based on current beamline state.""" + """Show/hide the advisory busy banner based on current beamline state. + + Hiding is debounced: once shown, the banner stays up for at least 5s + of continuous idle before it's cleared, so brief blips in the queue + status (or quick commands) don't cause it to flicker. + """ banner = getattr(self, "_busy_banner", None) if banner is None: return reason = self._beamline_busy_reason() if reason: + self._banner_idle_timer.stop() banner.setText( f"\u26a0 Beamline busy — {reason}.\n" 'Editing is allowed, but Submit is blocked -- use "Add to queue" ' "to save edits as a new job instead." ) banner.setVisible(True) - else: + self._banner_busy = True + elif self._banner_busy and not self._banner_idle_timer.isActive(): + self._banner_idle_timer.start() + + def _hide_busy_banner(self) -> None: + banner = getattr(self, "_busy_banner", None) + if banner is not None: banner.setVisible(False) + self._banner_busy = False # ── public refresh API ──────────────────────────────────────────────────── @@ -1310,6 +1328,7 @@ class TomoParamsWidget(BECWidget, QWidget): def cleanup(self) -> None: self._poll_timer.stop() + self._banner_idle_timer.stop() super().cleanup() -- 2.54.0