fix: disable controls for device input and toggles

This commit is contained in:
2026-05-19 11:20:36 +02:00
parent 32034a7c59
commit a568656389
@@ -149,6 +149,11 @@ class XRayEye(BECWidget, QWidget):
super().__init__(parent=parent, **kwargs)
self._connected_motor = None
self._dap_params_forwarding_connected = False
self._queue_busy = False
self._queue_idle_timer = QTimer(self)
self._queue_idle_timer.setSingleShot(True)
self._queue_idle_timer.setInterval(800)
self._queue_idle_timer.timeout.connect(self._release_queue_toggles)
self.get_bec_shortcuts()
self._init_ui()
@@ -161,6 +166,9 @@ class XRayEye(BECWidget, QWidget):
self.bec_dispatcher.connect_slot(
self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0])
)
self.bec_dispatcher.connect_slot(
self.on_queue_status_update, MessageEndpoints.scan_queue_status()
)
self.connect_motors()
self.resize(800, 600)
@@ -205,7 +213,8 @@ class XRayEye(BECWidget, QWidget):
header_row.addWidget(self.live_preview_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
self.control_panel_layout.addLayout(header_row)
switch_row = QHBoxLayout()
self.switch_row_widget = QWidget(parent=self)
switch_row = QHBoxLayout(self.switch_row_widget)
switch_row.setContentsMargins(0, 0, 0, 0)
switch_row.setSpacing(8)
switch_row.addStretch()
@@ -221,7 +230,7 @@ class XRayEye(BECWidget, QWidget):
switch_row.addWidget(self.shutter_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
switch_row.addWidget(self.camera_running_label, 0, Qt.AlignmentFlag.AlignVCenter)
switch_row.addWidget(self.camera_running_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
self.control_panel_layout.addLayout(switch_row)
self.control_panel_layout.addWidget(self.switch_row_widget)
# separator
self.control_panel_layout.addWidget(self._create_separator())
@@ -388,6 +397,59 @@ class XRayEye(BECWidget, QWidget):
def enable_move_buttons(self, enabled: bool):
self.motor_control_2d.setEnabled(enabled)
def _queue_guarded_toggles(self) -> tuple[ToggleSwitch, ToggleSwitch, ToggleSwitch]:
return (self.live_preview_toggle, self.shutter_toggle, self.camera_running_toggle)
def _set_queue_toggles_blocked(self, blocked: bool):
if blocked == self._queue_busy:
return
self._queue_busy = blocked
self._refresh_queue_toggle_availability()
def _refresh_queue_toggle_availability(self):
tooltip = "Disabled while scan queue is busy." if self._queue_busy else ""
for toggle in self._queue_guarded_toggles():
toggle.setEnabled(not self._queue_busy)
toggle.setToolTip(tooltip)
def _manual_toggle_blocked_by_queue(self) -> bool:
return self._queue_busy and self.sender() in self._queue_guarded_toggles()
def _update_queue_toggles_from_busy_state(self, busy: bool):
if busy:
self._queue_idle_timer.stop()
self._set_queue_toggles_blocked(True)
return
if self._queue_busy and not self._queue_idle_timer.isActive():
self._queue_idle_timer.start()
def _release_queue_toggles(self):
self._set_queue_toggles_blocked(False)
@staticmethod
def _is_queue_busy(msg_content: dict) -> bool:
queues = msg_content.get("queue", {}) if isinstance(msg_content, dict) else {}
primary_queue = queues.get("primary") if isinstance(queues, dict) else None
if primary_queue is None:
return False
queue_info = getattr(primary_queue, "info", None)
if queue_info is None and isinstance(primary_queue, dict):
queue_info = primary_queue.get("info", [])
if not queue_info:
return False
idle_statuses = {"STOPPED", "COMPLETED", "IDLE"}
for item in queue_info:
status = getattr(item, "status", None)
if status is None and isinstance(item, dict):
status = item.get("status")
if str(status).upper() not in idle_statuses:
return True
return False
def active_roi(self) -> BaseROI | None:
"""Return the currently active ROI, or None if no ROI is active."""
return self.roi_manager.single_active_roi
@@ -417,6 +479,9 @@ class XRayEye(BECWidget, QWidget):
@SafeSlot(bool)
@rpc_timeout(20)
def on_live_view_enabled(self, enabled: bool):
if self._manual_toggle_blocked_by_queue():
logger.warning("Ignoring live-preview toggle while scan queue is busy.")
return
logger.info(f"Live view is enabled: {enabled}")
self.live_preview_toggle.blockSignals(True)
if enabled:
@@ -431,6 +496,9 @@ class XRayEye(BECWidget, QWidget):
@SafeSlot(bool)
def camera_running_enabled(self, enabled: bool):
if self._manual_toggle_blocked_by_queue():
logger.warning("Ignoring camera live-mode toggle while scan queue is busy.")
return
logger.info(f"Camera running: {enabled}")
self.camera_running_toggle.blockSignals(True)
self.dev.get(CAMERA[0]).live_mode_enabled.put(enabled)
@@ -447,6 +515,9 @@ class XRayEye(BECWidget, QWidget):
@SafeSlot(bool)
def opening_shutter(self, enabled: bool):
if self._manual_toggle_blocked_by_queue():
logger.warning("Ignoring shutter toggle while scan queue is busy.")
return
logger.info(f"Shutter changed from GUI to: {enabled}")
self.shutter_toggle.blockSignals(True)
if enabled:
@@ -463,6 +534,10 @@ class XRayEye(BECWidget, QWidget):
self.shutter_toggle.checked = shutter_open
self.shutter_toggle.blockSignals(False)
@SafeSlot(dict, dict)
def on_queue_status_update(self, data, meta):
self._update_queue_toggles_from_busy_state(self._is_queue_busy(data))
@SafeSlot(bool, bool)
@rpc_timeout(20)
def on_motors_enable(self, x_enable: bool, y_enable: bool):
@@ -585,6 +660,7 @@ class XRayEye(BECWidget, QWidget):
def cleanup(self):
"""Cleanup connections on widget close -> disconnect slots and stop live mode of camera."""
self._queue_idle_timer.stop()
if self._connected_motor is not None:
self.bec_dispatcher.disconnect_slot(
self.on_tomo_angle_readback, MessageEndpoints.device_readback(self._connected_motor)
@@ -596,6 +672,9 @@ class XRayEye(BECWidget, QWidget):
self.bec_dispatcher.disconnect_slot(
self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0])
)
self.bec_dispatcher.disconnect_slot(
self.on_queue_status_update, MessageEndpoints.scan_queue_status()
)
getattr(self.dev, CAMERA[0]).stop_live_mode()
super().cleanup()