From 21b323f38ca15cfa37ccf7a0d56f30d3590e8e5d Mon Sep 17 00:00:00 2001 From: x01da Date: Wed, 5 Aug 2026 14:33:24 +0200 Subject: [PATCH] wip --- debye_bec/bec_widgets/widgets/client.py | 24 ++++ .../bec_widgets/widgets/designer_plugins.py | 10 +- .../widgets/scan_control_advanced/__init__,py | 0 .../register_scan_control_advanced.py | 15 +++ .../scan_control_advanced.py | 103 ++++++++++++++++++ .../scan_control_advanced.pyproject | 1 + .../scan_control_advanced_plugin.py | 57 ++++++++++ 7 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 debye_bec/bec_widgets/widgets/scan_control_advanced/__init__,py create mode 100644 debye_bec/bec_widgets/widgets/scan_control_advanced/register_scan_control_advanced.py create mode 100644 debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.py create mode 100644 debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.pyproject create mode 100644 debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced_plugin.py diff --git a/debye_bec/bec_widgets/widgets/client.py b/debye_bec/bec_widgets/widgets/client.py index c82894c..436a68e 100644 --- a/debye_bec/bec_widgets/widgets/client.py +++ b/debye_bec/bec_widgets/widgets/client.py @@ -15,6 +15,7 @@ logger = bec_logger.logger _Widgets = { "DataViewer": "DataViewer", "DigitalTwin": "DigitalTwin", + "ScanControlAdvanced": "ScanControlAdvanced", } @@ -64,3 +65,26 @@ class DigitalTwin(RPCBase): """ Detach the widget from its parent dock widget (if widget is in the dock), making it a floating widget. """ + + +class ScanControlAdvanced(RPCBase): + _IMPORT_MODULE = "debye_bec.bec_widgets.widgets.scan_control_advanced.scan_control_advanced" + + @rpc_call + def attach(self): + """ + None + """ + + @rpc_call + def detach(self): + """ + Detach the widget from its parent dock widget (if widget is in the dock), making it a floating widget. + """ + + @rpc_timeout(None) + @rpc_call + def screenshot(self, file_name: "str | None" = None): + """ + Take a screenshot of the dock area and save it to a file. + """ diff --git a/debye_bec/bec_widgets/widgets/designer_plugins.py b/debye_bec/bec_widgets/widgets/designer_plugins.py index a571172..43dffd4 100644 --- a/debye_bec/bec_widgets/widgets/designer_plugins.py +++ b/debye_bec/bec_widgets/widgets/designer_plugins.py @@ -7,6 +7,14 @@ from __future__ import annotations designer_plugins = { "DataViewer": ("debye_bec.bec_widgets.widgets.data_viewer.data_viewer", "DataViewer"), "DigitalTwin": ("debye_bec.bec_widgets.widgets.digital_twin.digital_twin", "DigitalTwin"), + "ScanControlAdvanced": ( + "debye_bec.bec_widgets.widgets.scan_control_advanced.scan_control_advanced", + "ScanControlAdvanced", + ), } -widget_icons = {"DataViewer": "find_in_page", "DigitalTwin": "lightbulb"} +widget_icons = { + "DataViewer": "find_in_page", + "DigitalTwin": "lightbulb", + "ScanControlAdvanced": "tune", +} diff --git a/debye_bec/bec_widgets/widgets/scan_control_advanced/__init__,py b/debye_bec/bec_widgets/widgets/scan_control_advanced/__init__,py new file mode 100644 index 0000000..e69de29 diff --git a/debye_bec/bec_widgets/widgets/scan_control_advanced/register_scan_control_advanced.py b/debye_bec/bec_widgets/widgets/scan_control_advanced/register_scan_control_advanced.py new file mode 100644 index 0000000..70bf187 --- /dev/null +++ b/debye_bec/bec_widgets/widgets/scan_control_advanced/register_scan_control_advanced.py @@ -0,0 +1,15 @@ +def main(): # pragma: no cover + from qtpy import PYSIDE6 + + if not PYSIDE6: + print("PYSIDE6 is not available in the environment. Cannot patch designer.") + return + from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection + + from .scan_control_advanced_plugin import ScanControlAdvancedPlugin + + QPyDesignerCustomWidgetCollection.addCustomWidget(ScanControlAdvancedPlugin()) + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.py b/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.py new file mode 100644 index 0000000..b3bbabe --- /dev/null +++ b/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.py @@ -0,0 +1,103 @@ +import sys + +import numpy as np +import pyqtgraph as pg +from bec_lib import bec_logger +from bec_widgets.utils.bec_dispatcher import BECDispatcher +from bec_widgets.utils.colors import apply_theme +from bec_widgets.widgets.control.scan_control.scan_control import ScanControl +from qtpy.QtCore import Qt, Signal + +# pylint: disable=E0611 +from qtpy.QtWidgets import QApplication, QDoubleSpinBox, QPushButton, QVBoxLayout, QWidget + +logger = bec_logger.logger + +SHOW_MOTION_PROFILE = [ + "xas_simple_scan", + "xas_simple_scan_with_xrd", + "xas_advanced_scan", + "xas_advanced_scan_with_xrd", +] + + +class ScanControlAdvanced(ScanControl): + + def __init__(self, *args, **kwargs): + self.motion_profile_widget = MotionProfileWidget(self) + super().__init__(*args, **kwargs) + self.show_motion_profile(self._selected_scan) + + def _add_metadata_form(self): + self.layout.addWidget(self.motion_profile_widget) + super()._add_metadata_form() + self.connect_scan_parameter_signals() + + def on_scan_selection_changed(self, scan_name: str): + super().on_scan_selection_changed(scan_name) + self.show_motion_profile(scan_name) + if scan_name in SHOW_MOTION_PROFILE: + self.connect_scan_parameter_signals() + self.scan_parameter_changed() + + def show_motion_profile(self, scan_name): + if scan_name in SHOW_MOTION_PROFILE: + self.motion_profile_widget.setVisible(True) + else: + self.motion_profile_widget.setVisible(False) + + def connect_scan_parameter_signals(self): + for box in self.kwarg_boxes: + for widget in box.findChildren(QWidget): + try: + if hasattr(widget, "valueChanged"): + widget.valueChanged.connect( + self.scan_parameter_changed, Qt.ConnectionType.UniqueConnection + ) + elif hasattr(widget, "textChanged"): + widget.textChanged.connect( + self.scan_parameter_changed, Qt.ConnectionType.UniqueConnection + ) + except TypeError: + # Raised if a connection would not be unique anymore, i.e. if + # connect_scan_parameter_signals is called more than once with + # the same kwarg_boxes + pass + + def scan_parameter_changed(self, *_): + params = self.get_scan_parameters()[1] + logger.info(f"Scan parameters: {params}") + self.motion_profile_widget.update_plot(params) + + +PLOT_RESOLUTION = 100 + + +class MotionProfileWidget(QWidget): + + parameters_changed = Signal(dict) + + def __init__(self, parent): + super().__init__() + self.parent = parent + layout = QVBoxLayout(self) + self.plot = pg.PlotWidget() + self.plot.enableAutoRange() + layout.addWidget(self.plot) + + def update_plot(self, scan_parameters): + x = np.linspace(scan_parameters["start"], scan_parameters["stop"], PLOT_RESOLUTION) + x_sin = np.linspace(0, np.pi, PLOT_RESOLUTION) + y = 1 * np.sin(x_sin) + + self.plot.clear() + self.plot.plot(x, y) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + apply_theme("light") + dispatcher = BECDispatcher(gui_id="scan_control_advanced") + win = ScanControlAdvanced() + win.show() + sys.exit(app.exec_()) diff --git a/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.pyproject b/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.pyproject new file mode 100644 index 0000000..480639d --- /dev/null +++ b/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced.pyproject @@ -0,0 +1 @@ +{'files': ['scan_control_advanced.py']} \ No newline at end of file diff --git a/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced_plugin.py b/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced_plugin.py new file mode 100644 index 0000000..78c1b32 --- /dev/null +++ b/debye_bec/bec_widgets/widgets/scan_control_advanced/scan_control_advanced_plugin.py @@ -0,0 +1,57 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from bec_widgets.utils.bec_designer import designer_material_icon +from qtpy.QtDesigner import QDesignerCustomWidgetInterface +from qtpy.QtWidgets import QWidget + +from .scan_control_advanced import ScanControlAdvanced + +DOM_XML = """ + + + + +""" + + +class ScanControlAdvancedPlugin(QDesignerCustomWidgetInterface): # pragma: no cover + def __init__(self): + super().__init__() + self._form_editor = None + + def createWidget(self, parent): + if parent is None: + return QWidget() + t = ScanControlAdvanced(parent) + return t + + def domXml(self): + return DOM_XML + + def group(self): + return "" + + def icon(self): + return designer_material_icon(ScanControlAdvanced.ICON_NAME) + + def includeFile(self): + return "scan_control_advanced" + + def initialize(self, form_editor): + self._form_editor = form_editor + + def isContainer(self): + return False + + def isInitialized(self): + return self._form_editor is not None + + def name(self): + return "ScanControlAdvanced" + + def toolTip(self): + return "ScanControlAdvanced" + + def whatsThis(self): + return self.toolTip()