wip adjusting to V3

This commit is contained in:
x12sa
2026-01-29 11:33:57 +01:00
committed by wyzula-jan
parent 9615552f40
commit c67aac242a
6 changed files with 144 additions and 114 deletions
@@ -1,7 +1,5 @@
import builtins
from bec_widgets.cli.client import BECDockArea
# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
if builtins.__dict__.get("bec") is not None:
@@ -9,6 +7,7 @@ if builtins.__dict__.get("bec") is not None:
dev = builtins.__dict__.get("dev")
scans = builtins.__dict__.get("scans")
def umv(*args):
return scans.umv(*args, relative=False)
@@ -28,10 +27,11 @@ class flomniGuiTools:
self.gui = self.client.gui
def flomnigui_show_gui(self):
if "flomni" in self.gui.windows:
self.gui.flomni.show()
else:
self.gui.new("flomni")
self.gui.new("flomni")
# if "flomni" in self.gui.windows:
# self.gui.flomni.show()
# else:
# self.gui.new("flomni")
def flomnigui_stop_gui(self):
self.gui.flomni.hide()
@@ -43,32 +43,33 @@ class flomniGuiTools:
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("xeyegui"):
self.flomnigui_remove_all_docks()
self.xeyegui = self.gui.flomni.new("xeyegui").new("XRayEye")
self.xeyegui = self.gui.flomni.new("XRayEye", object_name="xrayeye")
# start live
if not dev.cam_xeye.live_mode_enabled.get():
# dev.cam_xeye.live_mode = True
dev.cam_xeye.live_mode_enabled.put(True)
self.xeyegui.switch_tab('alignment')
self.xeyegui.switch_tab("alignment")
def flomnigui_show_xeyealign_fittab(self):
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("xeyegui"):
self.flomnigui_remove_all_docks()
self.xeyegui = self.gui.flomni.new("xeyegui").new("XRayEye")
self.xeyegui.switch_tab('fit')
self.xeyegui = self.gui.flomni.new("XRayEye")
self.xeyegui.switch_tab("fit")
def _flomnigui_check_attribute_not_exists(self, attribute_name):
if hasattr(self.gui,"flomni"):
if hasattr(self.gui.flomni,attribute_name):
if hasattr(self.gui, "flomni"):
if hasattr(self.gui.flomni, attribute_name):
return False
return True
def flomnigui_show_cameras(self):
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("camera_gripper") or self._flomnigui_check_attribute_not_exists("camera_overview"):
if self._flomnigui_check_attribute_not_exists(
"camera_gripper"
) or self._flomnigui_check_attribute_not_exists("camera_overview"):
self.flomnigui_remove_all_docks()
camera_gripper_image = self.gui.flomni.new("camera_gripper").new("Image")
camera_gripper_image = self.gui.flomni.new("Image")
if self._flomnicam_check_device_exists(dev.cam_flomni_gripper):
camera_gripper_image.image(("cam_flomni_gripper", "preview"))
camera_gripper_image.lock_aspect_ratio = True
@@ -79,7 +80,7 @@ class flomniGuiTools:
dev.cam_flomni_gripper.start_live_mode()
else:
print("Cannot open camera_gripper. Device does not exist.")
camera_overview_image = self.gui.flomni.new("camera_overview").new("Image")
camera_overview_image = self.gui.flomni.new("Image")
if self._flomnicam_check_device_exists(dev.cam_flomni_overview):
camera_overview_image.image(("cam_flomni_overview", "preview"))
camera_overview_image.lock_aspect_ratio = True
@@ -92,10 +93,10 @@ class flomniGuiTools:
print("Cannot open camera_overview. Device does not exist.")
def flomnigui_remove_all_docks(self):
#dev.cam_flomni_overview.stop_live_mode()
#dev.cam_flomni_gripper.stop_live_mode()
#dev.cam_xeye.live_mode = False
self.gui.flomni.delete_all()
# dev.cam_flomni_overview.stop_live_mode()
# dev.cam_flomni_gripper.stop_live_mode()
# dev.cam_xeye.live_mode = False
self.gui.flomni.delete_all()
self.progressbar = None
self.text_box = None
@@ -103,7 +104,7 @@ class flomniGuiTools:
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("idle_text_box"):
self.flomnigui_remove_all_docks()
idle_text_box = self.gui.flomni.new("idle_textbox").new("TextBox")
idle_text_box = self.gui.flomni.new("TextBox")
text = (
"<pre>"
+ " ,---.,--. ,-----. ,--. ,--.,--. ,--.,--. \n"
@@ -119,13 +120,12 @@ class flomniGuiTools:
import csaxs_bec
from pathlib import Path
print("The general flOMNI documentation is at \nhttps://sls-csaxs.readthedocs.io/en/latest/user/ptychography/flomni.html#user-ptychography-flomni")
print(
"The general flOMNI documentation is at \nhttps://sls-csaxs.readthedocs.io/en/latest/user/ptychography/flomni.html#user-ptychography-flomni"
)
csaxs_bec_basepath = Path(csaxs_bec.__file__).parent
docs_folder = (
csaxs_bec_basepath /
"bec_ipython_client" / "plugins" / "flomni" / "docs"
)
docs_folder = csaxs_bec_basepath / "bec_ipython_client" / "plugins" / "flomni" / "docs"
if not docs_folder.is_dir():
raise NotADirectoryError(f"Docs folder not found: {docs_folder}")
@@ -163,10 +163,9 @@ class flomniGuiTools:
self.pdf_viewer = self.gui.flomni.new(widget="PdfViewerWidget")
# --- Load PDF ---------------------------------------------------------
self.pdf_viewer.PdfViewerWidget.load_pdf(str(pdf_file.resolve()))
self.pdf_viewer.load_pdf(str(pdf_file.resolve()))
print(f"\nLoaded: {pdf_file.name}\n")
def _flomnicam_check_device_exists(self, device):
try:
device
@@ -180,7 +179,7 @@ class flomniGuiTools:
if self._flomnigui_check_attribute_not_exists("progressbar"):
self.flomnigui_remove_all_docks()
# Add a new dock with a RingProgressBar widget
self.progressbar = self.gui.flomni.new("progressbar").new("RingProgressBar")
self.progressbar = self.gui.flomni.new("RingProgressBar")
# Customize the size of the progress ring
self.progressbar.set_line_widths(20)
# Disable automatic updates and manually set the self.progressbar value
@@ -195,7 +194,7 @@ class flomniGuiTools:
# Set the values of the rings to 50, 75, and 25 from outer to inner ring
# self.progressbar.set_value([50, 75])
# Add a new dock with a TextBox widget
self.text_box = self.gui.flomni.new(name="progress_text").new("TextBox")
self.text_box = self.gui.flomni.new("TextBox")
self._flomnigui_update_progress()
@@ -221,6 +220,7 @@ if __name__ == "__main__":
client.start()
client.gui = BECGuiClient()
flomni_gui = flomniGuiTools(client)
flomni_gui = flomniGuiTools()
flomni_gui.set_client(client)
flomni_gui.flomnigui_show_gui()
flomni_gui.flomnigui_show_progress()
@@ -48,7 +48,7 @@ class XrayEyeAlign:
def update_frame(self, keep_shutter_open=False):
self.flomni.flomnigui_show_xeyealign()
# self.flomni.flomnigui_show_xeyealign()
if not dev.cam_xeye.live_mode_enabled.get():
dev.cam_xeye.live_mode_enabled.put(True)
@@ -112,8 +112,8 @@ class XrayEyeAlign:
self.flomni.lights_off()
self.flomni.flomnigui_show_xeyealign()
self.flomni.flomnigui_raise()
# self.flomni.flomnigui_show_xeyealign()
# self.flomni.flomnigui_raise()
if not self.test_wo_movements:
self.tomo_rotate(0)
@@ -282,4 +282,4 @@ class XrayEyeAlign:
self.gui.submit_fit_array(data)
print(f"fit submited with {data}")
print("todo mirko: submitted data is 1000 fold in amplitude")
self.flomni.flomnigui_show_xeyealign_fittab()
# self.flomni.flomnigui_show_xeyealign_fittab()
@@ -1,7 +1,5 @@
import builtins
from bec_widgets.cli.client import BECDockArea
# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
if builtins.__dict__.get("bec") is not None:
@@ -21,7 +19,7 @@ class OMNYGuiTools:
def __init__(self, client):
self.gui = getattr(client, "gui", None)
self.gui_window = self.gui.windows['main'].widget
self.gui_window = self.gui.windows["main"].widget
self.fig200 = None
self.fig201 = None
self.fig202 = None
@@ -139,7 +137,9 @@ class OMNYGuiTools:
if self.progressbar is None:
self.omnygui_remove_all_docks()
# Add a new dock with a RingProgressBar widget
self.progressbar = self.gui_window.add_dock(name="progress").add_widget("RingProgressBar")
self.progressbar = self.gui_window.add_dock(name="progress").add_widget(
"RingProgressBar"
)
# Customize the size of the progress ring
self.progressbar.set_line_widths(20)
# Disable automatic updates and manually set the self.progressbar value
@@ -23,7 +23,7 @@ from qtpy.QtWidgets import (
QVBoxLayout,
QWidget,
QTextEdit,
QTabWidget
QTabWidget,
)
import time
@@ -130,8 +130,20 @@ class XRayEye2DControl(BECWidget, QWidget):
class XRayEye(BECWidget, QWidget):
USER_ACCESS = ["active_roi", "user_message", "user_message.setter","on_live_view_enabled","on_motors_enable","enable_submit_button",
"sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter","switch_tab","submit_fit_array"]
USER_ACCESS = [
"active_roi",
"user_message",
"user_message.setter",
"on_live_view_enabled",
"on_motors_enable",
"enable_submit_button",
"sample_name",
"sample_name.setter",
"enable_move_buttons",
"enable_move_buttons.setter",
"switch_tab",
"submit_fit_array",
]
PLUGIN = True
def __init__(self, parent=None, **kwargs):
@@ -142,8 +154,12 @@ class XRayEye(BECWidget, QWidget):
self._make_connections()
# Connection to redis endpoints
self.bec_dispatcher.connect_slot(self.getting_shutter_status, MessageEndpoints.device_readback("omnyfsh"))
self.bec_dispatcher.connect_slot(self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0]))
self.bec_dispatcher.connect_slot(
self.getting_shutter_status, MessageEndpoints.device_readback("omnyfsh")
)
self.bec_dispatcher.connect_slot(
self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0])
)
self.connect_motors()
self.resize(800, 600)
@@ -159,11 +175,12 @@ class XRayEye(BECWidget, QWidget):
self.image = Image(parent=self.alignment_tab)
self.image.color_map = "CET-L2"
self.image.enable_toolbar = False # Disable default toolbar to not allow to user set anything
self.image.enable_toolbar = (
False # Disable default toolbar to not allow to user set anything
)
self.image.inner_axes = False # Disable inner axes to maximize image area
self.image.enable_full_colorbar = True
self.image.invert_y = True # Invert y axis to match image coordinates
self.image.invert_y = True # Invert y axis to match image coordinates
# Control panel on the right: vertical layout inside a fixed-width widget
self.control_panel = QWidget(parent=self.alignment_tab)
@@ -172,8 +189,9 @@ class XRayEye(BECWidget, QWidget):
self.control_panel_layout.setSpacing(10)
# ROI toolbar + Live toggle (header row)
self.roi_manager = ROIPropertyTree(parent=self, image_widget=self.image, compact=True,
compact_orientation="horizontal")
self.roi_manager = ROIPropertyTree(
parent=self, image_widget=self.image, compact=True, compact_orientation="horizontal"
)
header_row = QHBoxLayout()
header_row.setContentsMargins(0, 0, 0, 0)
header_row.setSpacing(8)
@@ -204,13 +222,14 @@ class XRayEye(BECWidget, QWidget):
switch_row.addWidget(self.camera_running_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
self.control_panel_layout.addLayout(switch_row)
# separator
self.control_panel_layout.addWidget(self._create_separator())
# 2D Positioner (fixed size)
self.motor_control_2d = XRayEye2DControl(parent=self)
self.control_panel_layout.addWidget(self.motor_control_2d, 0, Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter)
self.control_panel_layout.addWidget(
self.motor_control_2d, 0, Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter
)
# separator
self.control_panel_layout.addWidget(self._create_separator())
@@ -265,17 +284,24 @@ class XRayEye(BECWidget, QWidget):
self.waveform_x = Waveform(parent=self.fit_tab)
self.waveform_y = Waveform(parent=self.fit_tab)
self.waveform_x.plot(x=[0],y=[1], label="fit-x",dap="SineModel",dap_parameters={"frequency":{"value":0.0174533,"vary":False,"min":0.01,"max":0.02}},dap_oversample=5)
self.waveform_y.plot(x=[0],y=[2], label="fit-y",dap="SineModel")#,dap_oversample=5)
self.waveform_x.plot(
x=[0],
y=[1],
label="fit-x",
dap="SineModel",
dap_parameters={
"frequency": {"value": 0.0174533, "vary": False, "min": 0.01, "max": 0.02}
},
dap_oversample=5,
)
self.waveform_y.plot(x=[0], y=[2], label="fit-y", dap="SineModel") # ,dap_oversample=5)
self.fit_x = self.waveform_x.curves[0]
self.fit_y = self.waveform_y.curves[0]
self.waveform_x.dap_params_update.connect(self.on_dap_params)
self.waveform_y.dap_params_update.connect(self.on_dap_params)
for wave in (self.waveform_x,self.waveform_y):
for wave in (self.waveform_x, self.waveform_y):
wave.x_label = "Angle (deg)"
wave.x_grid = True
wave.y_grid = True
@@ -292,7 +318,9 @@ class XRayEye(BECWidget, QWidget):
# Make connections
self.live_preview_toggle.enabled.connect(self.on_live_view_enabled)
self.step_size.valueChanged.connect(lambda x: self.motor_control_2d.setProperty("step_size", x))
self.step_size.valueChanged.connect(
lambda x: self.motor_control_2d.setProperty("step_size", x)
)
self.submit_button.clicked.connect(self.submit)
def _create_separator(self):
@@ -316,7 +344,9 @@ class XRayEye(BECWidget, QWidget):
for motor in possible_motors:
if motor in self.dev:
self.bec_dispatcher.connect_slot(self.on_tomo_angle_readback, MessageEndpoints.device_readback(motor))
self.bec_dispatcher.connect_slot(
self.on_tomo_angle_readback, MessageEndpoints.device_readback(motor)
)
logger.info(f"Successfully connected to {motor}")
################################################################################
@@ -355,13 +385,12 @@ class XRayEye(BECWidget, QWidget):
################################################################################
@SafeSlot(str)
def switch_tab(self,tab:str):
def switch_tab(self, tab: str):
if tab == "fit":
self.tab_widget.setCurrentIndex(1)
else:
self.tab_widget.setCurrentIndex(0)
@SafeSlot()
def get_roi_coordinates(self) -> dict | None:
"""Get the coordinates of the currently active ROI."""
@@ -378,11 +407,11 @@ class XRayEye(BECWidget, QWidget):
self.live_preview_toggle.blockSignals(True)
if enabled:
self.live_preview_toggle.checked = enabled
self.image.image(CAMERA)
self.image.image(device=CAMERA[0], signal=CAMERA[1])
self.live_preview_toggle.blockSignals(False)
return
self.image.disconnect_monitor(CAMERA)
self.image.disconnect_monitor(CAMERA[0], CAMERA[1])
self.live_preview_toggle.checked = enabled
self.live_preview_toggle.blockSignals(False)
@@ -394,8 +423,8 @@ class XRayEye(BECWidget, QWidget):
self.camera_running_toggle.checked = enabled
self.camera_running_toggle.blockSignals(False)
@SafeSlot(dict,dict)
def getting_camera_status(self,data,meta):
@SafeSlot(dict, dict)
def getting_camera_status(self, data, meta):
print(f"msg:{data}")
live_mode_enabled = data.get("signals").get(f"{CAMERA[0]}_live_mode_enabled").get("value")
self.camera_running_toggle.blockSignals(True)
@@ -413,15 +442,13 @@ class XRayEye(BECWidget, QWidget):
# self.shutter_toggle.checked = enabled
self.shutter_toggle.blockSignals(False)
@SafeSlot(dict,dict)
def getting_shutter_status(self,data,meta):
@SafeSlot(dict, dict)
def getting_shutter_status(self, data, meta):
shutter_open = bool(data.get("signals").get("omnyfsh_shutter").get("value"))
self.shutter_toggle.blockSignals(True)
self.shutter_toggle.checked = shutter_open
self.shutter_toggle.blockSignals(False)
@SafeSlot(bool, bool)
def on_motors_enable(self, x_enable: bool, y_enable: bool):
"""
@@ -446,10 +473,10 @@ class XRayEye(BECWidget, QWidget):
else:
self.submit_button.setEnabled(False)
@SafeSlot(dict,dict)
def on_dap_params(self,data,meta):
print('#######################################')
print('getting dap parameters')
@SafeSlot(dict, dict)
def on_dap_params(self, data, meta):
print("#######################################")
print("getting dap parameters")
print(f"data: {data}")
print(f"meta: {meta}")
self.waveform_x.auto_range(True)
@@ -457,8 +484,6 @@ class XRayEye(BECWidget, QWidget):
# self.bec_dispatcher.disconnect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
curve_id = meta.get("curve_id")
if curve_id == "fit-x-SineModel":
self.dev.omny_xray_gui.fit_params_x.set(data).wait()
print(f"setting x data to {data}")
@@ -469,22 +494,23 @@ class XRayEye(BECWidget, QWidget):
@SafeSlot(bool, bool)
def on_tomo_angle_readback(self, data: dict, meta: dict):
#TODO implement if needed
# TODO implement if needed
print(f"data: {data}")
print(f"meta: {meta}")
@SafeSlot()
def submit_fit_array(self,fit_array):
def submit_fit_array(self, fit_array):
self.tab_widget.setCurrentIndex(1)
# self.fix_x.title = " got fit array"
print(f"got fit array {fit_array}")
self.fit_x.set_data(x=fit_array[0],y=fit_array[1])
self.fit_y.set_data(x=fit_array[0],y=fit_array[2])
self.waveform_x.curves[0].set_data(x=fit_array[0], y=fit_array[1])
# self.fit_x.set_data(x=fit_array[0],y=fit_array[1])
# self.fit_y.set_data(x=fit_array[0],y=fit_array[2])
@SafeSlot()
def submit(self):
"""Execute submit action by submit button."""
print('submit pushed')
print("submit pushed")
self.submit_button.blockSignals(True)
if self.roi_manager.single_active_roi is None:
logger.warning("No active ROI")
@@ -503,7 +529,9 @@ class XRayEye(BECWidget, QWidget):
logger.warning("Unsupported ROI type for submit action.")
return
print(f"current roi: x:{roi_center_x}, y:{roi_center_y}, w:{roi_width},h:{roi_height}") #TODO remove when will be not needed for debugging
print(
f"current roi: x:{roi_center_x}, y:{roi_center_y}, w:{roi_width},h:{roi_height}"
) # TODO remove when will be not needed for debugging
# submit roi coordinates
step = int(self.dev.omny_xray_gui.step.read().get("omny_xray_gui_step").get("value"))
@@ -512,14 +540,16 @@ class XRayEye(BECWidget, QWidget):
width_x = getattr(self.dev.omny_xray_gui, f"width_x_{step}").set(roi_width)
width_y = getattr(self.dev.omny_xray_gui, f"width_y_{step}").set(roi_height)
self.dev.omny_xray_gui.submit.set(1)
print('submit done')
print("submit done")
self.submit_button.blockSignals(False)
def cleanup(self):
"""Cleanup connections on widget close -> disconnect slots and stop live mode of camera."""
self.bec_dispatcher.disconnect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
getattr(self.dev,CAMERA[0]).live_mode = False
self.bec_dispatcher.disconnect_slot(
self.device_updates, MessageEndpoints.device_readback("omny_xray_gui")
)
getattr(self.dev, CAMERA[0]).live_mode = False
super().cleanup()
@@ -528,9 +558,11 @@ if __name__ == "__main__":
from qtpy.QtWidgets import QApplication
from bec_widgets.utils import BECDispatcher
from bec_widgets.utils.colors import apply_theme
app = QApplication(sys.argv)
dispatcher = BECDispatcher(gui_id='xray')
apply_theme("light")
dispatcher = BECDispatcher(gui_id="xray")
win = XRayEye()
win.resize(1000, 800)
+14 -14
View File
@@ -429,20 +429,20 @@ cam_xeye:
readOnly: false
readoutPriority: async
cam_ids_rgb:
description: Camera flOMNI Xray eye ID203
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_id: 203
bits_per_pixel: 24
num_rotation_90: 2
transpose: false
force_monochrome: false
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
# cam_ids_rgb:
# description: Camera flOMNI Xray eye ID203
# deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
# deviceConfig:
# camera_id: 203
# bits_per_pixel: 24
# num_rotation_90: 2
# transpose: false
# force_monochrome: false
# m_n_colormode: 1
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: async
# ############################################################
+12 -14
View File
@@ -1,7 +1,7 @@
import numpy as np
from ophyd import Component as Cpt, Signal, EpicsSignal
from ophyd import Device
from ophyd import DynamicDeviceComponent as Dcpt
class OMNYXRayAlignGUI(Device):
@@ -10,12 +10,10 @@ class OMNYXRayAlignGUI(Device):
enable_mv_x = Cpt(Signal, value=0)
enable_mv_y = Cpt(Signal, value=0)
send_message = Cpt(Signal, value=0)
sample_name = Cpt(Signal, value=0)
sample_name = Cpt(Signal, value=0)
angle = Cpt(Signal, value=0)
pixel_size = Cpt(Signal, value=0)
submit = Cpt(
EpicsSignal, name="submit", read_pv="XOMNYI-XEYE-SUBMIT:0",auto_monitor=True
)
submit = Cpt(EpicsSignal, name="submit", read_pv="XOMNYI-XEYE-SUBMIT:0", auto_monitor=True)
step = Cpt(Signal, value=0)
recbg = Cpt(Signal, value=0)
mvx = Cpt(Signal, value=0)
@@ -27,20 +25,20 @@ class OMNYXRayAlignGUI(Device):
# Generate width_y_0 to width_y_10
for i in range(11):
locals()[f'width_y_{i}'] = Cpt(Signal, value=0)
locals()[f"width_y_{i}"] = Cpt(Signal, value=0)
# Generate width_x_0 to width_x_10
for i in range(11):
locals()[f'width_x_{i}'] = Cpt(Signal, value=0)
locals()[f"width_x_{i}"] = Cpt(Signal, value=0)
# Generate xval_x_0 to xval_x_10
for i in range(11):
locals()[f'xval_x_{i}'] = Cpt(Signal, value=0)
locals()[f"xval_x_{i}"] = Cpt(Signal, value=0)
# Generate yval_y_0 to yval_y_10
for i in range(11):
locals()[f'yval_y_{i}'] = Cpt(Signal, value=0)
locals()[f"yval_y_{i}"] = Cpt(Signal, value=0)
# Generate stage_pos_x_1 to stage_pos_x_5
for i in range(1, 6):
locals()[f'stage_pos_x_{i}'] = Cpt(Signal, value=0)
locals()[f"stage_pos_x_{i}"] = Cpt(Signal, value=0)