bec-widgets 1.25.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- .gitlab-ci.yml +11 -6
- CHANGELOG.md +650 -0
- PKG-INFO +3 -3
- bec_widgets/__init__.py +4 -0
- bec_widgets/applications/bw_launch.py +23 -0
- bec_widgets/applications/launch_window.py +430 -0
- bec_widgets/assets/app_icons/auto_update.png +0 -0
- bec_widgets/assets/app_icons/ui_loader_tile.png +0 -0
- bec_widgets/cli/__init__.py +0 -1
- bec_widgets/cli/client.py +1779 -2064
- bec_widgets/cli/client_utils.py +346 -174
- bec_widgets/cli/generate_cli.py +143 -37
- bec_widgets/cli/rpc/rpc_base.py +152 -21
- bec_widgets/cli/rpc/rpc_register.py +113 -6
- bec_widgets/cli/rpc/rpc_widget_handler.py +13 -11
- bec_widgets/cli/server.py +125 -239
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +97 -145
- bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +1 -1
- bec_widgets/utils/bec_connector.py +190 -21
- bec_widgets/utils/bec_designer.py +7 -0
- bec_widgets/utils/bec_dispatcher.py +71 -4
- bec_widgets/utils/bec_plugin_helper.py +89 -0
- bec_widgets/utils/bec_signal_proxy.py +1 -1
- bec_widgets/utils/bec_widget.py +26 -10
- bec_widgets/utils/colors.py +1 -1
- bec_widgets/{qt_utils → utils}/compact_popup.py +2 -0
- bec_widgets/utils/container_utils.py +37 -12
- bec_widgets/utils/crosshair.py +25 -8
- bec_widgets/utils/entry_validator.py +3 -1
- bec_widgets/{qt_utils → utils}/error_popups.py +18 -0
- bec_widgets/{qt_utils → utils}/expandable_frame.py +2 -2
- bec_widgets/utils/forms_from_types/forms.py +182 -0
- bec_widgets/{widgets/editors/scan_metadata/_metadata_widgets.py → utils/forms_from_types/items.py} +41 -30
- bec_widgets/utils/generate_designer_plugin.py +40 -36
- bec_widgets/utils/linear_region_selector.py +2 -0
- bec_widgets/utils/name_utils.py +16 -0
- bec_widgets/{qt_utils → utils}/palette_viewer.py +2 -2
- bec_widgets/utils/plot_indicator_items.py +2 -5
- bec_widgets/utils/plugin_utils.py +47 -1
- bec_widgets/{qt_utils → utils}/round_frame.py +14 -14
- bec_widgets/utils/rpc_server.py +277 -0
- bec_widgets/utils/serialization.py +44 -0
- bec_widgets/{qt_utils → utils}/settings_dialog.py +26 -1
- bec_widgets/{qt_utils → utils}/side_panel.py +17 -10
- bec_widgets/{qt_utils → utils}/toolbar.py +69 -25
- bec_widgets/utils/ui_loader.py +8 -8
- bec_widgets/utils/widget_io.py +166 -25
- bec_widgets/widgets/containers/auto_update/auto_updates.py +364 -0
- bec_widgets/widgets/containers/dock/dock.py +157 -49
- bec_widgets/widgets/containers/dock/dock_area.py +186 -138
- bec_widgets/widgets/containers/layout_manager/layout_manager.py +2 -1
- bec_widgets/widgets/containers/main_window/addons/web_links.py +15 -0
- bec_widgets/widgets/containers/main_window/main_window.py +189 -41
- bec_widgets/widgets/control/buttons/button_abort/button_abort.py +3 -4
- bec_widgets/widgets/control/buttons/button_reset/button_reset.py +3 -4
- bec_widgets/widgets/control/buttons/button_resume/button_resume.py +3 -3
- bec_widgets/widgets/control/buttons/stop_button/stop_button.py +18 -7
- bec_widgets/widgets/control/device_control/position_indicator/position_indicator.py +22 -3
- bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +37 -18
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +28 -4
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.ui +27 -4
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +5 -2
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +97 -31
- bec_widgets/widgets/control/device_control/positioner_box/positioner_control_line/positioner_control_line.ui +11 -4
- bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +2 -3
- bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +29 -4
- bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +1 -0
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +2 -2
- bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py +2 -2
- bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py +1 -2
- bec_widgets/widgets/control/device_input/signal_line_edit/signal_line_edit.py +1 -2
- bec_widgets/widgets/control/scan_control/scan_control.py +7 -5
- bec_widgets/widgets/control/scan_control/scan_group_box.py +28 -5
- bec_widgets/widgets/dap/dap_combo_box/dap_combo_box.py +1 -2
- bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog.py +3 -4
- bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui +14 -8
- bec_widgets/widgets/editors/console/console.py +1 -1
- bec_widgets/widgets/editors/{scan_metadata/additional_metadata_table.py → dict_backed_table.py} +29 -6
- bec_widgets/widgets/editors/scan_metadata/__init__.py +0 -7
- bec_widgets/widgets/editors/scan_metadata/_util.py +1 -1
- bec_widgets/widgets/{plots/motor_map/register_bec_motor_map_widget.py → editors/scan_metadata/register_scan_metadata.py} +2 -4
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.py +42 -136
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.pyproject +1 -0
- bec_widgets/widgets/{plots/multi_waveform/bec_multi_waveform_widget_plugin.py → editors/scan_metadata/scan_metadata_plugin.py} +9 -9
- bec_widgets/widgets/editors/text_box/text_box.py +2 -3
- bec_widgets/widgets/editors/website/website.py +2 -2
- bec_widgets/widgets/games/minesweeper.py +3 -2
- bec_widgets/widgets/plots/image/image.py +960 -0
- bec_widgets/widgets/plots/image/image.pyproject +1 -0
- bec_widgets/widgets/plots/image/image_item.py +279 -0
- bec_widgets/widgets/plots/{motor_map/bec_motor_map_widget_plugin.py → image/image_plugin.py} +11 -13
- bec_widgets/widgets/{containers/figure/plots → plots}/image/image_processor.py +31 -64
- bec_widgets/widgets/plots/image/{register_bec_image_widget.py → register_image.py} +2 -2
- bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +59 -0
- bec_widgets/widgets/plots/image/toolbar_bundles/processing.py +79 -0
- bec_widgets/widgets/plots/motor_map/motor_map.py +832 -0
- bec_widgets/widgets/plots/motor_map/motor_map.pyproject +1 -0
- bec_widgets/widgets/plots/motor_map/motor_map_plugin.py +54 -0
- bec_widgets/widgets/plots/{multi_waveform/register_bec_multi_waveform_widget.py → motor_map/register_motor_map.py} +2 -4
- bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.py +129 -0
- bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.ui +120 -0
- bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py +70 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform.py +508 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform.pyproject +1 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_plugin.py +54 -0
- bec_widgets/widgets/plots/multi_waveform/register_multi_waveform.py +15 -0
- bec_widgets/widgets/plots/multi_waveform/settings/control_panel.py +144 -0
- bec_widgets/widgets/plots/multi_waveform/settings/multi_waveform_controls.ui +164 -0
- bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py +65 -0
- bec_widgets/widgets/{plots_next_gen → plots}/plot_base.py +321 -40
- bec_widgets/widgets/plots/{waveform/register_bec_waveform_widget.py → scatter_waveform/register_scatter_waveform.py} +3 -3
- bec_widgets/widgets/plots/scatter_waveform/scatter_curve.py +197 -0
- bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py +553 -0
- bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.pyproject +1 -0
- bec_widgets/widgets/plots/{image/bec_image_widget_plugin.py → scatter_waveform/scatter_waveform_plugin.py} +9 -13
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py +138 -0
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_horizontal.ui +195 -0
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_vertical.ui +204 -0
- bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings.py +8 -8
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/mouse_interactions.py +4 -18
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/plot_export.py +14 -3
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/roi_bundle.py +6 -1
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/save_state.py +2 -2
- bec_widgets/widgets/{containers/figure/plots/waveform/waveform_curve.py → plots/waveform/curve.py} +119 -49
- bec_widgets/widgets/plots/waveform/register_waveform.py +15 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +125 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +576 -0
- bec_widgets/widgets/plots/waveform/utils/__init__.py +0 -0
- bec_widgets/widgets/plots/waveform/utils/roi_manager.py +84 -0
- bec_widgets/widgets/plots/waveform/waveform.py +1794 -0
- bec_widgets/widgets/plots/waveform/waveform.pyproject +1 -0
- bec_widgets/widgets/plots/waveform/{bec_waveform_widget_plugin.py → waveform_plugin.py} +9 -13
- bec_widgets/widgets/progress/bec_progressbar/bec_progressbar.py +1 -2
- bec_widgets/widgets/progress/ring_progress_bar/ring.py +11 -10
- bec_widgets/widgets/progress/ring_progress_bar/ring_progress_bar.py +24 -14
- bec_widgets/widgets/services/bec_queue/bec_queue.py +13 -11
- bec_widgets/widgets/services/bec_status_box/bec_status_box.py +3 -4
- bec_widgets/widgets/services/device_browser/device_browser.py +5 -2
- bec_widgets/widgets/services/device_browser/device_item/device_item.py +1 -1
- bec_widgets/widgets/utility/logpanel/logpanel.py +36 -17
- bec_widgets/widgets/utility/spinbox/decimal_spinbox.py +3 -3
- bec_widgets/widgets/utility/spinner/spinner.py +2 -2
- bec_widgets/widgets/utility/visual/color_button/color_button.py +1 -1
- bec_widgets/widgets/utility/visual/colormap_widget/colormap_widget.py +4 -6
- bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py +4 -8
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/METADATA +3 -3
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/RECORD +169 -154
- pyproject.toml +3 -3
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +0 -198
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +0 -615
- bec_widgets/applications/bec_app.py +0 -84
- bec_widgets/cli/auto_updates.py +0 -168
- bec_widgets/widgets/containers/figure/__init__.py +0 -1
- bec_widgets/widgets/containers/figure/figure.py +0 -796
- bec_widgets/widgets/containers/figure/plots/axis_settings.py +0 -91
- bec_widgets/widgets/containers/figure/plots/axis_settings.ui +0 -256
- bec_widgets/widgets/containers/figure/plots/image/image.py +0 -772
- bec_widgets/widgets/containers/figure/plots/image/image_item.py +0 -337
- bec_widgets/widgets/containers/figure/plots/motor_map/motor_map.py +0 -525
- bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py +0 -340
- bec_widgets/widgets/containers/figure/plots/plot_base.py +0 -505
- bec_widgets/widgets/containers/figure/plots/waveform/waveform.py +0 -1563
- bec_widgets/widgets/plots/image/bec_image_widget.pyproject +0 -1
- bec_widgets/widgets/plots/image/image_widget.py +0 -515
- bec_widgets/widgets/plots/motor_map/bec_motor_map_widget.pyproject +0 -1
- bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.py +0 -56
- bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.ui +0 -108
- bec_widgets/widgets/plots/motor_map/motor_map_widget.py +0 -234
- bec_widgets/widgets/plots/multi_waveform/bec_multi_waveform_widget.pyproject +0 -1
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_controls.ui +0 -99
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_widget.py +0 -536
- bec_widgets/widgets/plots/waveform/bec_waveform_widget.pyproject +0 -1
- bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py +0 -336
- bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui +0 -372
- bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +0 -25
- bec_widgets/widgets/plots/waveform/waveform_widget.py +0 -751
- /bec_widgets/{qt_utils → utils}/collapsible_panel_manager.py +0 -0
- /bec_widgets/{applications/alignment → utils/forms_from_types}/__init__.py +0 -0
- /bec_widgets/{qt_utils → utils}/redis_message_waiter.py +0 -0
- /bec_widgets/{applications/alignment/alignment_1d → widgets/containers/auto_update}/__init__.py +0 -0
- /bec_widgets/{qt_utils → widgets/containers/main_window/addons}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots → plots/image/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/image → plots/motor_map/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/motor_map → plots/motor_map/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/multi_waveform → plots/multi_waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/waveform → plots/multi_waveform/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{motor_map/motor_map_dialog → scatter_waveform}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups → scatter_waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups/curve_dialog → setting_menus}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_horizontal.ui +0 -0
- /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_vertical.ui +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups/dap_summary_dialog → toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen/setting_menus → plots/waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen/toolbar_bundles → plots/waveform/settings/curve_settings}/__init__.py +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,525 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from collections import defaultdict
|
4
|
-
from typing import Optional, Union
|
5
|
-
|
6
|
-
import numpy as np
|
7
|
-
import pyqtgraph as pg
|
8
|
-
from bec_lib.endpoints import MessageEndpoints
|
9
|
-
from bec_lib.logger import bec_logger
|
10
|
-
from pydantic import Field, ValidationError, field_validator
|
11
|
-
from pydantic_core import PydanticCustomError
|
12
|
-
from qtpy import QtCore, QtGui
|
13
|
-
from qtpy.QtCore import Signal as pyqtSignal
|
14
|
-
from qtpy.QtWidgets import QWidget
|
15
|
-
|
16
|
-
from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
17
|
-
from bec_widgets.utils import Colors, EntryValidator
|
18
|
-
from bec_widgets.widgets.containers.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
19
|
-
from bec_widgets.widgets.containers.figure.plots.waveform.waveform import Signal, SignalData
|
20
|
-
|
21
|
-
logger = bec_logger.logger
|
22
|
-
|
23
|
-
|
24
|
-
class MotorMapConfig(SubplotConfig):
|
25
|
-
signals: Optional[Signal] = Field(None, description="Signals of the motor map")
|
26
|
-
color: Optional[str | tuple] = Field(
|
27
|
-
(255, 255, 255, 255), description="The color of the last point of current position."
|
28
|
-
)
|
29
|
-
scatter_size: Optional[int] = Field(5, description="Size of the scatter points.")
|
30
|
-
max_points: Optional[int] = Field(5000, description="Maximum number of points to display.")
|
31
|
-
num_dim_points: Optional[int] = Field(
|
32
|
-
100,
|
33
|
-
description="Number of points to dim before the color remains same for older recorded position.",
|
34
|
-
)
|
35
|
-
precision: Optional[int] = Field(2, description="Decimal precision of the motor position.")
|
36
|
-
background_value: Optional[int] = Field(
|
37
|
-
25, description="Background value of the motor map. Has to be between 0 and 255."
|
38
|
-
)
|
39
|
-
|
40
|
-
model_config: dict = {"validate_assignment": True}
|
41
|
-
|
42
|
-
_validate_color = field_validator("color")(Colors.validate_color)
|
43
|
-
|
44
|
-
@field_validator("background_value")
|
45
|
-
def validate_background_value(cls, value):
|
46
|
-
if not 0 <= value <= 255:
|
47
|
-
raise PydanticCustomError(
|
48
|
-
"wrong_value", f"'{value}' hs to be between 0 and 255.", {"wrong_value": value}
|
49
|
-
)
|
50
|
-
return value
|
51
|
-
|
52
|
-
|
53
|
-
class BECMotorMap(BECPlotBase):
|
54
|
-
USER_ACCESS = [
|
55
|
-
"_rpc_id",
|
56
|
-
"_config_dict",
|
57
|
-
"change_motors",
|
58
|
-
"set_max_points",
|
59
|
-
"set_precision",
|
60
|
-
"set_num_dim_points",
|
61
|
-
"set_background_value",
|
62
|
-
"set_scatter_size",
|
63
|
-
"get_data",
|
64
|
-
"export",
|
65
|
-
"remove",
|
66
|
-
"reset_history",
|
67
|
-
]
|
68
|
-
|
69
|
-
# QT Signals
|
70
|
-
update_signal = pyqtSignal()
|
71
|
-
|
72
|
-
def __init__(
|
73
|
-
self,
|
74
|
-
parent: Optional[QWidget] = None,
|
75
|
-
parent_figure=None,
|
76
|
-
config: Optional[MotorMapConfig] = None,
|
77
|
-
client=None,
|
78
|
-
gui_id: Optional[str] = None,
|
79
|
-
):
|
80
|
-
if config is None:
|
81
|
-
config = MotorMapConfig(widget_class=self.__class__.__name__)
|
82
|
-
super().__init__(
|
83
|
-
parent=parent, parent_figure=parent_figure, config=config, client=client, gui_id=gui_id
|
84
|
-
)
|
85
|
-
|
86
|
-
# Get bec shortcuts dev, scans, queue, scan_storage, dap
|
87
|
-
self.get_bec_shortcuts()
|
88
|
-
self.entry_validator = EntryValidator(self.dev)
|
89
|
-
|
90
|
-
# connect update signal to update plot
|
91
|
-
self.proxy_update_plot = pg.SignalProxy(
|
92
|
-
self.update_signal, rateLimit=25, slot=self._update_plot
|
93
|
-
)
|
94
|
-
self.apply_config(self.config)
|
95
|
-
|
96
|
-
def apply_config(self, config: dict | MotorMapConfig):
|
97
|
-
"""
|
98
|
-
Apply the config to the motor map.
|
99
|
-
|
100
|
-
Args:
|
101
|
-
config(dict|MotorMapConfig): Config to be applied.
|
102
|
-
"""
|
103
|
-
if isinstance(config, dict):
|
104
|
-
try:
|
105
|
-
config = MotorMapConfig(**config)
|
106
|
-
except ValidationError as e:
|
107
|
-
logger.error(f"Error in applying config: {e}")
|
108
|
-
return
|
109
|
-
|
110
|
-
self.config = config
|
111
|
-
self.plot_item.clear()
|
112
|
-
|
113
|
-
self.motor_x = None
|
114
|
-
self.motor_y = None
|
115
|
-
self.database_buffer = {"x": [], "y": []}
|
116
|
-
self.plot_components = defaultdict(dict) # container for plot components
|
117
|
-
|
118
|
-
self.apply_axis_config()
|
119
|
-
|
120
|
-
if self.config.signals is not None:
|
121
|
-
self.change_motors(
|
122
|
-
motor_x=self.config.signals.x.name,
|
123
|
-
motor_y=self.config.signals.y.name,
|
124
|
-
motor_x_entry=self.config.signals.x.entry,
|
125
|
-
motor_y_entry=self.config.signals.y.entry,
|
126
|
-
)
|
127
|
-
|
128
|
-
@Slot(str, str, str, str, bool)
|
129
|
-
def change_motors(
|
130
|
-
self,
|
131
|
-
motor_x: str,
|
132
|
-
motor_y: str,
|
133
|
-
motor_x_entry: str = None,
|
134
|
-
motor_y_entry: str = None,
|
135
|
-
validate_bec: bool = True,
|
136
|
-
) -> None:
|
137
|
-
"""
|
138
|
-
Change the active motors for the plot.
|
139
|
-
|
140
|
-
Args:
|
141
|
-
motor_x(str): Motor name for the X axis.
|
142
|
-
motor_y(str): Motor name for the Y axis.
|
143
|
-
motor_x_entry(str): Motor entry for the X axis.
|
144
|
-
motor_y_entry(str): Motor entry for the Y axis.
|
145
|
-
validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
|
146
|
-
"""
|
147
|
-
self.plot_item.clear()
|
148
|
-
|
149
|
-
motor_x_entry, motor_y_entry = self._validate_signal_entries(
|
150
|
-
motor_x, motor_y, motor_x_entry, motor_y_entry, validate_bec
|
151
|
-
)
|
152
|
-
|
153
|
-
motor_x_limit = self._get_motor_limit(motor_x)
|
154
|
-
motor_y_limit = self._get_motor_limit(motor_y)
|
155
|
-
|
156
|
-
signal = Signal(
|
157
|
-
source="device_readback",
|
158
|
-
x=SignalData(name=motor_x, entry=motor_x_entry, limits=motor_x_limit),
|
159
|
-
y=SignalData(name=motor_y, entry=motor_y_entry, limits=motor_y_limit),
|
160
|
-
)
|
161
|
-
self.config.signals = signal
|
162
|
-
|
163
|
-
# reconnect the signals
|
164
|
-
self._connect_motor_to_slots()
|
165
|
-
|
166
|
-
self.database_buffer = {"x": [], "y": []}
|
167
|
-
|
168
|
-
# Redraw the motor map
|
169
|
-
self._make_motor_map()
|
170
|
-
|
171
|
-
def get_data(self) -> dict:
|
172
|
-
"""
|
173
|
-
Get the data of the motor map.
|
174
|
-
|
175
|
-
Returns:
|
176
|
-
dict: Data of the motor map.
|
177
|
-
"""
|
178
|
-
data = {"x": self.database_buffer["x"], "y": self.database_buffer["y"]}
|
179
|
-
return data
|
180
|
-
|
181
|
-
def reset_history(self):
|
182
|
-
"""
|
183
|
-
Reset the history of the motor map.
|
184
|
-
"""
|
185
|
-
self.database_buffer["x"] = [self.database_buffer["x"][-1]]
|
186
|
-
self.database_buffer["y"] = [self.database_buffer["y"][-1]]
|
187
|
-
self.update_signal.emit()
|
188
|
-
|
189
|
-
def set_color(self, color: str | tuple):
|
190
|
-
"""
|
191
|
-
Set color of the motor trace.
|
192
|
-
|
193
|
-
Args:
|
194
|
-
color(str|tuple): Color of the motor trace. Can be HEX(str) or RGBA(tuple).
|
195
|
-
"""
|
196
|
-
if isinstance(color, str):
|
197
|
-
color = Colors.validate_color(color)
|
198
|
-
color = Colors.hex_to_rgba(color, 255)
|
199
|
-
self.config.color = color
|
200
|
-
self.update_signal.emit()
|
201
|
-
|
202
|
-
def set_max_points(self, max_points: int) -> None:
|
203
|
-
"""
|
204
|
-
Set the maximum number of points to display.
|
205
|
-
|
206
|
-
Args:
|
207
|
-
max_points(int): Maximum number of points to display.
|
208
|
-
"""
|
209
|
-
self.config.max_points = max_points
|
210
|
-
self.update_signal.emit()
|
211
|
-
|
212
|
-
def set_precision(self, precision: int) -> None:
|
213
|
-
"""
|
214
|
-
Set the decimal precision of the motor position.
|
215
|
-
|
216
|
-
Args:
|
217
|
-
precision(int): Decimal precision of the motor position.
|
218
|
-
"""
|
219
|
-
self.config.precision = precision
|
220
|
-
self.update_signal.emit()
|
221
|
-
|
222
|
-
def set_num_dim_points(self, num_dim_points: int) -> None:
|
223
|
-
"""
|
224
|
-
Set the number of dim points for the motor map.
|
225
|
-
|
226
|
-
Args:
|
227
|
-
num_dim_points(int): Number of dim points.
|
228
|
-
"""
|
229
|
-
self.config.num_dim_points = num_dim_points
|
230
|
-
self.update_signal.emit()
|
231
|
-
|
232
|
-
def set_background_value(self, background_value: int) -> None:
|
233
|
-
"""
|
234
|
-
Set the background value of the motor map.
|
235
|
-
|
236
|
-
Args:
|
237
|
-
background_value(int): Background value of the motor map.
|
238
|
-
"""
|
239
|
-
self.config.background_value = background_value
|
240
|
-
self._swap_limit_map()
|
241
|
-
|
242
|
-
def set_scatter_size(self, scatter_size: int) -> None:
|
243
|
-
"""
|
244
|
-
Set the scatter size of the motor map plot.
|
245
|
-
|
246
|
-
Args:
|
247
|
-
scatter_size(int): Size of the scatter points.
|
248
|
-
"""
|
249
|
-
self.config.scatter_size = scatter_size
|
250
|
-
self.update_signal.emit()
|
251
|
-
|
252
|
-
def _disconnect_current_motors(self):
|
253
|
-
"""Disconnect the current motors from the slots."""
|
254
|
-
if self.motor_x is not None and self.motor_y is not None:
|
255
|
-
endpoints = [
|
256
|
-
MessageEndpoints.device_readback(self.motor_x),
|
257
|
-
MessageEndpoints.device_readback(self.motor_y),
|
258
|
-
]
|
259
|
-
self.bec_dispatcher.disconnect_slot(self.on_device_readback, endpoints)
|
260
|
-
|
261
|
-
def _connect_motor_to_slots(self):
|
262
|
-
"""Connect motors to slots."""
|
263
|
-
self._disconnect_current_motors()
|
264
|
-
|
265
|
-
self.motor_x = self.config.signals.x.name
|
266
|
-
self.motor_y = self.config.signals.y.name
|
267
|
-
|
268
|
-
endpoints = [
|
269
|
-
MessageEndpoints.device_readback(self.motor_x),
|
270
|
-
MessageEndpoints.device_readback(self.motor_y),
|
271
|
-
]
|
272
|
-
|
273
|
-
self.bec_dispatcher.connect_slot(self.on_device_readback, endpoints)
|
274
|
-
|
275
|
-
def _swap_limit_map(self):
|
276
|
-
"""Swap the limit map."""
|
277
|
-
self.plot_item.removeItem(self.plot_components["limit_map"])
|
278
|
-
if self.config.signals.x.limits is not None and self.config.signals.y.limits is not None:
|
279
|
-
self.plot_components["limit_map"] = self._make_limit_map(
|
280
|
-
self.config.signals.x.limits, self.config.signals.y.limits
|
281
|
-
)
|
282
|
-
self.plot_components["limit_map"].setZValue(-1)
|
283
|
-
self.plot_item.addItem(self.plot_components["limit_map"])
|
284
|
-
|
285
|
-
def _make_motor_map(self):
|
286
|
-
"""
|
287
|
-
Create the motor map plot.
|
288
|
-
"""
|
289
|
-
# Create limit map
|
290
|
-
motor_x_limit = self.config.signals.x.limits
|
291
|
-
motor_y_limit = self.config.signals.y.limits
|
292
|
-
if motor_x_limit is not None or motor_y_limit is not None:
|
293
|
-
self.plot_components["limit_map"] = self._make_limit_map(motor_x_limit, motor_y_limit)
|
294
|
-
self.plot_item.addItem(self.plot_components["limit_map"])
|
295
|
-
self.plot_components["limit_map"].setZValue(-1)
|
296
|
-
|
297
|
-
# Create scatter plot
|
298
|
-
scatter_size = self.config.scatter_size
|
299
|
-
self.plot_components["scatter"] = pg.ScatterPlotItem(
|
300
|
-
size=scatter_size, brush=pg.mkBrush(255, 255, 255, 255)
|
301
|
-
)
|
302
|
-
self.plot_item.addItem(self.plot_components["scatter"])
|
303
|
-
self.plot_components["scatter"].setZValue(0)
|
304
|
-
|
305
|
-
# Enable Grid
|
306
|
-
self.set_grid(True, True)
|
307
|
-
|
308
|
-
# Add the crosshair for initial motor coordinates
|
309
|
-
initial_position_x = self._get_motor_init_position(
|
310
|
-
self.motor_x, self.config.signals.x.entry, self.config.precision
|
311
|
-
)
|
312
|
-
initial_position_y = self._get_motor_init_position(
|
313
|
-
self.motor_y, self.config.signals.y.entry, self.config.precision
|
314
|
-
)
|
315
|
-
|
316
|
-
self.database_buffer["x"] = [initial_position_x]
|
317
|
-
self.database_buffer["y"] = [initial_position_y]
|
318
|
-
|
319
|
-
self.plot_components["scatter"].setData([initial_position_x], [initial_position_y])
|
320
|
-
self._add_coordinantes_crosshair(initial_position_x, initial_position_y)
|
321
|
-
|
322
|
-
# Set default labels for the plot
|
323
|
-
self.set(x_label=f"Motor X ({self.motor_x})", y_label=f"Motor Y ({self.motor_y})")
|
324
|
-
|
325
|
-
self.update_signal.emit()
|
326
|
-
|
327
|
-
def _add_coordinantes_crosshair(self, x: float, y: float) -> None:
|
328
|
-
"""
|
329
|
-
Add crosshair to the plot to highlight the current position.
|
330
|
-
|
331
|
-
Args:
|
332
|
-
x(float): X coordinate.
|
333
|
-
y(float): Y coordinate.
|
334
|
-
"""
|
335
|
-
|
336
|
-
# Crosshair to highlight the current position
|
337
|
-
highlight_H = pg.InfiniteLine(
|
338
|
-
angle=0, movable=False, pen=pg.mkPen(color="r", width=1, style=QtCore.Qt.DashLine)
|
339
|
-
)
|
340
|
-
highlight_V = pg.InfiniteLine(
|
341
|
-
angle=90, movable=False, pen=pg.mkPen(color="r", width=1, style=QtCore.Qt.DashLine)
|
342
|
-
)
|
343
|
-
|
344
|
-
# Add crosshair to the curve list for future referencing
|
345
|
-
self.plot_components["highlight_H"] = highlight_H
|
346
|
-
self.plot_components["highlight_V"] = highlight_V
|
347
|
-
|
348
|
-
# Add crosshair to the plot
|
349
|
-
self.plot_item.addItem(highlight_H)
|
350
|
-
self.plot_item.addItem(highlight_V)
|
351
|
-
|
352
|
-
highlight_V.setPos(x)
|
353
|
-
highlight_H.setPos(y)
|
354
|
-
|
355
|
-
def _make_limit_map(self, limits_x: list, limits_y: list) -> pg.ImageItem:
|
356
|
-
"""
|
357
|
-
Create a limit map for the motor map plot.
|
358
|
-
|
359
|
-
Args:
|
360
|
-
limits_x(list): Motor limits for the x axis.
|
361
|
-
limits_y(list): Motor limits for the y axis.
|
362
|
-
|
363
|
-
Returns:
|
364
|
-
pg.ImageItem: Limit map.
|
365
|
-
"""
|
366
|
-
limit_x_min, limit_x_max = limits_x
|
367
|
-
limit_y_min, limit_y_max = limits_y
|
368
|
-
|
369
|
-
map_width = int(limit_x_max - limit_x_min + 1)
|
370
|
-
map_height = int(limit_y_max - limit_y_min + 1)
|
371
|
-
|
372
|
-
# Create limits map
|
373
|
-
background_value = self.config.background_value
|
374
|
-
limit_map_data = np.full((map_width, map_height), background_value, dtype=np.float32)
|
375
|
-
limit_map = pg.ImageItem()
|
376
|
-
limit_map.setImage(limit_map_data)
|
377
|
-
|
378
|
-
# Translate and scale the image item to match the motor coordinates
|
379
|
-
tr = QtGui.QTransform()
|
380
|
-
tr.translate(limit_x_min, limit_y_min)
|
381
|
-
limit_map.setTransform(tr)
|
382
|
-
|
383
|
-
return limit_map
|
384
|
-
|
385
|
-
def _get_motor_init_position(self, name: str, entry: str, precision: int) -> float:
|
386
|
-
"""
|
387
|
-
Get the motor initial position from the config.
|
388
|
-
|
389
|
-
Args:
|
390
|
-
name(str): Motor name.
|
391
|
-
entry(str): Motor entry.
|
392
|
-
precision(int): Decimal precision of the motor position.
|
393
|
-
|
394
|
-
Returns:
|
395
|
-
float: Motor initial position.
|
396
|
-
"""
|
397
|
-
init_position = round(float(self.dev[name].read()[entry]["value"]), precision)
|
398
|
-
return init_position
|
399
|
-
|
400
|
-
def _validate_signal_entries(
|
401
|
-
self,
|
402
|
-
x_name: str,
|
403
|
-
y_name: str,
|
404
|
-
x_entry: str | None,
|
405
|
-
y_entry: str | None,
|
406
|
-
validate_bec: bool = True,
|
407
|
-
) -> tuple[str, str]:
|
408
|
-
"""
|
409
|
-
Validate the signal name and entry.
|
410
|
-
|
411
|
-
Args:
|
412
|
-
x_name(str): Name of the x signal.
|
413
|
-
y_name(str): Name of the y signal.
|
414
|
-
x_entry(str|None): Entry of the x signal.
|
415
|
-
y_entry(str|None): Entry of the y signal.
|
416
|
-
validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
|
417
|
-
|
418
|
-
Returns:
|
419
|
-
tuple[str,str]: Validated x and y entries.
|
420
|
-
"""
|
421
|
-
if validate_bec:
|
422
|
-
x_entry = self.entry_validator.validate_signal(x_name, x_entry)
|
423
|
-
y_entry = self.entry_validator.validate_signal(y_name, y_entry)
|
424
|
-
else:
|
425
|
-
x_entry = x_name if x_entry is None else x_entry
|
426
|
-
y_entry = y_name if y_entry is None else y_entry
|
427
|
-
return x_entry, y_entry
|
428
|
-
|
429
|
-
def _get_motor_limit(self, motor: str) -> Union[list | None]: # TODO check if works correctly
|
430
|
-
"""
|
431
|
-
Get the motor limit from the config.
|
432
|
-
|
433
|
-
Args:
|
434
|
-
motor(str): Motor name.
|
435
|
-
|
436
|
-
Returns:
|
437
|
-
float: Motor limit.
|
438
|
-
"""
|
439
|
-
try:
|
440
|
-
limits = self.dev[motor].limits
|
441
|
-
if limits == [0, 0]:
|
442
|
-
return None
|
443
|
-
return limits
|
444
|
-
except AttributeError: # TODO maybe not needed, if no limits it returns [0,0]
|
445
|
-
# If the motor doesn't have a 'limits' attribute, return a default value or raise a custom exception
|
446
|
-
logger.error(f"The device '{motor}' does not have defined limits.")
|
447
|
-
return None
|
448
|
-
|
449
|
-
@Slot()
|
450
|
-
def _update_plot(self, _=None):
|
451
|
-
"""Update the motor map plot."""
|
452
|
-
# If the number of points exceeds max_points, delete the oldest points
|
453
|
-
if len(self.database_buffer["x"]) > self.config.max_points:
|
454
|
-
self.database_buffer["x"] = self.database_buffer["x"][-self.config.max_points :]
|
455
|
-
self.database_buffer["y"] = self.database_buffer["y"][-self.config.max_points :]
|
456
|
-
|
457
|
-
x = self.database_buffer["x"]
|
458
|
-
y = self.database_buffer["y"]
|
459
|
-
|
460
|
-
# Setup gradient brush for history
|
461
|
-
brushes = [pg.mkBrush(50, 50, 50, 255)] * len(x)
|
462
|
-
|
463
|
-
# RGB color
|
464
|
-
r, g, b, a = self.config.color
|
465
|
-
|
466
|
-
# Calculate the decrement step based on self.num_dim_points
|
467
|
-
num_dim_points = self.config.num_dim_points
|
468
|
-
decrement_step = (255 - 50) / num_dim_points
|
469
|
-
|
470
|
-
for i in range(1, min(num_dim_points + 1, len(x) + 1)):
|
471
|
-
brightness = max(60, 255 - decrement_step * (i - 1))
|
472
|
-
dim_r = int(r * (brightness / 255))
|
473
|
-
dim_g = int(g * (brightness / 255))
|
474
|
-
dim_b = int(b * (brightness / 255))
|
475
|
-
brushes[-i] = pg.mkBrush(dim_r, dim_g, dim_b, a)
|
476
|
-
brushes[-1] = pg.mkBrush(r, g, b, a) # Newest point is always full brightness
|
477
|
-
scatter_size = self.config.scatter_size
|
478
|
-
|
479
|
-
# Update the scatter plot
|
480
|
-
self.plot_components["scatter"].setData(
|
481
|
-
x=x, y=y, brush=brushes, pen=None, size=scatter_size
|
482
|
-
)
|
483
|
-
|
484
|
-
# Get last know position for crosshair
|
485
|
-
current_x = x[-1]
|
486
|
-
current_y = y[-1]
|
487
|
-
|
488
|
-
# Update the crosshair
|
489
|
-
self.plot_components["highlight_V"].setPos(current_x)
|
490
|
-
self.plot_components["highlight_H"].setPos(current_y)
|
491
|
-
|
492
|
-
# TODO not update title but some label
|
493
|
-
# Update plot title
|
494
|
-
precision = self.config.precision
|
495
|
-
self.set_title(
|
496
|
-
f"Motor position: ({round(float(current_x),precision)}, {round(float(current_y),precision)})"
|
497
|
-
)
|
498
|
-
|
499
|
-
@Slot(dict, dict)
|
500
|
-
def on_device_readback(self, msg: dict, metadata: dict) -> None:
|
501
|
-
"""
|
502
|
-
Update the motor map plot with the new motor position.
|
503
|
-
|
504
|
-
Args:
|
505
|
-
msg(dict): Message from the device readback.
|
506
|
-
metadata(dict): Metadata of the message.
|
507
|
-
"""
|
508
|
-
if self.motor_x is None or self.motor_y is None:
|
509
|
-
return
|
510
|
-
|
511
|
-
if self.motor_x in msg["signals"]:
|
512
|
-
x = msg["signals"][self.motor_x]["value"]
|
513
|
-
self.database_buffer["x"].append(x)
|
514
|
-
self.database_buffer["y"].append(self.database_buffer["y"][-1])
|
515
|
-
|
516
|
-
elif self.motor_y in msg["signals"]:
|
517
|
-
y = msg["signals"][self.motor_y]["value"]
|
518
|
-
self.database_buffer["y"].append(y)
|
519
|
-
self.database_buffer["x"].append(self.database_buffer["x"][-1])
|
520
|
-
|
521
|
-
self.update_signal.emit()
|
522
|
-
|
523
|
-
def cleanup(self):
|
524
|
-
"""Cleanup the widget."""
|
525
|
-
self._disconnect_current_motors()
|