diff --git a/csaxs_bec/bec_widgets/widgets/vision_tuning/vision_tuning.py b/csaxs_bec/bec_widgets/widgets/vision_tuning/vision_tuning.py index b6dd97a9..7d8b7e09 100644 --- a/csaxs_bec/bec_widgets/widgets/vision_tuning/vision_tuning.py +++ b/csaxs_bec/bec_widgets/widgets/vision_tuning/vision_tuning.py @@ -140,6 +140,13 @@ class VisionTuning(BECWidget, QWidget): control_row.addStretch() root.addLayout(control_row) + # Surfaces the last error from an Apply/Refresh action -- e.g. an even blur_ksize rejected + # by set_edge_params() -- instead of only logging it (a click that silently does nothing + # is indistinguishable from a bug from the operator's side). + self.status_label = QLabel("", parent=self) + self.status_label.setStyleSheet("color: #c0392b;") + root.addWidget(self.status_label) + self.image = Image(parent=self) root.addWidget(self.image, stretch=1) @@ -187,6 +194,14 @@ class VisionTuning(BECWidget, QWidget): self.camera_combo.blockSignals(False) if device_name: self.image.image(device=device_name, signal="diagnostic_image") + # The Image widget's subscription (bec_dispatcher.connect_slot, no newest_only) only + # catches publishes that happen *after* the Redis subscription is actually established + # -- a refresh() published immediately after .image() above can race it and be missed, + # leaving the preview blank until some later publish. A short deferred refresh, on top + # of the immediate one _on_camera_changed() triggers below, reliably lands after the + # subscription is live -- cheap and harmless since refresh() just republishes the same + # last frame. + QTimer.singleShot(200, self._on_refresh_clicked) if interface is not None: edge_params = interface.get_edge_params() for spin_box, key in ( @@ -267,8 +282,8 @@ class VisionTuning(BECWidget, QWidget): interface.set_edge_params( self.edge_low.value(), self.edge_high.value(), self.edge_blur.value() ) - except Exception: - logger.exception("VisionTuning: set_edge_params failed") + except Exception as exc: + self._set_status(f"set_edge_params failed: {exc}") return self._on_refresh_clicked() @@ -284,8 +299,14 @@ class VisionTuning(BECWidget, QWidget): return try: interface.refresh(self._current_camera_name) - except Exception: - logger.exception("VisionTuning: refresh failed") + except Exception as exc: + self._set_status(f"refresh failed: {exc}") + else: + self._set_status("") + + def _set_status(self, text: str): + logger.info(f"VisionTuning: {text}") if text else None + self.status_label.setText(text) @SafeSlot(bool) def _on_live_toggled(self, checked: bool):