bec-widgets 0.53.3__py3-none-any.whl → 0.55.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 (72) hide show
  1. CHANGELOG.md +24 -26
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +265 -13
  4. bec_widgets/cli/client_utils.py +0 -3
  5. bec_widgets/cli/generate_cli.py +10 -5
  6. bec_widgets/cli/rpc_wigdet_handler.py +2 -1
  7. bec_widgets/cli/server.py +5 -7
  8. bec_widgets/examples/jupyter_console/jupyter_console_window.py +11 -5
  9. bec_widgets/examples/motor_movement/motor_control_compilations.py +17 -16
  10. bec_widgets/widgets/__init__.py +1 -10
  11. bec_widgets/widgets/figure/figure.py +40 -23
  12. bec_widgets/widgets/figure/plots/__init__.py +0 -0
  13. bec_widgets/widgets/figure/plots/image/__init__.py +0 -0
  14. bec_widgets/widgets/{plots → figure/plots/image}/image.py +6 -416
  15. bec_widgets/widgets/figure/plots/image/image_item.py +277 -0
  16. bec_widgets/widgets/figure/plots/image/image_processor.py +152 -0
  17. bec_widgets/widgets/figure/plots/motor_map/__init__.py +0 -0
  18. bec_widgets/widgets/{plots → figure/plots/motor_map}/motor_map.py +2 -2
  19. bec_widgets/widgets/figure/plots/waveform/__init__.py +0 -0
  20. bec_widgets/widgets/{plots → figure/plots/waveform}/waveform.py +9 -222
  21. bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +227 -0
  22. bec_widgets/widgets/motor_control/__init__.py +0 -7
  23. bec_widgets/widgets/motor_control/motor_control.py +2 -948
  24. bec_widgets/widgets/motor_control/motor_table/__init__.py +0 -0
  25. bec_widgets/widgets/motor_control/motor_table/motor_table.py +483 -0
  26. bec_widgets/widgets/motor_control/movement_absolute/__init__.py +0 -0
  27. bec_widgets/widgets/motor_control/movement_absolute/movement_absolute.py +157 -0
  28. bec_widgets/widgets/motor_control/movement_relative/__init__.py +0 -0
  29. bec_widgets/widgets/motor_control/movement_relative/movement_relative.py +227 -0
  30. bec_widgets/widgets/motor_control/selection/__init__.py +0 -0
  31. bec_widgets/widgets/motor_control/selection/selection.py +110 -0
  32. bec_widgets/widgets/spiral_progress_bar/__init__.py +1 -0
  33. bec_widgets/widgets/spiral_progress_bar/ring.py +184 -0
  34. bec_widgets/widgets/spiral_progress_bar/spiral_progress_bar.py +594 -0
  35. {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/METADATA +1 -1
  36. {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/RECORD +56 -53
  37. docs/requirements.txt +1 -0
  38. pyproject.toml +1 -1
  39. tests/end-2-end/test_bec_dock_rpc_e2e.py +82 -1
  40. tests/end-2-end/test_bec_figure_rpc_e2e.py +4 -4
  41. tests/end-2-end/test_rpc_register_e2e.py +1 -1
  42. tests/unit_tests/test_bec_dock.py +1 -1
  43. tests/unit_tests/test_bec_figure.py +6 -4
  44. tests/unit_tests/test_bec_motor_map.py +2 -3
  45. tests/unit_tests/test_motor_control.py +6 -5
  46. tests/unit_tests/test_spiral_progress_bar.py +338 -0
  47. tests/unit_tests/test_waveform1d.py +13 -1
  48. bec_widgets/validation/__init__.py +0 -2
  49. bec_widgets/validation/monitor_config_validator.py +0 -258
  50. bec_widgets/widgets/monitor/__init__.py +0 -1
  51. bec_widgets/widgets/monitor/config_dialog.py +0 -574
  52. bec_widgets/widgets/monitor/config_dialog.ui +0 -210
  53. bec_widgets/widgets/monitor/example_configs/config_device.yaml +0 -60
  54. bec_widgets/widgets/monitor/example_configs/config_scans.yaml +0 -92
  55. bec_widgets/widgets/monitor/monitor.py +0 -845
  56. bec_widgets/widgets/monitor/tab_template.ui +0 -180
  57. bec_widgets/widgets/motor_map/__init__.py +0 -1
  58. bec_widgets/widgets/motor_map/motor_map.py +0 -594
  59. bec_widgets/widgets/plots/__init__.py +0 -4
  60. tests/unit_tests/test_bec_monitor.py +0 -220
  61. tests/unit_tests/test_config_dialog.py +0 -178
  62. tests/unit_tests/test_motor_map.py +0 -171
  63. tests/unit_tests/test_validator_errors.py +0 -110
  64. /bec_widgets/{cli → assets}/bec_widgets_icon.png +0 -0
  65. /bec_widgets/{examples/jupyter_console → assets}/terminal_icon.png +0 -0
  66. /bec_widgets/widgets/{plots → figure/plots}/plot_base.py +0 -0
  67. /bec_widgets/widgets/motor_control/{motor_control_table.ui → motor_table/motor_table.ui} +0 -0
  68. /bec_widgets/widgets/motor_control/{motor_control_absolute.ui → movement_absolute/movement_absolute.ui} +0 -0
  69. /bec_widgets/widgets/motor_control/{motor_control_relative.ui → movement_relative/movement_relative.ui} +0 -0
  70. /bec_widgets/widgets/motor_control/{motor_control_selection.ui → selection/selection.ui} +0 -0
  71. {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/WHEEL +0 -0
  72. {bec_widgets-0.53.3.dist-info → bec_widgets-0.55.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,594 +0,0 @@
1
- # pylint: disable = no-name-in-module,missing-module-docstring
2
- from __future__ import annotations
3
-
4
- import time
5
- from typing import Any, Union
6
-
7
- import numpy as np
8
- import pyqtgraph as pg
9
- from bec_lib.endpoints import MessageEndpoints
10
- from qtpy import QtCore, QtGui
11
- from qtpy.QtCore import Signal as pyqtSignal
12
- from qtpy.QtCore import Slot as pyqtSlot
13
- from qtpy.QtWidgets import QApplication
14
-
15
- from bec_widgets.utils.bec_dispatcher import BECDispatcher
16
- from bec_widgets.utils.yaml_dialog import load_yaml
17
-
18
- CONFIG_DEFAULT = {
19
- "plot_settings": {
20
- "colormap": "Greys",
21
- "scatter_size": 5,
22
- "max_points": 1000,
23
- "num_dim_points": 100,
24
- "precision": 2,
25
- "num_columns": 1,
26
- "background_value": 25,
27
- },
28
- "motors": [
29
- {
30
- "plot_name": "Motor Map",
31
- "x_label": "Motor X",
32
- "y_label": "Motor Y",
33
- "signals": {
34
- "x": [{"name": "samx", "entry": "samx"}],
35
- "y": [{"name": "samy", "entry": "samy"}],
36
- },
37
- },
38
- {
39
- "plot_name": "Motor Map 2 ",
40
- "x_label": "Motor X",
41
- "y_label": "Motor Y",
42
- "signals": {
43
- "x": [{"name": "aptrx", "entry": "aptrx"}],
44
- "y": [{"name": "aptry", "entry": "aptry"}],
45
- },
46
- },
47
- ],
48
- }
49
-
50
-
51
- class MotorMap(pg.GraphicsLayoutWidget):
52
- update_signal = pyqtSignal()
53
-
54
- def __init__(
55
- self,
56
- parent=None,
57
- client=None,
58
- config: dict = None,
59
- gui_id=None,
60
- skip_validation: bool = True,
61
- ):
62
- super().__init__(parent=parent)
63
-
64
- # Import BEC related stuff
65
- bec_dispatcher = BECDispatcher()
66
- self.client = bec_dispatcher.client if client is None else client
67
- self.dev = self.client.device_manager.devices
68
-
69
- # TODO import validator when prepared
70
- self.gui_id = gui_id
71
-
72
- if self.gui_id is None:
73
- self.gui_id = self.__class__.__name__ + str(time.time())
74
-
75
- # Current configuration
76
- self.config = config
77
- self.skip_validation = skip_validation # TODO implement validation when validator is ready
78
-
79
- # Connect the update signal to the update plot method
80
- self.proxy_update_plot = pg.SignalProxy(
81
- self.update_signal, rateLimit=25, slot=self._update_plots
82
- )
83
-
84
- # Config related variables
85
- self.plot_data = None
86
- self.plot_settings = None
87
- self.max_points = None
88
- self.num_dim_points = None
89
- self.scatter_size = None
90
- self.precision = None
91
- self.background_value = None
92
- self.database = {}
93
- self.device_mapping = {}
94
- self.plots = {}
95
- self.grid_coordinates = []
96
- self.curves_data = {}
97
-
98
- # Init UI with config
99
- if self.config is None:
100
- print("No initial config found for MotorMap. Using default config.")
101
- else:
102
- self.on_config_update(self.config)
103
-
104
- @pyqtSlot(dict)
105
- def on_config_update(self, config: dict) -> None:
106
- """
107
- Validate and update the configuration settings for the PlotApp.
108
- Args:
109
- config(dict): Configuration settings
110
- """
111
- # TODO implement BEC CLI commands similar to BECPlotter
112
- # convert config from BEC CLI to correct formatting
113
- config_tag = config.get("config", None)
114
- if config_tag is not None:
115
- config = config["config"]
116
-
117
- if self.skip_validation is True:
118
- self.config = config
119
- self._init_config()
120
-
121
- else: # TODO implement validator
122
- print("Do validation")
123
-
124
- @pyqtSlot(str, str, int)
125
- def change_motors(self, motor_x: str, motor_y: str, subplot: int = 0) -> None:
126
- """
127
- Change the active motors for the plot.
128
- Args:
129
- motor_x(str): Motor name for the X axis.
130
- motor_y(str): Motor name for the Y axis.
131
- subplot(int): Subplot number.
132
- """
133
- if subplot >= len(self.plot_data):
134
- print(f"Invalid subplot index: {subplot}. Available subplots: {len(self.plot_data)}")
135
- return
136
-
137
- # Update the motor names in the plot configuration
138
- self.config["motors"][subplot]["signals"]["x"][0]["name"] = motor_x
139
- self.config["motors"][subplot]["signals"]["x"][0]["entry"] = motor_x
140
- self.config["motors"][subplot]["signals"]["y"][0]["name"] = motor_y
141
- self.config["motors"][subplot]["signals"]["y"][0]["entry"] = motor_y
142
-
143
- # reinitialise the config and UI
144
- self._init_config()
145
-
146
- def _init_config(self):
147
- """Initiate the configuration."""
148
-
149
- # Global widget settings
150
- self._get_global_settings()
151
-
152
- # Motor settings
153
- self.plot_data = self.config.get("motors", {})
154
-
155
- # Include motor limits into the config
156
- self._add_limits_to_plot_data()
157
-
158
- # Initialize the database
159
- self.database = self._init_database()
160
-
161
- # Create device mapping for x/y motor pairs
162
- self.device_mapping = self._create_device_mapping()
163
-
164
- # Initialize the plot UI
165
- self._init_ui()
166
-
167
- # Connect motors to slots
168
- self._connect_motors_to_slots()
169
-
170
- # Render init position of selected motors
171
- self._update_plots()
172
-
173
- def _get_global_settings(self):
174
- """Get global settings from the config."""
175
- self.plot_settings = self.config.get("plot_settings", {})
176
-
177
- self.max_points = self.plot_settings.get("max_points", 5000)
178
- self.num_dim_points = self.plot_settings.get("num_dim_points", 100)
179
- self.scatter_size = self.plot_settings.get("scatter_size", 5)
180
- self.precision = self.plot_settings.get("precision", 2)
181
- self.background_value = self.plot_settings.get("background_value", 25)
182
-
183
- def _create_device_mapping(self):
184
- """
185
- Create a mapping of device names to their corresponding x/y devices.
186
- """
187
- mapping = {}
188
- for motor in self.config.get("motors", []):
189
- for axis in ["x", "y"]:
190
- for signal in motor["signals"][axis]:
191
- other_axis = "y" if axis == "x" else "x"
192
- corresponding_device = motor["signals"][other_axis][0]["name"]
193
- mapping[signal["name"]] = corresponding_device
194
- return mapping
195
-
196
- def _connect_motors_to_slots(self):
197
- """Connect motors to slots."""
198
-
199
- # Disconnect all slots before connecting a new ones
200
- bec_dispatcher = BECDispatcher()
201
- bec_dispatcher.disconnect_all()
202
-
203
- # Get list of all unique motors
204
- unique_motors = []
205
- for motor_config in self.plot_data:
206
- for axis in ["x", "y"]:
207
- for signal in motor_config["signals"][axis]:
208
- unique_motors.append(signal["name"])
209
- unique_motors = list(set(unique_motors))
210
-
211
- # Create list of endpoint
212
- endpoints = []
213
- for motor in unique_motors:
214
- endpoints.append(MessageEndpoints.device_readback(motor))
215
-
216
- # Connect all topics to a single slot
217
- bec_dispatcher.connect_slot(self.on_device_readback, endpoints)
218
-
219
- def _add_limits_to_plot_data(self):
220
- """
221
- Add limits to each motor signal in the plot_data.
222
- """
223
- for motor_config in self.plot_data:
224
- for axis in ["x", "y"]:
225
- for signal in motor_config["signals"][axis]:
226
- motor_name = signal["name"]
227
- motor_limits = self._get_motor_limit(motor_name)
228
- signal["limits"] = motor_limits
229
-
230
- def _get_motor_limit(self, motor: str) -> Union[list | None]:
231
- """
232
- Get the motor limit from the config.
233
- Args:
234
- motor(str): Motor name.
235
-
236
- Returns:
237
- float: Motor limit.
238
- """
239
- try:
240
- limits = self.dev[motor].limits
241
- if limits == [0, 0]:
242
- return None
243
- return limits
244
- except AttributeError: # TODO maybe not needed, if no limits it returns [0,0]
245
- # If the motor doesn't have a 'limits' attribute, return a default value or raise a custom exception
246
- print(f"The device '{motor}' does not have defined limits.")
247
- return None
248
-
249
- def _init_database(self):
250
- """Initiate the database according the config."""
251
- database = {}
252
-
253
- for plot in self.plot_data:
254
- for axis, signals in plot["signals"].items():
255
- for signal in signals:
256
- name = signal["name"]
257
- entry = signal.get("entry", name)
258
- if name not in database:
259
- database[name] = {}
260
- if entry not in database[name]:
261
- database[name][entry] = [self.get_coordinate(name, entry)]
262
- return database
263
-
264
- def get_coordinate(self, name, entry):
265
- """Get the initial coordinate value for a motor."""
266
- try:
267
- return self.dev[name].read()[entry]["value"]
268
- except Exception as e:
269
- print(f"Error getting initial value for {name}: {e}")
270
- return None
271
-
272
- def _init_ui(self, num_columns: int = 3) -> None:
273
- """
274
- Initialize the UI components, create plots and store their grid positions.
275
-
276
- Args:
277
- num_columns (int): Number of columns to wrap the layout.
278
-
279
- This method initializes a dictionary `self.plots` to store the plot objects
280
- along with their corresponding x and y signal names. It dynamically arranges
281
- the plots in a grid layout based on the given number of columns and dynamically
282
- stretches the last plots to fit the remaining space.
283
- """
284
- self.clear()
285
- self.plots = {}
286
- self.grid_coordinates = []
287
- self.curves_data = {} # TODO moved from init_curves
288
-
289
- num_plots = len(self.plot_data)
290
-
291
- # Check if num_columns exceeds the number of plots
292
- if num_columns >= num_plots:
293
- num_columns = num_plots
294
- self.plot_settings["num_columns"] = num_columns # Update the settings
295
- print(
296
- "Warning: num_columns in the YAML file was greater than the number of plots."
297
- f" Resetting num_columns to number of plots:{num_columns}."
298
- )
299
- else:
300
- self.plot_settings["num_columns"] = num_columns # Update the settings
301
-
302
- num_rows = num_plots // num_columns
303
- last_row_cols = num_plots % num_columns
304
- remaining_space = num_columns - last_row_cols
305
-
306
- for i, plot_config in enumerate(self.plot_data):
307
- row, col = i // num_columns, i % num_columns
308
- colspan = 1
309
-
310
- if row == num_rows and remaining_space > 0:
311
- if last_row_cols == 1:
312
- colspan = num_columns
313
- else:
314
- colspan = remaining_space // last_row_cols + 1
315
- remaining_space -= colspan - 1
316
- last_row_cols -= 1
317
-
318
- if "plot_name" not in plot_config:
319
- plot_name = f"Plot ({row}, {col})"
320
- plot_config["plot_name"] = plot_name
321
- else:
322
- plot_name = plot_config["plot_name"]
323
-
324
- x_label = plot_config.get("x_label", "")
325
- y_label = plot_config.get("y_label", "")
326
-
327
- plot = self.addPlot(row=row, col=col, colspan=colspan, title="Motor position: (X, Y)")
328
- plot.setLabel("bottom", f"{x_label} ({plot_config['signals']['x'][0]['name']})")
329
- plot.setLabel("left", f"{y_label} ({plot_config['signals']['y'][0]['name']})")
330
- plot.addLegend()
331
- # self._set_plot_colors(plot, self.plot_settings) #TODO implement colors
332
-
333
- self.plots[plot_name] = plot
334
- self.grid_coordinates.append((row, col))
335
-
336
- self._init_motor_map(plot_config)
337
-
338
- def _init_motor_map(self, plot_config: dict) -> None:
339
- """
340
- Initialize the motor map.
341
- Args:
342
- plot_config(dict): Plot configuration.
343
- """
344
-
345
- # Get plot name to find appropriate plot
346
- plot_name = plot_config.get("plot_name", "")
347
-
348
- # Reset the curves data
349
- plot = self.plots[plot_name]
350
- plot.clear()
351
-
352
- limits_x, limits_y = plot_config["signals"]["x"][0].get("limits", None), plot_config[
353
- "signals"
354
- ]["y"][0].get("limits", None)
355
- if limits_x is not None and limits_y is not None:
356
- self._make_limit_map(plot, [limits_x, limits_y])
357
-
358
- # Initiate ScatterPlotItem for motor coordinates
359
- self.curves_data[plot_name] = {
360
- "pos": pg.ScatterPlotItem(
361
- size=self.scatter_size, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 255)
362
- )
363
- }
364
-
365
- # Add the scatter plot to the plot
366
- plot.addItem(self.curves_data[plot_name]["pos"])
367
- # Set the point map to be always on the top
368
- self.curves_data[plot_name]["pos"].setZValue(0)
369
-
370
- # Add all layers to the plot
371
- plot.showGrid(x=True, y=True)
372
-
373
- # Add the crosshair for motor coordinates
374
- init_position_x = self._get_motor_init_position(
375
- plot_config["signals"]["x"][0]["name"], plot_config["signals"]["x"][0]["entry"]
376
- )
377
- init_position_y = self._get_motor_init_position(
378
- plot_config["signals"]["y"][0]["name"], plot_config["signals"]["y"][0]["entry"]
379
- )
380
- self._add_coordinantes_crosshair(plot_name, init_position_x, init_position_y)
381
-
382
- def _add_coordinantes_crosshair(self, plot_name: str, x: float, y: float) -> None:
383
- """
384
- Add crosshair to the plot to highlight the current position.
385
- Args:
386
- plot_name(str): Name of the plot.
387
- x(float): X coordinate.
388
- y(float): Y coordinate.
389
- """
390
- # find the current plot
391
- plot = self.plots[plot_name]
392
-
393
- # Crosshair to highlight the current position
394
- highlight_H = pg.InfiniteLine(
395
- angle=0, movable=False, pen=pg.mkPen(color="r", width=1, style=QtCore.Qt.DashLine)
396
- )
397
- highlight_V = pg.InfiniteLine(
398
- angle=90, movable=False, pen=pg.mkPen(color="r", width=1, style=QtCore.Qt.DashLine)
399
- )
400
-
401
- # Add crosshair to the curve list for future referencing
402
- self.curves_data[plot_name]["highlight_H"] = highlight_H
403
- self.curves_data[plot_name]["highlight_V"] = highlight_V
404
-
405
- # Add crosshair to the plot
406
- plot.addItem(highlight_H)
407
- plot.addItem(highlight_V)
408
-
409
- highlight_H.setPos(x)
410
- highlight_V.setPos(y)
411
-
412
- def _make_limit_map(self, plot: pg.PlotItem, limits: list):
413
- """
414
- Make a limit map from the limits list.
415
-
416
- Args:
417
- plot(pg.PlotItem): Plot to add the limit map to.
418
- limits(list): List of limits.
419
- """
420
- # Define the size of the image map based on the motor's limits
421
- limit_x_min, limit_x_max = limits[0]
422
- limit_y_min, limit_y_max = limits[1]
423
-
424
- map_width = int(limit_x_max - limit_x_min + 1)
425
- map_height = int(limit_y_max - limit_y_min + 1)
426
-
427
- limit_map_data = np.full((map_width, map_height), self.background_value, dtype=np.float32)
428
-
429
- # Create the image map
430
- limit_map = pg.ImageItem()
431
- limit_map.setImage(limit_map_data)
432
- plot.addItem(limit_map)
433
-
434
- # Translate and scale the image item to match the motor coordinates
435
- tr = QtGui.QTransform()
436
- tr.translate(limit_x_min, limit_y_min)
437
- limit_map.setTransform(tr)
438
-
439
- def _get_motor_init_position(self, name: str, entry: str) -> float:
440
- """
441
- Get the motor initial position from the config.
442
- Args:
443
- name(str): Motor name.
444
- entry(str): Motor entry.
445
- Returns:
446
- float: Motor initial position.
447
- """
448
- init_position = round(self.dev[name].read()[entry]["value"], self.precision)
449
- return init_position
450
-
451
- def _update_plots(self):
452
- """Update the motor position on plots."""
453
- for plot_name, curve_list in self.curves_data.items():
454
- plot_config = next(
455
- (pc for pc in self.plot_data if pc.get("plot_name") == plot_name), None
456
- )
457
- if not plot_config:
458
- continue
459
-
460
- # Get the motor coordinates
461
- x_motor_name = plot_config["signals"]["x"][0]["name"]
462
- x_motor_entry = plot_config["signals"]["x"][0]["entry"]
463
- y_motor_name = plot_config["signals"]["y"][0]["name"]
464
- y_motor_entry = plot_config["signals"]["y"][0]["entry"]
465
-
466
- # update motor position only if there is data
467
- if (
468
- len(self.database[x_motor_name][x_motor_entry]) >= 1
469
- and len(self.database[y_motor_name][y_motor_entry]) >= 1
470
- ):
471
- # Relevant data for the plot
472
- motor_x_data = self.database[x_motor_name][x_motor_entry]
473
- motor_y_data = self.database[y_motor_name][y_motor_entry]
474
-
475
- # Setup gradient brush for history
476
- brushes = [pg.mkBrush(50, 50, 50, 255)] * len(motor_x_data)
477
-
478
- # Calculate the decrement step based on self.num_dim_points
479
- decrement_step = (255 - 50) / self.num_dim_points
480
-
481
- for i in range(1, min(self.num_dim_points + 1, len(motor_x_data) + 1)):
482
- brightness = max(60, 255 - decrement_step * (i - 1))
483
- brushes[-i] = pg.mkBrush(brightness, brightness, brightness, 255)
484
-
485
- brushes[-1] = pg.mkBrush(
486
- 255, 255, 255, 255
487
- ) # Newest point is always full brightness
488
-
489
- # Update the scatter plot
490
- self.curves_data[plot_name]["pos"].setData(
491
- x=motor_x_data, y=motor_y_data, brush=brushes, pen=None, size=self.scatter_size
492
- )
493
-
494
- # Get last know position for crosshair
495
- current_x = motor_x_data[-1]
496
- current_y = motor_y_data[-1]
497
-
498
- # Update plot title
499
- self.plots[plot_name].setTitle(
500
- f"Motor position: ({round(current_x,self.precision)}, {round(current_y,self.precision)})"
501
- )
502
-
503
- # Update the crosshair
504
- self.curves_data[plot_name]["highlight_V"].setPos(current_x)
505
- self.curves_data[plot_name]["highlight_H"].setPos(current_y)
506
-
507
- @pyqtSlot(list, str, str)
508
- def plot_saved_coordinates(self, coordinates: list, tag: str, color: str):
509
- """
510
- Plot saved coordinates on the map.
511
- Args:
512
- coordinates(list): List of coordinates to be plotted.
513
- tag(str): Tag for the coordinates for future reference.
514
- color(str): Color to plot coordinates in.
515
- """
516
- for plot_name in self.plots:
517
- plot = self.plots[plot_name]
518
-
519
- # Clear previous saved points
520
- if tag in self.curves_data[plot_name]:
521
- plot.removeItem(self.curves_data[plot_name][tag])
522
-
523
- # Filter coordinates to be shown
524
- visible_coords = [coord[:2] for coord in coordinates if coord[2]]
525
-
526
- if visible_coords:
527
- saved_points = pg.ScatterPlotItem(
528
- pos=np.array(visible_coords), brush=pg.mkBrush(color)
529
- )
530
- plot.addItem(saved_points)
531
- self.curves_data[plot_name][tag] = saved_points
532
-
533
- @pyqtSlot(dict)
534
- def on_device_readback(self, msg: dict):
535
- """
536
- Update the motor coordinates on the plots.
537
- Args:
538
- msg (dict): Message received with device readback data.
539
- """
540
-
541
- for device_name, device_info in msg["signals"].items():
542
- # Check if the device is relevant to our current context
543
- if device_name in self.device_mapping:
544
- self._update_device_data(device_name, device_info["value"])
545
-
546
- self.update_signal.emit()
547
-
548
- def _update_device_data(self, device_name: str, value: float):
549
- """
550
- Update the device data.
551
- Args:
552
- device_name (str): Device name.
553
- value (float): Device value.
554
- """
555
- if device_name in self.database:
556
- self.database[device_name][device_name].append(value)
557
-
558
- corresponding_device = self.device_mapping.get(device_name)
559
- if corresponding_device and corresponding_device in self.database:
560
- last_value = (
561
- self.database[corresponding_device][corresponding_device][-1]
562
- if self.database[corresponding_device][corresponding_device]
563
- else None
564
- )
565
- self.database[corresponding_device][corresponding_device].append(last_value)
566
-
567
-
568
- if __name__ == "__main__": # pragma: no cover
569
- import argparse
570
- import json
571
- import sys
572
-
573
- parser = argparse.ArgumentParser()
574
- parser.add_argument("--config_file", help="Path to the config file.")
575
- parser.add_argument("--config", help="Path to the config file.")
576
- parser.add_argument("--id", help="GUI ID.")
577
- args = parser.parse_args()
578
-
579
- if args.config is not None:
580
- # Load config from file
581
- config = json.loads(args.config)
582
- elif args.config_file is not None:
583
- # Load config from file
584
- config = load_yaml(args.config_file)
585
- else:
586
- config = CONFIG_DEFAULT
587
-
588
- client = BECDispatcher().client
589
- client.start()
590
- app = QApplication(sys.argv)
591
- motor_map = MotorMap(config=config, gui_id=args.id, skip_validation=True)
592
- motor_map.show()
593
-
594
- sys.exit(app.exec())
@@ -1,4 +0,0 @@
1
- from .image import BECImageItem, BECImageShow, ImageItemConfig
2
- from .motor_map import BECMotorMap, MotorMapConfig
3
- from .plot_base import AxisConfig, BECPlotBase, SubplotConfig
4
- from .waveform import BECCurve, BECWaveform, Waveform1DConfig