From 4f6e25e16e5e047cb136f63b8d4757ea393ffb6b Mon Sep 17 00:00:00 2001 From: kferjaoui Date: Sat, 13 Dec 2025 16:42:16 +0100 Subject: [PATCH 1/6] fix: avoid import-time cfg lookups; init globals after CLI parse - Remove Redis-backed cfg access from globals.py module scope - Avoid cfg.temserver as argparse default (prevents ValueError at startup) - Initialize globals via globals.init() after parsing args and resolving cfg --- jungfrau_gui/globals.py | 62 +++++++++++++------- jungfrau_gui/main_ui.py | 125 ++++++++++++++++++++++++++-------------- 2 files changed, 125 insertions(+), 62 deletions(-) diff --git a/jungfrau_gui/globals.py b/jungfrau_gui/globals.py index 1db1125a..70f4b755 100644 --- a/jungfrau_gui/globals.py +++ b/jungfrau_gui/globals.py @@ -1,7 +1,6 @@ import ctypes import numpy as np import multiprocessing as mp -from epoc import ConfigurationClient, auth_token, redis_host import subprocess def get_git_info(): @@ -49,46 +48,69 @@ def get_git_info(): # Git not installed or command failed return defaults -cfg = ConfigurationClient(redis_host(), token=auth_token()) +# ---------------------------- +# Runtime-configurable globals +# ---------------------------- stream = "tcp://localhost:4545" tem_mode = True -# jfj = False - -tem_host = cfg.temserver +tem_host = "localhost" dev = False -#Configuration -nrow = cfg.nrows -ncol = cfg.ncols + +nrow = 0 +ncol = 0 dtype = np.float32 cdtype = ctypes.c_float -# fitterWorkerReady = mp.Value(ctypes.c_bool) -# fitterWorkerReady.value = False - accframes = 0 -acc_image = np.zeros((nrow,ncol), dtype = dtype) +acc_image = np.zeros((0, 0), dtype=dtype) # allocated properly in init() exit_flag = mp.Value(ctypes.c_bool) exit_flag.value = False -#Data type to write to file +# Data type to write to file file_dt = np.int32 -#Data type to receive from the stream +# Data type to receive from the stream stream_dt = np.float32 # Flags for non-updated magnification values in MAG and DIFF modes -mag_value_img = [1, 'X', 'X1'] -mag_value_diff = [1, 'mm', '1cm'] +mag_value_img = [1, "X", "X1"] +mag_value_diff = [1, "mm", "1cm"] -tag, branch, commit = get_git_info() +# Version info (safe at import; no Redis!) +tag, branch, commit = get_git_info() # constants, presets UM_TO_NM = 1e3 MM_TO_UM = 1e3 -KV_TO_V = 1e3 -PIXEL = 0.075 # mm +KV_TO_V = 1e3 +PIXEL = 0.075 # mm -default_HT = 200000.00 # V +default_HT = 200000.00 # V backlash = [100, 80, 0, 0] + + +def init(*, stream_, dtype_, cdtype_, tem_mode_, tem_host_, dev_, nrow_, ncol_): + """ + Initialize globals that previously depended on Redis at import-time. + + Call this exactly once in launch_gui.py *before* importing modules that use globals. + """ + global stream, dtype, cdtype, tem_mode, tem_host, dev, nrow, ncol, acc_image + + stream = stream_ + dtype = np.dtype(dtype_) + cdtype = cdtype_ + + tem_mode = bool(tem_mode_) + tem_host = str(tem_host_) + dev = bool(dev_) + + nrow = int(nrow_) + ncol = int(ncol_) + + if nrow <= 0 or ncol <= 0: + raise ValueError(f"Invalid detector geometry: nrow={nrow}, ncol={ncol}") + + acc_image = np.zeros((nrow, ncol), dtype=dtype) \ No newline at end of file diff --git a/jungfrau_gui/main_ui.py b/jungfrau_gui/main_ui.py index ac62a19b..6b1f34a6 100755 --- a/jungfrau_gui/main_ui.py +++ b/jungfrau_gui/main_ui.py @@ -20,6 +20,26 @@ import os import datetime +def _cfg_get(cfg, name: str, default=None): + """ + Safely read a config attribute from ConfigurationClient. + + Some properties (e.g. cfg.temserver) raise ValueError if missing. + """ + try: + return getattr(cfg, name) + except (ValueError, AttributeError): + return default + + +def _parse_dtype(dtype_str: str) -> np.dtype: + s = (dtype_str or "").strip().lower() + if s in ("float32", "f4", "np.float32"): + return np.dtype(np.float32) + if s in ("float64", "double", "f8", "np.float64", "np.double"): + return np.dtype(np.float64) + raise ValueError(f"Unknown dtype '{dtype_str}'. Use float32 or float64.") + class CustomFormatter(logging.Formatter): # Define color codes for different log levels and additional styles # Foreground (text) colors @@ -85,73 +105,94 @@ def format(self, record): def main(): os.environ["QT_LOGGING_RULES"] = "qt.core.qobject.connect=false" - + app = QApplication(sys.argv) app.setStyle("Fusion") - cfg = ConfigurationClient(redis_host(), token=auth_token()) - + # ---- Command-Line Interface FIRST (no Redis-backed defaults!) ---- parser = argparse.ArgumentParser() - parser.add_argument('-s', '--stream', type=str, default="tcp://noether:5501", help="zmq stream") # default="tcp://localhost:4545" - parser.add_argument("-d", "--dtype", help="Data type", type = np.dtype, default=np.float32) - parser.add_argument("-p", "--playmode", action="store_true", help="Activates simplified GUI") - parser.add_argument("-th", "--temhost", default=cfg.temserver, help="Choose host for tem-gui communication") - parser.add_argument('-l', '--log', default='INFO', help='Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)') - parser.add_argument("-f", "--logfile", action="store_true", help="File-output of logging") - parser.add_argument("-e", "--dev", action="store_true", help="Activate developing function") - parser.add_argument("-v", "--version", action="store_true", help="Detailed version description") + parser.add_argument("-s", "--stream", type=str, default="tcp://noether:5501", help="ZMQ stream endpoint",) + parser.add_argument("-d", "--dtype", type=str, default="float32", help="Data type (float32 or float64)",) + parser.add_argument("-p", "--playmode", action="store_true", help="Activates simplified GUI",) + parser.add_argument("-th", "--temhost", default=None, help="Host for tem-gui communication (defaults to cfg.temserver if set)",) + parser.add_argument("--nrow", type=int, default=None, help="Override detector rows (defaults to cfg.nrows)",) + parser.add_argument("--ncol", type=int, default=None, help="Override detector cols (defaults to cfg.ncols)",) + parser.add_argument("-l", "--log", default="INFO", help="Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",) + parser.add_argument("-f", "--logfile", action="store_true", help="File-output of logging",) + parser.add_argument("-e", "--dev", action="store_true", help="Activate developing function",) + parser.add_argument("-v", "--version", action="store_true", help="Detailed version description",) args = parser.parse_args() - # Initialize logger + # ---- Logger setup ---- logger = logging.getLogger() - - # Dynamically set the log level based on args.log - log_level = getattr(logging, args.log.upper(), None) + log_level = getattr(logging, args.log.upper(), None) if log_level is None: - raise ValueError(f"Invalid log level: {args.log}. Choose from DEBUG, INFO, WARNING, ERROR, CRITICAL.") - + raise ValueError( + f"Invalid log level: {args.log}. Choose from DEBUG, INFO, WARNING, ERROR, CRITICAL." + ) logger.setLevel(log_level) - # Create the handler for console output console_handler = logging.StreamHandler() - - # Apply the custom formatter to the handler - formatter = CustomFormatter('%(asctime)s - %(levelname)s - %(message)s') + formatter = CustomFormatter("%(asctime)s - %(levelname)s - %(message)s") console_handler.setFormatter(formatter) - - # Add the handler to the logger logger.addHandler(console_handler) if args.logfile: - - # Determine the directory of the script being run launch_script_path = Path(sys.argv[0]).resolve().parent log_file_path = launch_script_path / f'JFGUI{time.strftime("_%Y%m%d-%H%M%S.log", time.localtime())}' + logging.info(f"Writing console loggings to: {log_file_path}") - logging.info(f"Writing console loggings to: {log_file_path}") # Debugging line to verify file creation - file_handler = logging.FileHandler(log_file_path.as_posix()) file_handler.setLevel(log_level) - file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S') + file_formatter = logging.Formatter( + "%(asctime)s - %(levelname)s - %(message)s", + datefmt="%H:%M:%S", + ) file_handler.setFormatter(file_formatter) logger.addHandler(file_handler) - if args.dtype == np.float32: - globals.cdtype = ctypes.c_float - elif args.dtype == np.double: - cdtype = ctypes.c_double + # ---- Resolve dtype ---- + dtype = _parse_dtype(args.dtype) + if dtype == np.dtype(np.float32): + cdtype = ctypes.c_float else: - raise ValueError("unknown data type") - - # Update the type of global variables - globals.stream = args.stream - globals.dtype = args.dtype - globals.acc_image = np.zeros((globals.nrow,globals.ncol), dtype = args.dtype) - globals.tem_mode = not args.playmode - globals.tem_host = args.temhost - globals.dev = args.dev - + cdtype = ctypes.c_double + + # ---- Connect to config (Redis) AFTER parsing ---- + cfg = ConfigurationClient(redis_host(), token=auth_token()) + + # Resolve TEM host safely (missing key should NOT crash) + tem_host = args.temhost or _cfg_get(cfg, "temserver", default=None) + if tem_host is None: + # pick a sensible fallback; you can change this + tem_host = "localhost" + logging.warning("cfg.temserver not set; defaulting tem_host to 'localhost'.") + + # Resolve detector geometry safely + nrow = args.nrow if args.nrow is not None else _cfg_get(cfg, "nrows", default=None) + ncol = args.ncol if args.ncol is not None else _cfg_get(cfg, "ncols", default=None) + + if nrow is None or ncol is None: + raise RuntimeError( + "Detector geometry missing (nrows/ncols). " + "Set cfg.nrows/cfg.ncols in Redis or pass --nrow/--ncol." + ) + + # ---- Initialize globals explicitly (NO import-time Redis reads) ---- + from jungfrau_gui import globals + + globals.init( + stream_=args.stream, + dtype_=dtype, + cdtype_=cdtype, + tem_mode_=not args.playmode, + tem_host_=tem_host, + dev_=args.dev, + nrow_=nrow, + ncol_=ncol, + ) + logging.info(f"{get_gui_info()}") if args.version: From 5bf0870a8ca3e59ebe83c610e229715cd05432c8 Mon Sep 17 00:00:00 2001 From: kferjaoui Date: Sat, 13 Dec 2025 20:08:51 +0100 Subject: [PATCH 2/6] refactor: normalize imports to jungfrau_gui.* absolute paths --- jungfrau_gui/main_ui.py | 8 +++---- .../metadata_update_client.py | 2 +- .../file_operations/file_operations.py | 12 +++++----- .../file_operations/processresult_updater.py | 1 - .../tem_controls/connectivity_inspector.py | 2 +- .../tem_controls/gaussian_fitter.py | 2 +- .../tem_controls/gaussian_fitter_mp.py | 6 ++--- .../tem_controls/task/beam_focus_task.py | 4 ++-- .../tem_controls/task/get_teminfo_task.py | 2 +- .../tem_controls/task/record_task.py | 9 ++++--- .../tem_controls/task/stage_centering_task.py | 5 ++-- .../tem_controls/task/task_manager.py | 24 +++++++------------ .../ui_components/tem_controls/tem_action.py | 17 ++++++------- .../tem_controls/tem_controls.py | 16 ++++++------- .../tem_controls/toolbox/config.py | 2 +- .../toolbox/fit_beam_intensity.py | 2 +- .../tem_controls/toolbox/plot_dialog.py | 2 +- .../tem_controls/toolbox/tool.py | 2 +- .../tem_controls/ui_tem_specific.py | 6 ++--- .../visualization_panel/reader.py | 2 +- .../visualization_panel.py | 20 +++++++--------- jungfrau_gui/ui_main_window.py | 14 +++++------ jungfrau_gui/zmq_receiver.py | 4 ++-- 23 files changed, 75 insertions(+), 89 deletions(-) diff --git a/jungfrau_gui/main_ui.py b/jungfrau_gui/main_ui.py index 6b1f34a6..2c24e242 100755 --- a/jungfrau_gui/main_ui.py +++ b/jungfrau_gui/main_ui.py @@ -9,10 +9,10 @@ from PySide6.QtWidgets import QApplication from PySide6.QtCore import QCoreApplication -from . import globals -from .ui_components import palette -from .zmq_receiver import ZmqReceiver -from .ui_main_window import ApplicationWindow, get_gui_info +from jungfrau_gui import globals +from jungfrau_gui.ui_components import palette +from jungfrau_gui.zmq_receiver import ZmqReceiver +from jungfrau_gui.ui_main_window import ApplicationWindow, get_gui_info from pathlib import Path from epoc import ConfigurationClient, auth_token, redis_host diff --git a/jungfrau_gui/metadata_uploader/metadata_update_client.py b/jungfrau_gui/metadata_uploader/metadata_update_client.py index 30b29494..958b4fea 100644 --- a/jungfrau_gui/metadata_uploader/metadata_update_client.py +++ b/jungfrau_gui/metadata_uploader/metadata_update_client.py @@ -8,7 +8,7 @@ from datetime import datetime import argparse from pathlib import Path -from .. import globals +from jungfrau_gui import globals # Handle imports correctly when running as a standalone script if __name__ == "__main__" and __package__ is None: diff --git a/jungfrau_gui/ui_components/file_operations/file_operations.py b/jungfrau_gui/ui_components/file_operations/file_operations.py index 4e0052aa..931bf2cd 100644 --- a/jungfrau_gui/ui_components/file_operations/file_operations.py +++ b/jungfrau_gui/ui_components/file_operations/file_operations.py @@ -4,14 +4,14 @@ from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QSpinBox, QCheckBox, QComboBox, QCompleter) -from ...ui_components.toggle_button import ToggleButton -from ...ui_components.utils import create_horizontal_line_with_margin -from ...ui_components.palette import * -from ...ui_components.tem_controls.toolbox.tool import send_with_retries -from ...metadata_uploader.metadata_update_client import MetadataNotifier +from jungfrau_gui.ui_components.toggle_button import ToggleButton +from jungfrau_gui.ui_components.utils import create_horizontal_line_with_margin +from jungfrau_gui.ui_components.palette import * +from jungfrau_gui.ui_components.tem_controls.toolbox.tool import send_with_retries +from jungfrau_gui.metadata_uploader.metadata_update_client import MetadataNotifier from epoc import ConfigurationClient, auth_token, redis_host -from ... import globals +from jungfrau_gui import globals import os import re diff --git a/jungfrau_gui/ui_components/file_operations/processresult_updater.py b/jungfrau_gui/ui_components/file_operations/processresult_updater.py index 637a0a34..9355ddde 100644 --- a/jungfrau_gui/ui_components/file_operations/processresult_updater.py +++ b/jungfrau_gui/ui_components/file_operations/processresult_updater.py @@ -5,7 +5,6 @@ from datetime import datetime import argparse from pathlib import Path -# from .. import globals from PySide6.QtCore import Signal, Slot, QObject import time diff --git a/jungfrau_gui/ui_components/tem_controls/connectivity_inspector.py b/jungfrau_gui/ui_components/tem_controls/connectivity_inspector.py index 29ca6ea1..1f813e91 100644 --- a/jungfrau_gui/ui_components/tem_controls/connectivity_inspector.py +++ b/jungfrau_gui/ui_components/tem_controls/connectivity_inspector.py @@ -3,7 +3,7 @@ from simple_tem import TEMClient -from ... import globals +from jungfrau_gui import globals class TEM_Connector(QObject): finished = Signal(bool) diff --git a/jungfrau_gui/ui_components/tem_controls/gaussian_fitter.py b/jungfrau_gui/ui_components/tem_controls/gaussian_fitter.py index 90af1957..d68d0ed9 100644 --- a/jungfrau_gui/ui_components/tem_controls/gaussian_fitter.py +++ b/jungfrau_gui/ui_components/tem_controls/gaussian_fitter.py @@ -3,7 +3,7 @@ from PySide6.QtCore import QObject, Signal, Slot # from line_profiler import LineProfiler -from .toolbox.fit_beam_intensity import gaussian2d_rotated, super_gaussian2d_rotated, fit_2d_gaussian_roi_NaN +from jungfrau_gui.ui_components.tem_controls.toolbox.fit_beam_intensity import gaussian2d_rotated, super_gaussian2d_rotated, fit_2d_gaussian_roi_NaN class GaussianFitter(QObject): finished = Signal(object) diff --git a/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py b/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py index 9197c25f..46c140b3 100644 --- a/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py +++ b/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py @@ -5,10 +5,10 @@ from line_profiler import LineProfiler import zmq import cbor2 -from ...decoder import tag_hook -from ... import globals +from jungfrau_gui.decoder import tag_hook +from jungfrau_gui. import globals -from .toolbox.fit_beam_intensity import gaussian2d_rotated, super_gaussian2d_rotated, fit_2d_gaussian_roi_NaN_fast +from jungfrau_gui.ui_components.tem_controls.toolbox.fit_beam_intensity import gaussian2d_rotated, super_gaussian2d_rotated, fit_2d_gaussian_roi_NaN_fast from datetime import datetime # import globals diff --git a/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py b/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py index c1811829..5064f356 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py @@ -2,9 +2,9 @@ import time import logging import numpy as np -from .task import Task +from jungfrau_gui.ui_components.tem_controls.task import Task -from .... import globals +from jungfrau_gui import globals from PySide6.QtCore import Qt, QMetaObject, Signal from datetime import datetime diff --git a/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py b/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py index a7ad573b..ec9694dc 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py @@ -2,7 +2,7 @@ import logging import numpy as np -from .task import Task +from jungfrau_gui.ui_components.tem_controls.task import Task from epoc import ConfigurationClient, auth_token, redis_host class GetInfoTask(Task): diff --git a/jungfrau_gui/ui_components/tem_controls/task/record_task.py b/jungfrau_gui/ui_components/tem_controls/task/record_task.py index 27b8e7f9..1b5c0bea 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/record_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/record_task.py @@ -3,16 +3,15 @@ import h5py import logging import numpy as np -from .task import Task from PySide6.QtWidgets import QMessageBox from PySide6.QtCore import Signal, Qt, QMetaObject from simple_tem import TEMClient from epoc import ConfigurationClient, auth_token, redis_host -from ..toolbox.tool import send_with_retries -from ....metadata_uploader.metadata_update_client import MetadataNotifier - -from .... import globals +from jungfrau_gui import globals +from jungfrau_gui.ui_components.tem_controls.toolbox.tool import send_with_retries +from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.metadata_uploader.metadata_update_client import MetadataNotifier class RecordTask(Task): reset_rotation_signal = Signal() diff --git a/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py b/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py index 20a6917b..da25d24d 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py @@ -1,10 +1,11 @@ import time import logging import numpy as np -from .task import Task + +from jungfrau_gui import globals +from jungfrau_gui.ui_components.tem_controls.task import Task from simple_tem import TEMClient -from .... import globals from epoc import ConfigurationClient, auth_token, redis_host from jungfrau_gui.ui_components.tem_controls.toolbox import config as cfg_jf diff --git a/jungfrau_gui/ui_components/tem_controls/task/task_manager.py b/jungfrau_gui/ui_components/tem_controls/task/task_manager.py index 1a9b16e7..1f4520c9 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/task_manager.py +++ b/jungfrau_gui/ui_components/tem_controls/task/task_manager.py @@ -7,25 +7,19 @@ from PySide6.QtCore import Signal, Slot, QObject, QThread, QMetaObject, Qt, QTimer -from .task import Task -from .record_task import RecordTask - -from .beam_focus_task import AutoFocusTask - -from .get_teminfo_task import GetInfoTask -from .stage_centering_task import CenteringTask +from jungfrau_gui import globals +import jungfrau_gui.ui_threading_helpers as thread_manager +from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.ui_components.tem_controls.task.record_task import RecordTask +from jungfrau_gui.ui_components.tem_controls.task.beam_focus_task import AutoFocusTask +from jungfrau_gui.ui_components.tem_controls.task.get_teminfo_task import GetInfoTask +from jungfrau_gui.ui_components.tem_controls.task.stage_centering_task import CenteringTask +from jungfrau_gui.ui_components.tem_controls.toolbox import tool as tools +from jungfrau_gui.ui_components.tem_controls.gaussian_fitter_mp import GaussianFitterMP from simple_tem import TEMClient -from ..toolbox import tool as tools - from epoc import ConfigurationClient, auth_token, redis_host -import jungfrau_gui.ui_threading_helpers as thread_manager - -from .... import globals - -from ..gaussian_fitter_mp import GaussianFitterMP - def on_new_best_result_in_main_thread(result_dict): # This runs in the main thread. We can safely update GUI elements, logs, etc. print("New best result =>", result_dict) diff --git a/jungfrau_gui/ui_components/tem_controls/tem_action.py b/jungfrau_gui/ui_components/tem_controls/tem_action.py index 9ee91cdd..f97b330a 100644 --- a/jungfrau_gui/ui_components/tem_controls/tem_action.py +++ b/jungfrau_gui/ui_components/tem_controls/tem_action.py @@ -5,18 +5,15 @@ from PySide6.QtCore import QRectF, QObject, QTimer, Qt, QMetaObject, Signal, Slot from PySide6.QtGui import QFont, QTransform -from .toolbox.tool import * -from .toolbox import config as cfg_jf - -from .task.task_manager import * +import jungfrau_gui.ui_threading_helpers as thread_manager +from jungfrau_gui.ui_components.tem_controls.toolbox.tool import * +from jungfrau_gui.ui_components.tem_controls.toolbox import config as cfg_jf +from jungfrau_gui.ui_components.tem_controls.task.task_manager import * +from jungfrau_gui.ui_components.tem_controls.connectivity_inspector import TEM_Connector +from jungfrau_gui.ui_components.file_operations.processresult_updater import ProcessedDataReceiver +from jungfrau_gui.ui_components.tem_controls.tem_status_updater import TemUpdateWorker from epoc import ConfigurationClient, auth_token, redis_host - -from .connectivity_inspector import TEM_Connector -from ..file_operations.processresult_updater import ProcessedDataReceiver -from .tem_status_updater import TemUpdateWorker - -import jungfrau_gui.ui_threading_helpers as thread_manager import time from jungfrau_gui import globals diff --git a/jungfrau_gui/ui_components/tem_controls/tem_controls.py b/jungfrau_gui/ui_components/tem_controls/tem_controls.py index e2025768..9171f654 100644 --- a/jungfrau_gui/ui_components/tem_controls/tem_controls.py +++ b/jungfrau_gui/ui_components/tem_controls/tem_controls.py @@ -1,7 +1,8 @@ import math import logging +import threading import numpy as np -from ... import globals +from jungfrau_gui import globals import pyqtgraph as pg from datetime import datetime from PySide6.QtCore import QThread, Qt, QRectF, QMetaObject, Slot, Signal, QTimer @@ -9,18 +10,17 @@ from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QLabel, QSpinBox, QDoubleSpinBox, QCheckBox, QGraphicsEllipseItem, QGraphicsRectItem) -from .toolbox.plot_dialog import PlotDialog -from .gaussian_fitter import GaussianFitter +from jungfrau_gui.ui_components.tem_controls.toolbox.plot_dialog import PlotDialog +from jungfrau_gui.ui_components.tem_controls.gaussian_fitter import GaussianFitter -from ...ui_components.toggle_button import ToggleButton -from .ui_tem_specific import TEMStageCtrl, TEMTasks #, XtalInfo -from .tem_action import TEMAction +from jungfrau_gui.ui_components.toggle_button import ToggleButton +from jungfrau_gui.ui_components.tem_controls.ui_tem_specific import TEMStageCtrl, TEMTasks #, XtalInfo +from jungfrau_gui.ui_components.tem_controls.tem_action import TEMAction import jungfrau_gui.ui_threading_helpers as thread_manager from epoc import ConfigurationClient, auth_token, redis_host -from ...ui_components.palette import * -import threading +from jungfrau_gui.ui_components.palette import * from PySide6.QtWidgets import QApplication class TemControls(QGroupBox): diff --git a/jungfrau_gui/ui_components/tem_controls/toolbox/config.py b/jungfrau_gui/ui_components/tem_controls/toolbox/config.py index 67c03b58..4bead7fc 100644 --- a/jungfrau_gui/ui_components/tem_controls/toolbox/config.py +++ b/jungfrau_gui/ui_components/tem_controls/toolbox/config.py @@ -12,7 +12,7 @@ from PySide6.QtCore import QRectF from epoc import ConfigurationClient, auth_token, redis_host -from .... import globals +from jungfrau_gui import globals f = files('jungfrau_gui').joinpath('ui_components/tem_controls/toolbox/jfgui2_config.json') parser = json.loads(f.read_text()) diff --git a/jungfrau_gui/ui_components/tem_controls/toolbox/fit_beam_intensity.py b/jungfrau_gui/ui_components/tem_controls/toolbox/fit_beam_intensity.py index c5e5b039..17f38a17 100644 --- a/jungfrau_gui/ui_components/tem_controls/toolbox/fit_beam_intensity.py +++ b/jungfrau_gui/ui_components/tem_controls/toolbox/fit_beam_intensity.py @@ -7,7 +7,7 @@ from scipy.interpolate import griddata from line_profiler import LineProfiler -from .... import globals +from jungfrau_gui import globals def filter_outliers(im_roi, lower_percentile=1, upper_percentile=99.99): """ diff --git a/jungfrau_gui/ui_components/tem_controls/toolbox/plot_dialog.py b/jungfrau_gui/ui_components/tem_controls/toolbox/plot_dialog.py index 1cde965b..724fc5eb 100644 --- a/jungfrau_gui/ui_components/tem_controls/toolbox/plot_dialog.py +++ b/jungfrau_gui/ui_components/tem_controls/toolbox/plot_dialog.py @@ -4,7 +4,7 @@ from PySide6.QtWidgets import QPushButton, QVBoxLayout, QDialog from PySide6.QtCore import QTime -from ... import palette +from jungfrau_gui.ui_components import palette class PlotDialog(QDialog): diff --git a/jungfrau_gui/ui_components/tem_controls/toolbox/tool.py b/jungfrau_gui/ui_components/tem_controls/toolbox/tool.py index 0f41f058..088dec88 100644 --- a/jungfrau_gui/ui_components/tem_controls/toolbox/tool.py +++ b/jungfrau_gui/ui_components/tem_controls/toolbox/tool.py @@ -3,7 +3,7 @@ import logging import zmq -from .... import globals +from jungfrau_gui import globals def create_full_mapping(info_queries, more_queries, init_queries, info_queries_client, more_queries_client, init_queries_client): """ diff --git a/jungfrau_gui/ui_components/tem_controls/ui_tem_specific.py b/jungfrau_gui/ui_components/tem_controls/ui_tem_specific.py index b52af456..556c8c74 100644 --- a/jungfrau_gui/ui_components/tem_controls/ui_tem_specific.py +++ b/jungfrau_gui/ui_components/tem_controls/ui_tem_specific.py @@ -2,12 +2,12 @@ QRadioButton, QPushButton, QCheckBox, QDoubleSpinBox, QSizePolicy, QComboBox, QSpinBox, QWidget, QGridLayout) from PySide6.QtGui import QFont -from ..toggle_button import ToggleButton -from ..utils import create_horizontal_line_with_margin +from jungfrau_gui.ui_components.toggle_button import ToggleButton +from jungfrau_gui.ui_components.utils import create_horizontal_line_with_margin +from jungfrau_gui import globals from epoc import ConfigurationClient, auth_token, redis_host -from ... import globals import pyqtgraph as pg import numpy as np diff --git a/jungfrau_gui/ui_components/visualization_panel/reader.py b/jungfrau_gui/ui_components/visualization_panel/reader.py index 5b852f64..d1885c64 100644 --- a/jungfrau_gui/ui_components/visualization_panel/reader.py +++ b/jungfrau_gui/ui_components/visualization_panel/reader.py @@ -1,6 +1,6 @@ import logging import numpy as np -from ... import globals +from jungfrau_gui import globals from PySide6.QtCore import QObject, Signal, Slot diff --git a/jungfrau_gui/ui_components/visualization_panel/visualization_panel.py b/jungfrau_gui/ui_components/visualization_panel/visualization_panel.py index f7a2120d..93644b48 100644 --- a/jungfrau_gui/ui_components/visualization_panel/visualization_panel.py +++ b/jungfrau_gui/ui_components/visualization_panel/visualization_panel.py @@ -9,22 +9,18 @@ QLabel, QPushButton, QSpinBox, QCheckBox, QGridLayout, QSizePolicy, QSpacerItem, QMessageBox) -from epoc import ConfigurationClient, auth_token, redis_host - -from .reader import Reader - -from ... import globals -from ...ui_components.toggle_button import ToggleButton -from ..tem_controls.ui_tem_specific import TEMDetector -from ...ui_components.utils import create_horizontal_line_with_margin - +from jungfrau_gui import globals import jungfrau_gui.ui_threading_helpers as thread_manager +from jungfrau_gui.ui_components.palette import * +from jungfrau_gui.ui_components.toggle_button import ToggleButton +from jungfrau_gui.ui_components.utils import create_horizontal_line_with_margin +from jungfrau_gui.ui_components.tem_controls.toolbox import config as cfg_jf +from jungfrau_gui.ui_components.tem_controls.ui_tem_specific import TEMDetector +from jungfrau_gui.ui_components.tem_controls.toolbox.progress_pop_up import ProgressPopup +from jungfrau_gui.ui_components.visualization_panel.reader import Reader from epoc import JungfraujochWrapper, ConfigurationClient, auth_token, redis_host -from ...ui_components.palette import * from rich import print -from ..tem_controls.toolbox.progress_pop_up import ProgressPopup -from jungfrau_gui.ui_components.tem_controls.toolbox import config as cfg_jf font_big = QFont("Arial", 11) font_big.setBold(True) diff --git a/jungfrau_gui/ui_main_window.py b/jungfrau_gui/ui_main_window.py index fec1eeee..17801c92 100644 --- a/jungfrau_gui/ui_main_window.py +++ b/jungfrau_gui/ui_main_window.py @@ -1,19 +1,19 @@ import logging -from . import globals +from jungfrau_gui import globals import numpy as np import pyqtgraph as pg -from .ui_components.overlay import draw_overlay +from jungfrau_gui.ui_components.overlay import draw_overlay from pyqtgraph.dockarea import Dock from PySide6.QtWidgets import (QMainWindow, QVBoxLayout, QWidget, QHBoxLayout, QPushButton, QGridLayout, QMessageBox, QTabWidget, QLabel) from PySide6.QtCore import Qt, QObject, QEvent, QTimer from PySide6.QtGui import QShortcut, QKeySequence -from .ui_components.visualization_panel.visualization_panel import VisualizationPanel -from .ui_components.tem_controls.tem_controls import TemControls -from .ui_components.file_operations.file_operations import FileOperations -from .ui_components.utils import create_gaussian -from .ui_components.toggle_button import ToggleButton +from jungfrau_gui.ui_components.visualization_panel.visualization_panel import VisualizationPanel +from jungfrau_gui.ui_components.tem_controls.tem_controls import TemControls +from jungfrau_gui.ui_components.file_operations.file_operations import FileOperations +from jungfrau_gui.ui_components.utils import create_gaussian +from jungfrau_gui.ui_components.toggle_button import ToggleButton import jungfrau_gui.ui_threading_helpers as thread_manager diff --git a/jungfrau_gui/zmq_receiver.py b/jungfrau_gui/zmq_receiver.py index 019f8b2f..7d6feee8 100644 --- a/jungfrau_gui/zmq_receiver.py +++ b/jungfrau_gui/zmq_receiver.py @@ -2,9 +2,9 @@ import time import logging import numpy as np -from . import globals +from jungfrau_gui import globals import cbor2 -from .decoder import tag_hook +from jungfrau_gui.decoder import tag_hook # Receiver of the ZMQ stream From b874e6990bd48d5e50487404799eee335b4987c4 Mon Sep 17 00:00:00 2001 From: kferjaoui Date: Sat, 13 Dec 2025 20:58:49 +0100 Subject: [PATCH 3/6] fix: correct global imports --- jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py | 2 +- jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py | 2 +- .../ui_components/tem_controls/task/get_teminfo_task.py | 2 +- jungfrau_gui/ui_components/tem_controls/task/record_task.py | 2 +- .../ui_components/tem_controls/task/stage_centering_task.py | 2 +- jungfrau_gui/ui_components/tem_controls/task/task_manager.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py b/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py index 46c140b3..131eadae 100644 --- a/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py +++ b/jungfrau_gui/ui_components/tem_controls/gaussian_fitter_mp.py @@ -6,7 +6,7 @@ import zmq import cbor2 from jungfrau_gui.decoder import tag_hook -from jungfrau_gui. import globals +from jungfrau_gui import globals from jungfrau_gui.ui_components.tem_controls.toolbox.fit_beam_intensity import gaussian2d_rotated, super_gaussian2d_rotated, fit_2d_gaussian_roi_NaN_fast from datetime import datetime diff --git a/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py b/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py index 5064f356..b6e8bdb1 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/beam_focus_task.py @@ -2,7 +2,7 @@ import time import logging import numpy as np -from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.ui_components.tem_controls.task.task import Task from jungfrau_gui import globals from PySide6.QtCore import Qt, QMetaObject, Signal diff --git a/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py b/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py index ec9694dc..ec9e8bff 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/get_teminfo_task.py @@ -2,7 +2,7 @@ import logging import numpy as np -from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.ui_components.tem_controls.task.task import Task from epoc import ConfigurationClient, auth_token, redis_host class GetInfoTask(Task): diff --git a/jungfrau_gui/ui_components/tem_controls/task/record_task.py b/jungfrau_gui/ui_components/tem_controls/task/record_task.py index 1b5c0bea..7c4b462d 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/record_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/record_task.py @@ -10,7 +10,7 @@ from jungfrau_gui import globals from jungfrau_gui.ui_components.tem_controls.toolbox.tool import send_with_retries -from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.ui_components.tem_controls.task.task import Task from jungfrau_gui.metadata_uploader.metadata_update_client import MetadataNotifier class RecordTask(Task): diff --git a/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py b/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py index da25d24d..9e7c3031 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py @@ -3,7 +3,7 @@ import numpy as np from jungfrau_gui import globals -from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.ui_components.tem_controls.task.task import Task from simple_tem import TEMClient from epoc import ConfigurationClient, auth_token, redis_host diff --git a/jungfrau_gui/ui_components/tem_controls/task/task_manager.py b/jungfrau_gui/ui_components/tem_controls/task/task_manager.py index 1f4520c9..d40d7fef 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/task_manager.py +++ b/jungfrau_gui/ui_components/tem_controls/task/task_manager.py @@ -9,7 +9,7 @@ from jungfrau_gui import globals import jungfrau_gui.ui_threading_helpers as thread_manager -from jungfrau_gui.ui_components.tem_controls.task import Task +from jungfrau_gui.ui_components.tem_controls.task.task import Task from jungfrau_gui.ui_components.tem_controls.task.record_task import RecordTask from jungfrau_gui.ui_components.tem_controls.task.beam_focus_task import AutoFocusTask from jungfrau_gui.ui_components.tem_controls.task.get_teminfo_task import GetInfoTask From d0cd89a3db08ee337f3ffa071fbd956e00efa1e9 Mon Sep 17 00:00:00 2001 From: kferjaoui Date: Sun, 14 Dec 2025 11:59:59 +0100 Subject: [PATCH 4/6] fix f-string --- .../ui_components/tem_controls/task/stage_centering_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py b/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py index 9e7c3031..b61a7e4f 100644 --- a/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py +++ b/jungfrau_gui/ui_components/tem_controls/task/stage_centering_task.py @@ -72,7 +72,7 @@ def run(self): if tilt_X_abs < 5: if np.abs(movexy[0]) < self.thresholds['dxy_min'] and np.abs(movexy[1]) < self.thresholds['dxy_min']: - logging.info(f'Vector already small enough (< {self.thresholds['dxy_min']} um): {movexy[0]}, {movexy[1]}') + logging.info(f"Vector already small enough (< {self.thresholds['dxy_min']} um): {movexy[0]}, {movexy[1]}") return logging.info(f'Move X: {movexy[0]} um, Y: {movexy[1]} um with MAG: {magnification[2]}') self.control.trigger_movewithbacklash.emit(np.sign(movexy[0]) > 0, -movexy[0]*globals.UM_TO_NM, globals.backlash[0], False) From 108d9acf621fcbf6c693d646778c592961c514da Mon Sep 17 00:00:00 2001 From: kferjaoui Date: Sun, 14 Dec 2025 21:08:27 +0100 Subject: [PATCH 5/6] fix: remove unused/redundant imports --- jungfrau_gui/main_ui.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jungfrau_gui/main_ui.py b/jungfrau_gui/main_ui.py index 2c24e242..fb940a66 100755 --- a/jungfrau_gui/main_ui.py +++ b/jungfrau_gui/main_ui.py @@ -7,9 +7,7 @@ import numpy as np import time from PySide6.QtWidgets import QApplication -from PySide6.QtCore import QCoreApplication -from jungfrau_gui import globals from jungfrau_gui.ui_components import palette from jungfrau_gui.zmq_receiver import ZmqReceiver from jungfrau_gui.ui_main_window import ApplicationWindow, get_gui_info From 4ce77e8d8d9efd3bd230e53a4ea680d16e1e5e3f Mon Sep 17 00:00:00 2001 From: Khalil Daniel Ferjaoui Date: Fri, 13 Feb 2026 10:20:48 +0100 Subject: [PATCH 6/6] refactor: make Redis mandatory at startup, add config bootstrap files Entry point (main_ui.py): - Remove graceful fallback for missing Redis connection; fail fast with logging.critical + sys.exit(1) instead - Remove _cfg_get helper - Simplify parameter resolution to: CLI override -> cfg attribute - Update ABOUT_TEXT version description New files: - epoc-config.yaml: default Redis configuration template with documented fields for detector geometry, network endpoints, acquisition settings, and display overlays - init_redis.py: bootstrap script to populate Redis from YAML (flush_db=True) for fresh deployments --- epoc-config.yaml | 42 +++++ init_redis.py | 11 ++ jungfrau_gui/main_ui.py | 158 +++++++++++------- .../tem_controls/toolbox/config.py | 4 +- 4 files changed, 149 insertions(+), 66 deletions(-) create mode 100644 epoc-config.yaml create mode 100644 init_redis.py diff --git a/epoc-config.yaml b/epoc-config.yaml new file mode 100644 index 00000000..87048c4f --- /dev/null +++ b/epoc-config.yaml @@ -0,0 +1,42 @@ +# EPOC Redis Configuration + +# Experiment metadata +affiliation: "YourInstitution" +PI_name: "DefaultPI" +project_id: "ProjectID" +experiment_class: "External" +measurement_tag: "sample1" + +# Data directories +base_data_dir: "/data/jungfrau" +XDS_template: "/path/to/XDS.INP" +cal_dir: "/path/to/calibration" + +# Detector settings (e.g. of 1 Megapixel JUNGFRAU) +nrows: 1064 +ncols: 1030 +threshold: 5 + +# Viewer settings +viewer_interval: 20.0 +viewer_cmin: 0.0 +viewer_cmax: 12000.0 + +# Network endpoints +jfjoch_host: "http://jungfrau-server:5232" # Replace by the HTTP address of the Jungfraujoch web GUI +receiver_endpoint: "tcp://jungfrau-server:5000" # look at the ZMQ stream endpoint in the Jungfraujoch Web GUI +temserver: "temserver" # IP address of the TEM control machine (define in /etc/hosts) + +# Acquisition settings +rotation_speed_idx: 0 +file_id: 0 + +# Display overlays (optional) +overlays: + - {'type': 'circle', 'xy': [526, 253], 'radius': 188, 'ec': 'r', 'fill': False, 'lw': 2} + - {'type': 'circle', 'xy': [526, 253], 'radius': 5, 'ec': 'g', 'fill': False, 'lw': 2} + - {'type': 'rectangle', 'xy': [574, 52], 'width': 60, 'height': 31, 'angle': 27.5, 'ec': 'y', 'fill': False} + - {'type': 'rectangle', 'xy': [0, 0], 'width': 1000, 'height': 1, 'angle': 19.8, 'ec': 'r', 'fill': False} + +# Used affiliations list +usedAffiliations: ["YourInstitution", "External"] \ No newline at end of file diff --git a/init_redis.py b/init_redis.py new file mode 100644 index 00000000..5d0fe1bc --- /dev/null +++ b/init_redis.py @@ -0,0 +1,11 @@ +from pathlib import Path +from epoc import ConfigurationClient, auth_token, redis_host + +# Connect to Redis +cfg = ConfigurationClient(redis_host(), token=auth_token()) + +# Load configuration from YAML (flush_db=True clears any existing data) +cfg.from_yaml(Path('epoc-config.yaml'), flush_db=True) + +print("Redis database initialized successfully!") +print(cfg) \ No newline at end of file diff --git a/jungfrau_gui/main_ui.py b/jungfrau_gui/main_ui.py index fb940a66..2b26debd 100755 --- a/jungfrau_gui/main_ui.py +++ b/jungfrau_gui/main_ui.py @@ -18,6 +18,41 @@ import os import datetime +import textwrap + +ABOUT_TEXT = textwrap.dedent("""\ + ┌───────────────────────────────────────────────────────────────────┐ + │ Graphical User Interface for Electron Diffraction (JUNGFRAU GUI) │ + └───────────────────────────────────────────────────────────────────┘ + + Project: EPOC (Electrostatic Potential Of Compounds ─ DOI: 10.55776/I6546) + Years: 2024– + Version: {version} + + Repositories: + - https://github.com/epoc-ed/GUI + - https://github.com/epoc-ed + + Documentation: + - https://epoc-ed.github.io/manual/index.html + + License: + - MIT License + This project is distributed under the MIT License. + See the LICENSE file for the full text. + + Authors & Acknowledgments + Core contributors: + - Khalil Ferjaoui — PSI + - Kiyofumi Takaba — University of Vienna + - Erik Fröjd — PSI + - Tim Gruene — University of Vienna +""") + +def log_version_info(version: str) -> None: + logging.info("\n%s", ABOUT_TEXT.format(version=version)) + + def _cfg_get(cfg, name: str, default=None): """ Safely read a config attribute from ConfigurationClient. @@ -103,23 +138,32 @@ def format(self, record): def main(): os.environ["QT_LOGGING_RULES"] = "qt.core.qobject.connect=false" - + app = QApplication(sys.argv) app.setStyle("Fusion") - - # ---- Command-Line Interface FIRST (no Redis-backed defaults!) ---- - parser = argparse.ArgumentParser() - parser.add_argument("-s", "--stream", type=str, default="tcp://noether:5501", help="ZMQ stream endpoint",) - parser.add_argument("-d", "--dtype", type=str, default="float32", help="Data type (float32 or float64)",) - parser.add_argument("-p", "--playmode", action="store_true", help="Activates simplified GUI",) - parser.add_argument("-th", "--temhost", default=None, help="Host for tem-gui communication (defaults to cfg.temserver if set)",) - parser.add_argument("--nrow", type=int, default=None, help="Override detector rows (defaults to cfg.nrows)",) - parser.add_argument("--ncol", type=int, default=None, help="Override detector cols (defaults to cfg.ncols)",) - parser.add_argument("-l", "--log", default="INFO", help="Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",) - parser.add_argument("-f", "--logfile", action="store_true", help="File-output of logging",) - parser.add_argument("-e", "--dev", action="store_true", help="Activate developing function",) - parser.add_argument("-v", "--version", action="store_true", help="Detailed version description",) + # ---- CLI ---- + parser = argparse.ArgumentParser() + parser.add_argument("-s", "--stream", type=str, default=None, + help="ZMQ stream endpoint (overrides cfg.receiver_endpoint)") + parser.add_argument("-d", "--dtype", type=str, default="float32", + help="Data type (float32 or float64)") + parser.add_argument("-p", "--playmode", action="store_true", + help="Activates simplified GUI") + parser.add_argument("-th", "--temhost", default=None, + help="Host for tem-gui communication (overrides cfg.temserver)") + parser.add_argument("--nrow", type=int, default=None, + help="Override detector rows (overrides cfg.nrows)") + parser.add_argument("--ncol", type=int, default=None, + help="Override detector cols (overrides cfg.ncols)") + parser.add_argument("-l", "--log", default="INFO", + help="Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)") + parser.add_argument("-f", "--logfile", action="store_true", + help="File-output of logging") + parser.add_argument("-e", "--dev", action="store_true", + help="Activate developing function") + parser.add_argument("-v", "--version", action="store_true", + help="Detailed version description") args = parser.parse_args() # ---- Logger setup ---- @@ -127,7 +171,8 @@ def main(): log_level = getattr(logging, args.log.upper(), None) if log_level is None: raise ValueError( - f"Invalid log level: {args.log}. Choose from DEBUG, INFO, WARNING, ERROR, CRITICAL." + f"Invalid log level: {args.log}. " + "Choose from DEBUG, INFO, WARNING, ERROR, CRITICAL." ) logger.setLevel(log_level) @@ -138,50 +183,41 @@ def main(): if args.logfile: launch_script_path = Path(sys.argv[0]).resolve().parent - log_file_path = launch_script_path / f'JFGUI{time.strftime("_%Y%m%d-%H%M%S.log", time.localtime())}' - logging.info(f"Writing console loggings to: {log_file_path}") - + log_file_path = launch_script_path / ( + f'JFGUI{time.strftime("_%Y%m%d-%H%M%S.log", time.localtime())}' + ) + logging.info("Writing console loggings to: %s", log_file_path) file_handler = logging.FileHandler(log_file_path.as_posix()) file_handler.setLevel(log_level) - file_formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - %(message)s", - datefmt="%H:%M:%S", - ) - file_handler.setFormatter(file_formatter) + file_handler.setFormatter(logging.Formatter( + "%(asctime)s - %(levelname)s - %(message)s", datefmt="%H:%M:%S", + )) logger.addHandler(file_handler) - # ---- Resolve dtype ---- + # ---- dtype ---- dtype = _parse_dtype(args.dtype) - if dtype == np.dtype(np.float32): - cdtype = ctypes.c_float - else: - cdtype = ctypes.c_double - - # ---- Connect to config (Redis) AFTER parsing ---- - cfg = ConfigurationClient(redis_host(), token=auth_token()) - - # Resolve TEM host safely (missing key should NOT crash) - tem_host = args.temhost or _cfg_get(cfg, "temserver", default=None) - if tem_host is None: - # pick a sensible fallback; you can change this - tem_host = "localhost" - logging.warning("cfg.temserver not set; defaulting tem_host to 'localhost'.") - - # Resolve detector geometry safely - nrow = args.nrow if args.nrow is not None else _cfg_get(cfg, "nrows", default=None) - ncol = args.ncol if args.ncol is not None else _cfg_get(cfg, "ncols", default=None) - - if nrow is None or ncol is None: - raise RuntimeError( - "Detector geometry missing (nrows/ncols). " - "Set cfg.nrows/cfg.ncols in Redis or pass --nrow/--ncol." - ) + cdtype = ctypes.c_float if dtype == np.dtype(np.float32) else ctypes.c_double - # ---- Initialize globals explicitly (NO import-time Redis reads) ---- + # ---- Redis (mandatory) ---- + try: + cfg = ConfigurationClient(redis_host(), token=auth_token()) + except Exception as e: + logging.critical("Cannot connect to Redis configuration server: %s", e) + sys.exit(1) + + logging.info("Connected to Redis configuration.") + + # ---- Resolve parameters: CLI override Redis ---- + stream = args.stream or cfg.receiver_endpoint + tem_host = args.temhost or cfg.temserver + nrow = args.nrow if args.nrow is not None else cfg.nrows + ncol = args.ncol if args.ncol is not None else cfg.ncols + + # ---- Initialize globals ---- from jungfrau_gui import globals globals.init( - stream_=args.stream, + stream_=stream, dtype_=dtype, cdtype_=cdtype, tem_mode_=not args.playmode, @@ -191,27 +227,21 @@ def main(): ncol_=ncol, ) - logging.info(f"{get_gui_info()}") + info = get_gui_info() + logging.info("%s", info) if args.version: - logging.info(''' - **Detailed information of authors, years, project name, Github URL, license, contact address, etc.** - Graphical User Interface for Electron Diffraction with JUNGFRAU (2024-) - https://github.com/epoc-ed/GUI - EPOC Project (2024-) - https://github.com/epoc-ed - https://epoc-ed.github.io/manual/index.html - ''') + version = info.removeprefix("Jungfrau GUI ").strip() + log_version_info(version) + raise SystemExit(0) - Rcv = ZmqReceiver(endpoint=args.stream, dtype=args.dtype) + Rcv = ZmqReceiver(endpoint=stream, dtype=dtype) viewer = ApplicationWindow(Rcv, app) - app_palette = palette.get_palette("dark") - viewer.setPalette(app_palette) - + viewer.setPalette(palette.get_palette("dark")) viewer.show() - # QCoreApplication.processEvents() sys.exit(app.exec()) + if __name__ == "__main__": main() diff --git a/jungfrau_gui/ui_components/tem_controls/toolbox/config.py b/jungfrau_gui/ui_components/tem_controls/toolbox/config.py index 4bead7fc..7677bb75 100644 --- a/jungfrau_gui/ui_components/tem_controls/toolbox/config.py +++ b/jungfrau_gui/ui_components/tem_controls/toolbox/config.py @@ -16,7 +16,6 @@ f = files('jungfrau_gui').joinpath('ui_components/tem_controls/toolbox/jfgui2_config.json') parser = json.loads(f.read_text()) -cfg = ConfigurationClient(redis_host(), token=auth_token()) class lut: distance = parser['distances'] @@ -31,6 +30,7 @@ def __init__(self): self.array_data = np.array([list(d.values()) for d in self.distance]) self.raw_grid = np.delete(self.array_data, [2, 4, 5, 6], -1)[:-3,:] # remove date, unit, mag, and brightness at the moment self.data_grid = np.array([[int(nominal[:-2])*10, int(ht_value), float(calibrated)] for nominal, calibrated, ht_value in self.raw_grid]) + self.cfg = ConfigurationClient(redis_host(), token=auth_token()) def _lookup(self, dic, key, label_search, label_get, index=0): df_lut = pd.json_normalize(dic) @@ -89,7 +89,7 @@ def overlays_for_ht(self, ht_in_V): item_circle = QGraphicsEllipseItem(QRectF(x-r, y-r, 2*r, 2*r)) item_circle.setPen(pg.mkPen('r', width=2)) - r = cfg.overlays[0]['radius'] + r = self.cfg.overlays[0]['radius'] item_common = QGraphicsEllipseItem(QRectF(x-r, y-r, 2*r, 2*r)) item_common.setPen(pg.mkPen('r', width=2))