bec-widgets 0.76.0__py3-none-any.whl → 0.77.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.
- CHANGELOG.md +42 -44
- PKG-INFO +2 -1
- bec_widgets/cli/client.py +73 -196
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +25 -4
- bec_widgets/utils/bec_connector.py +66 -8
- bec_widgets/utils/colors.py +38 -0
- bec_widgets/utils/generate_designer_plugin.py +15 -14
- bec_widgets/utils/yaml_dialog.py +27 -3
- bec_widgets/widgets/console/console.py +496 -0
- bec_widgets/widgets/dock/dock.py +2 -2
- bec_widgets/widgets/dock/dock_area.py +2 -2
- bec_widgets/widgets/figure/figure.py +149 -195
- bec_widgets/widgets/figure/plots/image/image.py +62 -49
- bec_widgets/widgets/figure/plots/image/image_item.py +4 -3
- bec_widgets/widgets/figure/plots/motor_map/motor_map.py +98 -29
- bec_widgets/widgets/figure/plots/plot_base.py +1 -1
- bec_widgets/widgets/figure/plots/waveform/waveform.py +7 -8
- bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +2 -2
- bec_widgets/widgets/ring_progress_bar/ring.py +3 -3
- bec_widgets/widgets/ring_progress_bar/ring_progress_bar.py +3 -3
- {bec_widgets-0.76.0.dist-info → bec_widgets-0.77.0.dist-info}/METADATA +2 -1
- {bec_widgets-0.76.0.dist-info → bec_widgets-0.77.0.dist-info}/RECORD +40 -38
- pyproject.toml +2 -1
- tests/end-2-end/test_bec_dock_rpc_e2e.py +16 -16
- tests/end-2-end/test_bec_figure_rpc_e2e.py +7 -7
- tests/end-2-end/test_rpc_register_e2e.py +8 -8
- tests/unit_tests/client_mocks.py +1 -0
- tests/unit_tests/test_bec_figure.py +49 -26
- tests/unit_tests/test_bec_motor_map.py +179 -41
- tests/unit_tests/test_color_validation.py +15 -0
- tests/unit_tests/test_device_input_base.py +1 -1
- tests/unit_tests/test_device_input_widgets.py +2 -0
- tests/unit_tests/test_generate_plugin.py +155 -0
- tests/unit_tests/test_motor_control.py +5 -4
- tests/unit_tests/test_plot_base.py +3 -3
- tests/unit_tests/test_waveform1d.py +18 -17
- tests/unit_tests/test_yaml_dialog.py +7 -7
- {bec_widgets-0.76.0.dist-info → bec_widgets-0.77.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.76.0.dist-info → bec_widgets-0.77.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.76.0.dist-info → bec_widgets-0.77.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,7 +5,7 @@ from typing import Any, Literal, Optional
|
|
5
5
|
|
6
6
|
import numpy as np
|
7
7
|
from bec_lib.endpoints import MessageEndpoints
|
8
|
-
from pydantic import Field, ValidationError
|
8
|
+
from pydantic import BaseModel, Field, ValidationError
|
9
9
|
from qtpy.QtCore import QThread
|
10
10
|
from qtpy.QtCore import Slot as pyqtSlot
|
11
11
|
from qtpy.QtWidgets import QWidget
|
@@ -29,11 +29,9 @@ class ImageConfig(SubplotConfig):
|
|
29
29
|
|
30
30
|
class BECImageShow(BECPlotBase):
|
31
31
|
USER_ACCESS = [
|
32
|
-
"
|
33
|
-
"
|
32
|
+
"_rpc_id",
|
33
|
+
"_config_dict",
|
34
34
|
"add_image_by_config",
|
35
|
-
"get_image_config",
|
36
|
-
"get_image_dict",
|
37
35
|
"add_monitor_image",
|
38
36
|
"add_custom_image",
|
39
37
|
"set_vrange",
|
@@ -47,7 +45,6 @@ class BECImageShow(BECPlotBase):
|
|
47
45
|
"set_log",
|
48
46
|
"set_rotation",
|
49
47
|
"set_transpose",
|
50
|
-
"toggle_threading",
|
51
48
|
"set",
|
52
49
|
"set_title",
|
53
50
|
"set_x_label",
|
@@ -138,7 +135,8 @@ class BECImageShow(BECPlotBase):
|
|
138
135
|
self.apply_axis_config()
|
139
136
|
self._images = defaultdict(dict)
|
140
137
|
|
141
|
-
|
138
|
+
for image_id, image_config in config.images.items():
|
139
|
+
self.add_image_by_config(image_config)
|
142
140
|
|
143
141
|
def change_gui_id(self, new_gui_id: str):
|
144
142
|
"""
|
@@ -226,7 +224,7 @@ class BECImageShow(BECPlotBase):
|
|
226
224
|
self,
|
227
225
|
monitor: str,
|
228
226
|
color_map: Optional[str] = "magma",
|
229
|
-
color_bar: Optional[Literal["simple", "full"]] = "
|
227
|
+
color_bar: Optional[Literal["simple", "full"]] = "full",
|
230
228
|
downsample: Optional[bool] = True,
|
231
229
|
opacity: Optional[float] = 1.0,
|
232
230
|
vrange: Optional[tuple[int, int]] = None,
|
@@ -241,7 +239,7 @@ class BECImageShow(BECPlotBase):
|
|
241
239
|
f"Monitor with ID '{monitor}' already exists in widget '{self.gui_id}'."
|
242
240
|
)
|
243
241
|
|
244
|
-
monitor = self.entry_validator.validate_monitor(monitor)
|
242
|
+
# monitor = self.entry_validator.validate_monitor(monitor)
|
245
243
|
|
246
244
|
image_config = ImageItemConfig(
|
247
245
|
widget_class="BECImageItem",
|
@@ -251,12 +249,13 @@ class BECImageShow(BECPlotBase):
|
|
251
249
|
downsample=downsample,
|
252
250
|
opacity=opacity,
|
253
251
|
vrange=vrange,
|
252
|
+
source=image_source,
|
253
|
+
monitor=monitor,
|
254
254
|
# post_processing=post_processing,
|
255
255
|
**kwargs,
|
256
256
|
)
|
257
257
|
|
258
258
|
image = self._add_image_object(source=image_source, name=monitor, config=image_config)
|
259
|
-
self._connect_device_monitor(monitor)
|
260
259
|
return image
|
261
260
|
|
262
261
|
def add_custom_image(
|
@@ -264,16 +263,17 @@ class BECImageShow(BECPlotBase):
|
|
264
263
|
name: str,
|
265
264
|
data: Optional[np.ndarray] = None,
|
266
265
|
color_map: Optional[str] = "magma",
|
267
|
-
color_bar: Optional[Literal["simple", "full"]] = "
|
266
|
+
color_bar: Optional[Literal["simple", "full"]] = "full",
|
268
267
|
downsample: Optional[bool] = True,
|
269
268
|
opacity: Optional[float] = 1.0,
|
270
269
|
vrange: Optional[tuple[int, int]] = None,
|
271
270
|
# post_processing: Optional[PostProcessingConfig] = None,
|
272
271
|
**kwargs,
|
273
272
|
):
|
274
|
-
image_source = "
|
273
|
+
image_source = "custom"
|
274
|
+
# image_source = "device_monitor"
|
275
275
|
|
276
|
-
image_exits = self.
|
276
|
+
image_exits = self._check_image_id(name, self._images)
|
277
277
|
if image_exits:
|
278
278
|
raise ValueError(f"Monitor with ID '{name}' already exists in widget '{self.gui_id}'.")
|
279
279
|
|
@@ -290,7 +290,9 @@ class BECImageShow(BECPlotBase):
|
|
290
290
|
**kwargs,
|
291
291
|
)
|
292
292
|
|
293
|
-
image = self._add_image_object(
|
293
|
+
image = self._add_image_object(
|
294
|
+
source=image_source, name=name, config=image_config, data=data
|
295
|
+
)
|
294
296
|
return image
|
295
297
|
|
296
298
|
def apply_setting_to_images(
|
@@ -313,6 +315,7 @@ class BECImageShow(BECPlotBase):
|
|
313
315
|
for source, images in self._images.items():
|
314
316
|
for _, image in images.items():
|
315
317
|
getattr(image, setting_method_name)(*args, **kwargs)
|
318
|
+
self.refresh_image()
|
316
319
|
|
317
320
|
def set_vrange(self, vmin: float, vmax: float, name: str = None):
|
318
321
|
"""
|
@@ -460,18 +463,19 @@ class BECImageShow(BECPlotBase):
|
|
460
463
|
if self.use_threading is False and self.thread.isRunning():
|
461
464
|
self.cleanup()
|
462
465
|
|
463
|
-
|
464
|
-
def on_image_update(self, msg: dict):
|
466
|
+
def process_image(self, device: str, image: BECImageItem, data: np.ndarray):
|
465
467
|
"""
|
466
|
-
|
468
|
+
Process the image data.
|
467
469
|
|
468
470
|
Args:
|
469
|
-
|
471
|
+
device(str): The name of the device - image_id of image.
|
472
|
+
image(np.ndarray): The image data to be processed.
|
473
|
+
data(np.ndarray): The image data to be processed.
|
474
|
+
|
475
|
+
Returns:
|
476
|
+
np.ndarray: The processed image data.
|
470
477
|
"""
|
471
|
-
|
472
|
-
device = msg["device"]
|
473
|
-
image_to_update = self._images["device_monitor"][device]
|
474
|
-
processing_config = image_to_update.config.processing
|
478
|
+
processing_config = image.config.processing
|
475
479
|
self.processor.set_config(processing_config)
|
476
480
|
if self.use_threading:
|
477
481
|
self._create_thread_worker(device, data)
|
@@ -480,6 +484,19 @@ class BECImageShow(BECPlotBase):
|
|
480
484
|
self.update_image(device, data)
|
481
485
|
self.update_vrange(device, self.processor.config.stats)
|
482
486
|
|
487
|
+
@pyqtSlot(dict)
|
488
|
+
def on_image_update(self, msg: dict):
|
489
|
+
"""
|
490
|
+
Update the image of the device monitor from bec.
|
491
|
+
|
492
|
+
Args:
|
493
|
+
msg(dict): The message from bec.
|
494
|
+
"""
|
495
|
+
data = msg["data"]
|
496
|
+
device = msg["device"]
|
497
|
+
image = self._images["device_monitor"][device]
|
498
|
+
self.process_image(device, image, data)
|
499
|
+
|
483
500
|
@pyqtSlot(str, np.ndarray)
|
484
501
|
def update_image(self, device: str, data: np.ndarray):
|
485
502
|
"""
|
@@ -504,6 +521,15 @@ class BECImageShow(BECPlotBase):
|
|
504
521
|
if image_to_update.config.autorange:
|
505
522
|
image_to_update.auto_update_vrange(stats)
|
506
523
|
|
524
|
+
def refresh_image(self):
|
525
|
+
"""
|
526
|
+
Refresh the image.
|
527
|
+
"""
|
528
|
+
for source, images in self._images.items():
|
529
|
+
for image_id, image in images.items():
|
530
|
+
data = image.get_data()
|
531
|
+
self.process_image(image_id, image, data)
|
532
|
+
|
507
533
|
def _connect_device_monitor(self, monitor: str):
|
508
534
|
"""
|
509
535
|
Connect to the device monitor.
|
@@ -516,16 +542,18 @@ class BECImageShow(BECPlotBase):
|
|
516
542
|
previous_monitor = image_item.config.monitor
|
517
543
|
except AttributeError:
|
518
544
|
previous_monitor = None
|
519
|
-
if previous_monitor
|
520
|
-
|
521
|
-
self.
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
)
|
528
|
-
|
545
|
+
if previous_monitor and image_item.connected is True:
|
546
|
+
self.bec_dispatcher.disconnect_slot(
|
547
|
+
self.on_image_update, MessageEndpoints.device_monitor(previous_monitor)
|
548
|
+
)
|
549
|
+
image_item.connected = False
|
550
|
+
if monitor and image_item.connected is False:
|
551
|
+
self.entry_validator.validate_monitor(monitor)
|
552
|
+
self.bec_dispatcher.connect_slot(
|
553
|
+
self.on_image_update, MessageEndpoints.device_monitor(monitor)
|
554
|
+
)
|
555
|
+
image_item.set_monitor(monitor)
|
556
|
+
image_item.connected = True
|
529
557
|
|
530
558
|
def _add_image_object(
|
531
559
|
self, source: str, name: str, config: ImageItemConfig, data=None
|
@@ -534,6 +562,8 @@ class BECImageShow(BECPlotBase):
|
|
534
562
|
image = BECImageItem(config=config, parent_image=self)
|
535
563
|
self.plot_item.addItem(image)
|
536
564
|
self._images[source][name] = image
|
565
|
+
if source == "device_monitor":
|
566
|
+
self._connect_device_monitor(config.monitor)
|
537
567
|
self.config.images[name] = config
|
538
568
|
if data is not None:
|
539
569
|
image.setImage(data)
|
@@ -558,23 +588,6 @@ class BECImageShow(BECPlotBase):
|
|
558
588
|
return True
|
559
589
|
return False
|
560
590
|
|
561
|
-
def _validate_monitor(self, monitor: str, validate_bec: bool = True):
|
562
|
-
"""
|
563
|
-
Validate the monitor name.
|
564
|
-
|
565
|
-
Args:
|
566
|
-
monitor(str): The name of the monitor.
|
567
|
-
validate_bec(bool): Whether to validate the monitor name with BEC.
|
568
|
-
|
569
|
-
Returns:
|
570
|
-
bool: True if the monitor name is valid, False otherwise.
|
571
|
-
"""
|
572
|
-
if not monitor or monitor == "":
|
573
|
-
return False
|
574
|
-
if validate_bec:
|
575
|
-
return monitor in self.dev
|
576
|
-
return True
|
577
|
-
|
578
591
|
def cleanup(self):
|
579
592
|
"""
|
580
593
|
Clean up the widget.
|
@@ -20,7 +20,7 @@ class ImageItemConfig(ConnectionConfig):
|
|
20
20
|
color_map: Optional[str] = Field("magma", description="The color map of the image.")
|
21
21
|
downsample: Optional[bool] = Field(True, description="Whether to downsample the image.")
|
22
22
|
opacity: Optional[float] = Field(1.0, description="The opacity of the image.")
|
23
|
-
vrange: Optional[tuple[float, float]] = Field(
|
23
|
+
vrange: Optional[tuple[float | int, float | int]] = Field(
|
24
24
|
None, description="The range of the color bar. If None, the range is automatically set."
|
25
25
|
)
|
26
26
|
color_bar: Optional[Literal["simple", "full"]] = Field(
|
@@ -37,8 +37,8 @@ class ImageItemConfig(ConnectionConfig):
|
|
37
37
|
|
38
38
|
class BECImageItem(BECConnector, pg.ImageItem):
|
39
39
|
USER_ACCESS = [
|
40
|
-
"
|
41
|
-
"
|
40
|
+
"_rpc_id",
|
41
|
+
"_config_dict",
|
42
42
|
"set",
|
43
43
|
"set_fft",
|
44
44
|
"set_log",
|
@@ -78,6 +78,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
78
78
|
self.apply_config()
|
79
79
|
if kwargs:
|
80
80
|
self.set(**kwargs)
|
81
|
+
self.connected = False
|
81
82
|
|
82
83
|
def apply_config(self):
|
83
84
|
"""
|
@@ -6,22 +6,23 @@ from typing import Optional, Union
|
|
6
6
|
import numpy as np
|
7
7
|
import pyqtgraph as pg
|
8
8
|
from bec_lib.endpoints import MessageEndpoints
|
9
|
-
from pydantic import Field
|
9
|
+
from pydantic import Field, ValidationError, field_validator
|
10
|
+
from pydantic_core import PydanticCustomError
|
10
11
|
from qtpy import QtCore, QtGui
|
11
12
|
from qtpy.QtCore import Signal as pyqtSignal
|
12
13
|
from qtpy.QtCore import Slot as pyqtSlot
|
13
14
|
from qtpy.QtWidgets import QWidget
|
14
15
|
|
15
|
-
from bec_widgets.utils import EntryValidator
|
16
|
+
from bec_widgets.utils import Colors, EntryValidator
|
16
17
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
17
18
|
from bec_widgets.widgets.figure.plots.waveform.waveform import Signal, SignalData
|
18
19
|
|
19
20
|
|
20
21
|
class MotorMapConfig(SubplotConfig):
|
21
22
|
signals: Optional[Signal] = Field(None, description="Signals of the motor map")
|
22
|
-
|
23
|
-
|
24
|
-
)
|
23
|
+
color: Optional[str | tuple] = Field(
|
24
|
+
(255, 255, 255, 255), description="The color of the last point of current position."
|
25
|
+
)
|
25
26
|
scatter_size: Optional[int] = Field(5, description="Size of the scatter points.")
|
26
27
|
max_points: Optional[int] = Field(1000, description="Maximum number of points to display.")
|
27
28
|
num_dim_points: Optional[int] = Field(
|
@@ -30,14 +31,26 @@ class MotorMapConfig(SubplotConfig):
|
|
30
31
|
)
|
31
32
|
precision: Optional[int] = Field(2, description="Decimal precision of the motor position.")
|
32
33
|
background_value: Optional[int] = Field(
|
33
|
-
25, description="Background value of the motor map."
|
34
|
-
)
|
34
|
+
25, description="Background value of the motor map. Has to be between 0 and 255."
|
35
|
+
)
|
36
|
+
|
37
|
+
model_config: dict = {"validate_assignment": True}
|
38
|
+
|
39
|
+
_validate_color = field_validator("color")(Colors.validate_color)
|
40
|
+
|
41
|
+
@field_validator("background_value")
|
42
|
+
def validate_background_value(cls, value):
|
43
|
+
if not 0 <= value <= 255:
|
44
|
+
raise PydanticCustomError(
|
45
|
+
"wrong_value", f"'{value}' hs to be between 0 and 255.", {"wrong_value": value}
|
46
|
+
)
|
47
|
+
return value
|
35
48
|
|
36
49
|
|
37
50
|
class BECMotorMap(BECPlotBase):
|
38
51
|
USER_ACCESS = [
|
39
|
-
"
|
40
|
-
"
|
52
|
+
"_rpc_id",
|
53
|
+
"_config_dict",
|
41
54
|
"change_motors",
|
42
55
|
"set_max_points",
|
43
56
|
"set_precision",
|
@@ -69,29 +82,43 @@ class BECMotorMap(BECPlotBase):
|
|
69
82
|
self.get_bec_shortcuts()
|
70
83
|
self.entry_validator = EntryValidator(self.dev)
|
71
84
|
|
85
|
+
# connect update signal to update plot
|
86
|
+
self.proxy_update_plot = pg.SignalProxy(
|
87
|
+
self.update_signal, rateLimit=25, slot=self._update_plot
|
88
|
+
)
|
89
|
+
self.apply_config(self.config)
|
90
|
+
|
91
|
+
def apply_config(self, config: dict | MotorMapConfig):
|
92
|
+
"""
|
93
|
+
Apply the config to the motor map.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
config(dict|MotorMapConfig): Config to be applied.
|
97
|
+
"""
|
98
|
+
if isinstance(config, dict):
|
99
|
+
try:
|
100
|
+
config = MotorMapConfig(**config)
|
101
|
+
except ValidationError as e:
|
102
|
+
print(f"Error in applying config: {e}")
|
103
|
+
return
|
104
|
+
|
105
|
+
self.config = config
|
106
|
+
self.plot_item.clear()
|
107
|
+
|
72
108
|
self.motor_x = None
|
73
109
|
self.motor_y = None
|
74
110
|
self.database_buffer = {"x": [], "y": []}
|
75
111
|
self.plot_components = defaultdict(dict) # container for plot components
|
76
112
|
|
77
|
-
|
78
|
-
self.proxy_update_plot = pg.SignalProxy(
|
79
|
-
self.update_signal, rateLimit=25, slot=self._update_plot
|
80
|
-
)
|
113
|
+
self.apply_axis_config()
|
81
114
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
# Returns:
|
90
|
-
# BECCurve: The curve object.
|
91
|
-
# """
|
92
|
-
# for curve in self.plot_item.curves:
|
93
|
-
# if curve.gui_id == item_id:
|
94
|
-
# return curve
|
115
|
+
if self.config.signals is not None:
|
116
|
+
self.change_motors(
|
117
|
+
motor_x=self.config.signals.x.name,
|
118
|
+
motor_y=self.config.signals.y.name,
|
119
|
+
motor_x_entry=self.config.signals.x.entry,
|
120
|
+
motor_y_entry=self.config.signals.y.entry,
|
121
|
+
)
|
95
122
|
|
96
123
|
@pyqtSlot(str, str, str, str, bool)
|
97
124
|
def change_motors(
|
@@ -129,6 +156,8 @@ class BECMotorMap(BECPlotBase):
|
|
129
156
|
# reconnect the signals
|
130
157
|
self._connect_motor_to_slots()
|
131
158
|
|
159
|
+
self.database_buffer = {"x": [], "y": []}
|
160
|
+
|
132
161
|
# Redraw the motor map
|
133
162
|
self._make_motor_map()
|
134
163
|
|
@@ -141,7 +170,19 @@ class BECMotorMap(BECPlotBase):
|
|
141
170
|
data = {"x": self.database_buffer["x"], "y": self.database_buffer["y"]}
|
142
171
|
return data
|
143
172
|
|
144
|
-
|
173
|
+
def set_color(self, color: [str | tuple]):
|
174
|
+
"""
|
175
|
+
Set color of the motor trace.
|
176
|
+
|
177
|
+
Args:
|
178
|
+
color(str|tuple): Color of the motor trace. Can be HEX(str) or RGBA(tuple).
|
179
|
+
"""
|
180
|
+
if isinstance(color, str):
|
181
|
+
color = Colors.validate_color(color)
|
182
|
+
color = Colors.hex_to_rgba(color, 255)
|
183
|
+
self.config.color = color
|
184
|
+
self.update_signal.emit()
|
185
|
+
|
145
186
|
def set_max_points(self, max_points: int) -> None:
|
146
187
|
"""
|
147
188
|
Set the maximum number of points to display.
|
@@ -150,6 +191,7 @@ class BECMotorMap(BECPlotBase):
|
|
150
191
|
max_points(int): Maximum number of points to display.
|
151
192
|
"""
|
152
193
|
self.config.max_points = max_points
|
194
|
+
self.update_signal.emit()
|
153
195
|
|
154
196
|
def set_precision(self, precision: int) -> None:
|
155
197
|
"""
|
@@ -159,6 +201,7 @@ class BECMotorMap(BECPlotBase):
|
|
159
201
|
precision(int): Decimal precision of the motor position.
|
160
202
|
"""
|
161
203
|
self.config.precision = precision
|
204
|
+
self.update_signal.emit()
|
162
205
|
|
163
206
|
def set_num_dim_points(self, num_dim_points: int) -> None:
|
164
207
|
"""
|
@@ -168,6 +211,7 @@ class BECMotorMap(BECPlotBase):
|
|
168
211
|
num_dim_points(int): Number of dim points.
|
169
212
|
"""
|
170
213
|
self.config.num_dim_points = num_dim_points
|
214
|
+
self.update_signal.emit()
|
171
215
|
|
172
216
|
def set_background_value(self, background_value: int) -> None:
|
173
217
|
"""
|
@@ -177,6 +221,7 @@ class BECMotorMap(BECPlotBase):
|
|
177
221
|
background_value(int): Background value of the motor map.
|
178
222
|
"""
|
179
223
|
self.config.background_value = background_value
|
224
|
+
self._swap_limit_map()
|
180
225
|
|
181
226
|
def set_scatter_size(self, scatter_size: int) -> None:
|
182
227
|
"""
|
@@ -186,6 +231,7 @@ class BECMotorMap(BECPlotBase):
|
|
186
231
|
scatter_size(int): Size of the scatter points.
|
187
232
|
"""
|
188
233
|
self.config.scatter_size = scatter_size
|
234
|
+
self.update_signal.emit()
|
189
235
|
|
190
236
|
def _disconnect_current_motors(self):
|
191
237
|
"""Disconnect the current motors from the slots."""
|
@@ -210,6 +256,15 @@ class BECMotorMap(BECPlotBase):
|
|
210
256
|
|
211
257
|
self.bec_dispatcher.connect_slot(self.on_device_readback, endpoints)
|
212
258
|
|
259
|
+
def _swap_limit_map(self):
|
260
|
+
"""Swap the limit map."""
|
261
|
+
self.plot_item.removeItem(self.plot_components["limit_map"])
|
262
|
+
self.plot_components["limit_map"] = self._make_limit_map(
|
263
|
+
self.config.signals.x.limits, self.config.signals.y.limits
|
264
|
+
)
|
265
|
+
self.plot_components["limit_map"].setZValue(-1)
|
266
|
+
self.plot_item.addItem(self.plot_components["limit_map"])
|
267
|
+
|
213
268
|
def _make_motor_map(self):
|
214
269
|
"""
|
215
270
|
Create the motor map plot.
|
@@ -249,6 +304,8 @@ class BECMotorMap(BECPlotBase):
|
|
249
304
|
# Set default labels for the plot
|
250
305
|
self.set(x_label=f"Motor X ({self.motor_x})", y_label=f"Motor Y ({self.motor_y})")
|
251
306
|
|
307
|
+
self.update_signal.emit()
|
308
|
+
|
252
309
|
def _add_coordinantes_crosshair(self, x: float, y: float) -> None:
|
253
310
|
"""
|
254
311
|
Add crosshair to the plot to highlight the current position.
|
@@ -373,19 +430,31 @@ class BECMotorMap(BECPlotBase):
|
|
373
430
|
|
374
431
|
def _update_plot(self):
|
375
432
|
"""Update the motor map plot."""
|
433
|
+
# If the number of points exceeds max_points, delete the oldest points
|
434
|
+
if len(self.database_buffer["x"]) > self.config.max_points:
|
435
|
+
self.database_buffer["x"] = self.database_buffer["x"][-self.config.max_points :]
|
436
|
+
self.database_buffer["y"] = self.database_buffer["y"][-self.config.max_points :]
|
437
|
+
|
376
438
|
x = self.database_buffer["x"]
|
377
439
|
y = self.database_buffer["y"]
|
378
440
|
|
379
441
|
# Setup gradient brush for history
|
380
442
|
brushes = [pg.mkBrush(50, 50, 50, 255)] * len(x)
|
381
443
|
|
444
|
+
# RGB color
|
445
|
+
r, g, b, a = self.config.color
|
446
|
+
|
382
447
|
# Calculate the decrement step based on self.num_dim_points
|
383
448
|
num_dim_points = self.config.num_dim_points
|
384
449
|
decrement_step = (255 - 50) / num_dim_points
|
450
|
+
|
385
451
|
for i in range(1, min(num_dim_points + 1, len(x) + 1)):
|
386
452
|
brightness = max(60, 255 - decrement_step * (i - 1))
|
387
|
-
|
388
|
-
|
453
|
+
dim_r = int(r * (brightness / 255))
|
454
|
+
dim_g = int(g * (brightness / 255))
|
455
|
+
dim_b = int(b * (brightness / 255))
|
456
|
+
brushes[-i] = pg.mkBrush(dim_r, dim_g, dim_b, a)
|
457
|
+
brushes[-1] = pg.mkBrush(r, g, b, a) # Newest point is always full brightness
|
389
458
|
scatter_size = self.config.scatter_size
|
390
459
|
|
391
460
|
# Update the scatter plot
|
@@ -35,8 +35,8 @@ class Waveform1DConfig(SubplotConfig):
|
|
35
35
|
|
36
36
|
class BECWaveform(BECPlotBase):
|
37
37
|
USER_ACCESS = [
|
38
|
-
"
|
39
|
-
"
|
38
|
+
"_rpc_id",
|
39
|
+
"_config_dict",
|
40
40
|
"plot",
|
41
41
|
"add_dap",
|
42
42
|
"get_dap_params",
|
@@ -44,8 +44,6 @@ class BECWaveform(BECPlotBase):
|
|
44
44
|
"scan_history",
|
45
45
|
"curves",
|
46
46
|
"get_curve",
|
47
|
-
"get_curve_config",
|
48
|
-
"apply_config",
|
49
47
|
"get_all_data",
|
50
48
|
"set",
|
51
49
|
"set_title",
|
@@ -705,14 +703,15 @@ class BECWaveform(BECPlotBase):
|
|
705
703
|
data_y = data[y_name][y_entry].val
|
706
704
|
if curve.config.signals.z:
|
707
705
|
data_z = data[z_name][z_entry].val
|
708
|
-
color_z = self._make_z_gradient(
|
709
|
-
data_z, curve.config.color_map_z
|
710
|
-
) # TODO decide how to implement custom gradient
|
706
|
+
color_z = self._make_z_gradient(data_z, curve.config.color_map_z)
|
711
707
|
except TypeError:
|
712
708
|
continue
|
713
709
|
|
714
710
|
if data_z is not None and color_z is not None:
|
715
|
-
|
711
|
+
try:
|
712
|
+
curve.setData(x=data_x, y=data_y, symbolBrush=color_z)
|
713
|
+
except:
|
714
|
+
return
|
716
715
|
else:
|
717
716
|
curve.setData(data_x, data_y)
|
718
717
|
|
@@ -68,9 +68,9 @@ class RingProgressBarConfig(ConnectionConfig):
|
|
68
68
|
|
69
69
|
class RingProgressBar(BECConnector, QWidget):
|
70
70
|
USER_ACCESS = [
|
71
|
-
"
|
72
|
-
"
|
73
|
-
"
|
71
|
+
"_get_all_rpc",
|
72
|
+
"_rpc_id",
|
73
|
+
"_config_dict",
|
74
74
|
"rings",
|
75
75
|
"update_config",
|
76
76
|
"add_ring",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: bec_widgets
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.77.0
|
4
4
|
Summary: BEC Widgets
|
5
5
|
Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
|
6
6
|
Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
|
@@ -16,6 +16,7 @@ Requires-Dist: isort>=5.13.2,~=5.13
|
|
16
16
|
Requires-Dist: pydantic~=2.0
|
17
17
|
Requires-Dist: pyqtdarktheme~=2.1
|
18
18
|
Requires-Dist: pyqtgraph~=0.13
|
19
|
+
Requires-Dist: pyte
|
19
20
|
Requires-Dist: qtconsole>=5.5.1,~=5.5
|
20
21
|
Requires-Dist: qtpy~=2.4
|
21
22
|
Provides-Extra: dev
|