diff --git a/src/aare/gui/main_window.py b/src/aare/gui/main_window.py index e87c9143..79dd5067 100644 --- a/src/aare/gui/main_window.py +++ b/src/aare/gui/main_window.py @@ -638,6 +638,9 @@ class MainWindow(QMainWindow): self.beamline.samcam.show_detection_polygons_changed.connect( self.sample_camera.set_show_detection_polygons ) + for camera in self.__sample_cameras(): + self.beamline.samcam.show_beam_marker_changed.connect(camera.set_show_beam_marker) + self.beamline.samcam.show_beam_crosshair_changed.connect(camera.set_show_beam_crosshair) self.beamline.samcam.show_target_point_changed.connect( self.sample_camera.set_show_target_point ) @@ -936,10 +939,15 @@ class MainWindow(QMainWindow): except Exception as e: logger.warning(f"Failed to return to main view during shutdown: {e}") + def __sample_cameras(self): + return (self.sample_camera, self.compact_sample_camera, self.portrait_sample_camera) + def _restore_samcam_overlay_settings(self) -> None: settings = QSettings("PSI", "AareGUI") show_detections = settings.value("samcam/show_detections", True, type=bool) show_detection_polygons = settings.value("samcam/show_detection_polygons", True, type=bool) + show_beam_marker = settings.value("samcam/show_beam_marker", True, type=bool) + show_beam_crosshair = settings.value("samcam/show_beam_crosshair", True, type=bool) show_target_point = settings.value("samcam/show_target_point", True, type=bool) show_target_coordinates = settings.value("samcam/show_target_coordinates", True, type=bool) show_overlay_legend = settings.value("samcam/show_overlay_legend", True, type=bool) @@ -949,12 +957,17 @@ class MainWindow(QMainWindow): self.beamline.samcam.apply_overlay_settings( show_detections=show_detections, show_detection_polygons=show_detection_polygons, + show_beam_marker=show_beam_marker, + show_beam_crosshair=show_beam_crosshair, show_target_point=show_target_point, show_target_coordinates=show_target_coordinates, show_overlay_legend=show_overlay_legend, compact_overlay_legend=compact_overlay_legend, target_color=target_color, ) + for camera in self.__sample_cameras(): + camera.set_show_beam_marker(show_beam_marker) + camera.set_show_beam_crosshair(show_beam_crosshair) self.sample_camera.set_show_detections(show_detections) self.sample_camera.set_show_detection_polygons(show_detection_polygons) self.sample_camera.set_show_target_point(show_target_point) @@ -970,6 +983,8 @@ class MainWindow(QMainWindow): settings.setValue( "samcam/show_detection_polygons", overlay["show_detection_polygons"] ) # NEW + settings.setValue("samcam/show_beam_marker", overlay["show_beam_marker"]) + settings.setValue("samcam/show_beam_crosshair", overlay["show_beam_crosshair"]) settings.setValue("samcam/show_target_point", overlay["show_target_point"]) settings.setValue("samcam/show_target_coordinates", overlay["show_target_coordinates"]) settings.setValue("samcam/show_overlay_legend", overlay["show_overlay_legend"]) diff --git a/src/aare/gui/panels/samcam_panel.py b/src/aare/gui/panels/samcam_panel.py index 959b7ca6..869f488d 100644 --- a/src/aare/gui/panels/samcam_panel.py +++ b/src/aare/gui/panels/samcam_panel.py @@ -20,6 +20,8 @@ class SamcamPanel(QWidget): changed = Signal(SampleCameraSettings) show_detections_changed = Signal(bool) show_detection_polygons_changed = Signal(bool) + show_beam_marker_changed = Signal(bool) + show_beam_crosshair_changed = Signal(bool) show_target_point_changed = Signal(bool) show_target_coordinates_changed = Signal(bool) show_overlay_legend_changed = Signal(bool) @@ -102,6 +104,20 @@ class SamcamPanel(QWidget): ) detection_polygons_layout.addWidget(self.show_detection_polygons_checkbox) + # Show beam marker checkbox + beam_marker_layout = QHBoxLayout() + self.show_beam_marker_checkbox = QCheckBox("Show beam marker") + self.show_beam_marker_checkbox.setChecked(True) + self.show_beam_marker_checkbox.toggled.connect(self.show_beam_marker_changed.emit) + beam_marker_layout.addWidget(self.show_beam_marker_checkbox) + + # Show beam crosshair checkbox + beam_crosshair_layout = QHBoxLayout() + self.show_beam_crosshair_checkbox = QCheckBox("Show beam crosshair") + self.show_beam_crosshair_checkbox.setChecked(True) + self.show_beam_crosshair_checkbox.toggled.connect(self.show_beam_crosshair_changed.emit) + beam_crosshair_layout.addWidget(self.show_beam_crosshair_checkbox) + # Show target point checkbox target_point_layout = QHBoxLayout() self.show_target_point_checkbox = QCheckBox("Show target point") @@ -153,6 +169,8 @@ class SamcamPanel(QWidget): layout.addWidget(self.screenshot_button) layout.addLayout(detections_layout) layout.addLayout(detection_polygons_layout) + layout.addLayout(beam_marker_layout) + layout.addLayout(beam_crosshair_layout) layout.addLayout(target_point_layout) layout.addLayout(target_coords_layout) layout.addLayout(legend_layout) @@ -177,6 +195,8 @@ class SamcamPanel(QWidget): *, show_detections: bool, show_detection_polygons: bool, + show_beam_marker: bool, + show_beam_crosshair: bool, show_target_point: bool, show_target_coordinates: bool, show_overlay_legend: bool, @@ -186,6 +206,8 @@ class SamcamPanel(QWidget): for widget in ( self.show_detections_checkbox, self.show_detection_polygons_checkbox, + self.show_beam_marker_checkbox, + self.show_beam_crosshair_checkbox, self.show_target_point_checkbox, self.show_target_coordinates_checkbox, self.show_overlay_legend_checkbox, @@ -196,6 +218,8 @@ class SamcamPanel(QWidget): self.show_detections_checkbox.setChecked(show_detections) self.show_detection_polygons_checkbox.setChecked(show_detection_polygons) + self.show_beam_marker_checkbox.setChecked(show_beam_marker) + self.show_beam_crosshair_checkbox.setChecked(show_beam_crosshair) self.show_target_point_checkbox.setChecked(show_target_point) self.show_target_coordinates_checkbox.setChecked(show_target_coordinates) self.show_overlay_legend_checkbox.setChecked(show_overlay_legend) @@ -205,6 +229,8 @@ class SamcamPanel(QWidget): for widget in ( self.show_detections_checkbox, self.show_detection_polygons_checkbox, + self.show_beam_marker_checkbox, + self.show_beam_crosshair_checkbox, self.show_target_point_checkbox, self.show_target_coordinates_checkbox, self.show_overlay_legend_checkbox, diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index 07d43042..cd5a2375 100644 --- a/src/aare/gui/widgets/camera_image.py +++ b/src/aare/gui/widgets/camera_image.py @@ -44,6 +44,10 @@ from aare.gui.widgets.busy_overlay import BusyOverlayStyle, build_busy_overlay_s logger = setup_logger(LOGGER_NAME) +# The crosshair spans half the beam marker. beam_size_pxl shrinks with camera zoom, so +# clamp to a floor that keeps the cross visible without overflowing a typical ~10 px marker. +BEAM_CROSSHAIR_MIN_SIZE_PXL = 6 + class SampleCameraImageState(Enum): IDLE = 0 @@ -103,6 +107,8 @@ class SampleCameraImageLabel(QGraphicsView): self.__show_detections = True self.__show_detection_polygons = True + self.__show_beam_marker = True + self.__show_beam_crosshair = True self.__show_target_point = True self.__show_target_coordinates = True self.__show_overlay_legend = True @@ -986,6 +992,16 @@ class SampleCameraImageLabel(QGraphicsView): self.__show_detection_polygons = show self.update() + @Slot(bool) + def set_show_beam_marker(self, show: bool): + self.__show_beam_marker = show + self.update() + + @Slot(bool) + def set_show_beam_crosshair(self, show: bool): + self.__show_beam_crosshair = show + self.update() + @Slot(bool) def set_show_target_point(self, show: bool): self.__show_target_point = show @@ -1013,6 +1029,8 @@ class SampleCameraImageLabel(QGraphicsView): def target_overlay_settings(self) -> dict: return { + "show_beam_marker": self.__show_beam_marker, + "show_beam_crosshair": self.__show_beam_crosshair, "show_target_point": self.__show_target_point, "show_target_coordinates": self.__show_target_coordinates, "show_detections": self.__show_detections, @@ -1038,30 +1056,56 @@ class SampleCameraImageLabel(QGraphicsView): ) ) + def __beam_color(self) -> QColor: + if self.__state == SampleCameraImageState.BEAM_MARKING: + return QColor(102, 51, 153) + + if self.__shutter: + return QColor(0, 255, 0) + + if self.__is_daq_busy is True: + # Use red color to indicate busy state + return QColor(255, 0, 0) + + return QColor(245, 121, 0) + def __draw_beam_center(self, painter: QPainter): beam_size_pxl = self.__geom.beam_size_pxl - - if self.__state == SampleCameraImageState.BEAM_MARKING: - painter.setPen(QPen(QColor(102, 51, 153), 3, Qt.PenStyle.SolidLine)) - - elif self.__shutter: - painter.setPen(QPen(QColor(0, 255, 0), 3, Qt.PenStyle.SolidLine)) - - elif self.__is_daq_busy is True: - # Use red color to indicate busy state - painter.setPen(QPen(QColor(255, 0, 0), 3, Qt.PenStyle.SolidLine)) - else: - painter.setPen(QPen(QColor(245, 121, 0), 3, Qt.PenStyle.SolidLine)) + center = self.__geom.beam_location_pxl + color = self.__beam_color() painter.setBrush(Qt.BrushStyle.NoBrush) - painter.drawRect( - QRect( - int(self.__geom.beam_location_pxl.x - beam_size_pxl.x / 2), - int(self.__geom.beam_location_pxl.y - beam_size_pxl.y / 2), - int(beam_size_pxl.x), - int(beam_size_pxl.y), + + if self.__show_beam_marker: + painter.setPen(QPen(color, 3, Qt.PenStyle.SolidLine)) + painter.drawRect( + QRect( + int(center.x - beam_size_pxl.x / 2), + int(center.y - beam_size_pxl.y / 2), + int(beam_size_pxl.x), + int(beam_size_pxl.y), + ) ) - ) + + if self.__show_beam_crosshair: + arm_x = max(beam_size_pxl.x / 2, BEAM_CROSSHAIR_MIN_SIZE_PXL) / 2 + arm_y = max(beam_size_pxl.y / 2, BEAM_CROSSHAIR_MIN_SIZE_PXL) / 2 + painter.setPen(QPen(color, 2, Qt.PenStyle.SolidLine)) + painter.drawLine( + QPointF(center.x - arm_x, center.y), QPointF(center.x + arm_x, center.y) + ) + painter.drawLine( + QPointF(center.x, center.y - arm_y), QPointF(center.x, center.y + arm_y) + ) + + if not self.__show_beam_marker and not self.__show_beam_crosshair: + # Both overlays hidden: keep a minimal pip so the beam position is never lost. + painter.save() + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + painter.setPen(Qt.PenStyle.NoPen) + painter.setBrush(QBrush(color)) + painter.drawEllipse(QPointF(center.x, center.y), 2, 2) + painter.restore() @staticmethod def __draw_circle(painter, coord: Coordinate, color: QColor, radius=10):