From 88199c8d49c832139d3bc882233d88af7e36e39e Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 23 Jun 2025 13:11:45 +0200 Subject: [PATCH] GUI: For raster scan, one can browse through images --- daq/src/aaredaq/devices.py | 2 +- daq/src/aaredaq/server.py | 4 +- gui/src/aaregui/{main.py => gui.py} | 0 gui/src/aaregui/main_window.py | 5 ++ .../aaregui/scan_logic/raster_grid_manager.py | 14 ++++-- gui/src/aaregui/threads/daq_worker.py | 1 + gui/src/aaregui/threads/jfjoch_viewer.py | 49 +++++++++++++++++++ 7 files changed, 68 insertions(+), 7 deletions(-) rename gui/src/aaregui/{main.py => gui.py} (100%) create mode 100644 gui/src/aaregui/threads/jfjoch_viewer.py diff --git a/daq/src/aaredaq/devices.py b/daq/src/aaredaq/devices.py index 38ff135b..66eaf7a2 100644 --- a/daq/src/aaredaq/devices.py +++ b/daq/src/aaredaq/devices.py @@ -183,7 +183,7 @@ class BeamlineDevices: if value: self.__collimator.put(92.979, wait=True) else: - self.__collimator.put(42.979, wait=True) + self.__collimator.put(52.979, wait=True) @property def reflector_up(self) -> bool: diff --git a/daq/src/aaredaq/server.py b/daq/src/aaredaq/server.py index 3d98e30e..6e9eeb9c 100644 --- a/daq/src/aaredaq/server.py +++ b/daq/src/aaredaq/server.py @@ -367,10 +367,12 @@ LOGGING_CONFIG = { }, } +urllib3.disable_warnings() + def main(): # Remove in production! - urllib3.disable_warnings(InsecureRequestWarning) + urllib3.disable_warnings() # Run the application using uvicorn uvicorn.run("aaredaq.server:app", host="0.0.0.0", port=5210, workers=4, log_config=LOGGING_CONFIG) diff --git a/gui/src/aaregui/main.py b/gui/src/aaregui/gui.py similarity index 100% rename from gui/src/aaregui/main.py rename to gui/src/aaregui/gui.py diff --git a/gui/src/aaregui/main_window.py b/gui/src/aaregui/main_window.py index 1d495db8..ebf425cc 100644 --- a/gui/src/aaregui/main_window.py +++ b/gui/src/aaregui/main_window.py @@ -23,6 +23,7 @@ from aaregui.panels.tell_sample_panel import TellSamplePanel from aaregui.scan_logic.raster_grid_manager import RasterGridManager from aaregui.threads.camera_thread import SampleCameraThread from aaregui.threads.daq_worker import DAQWorker +from aaregui.threads.jfjoch_viewer import JFJochDBusClient from aaregui.widgets.camera_image import SampleCameraImageLabel from aaregui.widgets.no_wheel_scroll_area import NoWheelScrollArea from aaregui.widgets.status_bar import StatusBar @@ -39,6 +40,8 @@ class MainWindow(QMainWindow): self.__base_url = base_url self.__token = token + self.viewer = JFJochDBusClient() + # Decode the JWT without signature verification self.__decoded_token = TokenData(**jwt.decode(token, options={"verify_signature": False})) print(self.__decoded_token) @@ -212,6 +215,8 @@ class MainWindow(QMainWindow): self.status_bar.sample_exchange.connect(self.daq.sample_exchange) self.status_bar.sample_alignment.connect(self.daq.sample_alignment) self.status_bar.close_shutter.connect(self.daq.close_shutter) + self.raster.file_ready.connect(self.viewer.load_file) + self.raster.image_selected.connect(self.viewer.load_image) def create_menu_bar(self): """Create a menu bar with File->Quit and Help->About.""" diff --git a/gui/src/aaregui/scan_logic/raster_grid_manager.py b/gui/src/aaregui/scan_logic/raster_grid_manager.py index 305e1c58..f239471a 100644 --- a/gui/src/aaregui/scan_logic/raster_grid_manager.py +++ b/gui/src/aaregui/scan_logic/raster_grid_manager.py @@ -70,6 +70,9 @@ class RasterGridManager(QObject): grid_scan = Signal(RasterGridRequest) grid_scan_size_changed = Signal(int, int) + file_ready = Signal(str) + image_selected = Signal(int) + def __init__(self, geom): super().__init__() @@ -178,10 +181,10 @@ class RasterGridManager(QObject): def get_grid_coord(self, grid: RasterGridRequest, point: QPointF) -> Tuple[int, int]: point_bl = self.__geom.picture_to_sample(Coordinate(x=point.x(), y=point.y())) - delta = point_bl - self.__geom.smargon_to_beamline(grid.request.smargon.sh_mm) + delta = point_bl - self.__geom.smargon_to_beamline(grid.smargon.sh_mm) - elem_x = grid.request.grid_size_mm.x - elem_y = grid.request.grid_size_mm.y + elem_x = grid.grid_size_mm.x + elem_y = grid.grid_size_mm.y cell_x = math.floor(delta.x / elem_x) cell_y = math.floor(delta.y / elem_y) @@ -204,6 +207,7 @@ class RasterGridManager(QObject): self.__loaded_image_prefix = grid.result.file_prefix self.__loaded_image_index = grid.result.images[cell].number print(f"Load {grid.result.file_prefix} {grid.result.images[cell].number}") + self.image_selected.emit(grid.result.images[cell].number) def is_part_of_active_grid(self, point: QPointF) -> bool: if not self._is_grid_visible(self.__active_grid): @@ -268,13 +272,13 @@ class RasterGridManager(QObject): self.__completed_grids.append(r) self.__active_grid.n_x = 0 self.__active_grid.n_y = 0 + if r.result.file_prefix is not None: + self.file_ready.emit(r.result.file_prefix) def draw_grid(self, painter: QPainter, alpha: int = 127): self._draw_grid(painter, self.__active_grid) for i in self.__completed_grids: v = None - - match self.__metric: case RasterGridMetric.BKG: v = [obj.bkg for obj in i.result.images] diff --git a/gui/src/aaregui/threads/daq_worker.py b/gui/src/aaregui/threads/daq_worker.py index c7e2f803..d5c27942 100644 --- a/gui/src/aaregui/threads/daq_worker.py +++ b/gui/src/aaregui/threads/daq_worker.py @@ -22,6 +22,7 @@ class DAQWorker(QObject): automated_scan_done = Signal(int, bool) # sample ID, success run_number_incremented = Signal() raster_scan_completed = Signal(CompletedRasterGrid) + standard_scan_completed = Signal(str) def __init__(self, base_url: str | None, token: str, parent=None): super().__init__(parent) diff --git a/gui/src/aaregui/threads/jfjoch_viewer.py b/gui/src/aaregui/threads/jfjoch_viewer.py new file mode 100644 index 00000000..49fa2174 --- /dev/null +++ b/gui/src/aaregui/threads/jfjoch_viewer.py @@ -0,0 +1,49 @@ +from PySide6.QtCore import QObject, Slot +from PySide6.QtDBus import QDBusConnection, QDBusInterface + +class JFJochDBusClient(QObject): + def __init__(self): + super().__init__() + + self.__session_bus = QDBusConnection.sessionBus() + self.__interface = None + + if not self.__session_bus.isConnected(): + print("Failed to connect to session bus") + return + + + def _ensure_interface(self): + """Ensure we have a valid interface, creating one if needed.""" + if self.__interface is None or not self.__interface.isValid(): + if not self.__session_bus.isConnected(): + print("Session bus not connected") + return False + + self.__interface = QDBusInterface( + "ch.psi.jfjoch_viewer", # Service name + "/", # Object path + "ch.psi.jfjoch_viewer", # Interface name + self.__session_bus + ) + + if not self.__interface.isValid(): + print("Service 'ch.psi.jfjoch_viewer' not available") + return False + + return True + + @Slot(str) + def load_file(self, name: str): + path = "/sls/mx/data/" + name + "_master.h5" + print(f"load {path}") + if self._ensure_interface(): + reply = self.__interface.call("LoadFile", path) + + @Slot(int) + def load_image(self, number: int): + print(f"load image {number}") + if self._ensure_interface(): + reply = self.__interface.call("LoadImage", number, 1) + +