diff --git a/csaxs_bec/bec_ipython_client/plugins/LamNI/gui_tools.py b/csaxs_bec/bec_ipython_client/plugins/LamNI/gui_tools.py new file mode 100644 index 0000000..b0f24b3 --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/LamNI/gui_tools.py @@ -0,0 +1,188 @@ +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: + bec = builtins.__dict__.get("bec") + dev = builtins.__dict__.get("dev") + scans = builtins.__dict__.get("scans") + +def umv(*args): + return scans.umv(*args, relative=False) + + +class LamniGuiToolsError(Exception): + pass + + +class LamniGuiTools: + + def __init__(self): + self.text_box = None + self.progressbar = None + + def set_client(self, client): + self.client = client + self.gui = self.client.gui + + def lamnigui_show_gui(self): + if "lamni" in self.gui.windows: + self.gui.lamni.show() + else: + self.gui.new("lamni") + + def lamnigui_stop_gui(self): + self.gui.lamni.hide() + + def lamnigui_raise(self): + self.gui.lamni.raise_window() + + def lamnigui_show_xeyealign(self): + self.lamnigui_show_gui() + if self._lamnigui_check_attribute_not_exists("xeyegui"): + self.lamnigui_remove_all_docks() + self.xeyegui = self.gui.lamni.new("xeyegui").new("XRayEye") + # start live + if not dev.cam_xeye.live_mode: + dev.cam_xeye.live_mode = True + + + def _lamnigui_check_attribute_not_exists(self, attribute_name): + if hasattr(self.gui,"lamni"): + if hasattr(self.gui.lamni,attribute_name): + return False + return True + + def lamnigui_remove_all_docks(self): + self.gui.lamni.delete_all() + self.progressbar = None + self.text_box = None + + def lamnigui_idle(self): + self.lamnigui_show_gui() + if self._lamnigui_check_attribute_not_exists("idle_text_box"): + self.lamnigui_remove_all_docks() + idle_text_box = self.gui.lamni.new("idle_textbox").new("TextBox") + text = ( + "
" + + "██████╗ ███████╗ ██████╗ ██╗ █████╗ ███╗ ███╗███╗ ██╗██╗\n" + + "██╔══██╗██╔════╝██╔════╝ ██║ ██╔══██╗████╗ ████║████╗ ██║██║\n" + + "██████╔╝█████╗ ██║ ██║ ███████║██╔████╔██║██╔██╗ ██║██║\n" + + "██╔══██╗██╔══╝ ██║ ██║ ██╔══██║██║╚██╔╝██║██║╚██╗██║██║\n" + + "██████╔╝███████╗╚██████╗ ███████╗██║ ██║██║ ╚═╝ ██║██║ ╚████║██║\n" + + "╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝\n" + + "" + ) + idle_text_box.set_html_text(text) + + def lamnigui_docs(self, filename: str | None = None): + import csaxs_bec + from pathlib import Path + + print("The general lamni documentation is at \nhttps://sls-csaxs.readthedocs.io/en/latest/user/ptychography/lamni.html#user-ptychography-lamni") + + csaxs_bec_basepath = Path(csaxs_bec.__file__).parent + docs_folder = ( + csaxs_bec_basepath / + "bec_ipython_client" / "plugins" / "lamni" / "docs" + ) + + if not docs_folder.is_dir(): + raise NotADirectoryError(f"Docs folder not found: {docs_folder}") + + pdfs = sorted(docs_folder.glob("*.pdf")) + if not pdfs: + raise FileNotFoundError(f"No PDF files found in {docs_folder}") + + # --- Resolve PDF ------------------------------------------------------ + if filename is not None: + pdf_file = docs_folder / filename + if not pdf_file.exists(): + raise FileNotFoundError(f"Requested file not found: {filename}") + else: + print("\nAvailable lamni documentation PDFs:\n") + for i, pdf in enumerate(pdfs, start=1): + print(f" {i:2d}) {pdf.name}") + print() + + while True: + try: + choice = int(input(f"Select a file (1–{len(pdfs)}): ")) + if 1 <= choice <= len(pdfs): + pdf_file = pdfs[choice - 1] + break + print(f"Enter a number between 1 and {len(pdfs)}.") + except ValueError: + print("Invalid input. Please enter a number.") + + # --- GUI handling (active existence check) ---------------------------- + self.lamnigui_show_gui() + + if self._lamnigui_check_attribute_not_exists("PdfViewerWidget"): + self.lamnigui_remove_all_docks() + self.pdf_viewer = self.gui.lamni.new(widget="PdfViewerWidget") + + # --- Load PDF --------------------------------------------------------- + self.pdf_viewer.PdfViewerWidget.load_pdf(str(pdf_file.resolve())) + print(f"\nLoaded: {pdf_file.name}\n") + + + def _lamnicam_check_device_exists(self, device): + try: + device + except: + return False + else: + return True + + def lamnigui_show_progress(self): + self.lamnigui_show_gui() + if self._lamnigui_check_attribute_not_exists("progressbar"): + self.lamnigui_remove_all_docks() + # Add a new dock with a RingProgressBar widget + self.progressbar = self.gui.lamni.new("progressbar").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 + self.progressbar.enable_auto_updates(False) + # Set precision for the self.progressbar display + self.progressbar.set_precision(1) # Display self.progressbar with one decimal places + # Setting multiple rigns with different values + self.progressbar.set_number_of_bars(3) + self.progressbar.rings[0].set_update("manual") + self.progressbar.rings[1].set_update("manual") + self.progressbar.rings[2].set_update("scan") + # 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.lamni.new(name="progress_text").new("TextBox") + + self._lamnigui_update_progress() + + def _lamnigui_update_progress(self): + if self.progressbar is not None: + progress = self.progress["projection"] / self.progress["total_projections"] * 100 + subtomo_progress = ( + self.progress["subtomo_projection"] + / self.progress["subtomo_total_projections"] + * 100 + ) + self.progressbar.set_value([progress, subtomo_progress, 0]) + if self.text_box is not None: + text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}" + self.text_box.set_plain_text(text) + + +if __name__ == "__main__": + from bec_lib.client import BECClient + from bec_widgets.cli.client_utils import BECGuiClient + + client = BECClient() + client.start() + client.gui = BECGuiClient() + + lamni_gui = LamniGuiTools(client) + lamni_gui.lamnigui_show_gui() + lamni_gui.lamnigui_show_progress()