GUI: For raster scan, one can browse through images

This commit is contained in:
2025-06-23 13:11:45 +02:00
parent 674a0e36eb
commit 88199c8d49
7 changed files with 68 additions and 7 deletions
+1 -1
View File
@@ -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:
+3 -1
View File
@@ -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)
+5
View File
@@ -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."""
@@ -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]
+1
View File
@@ -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)
+49
View File
@@ -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)