bec-widgets 1.25.1__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.
Files changed (196) hide show
  1. .gitlab-ci.yml +3 -5
  2. CHANGELOG.md +631 -0
  3. PKG-INFO +3 -3
  4. bec_widgets/__init__.py +4 -0
  5. bec_widgets/applications/bw_launch.py +23 -0
  6. bec_widgets/applications/launch_window.py +430 -0
  7. bec_widgets/assets/app_icons/auto_update.png +0 -0
  8. bec_widgets/assets/app_icons/ui_loader_tile.png +0 -0
  9. bec_widgets/cli/__init__.py +0 -1
  10. bec_widgets/cli/client.py +1779 -2064
  11. bec_widgets/cli/client_utils.py +346 -174
  12. bec_widgets/cli/generate_cli.py +143 -37
  13. bec_widgets/cli/rpc/rpc_base.py +152 -21
  14. bec_widgets/cli/rpc/rpc_register.py +113 -6
  15. bec_widgets/cli/rpc/rpc_widget_handler.py +13 -11
  16. bec_widgets/cli/server.py +125 -239
  17. bec_widgets/examples/jupyter_console/jupyter_console_window.py +97 -145
  18. bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +1 -1
  19. bec_widgets/utils/bec_connector.py +190 -21
  20. bec_widgets/utils/bec_designer.py +7 -0
  21. bec_widgets/utils/bec_dispatcher.py +71 -4
  22. bec_widgets/utils/bec_plugin_helper.py +89 -0
  23. bec_widgets/utils/bec_signal_proxy.py +1 -1
  24. bec_widgets/utils/bec_widget.py +26 -10
  25. bec_widgets/utils/colors.py +1 -1
  26. bec_widgets/{qt_utils → utils}/compact_popup.py +2 -0
  27. bec_widgets/utils/container_utils.py +37 -12
  28. bec_widgets/utils/crosshair.py +25 -8
  29. bec_widgets/utils/entry_validator.py +3 -1
  30. bec_widgets/{qt_utils → utils}/error_popups.py +18 -0
  31. bec_widgets/{qt_utils → utils}/expandable_frame.py +2 -2
  32. bec_widgets/utils/forms_from_types/forms.py +182 -0
  33. bec_widgets/{widgets/editors/scan_metadata/_metadata_widgets.py → utils/forms_from_types/items.py} +41 -30
  34. bec_widgets/utils/generate_designer_plugin.py +40 -36
  35. bec_widgets/utils/linear_region_selector.py +2 -0
  36. bec_widgets/utils/name_utils.py +16 -0
  37. bec_widgets/{qt_utils → utils}/palette_viewer.py +2 -2
  38. bec_widgets/utils/plot_indicator_items.py +2 -5
  39. bec_widgets/utils/plugin_utils.py +47 -1
  40. bec_widgets/{qt_utils → utils}/round_frame.py +14 -14
  41. bec_widgets/utils/rpc_server.py +277 -0
  42. bec_widgets/utils/serialization.py +44 -0
  43. bec_widgets/{qt_utils → utils}/settings_dialog.py +26 -1
  44. bec_widgets/{qt_utils → utils}/side_panel.py +17 -10
  45. bec_widgets/{qt_utils → utils}/toolbar.py +69 -25
  46. bec_widgets/utils/ui_loader.py +8 -8
  47. bec_widgets/utils/widget_io.py +166 -25
  48. bec_widgets/widgets/containers/auto_update/auto_updates.py +364 -0
  49. bec_widgets/widgets/containers/dock/dock.py +157 -49
  50. bec_widgets/widgets/containers/dock/dock_area.py +186 -138
  51. bec_widgets/widgets/containers/layout_manager/layout_manager.py +2 -1
  52. bec_widgets/widgets/containers/main_window/addons/web_links.py +15 -0
  53. bec_widgets/widgets/containers/main_window/main_window.py +189 -41
  54. bec_widgets/widgets/control/buttons/button_abort/button_abort.py +3 -4
  55. bec_widgets/widgets/control/buttons/button_reset/button_reset.py +3 -4
  56. bec_widgets/widgets/control/buttons/button_resume/button_resume.py +3 -3
  57. bec_widgets/widgets/control/buttons/stop_button/stop_button.py +18 -7
  58. bec_widgets/widgets/control/device_control/position_indicator/position_indicator.py +22 -3
  59. bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +31 -13
  60. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +3 -1
  61. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.ui +27 -4
  62. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +5 -2
  63. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +97 -31
  64. bec_widgets/widgets/control/device_control/positioner_box/positioner_control_line/positioner_control_line.ui +11 -4
  65. bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +2 -3
  66. bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +29 -4
  67. bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +1 -0
  68. bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +2 -2
  69. bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py +2 -2
  70. bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py +1 -2
  71. bec_widgets/widgets/control/device_input/signal_line_edit/signal_line_edit.py +1 -2
  72. bec_widgets/widgets/control/scan_control/scan_control.py +7 -5
  73. bec_widgets/widgets/control/scan_control/scan_group_box.py +28 -5
  74. bec_widgets/widgets/dap/dap_combo_box/dap_combo_box.py +1 -2
  75. bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog.py +3 -4
  76. bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui +14 -8
  77. bec_widgets/widgets/editors/console/console.py +1 -1
  78. bec_widgets/widgets/editors/{scan_metadata/additional_metadata_table.py → dict_backed_table.py} +29 -6
  79. bec_widgets/widgets/editors/scan_metadata/__init__.py +0 -7
  80. bec_widgets/widgets/editors/scan_metadata/_util.py +1 -1
  81. bec_widgets/widgets/{plots/motor_map/register_bec_motor_map_widget.py → editors/scan_metadata/register_scan_metadata.py} +2 -4
  82. bec_widgets/widgets/editors/scan_metadata/scan_metadata.py +42 -136
  83. bec_widgets/widgets/editors/scan_metadata/scan_metadata.pyproject +1 -0
  84. bec_widgets/widgets/{plots/multi_waveform/bec_multi_waveform_widget_plugin.py → editors/scan_metadata/scan_metadata_plugin.py} +9 -9
  85. bec_widgets/widgets/editors/text_box/text_box.py +2 -3
  86. bec_widgets/widgets/editors/website/website.py +2 -2
  87. bec_widgets/widgets/games/minesweeper.py +3 -2
  88. bec_widgets/widgets/plots/image/image.py +960 -0
  89. bec_widgets/widgets/plots/image/image.pyproject +1 -0
  90. bec_widgets/widgets/plots/image/image_item.py +279 -0
  91. bec_widgets/widgets/plots/{motor_map/bec_motor_map_widget_plugin.py → image/image_plugin.py} +11 -13
  92. bec_widgets/widgets/{containers/figure/plots → plots}/image/image_processor.py +31 -64
  93. bec_widgets/widgets/plots/image/{register_bec_image_widget.py → register_image.py} +2 -2
  94. bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +59 -0
  95. bec_widgets/widgets/plots/image/toolbar_bundles/processing.py +79 -0
  96. bec_widgets/widgets/plots/motor_map/motor_map.py +832 -0
  97. bec_widgets/widgets/plots/motor_map/motor_map.pyproject +1 -0
  98. bec_widgets/widgets/plots/motor_map/motor_map_plugin.py +54 -0
  99. bec_widgets/widgets/plots/{multi_waveform/register_bec_multi_waveform_widget.py → motor_map/register_motor_map.py} +2 -4
  100. bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.py +129 -0
  101. bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.ui +120 -0
  102. bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py +70 -0
  103. bec_widgets/widgets/plots/multi_waveform/multi_waveform.py +508 -0
  104. bec_widgets/widgets/plots/multi_waveform/multi_waveform.pyproject +1 -0
  105. bec_widgets/widgets/plots/multi_waveform/multi_waveform_plugin.py +54 -0
  106. bec_widgets/widgets/plots/multi_waveform/register_multi_waveform.py +15 -0
  107. bec_widgets/widgets/plots/multi_waveform/settings/control_panel.py +144 -0
  108. bec_widgets/widgets/plots/multi_waveform/settings/multi_waveform_controls.ui +164 -0
  109. bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py +65 -0
  110. bec_widgets/widgets/{plots_next_gen → plots}/plot_base.py +321 -40
  111. bec_widgets/widgets/plots/{waveform/register_bec_waveform_widget.py → scatter_waveform/register_scatter_waveform.py} +3 -3
  112. bec_widgets/widgets/plots/scatter_waveform/scatter_curve.py +197 -0
  113. bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py +553 -0
  114. bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.pyproject +1 -0
  115. bec_widgets/widgets/plots/{image/bec_image_widget_plugin.py → scatter_waveform/scatter_waveform_plugin.py} +9 -13
  116. bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py +138 -0
  117. bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_horizontal.ui +195 -0
  118. bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_vertical.ui +204 -0
  119. bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings.py +8 -8
  120. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/mouse_interactions.py +4 -18
  121. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/plot_export.py +14 -3
  122. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/roi_bundle.py +6 -1
  123. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/save_state.py +2 -2
  124. bec_widgets/widgets/{containers/figure/plots/waveform/waveform_curve.py → plots/waveform/curve.py} +119 -49
  125. bec_widgets/widgets/plots/waveform/register_waveform.py +15 -0
  126. bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +125 -0
  127. bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +576 -0
  128. bec_widgets/widgets/plots/waveform/utils/__init__.py +0 -0
  129. bec_widgets/widgets/plots/waveform/utils/roi_manager.py +84 -0
  130. bec_widgets/widgets/plots/waveform/waveform.py +1794 -0
  131. bec_widgets/widgets/plots/waveform/waveform.pyproject +1 -0
  132. bec_widgets/widgets/plots/waveform/{bec_waveform_widget_plugin.py → waveform_plugin.py} +9 -13
  133. bec_widgets/widgets/progress/bec_progressbar/bec_progressbar.py +1 -2
  134. bec_widgets/widgets/progress/ring_progress_bar/ring.py +11 -10
  135. bec_widgets/widgets/progress/ring_progress_bar/ring_progress_bar.py +24 -14
  136. bec_widgets/widgets/services/bec_queue/bec_queue.py +13 -11
  137. bec_widgets/widgets/services/bec_status_box/bec_status_box.py +3 -4
  138. bec_widgets/widgets/services/device_browser/device_browser.py +5 -2
  139. bec_widgets/widgets/services/device_browser/device_item/device_item.py +1 -1
  140. bec_widgets/widgets/utility/logpanel/logpanel.py +36 -17
  141. bec_widgets/widgets/utility/spinbox/decimal_spinbox.py +3 -3
  142. bec_widgets/widgets/utility/visual/color_button/color_button.py +1 -1
  143. bec_widgets/widgets/utility/visual/colormap_widget/colormap_widget.py +4 -6
  144. bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py +4 -8
  145. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/METADATA +3 -3
  146. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/RECORD +168 -153
  147. pyproject.toml +3 -3
  148. bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +0 -198
  149. bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +0 -615
  150. bec_widgets/applications/bec_app.py +0 -84
  151. bec_widgets/cli/auto_updates.py +0 -168
  152. bec_widgets/widgets/containers/figure/__init__.py +0 -1
  153. bec_widgets/widgets/containers/figure/figure.py +0 -796
  154. bec_widgets/widgets/containers/figure/plots/axis_settings.py +0 -91
  155. bec_widgets/widgets/containers/figure/plots/axis_settings.ui +0 -256
  156. bec_widgets/widgets/containers/figure/plots/image/image.py +0 -772
  157. bec_widgets/widgets/containers/figure/plots/image/image_item.py +0 -337
  158. bec_widgets/widgets/containers/figure/plots/motor_map/motor_map.py +0 -525
  159. bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py +0 -340
  160. bec_widgets/widgets/containers/figure/plots/plot_base.py +0 -505
  161. bec_widgets/widgets/containers/figure/plots/waveform/waveform.py +0 -1563
  162. bec_widgets/widgets/plots/image/bec_image_widget.pyproject +0 -1
  163. bec_widgets/widgets/plots/image/image_widget.py +0 -515
  164. bec_widgets/widgets/plots/motor_map/bec_motor_map_widget.pyproject +0 -1
  165. bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.py +0 -56
  166. bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.ui +0 -108
  167. bec_widgets/widgets/plots/motor_map/motor_map_widget.py +0 -234
  168. bec_widgets/widgets/plots/multi_waveform/bec_multi_waveform_widget.pyproject +0 -1
  169. bec_widgets/widgets/plots/multi_waveform/multi_waveform_controls.ui +0 -99
  170. bec_widgets/widgets/plots/multi_waveform/multi_waveform_widget.py +0 -536
  171. bec_widgets/widgets/plots/waveform/bec_waveform_widget.pyproject +0 -1
  172. bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py +0 -336
  173. bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui +0 -372
  174. bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +0 -25
  175. bec_widgets/widgets/plots/waveform/waveform_widget.py +0 -751
  176. /bec_widgets/{qt_utils → utils}/collapsible_panel_manager.py +0 -0
  177. /bec_widgets/{applications/alignment → utils/forms_from_types}/__init__.py +0 -0
  178. /bec_widgets/{qt_utils → utils}/redis_message_waiter.py +0 -0
  179. /bec_widgets/{applications/alignment/alignment_1d → widgets/containers/auto_update}/__init__.py +0 -0
  180. /bec_widgets/{qt_utils → widgets/containers/main_window/addons}/__init__.py +0 -0
  181. /bec_widgets/widgets/{containers/figure/plots → plots/image/toolbar_bundles}/__init__.py +0 -0
  182. /bec_widgets/widgets/{containers/figure/plots/image → plots/motor_map/settings}/__init__.py +0 -0
  183. /bec_widgets/widgets/{containers/figure/plots/motor_map → plots/motor_map/toolbar_bundles}/__init__.py +0 -0
  184. /bec_widgets/widgets/{containers/figure/plots/multi_waveform → plots/multi_waveform/settings}/__init__.py +0 -0
  185. /bec_widgets/widgets/{containers/figure/plots/waveform → plots/multi_waveform/toolbar_bundles}/__init__.py +0 -0
  186. /bec_widgets/widgets/plots/{motor_map/motor_map_dialog → scatter_waveform}/__init__.py +0 -0
  187. /bec_widgets/widgets/plots/{waveform/waveform_popups → scatter_waveform/settings}/__init__.py +0 -0
  188. /bec_widgets/widgets/plots/{waveform/waveform_popups/curve_dialog → setting_menus}/__init__.py +0 -0
  189. /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_horizontal.ui +0 -0
  190. /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_vertical.ui +0 -0
  191. /bec_widgets/widgets/plots/{waveform/waveform_popups/dap_summary_dialog → toolbar_bundles}/__init__.py +0 -0
  192. /bec_widgets/widgets/{plots_next_gen/setting_menus → plots/waveform/settings}/__init__.py +0 -0
  193. /bec_widgets/widgets/{plots_next_gen/toolbar_bundles → plots/waveform/settings/curve_settings}/__init__.py +0 -0
  194. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/WHEEL +0 -0
  195. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/entry_points.txt +0 -0
  196. {bec_widgets-1.25.1.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()