From 90d2ccb2333dbc957f1d7bbf9d78351d92d317eb Mon Sep 17 00:00:00 2001 From: appleb_m Date: Tue, 31 Mar 2026 17:18:40 +0200 Subject: [PATCH] gui: continung efforts to fix baton system --- src/aare/gui/main_window.py | 44 ++++++++++++++++-------------- src/aare/gui/widgets/status_bar.py | 33 +++++++++++++++++----- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/aare/gui/main_window.py b/src/aare/gui/main_window.py index 282f2d8f..84eddfbd 100644 --- a/src/aare/gui/main_window.py +++ b/src/aare/gui/main_window.py @@ -868,7 +868,10 @@ class MainWindow(QMainWindow): @Slot(BatonStatus) def _on_baton_status_changed(self, status: BatonStatus): - """Close the pending dialog immediately if the baton request has been resolved via SSE.""" + """ + Update waiting UI based on SSE status. + StatusBar handles baton-granted p-group selection automatically. + """ if self._waiting_for_baton_response and not status.you_have_pending_request: self._waiting_for_baton_response = False self._close_baton_pending_dialog() @@ -878,28 +881,26 @@ class MainWindow(QMainWindow): else: self.alert_banner.show_message("Request declined or cancelled", False, auto_clear_ms=10000) - # Manage incoming request dialog + # Manage incoming request dialog (when someone requests from us) if not status.incoming_request and self._baton_request_dialog is not None: logger.info("Incoming baton request no longer active, closing request dialog") self._close_baton_dialog() @Slot(dict) def _on_baton_request_result(self, result: dict): - """Handle result of our baton request - show waiting banner with countdown.""" + """ + Handle result of our baton request. + - Show success/pending/error banners + - Manage pending dialog lifecycle + - StatusBar handles p-group selection automatically via update_baton_status + """ if result.get("granted"): self._waiting_for_baton_response = False self.alert_banner.show_message("Baton acquired!", False, auto_clear_ms=10000) logger.info("Baton acquired") - - # Close the pending dialog immediately before showing p-group prompt + # Close pending dialog; StatusBar will trigger p-group selection via SSE self._close_baton_pending_dialog() - available_pgroups = [str(p).strip() for p in (self.__decoded_token.pgroups or []) if p is not None and str(p).strip()] - if len(available_pgroups) == 1: - self.status_bar.set_pgroup.emit(available_pgroups[0]) - else: - self.status_bar._after_baton_granted_select_pgroup() - elif result.get("pending"): self._waiting_for_baton_response = True timeout = result.get("timeout_seconds", 30) @@ -907,7 +908,8 @@ class MainWindow(QMainWindow): is_busy = result.get("beamline_busy", False) if getattr(self, "_baton_pending_dialog", None) is None: - target_user = holder.replace("Request sent to ", "").replace(" (Note: beamline is currently busy, transfer will be queued if accepted)", "") + target_user = holder.replace("Request sent to ", "").replace( + " (Note: beamline is currently busy, transfer will be queued if accepted)", "") self._baton_pending_dialog = BatonPendingDialog(target_user=target_user, timeout_seconds=timeout, parent=self) self._baton_pending_dialog.cancelled_signal.connect(self.daq.cancel_baton_request) @@ -937,30 +939,34 @@ class MainWindow(QMainWindow): elif result.get("error"): self._waiting_for_baton_response = False - self.alert_banner.show_message(result.get("message", "Request failed"), True) + self.alert_banner.show_message(result.get("message", "Request failed"), True, + auto_clear_ms=15000) logger.warning(f"Baton request failed: {result.get('message')}") self._close_baton_pending_dialog() @Slot(dict) def _on_baton_response_result(self, result: dict): - """Handle result after we responded to someone else's request.""" + """ + Handle response after someone requests from us. + Just update UI banners; StatusBar handles session display. + """ logger.debug(f"Baton response result: {result}") if result.get("accepted"): self._waiting_for_baton_response = False self.alert_banner.show_message("Control transferred", False, auto_clear_ms=10000) self._close_baton_dialog() - self.status_bar.update_baton_status(self.status_bar._baton_status) # refresh label state + self.status_bar.update_baton_status(self.status_bar._baton_status) elif result.get("refused"): self._waiting_for_baton_response = False self.alert_banner.show_message("Request declined", False, auto_clear_ms=10000) self._close_baton_dialog() - self.status_bar.update_baton_status(self.status_bar._baton_status) # refresh label state + self.status_bar.update_baton_status(self.status_bar._baton_status) else: logger.debug(f"replied with {result}") @Slot(dict) def _on_baton_timeout_checked(self, result: dict): - """Refresh waiting UI when the backend confirms timeout state.""" + """Refresh waiting UI when backend confirms timeout state.""" logger.debug(f"Baton timeout checked: {result}") if result.get("pending"): remaining = int(result.get("remaining_seconds", 0)) @@ -972,10 +978,7 @@ class MainWindow(QMainWindow): elif result.get("granted"): self._waiting_for_baton_response = False self.alert_banner.show_message("Baton acquired!", False, auto_clear_ms=10000) - - # Close the pending dialog immediately before showing p-group prompt self._close_baton_pending_dialog() - # P-group logic will be handled automatically by the status_bar stream update elif result.get("queued"): self._waiting_for_baton_response = True @@ -1001,6 +1004,7 @@ class MainWindow(QMainWindow): self._close_baton_pending_dialog() def _close_baton_dialog(self) -> None: + """Close the incoming-request dialog (when someone requests from us).""" if getattr(self, "_baton_request_dialog", None) is not None: try: self._baton_request_dialog.close() diff --git a/src/aare/gui/widgets/status_bar.py b/src/aare/gui/widgets/status_bar.py index 78aaa9ab..6bf36c44 100644 --- a/src/aare/gui/widgets/status_bar.py +++ b/src/aare/gui/widgets/status_bar.py @@ -52,6 +52,7 @@ class StatusBar(QStatusBar): self._has_pending_request: bool = False self._pgroup_dialog_for_baton: PGroupDialog | None = None self._baton_request_dialog: BatonRequestDialog | None = None + self._pgroup_dialog_shown_for_current_baton: bool = False self.message_label = QLabel("", self) self.message_label.setVisible(False) @@ -231,10 +232,15 @@ class StatusBar(QStatusBar): self._has_pending_request = status.you_have_pending_request if status else False self._update_session_display() - # If we just received the baton (and weren't the holder a moment ago) - if now_holder and not was_holder: - self._after_baton_granted_select_pgroup() + # Reset one-shot flag when we lose the baton + if not now_holder: + self._pgroup_dialog_shown_for_current_baton = False + # If we just acquired the baton, prompt for p-group (and only once) + if now_holder and not was_holder: + self._show_pgroup_after_baton_grant() + + # Handle incoming request dialogs (when someone requests from us) incoming = bool(status and status.incoming_request) if incoming and not prev_incoming: logger.info(f"Incoming baton request detected: {status.pending_request}") @@ -245,7 +251,20 @@ class StatusBar(QStatusBar): self._baton_request_dialog.close() except Exception as e: logger.error(f"Error closing baton request dialog: {e}") - self._baton_request_dialog = None + self._baton_request_dialog = None\ + + + def _show_pgroup_after_baton_grant(self) -> None: + """ + Single-source for p-group selection after baton is granted. + Uses a one-shot flag to prevent duplicates from multiple status updates. + """ + if self._pgroup_dialog_shown_for_current_baton: + logger.debug("P-group dialog already shown for this baton grant, skipping") + return + + self._pgroup_dialog_shown_for_current_baton = True + self.show_change_dialog() def _emit_incoming_baton_request(self, status: BatonStatus) -> None: requester = "Another user" @@ -471,10 +490,10 @@ class StatusBar(QStatusBar): def _after_baton_granted_select_pgroup(self) -> None: """ - After baton grant, open the same p-group selection flow - used everywhere else so staff and non-staff behave consistently. + Deprecated: kept for backward compatibility. + Use _show_pgroup_after_baton_grant() instead. """ - self.show_change_dialog() + self._show_pgroup_after_baton_grant() def show_change_dialog(self): logger.debug(self.__decoded_token.pgroups)