bec-widgets 1.0.2__py3-none-any.whl → 1.1.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 (34) hide show
  1. CHANGELOG.md +19 -20
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +56 -0
  4. bec_widgets/tests/__init__.py +0 -0
  5. bec_widgets/tests/utils.py +226 -0
  6. bec_widgets/utils/filter_io.py +156 -0
  7. bec_widgets/utils/widget_io.py +12 -9
  8. bec_widgets/widgets/base_classes/device_input_base.py +331 -62
  9. bec_widgets/widgets/base_classes/device_signal_input_base.py +280 -0
  10. bec_widgets/widgets/dap_combo_box/dap_combo_box_plugin.py +1 -1
  11. bec_widgets/widgets/device_combobox/device_combo_box_plugin.py +1 -1
  12. bec_widgets/widgets/device_combobox/device_combobox.py +118 -41
  13. bec_widgets/widgets/device_line_edit/device_line_edit.py +122 -59
  14. bec_widgets/widgets/device_line_edit/device_line_edit_plugin.py +1 -1
  15. bec_widgets/widgets/image/image_widget.py +7 -1
  16. bec_widgets/widgets/motor_map/motor_map_widget.py +4 -2
  17. bec_widgets/widgets/positioner_box/positioner_box.py +4 -1
  18. bec_widgets/widgets/scan_control/scan_group_box.py +3 -1
  19. bec_widgets/widgets/signal_combobox/__init__.py +0 -0
  20. bec_widgets/widgets/signal_combobox/register_signal_combobox.py +15 -0
  21. bec_widgets/widgets/signal_combobox/signal_combobox.py +115 -0
  22. bec_widgets/widgets/signal_combobox/signal_combobox.pyproject +1 -0
  23. bec_widgets/widgets/signal_combobox/signal_combobox_plugin.py +54 -0
  24. bec_widgets/widgets/signal_line_edit/__init__.py +0 -0
  25. bec_widgets/widgets/signal_line_edit/register_signal_line_edit.py +15 -0
  26. bec_widgets/widgets/signal_line_edit/signal_line_edit.py +140 -0
  27. bec_widgets/widgets/signal_line_edit/signal_line_edit.pyproject +1 -0
  28. bec_widgets/widgets/signal_line_edit/signal_line_edit_plugin.py +54 -0
  29. {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/METADATA +1 -1
  30. {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/RECORD +34 -20
  31. pyproject.toml +1 -1
  32. {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/WHEEL +0 -0
  33. {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/entry_points.txt +0 -0
  34. {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,38 +1,133 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import enum
4
+
5
+ from bec_lib.device import ComputedSignal, Device, Positioner, ReadoutPriority
6
+ from bec_lib.device import Signal as BECSignal
7
+ from bec_lib.logger import bec_logger
8
+ from qtpy.QtCore import Property, Signal, Slot
9
+
3
10
  from bec_widgets.utils import ConnectionConfig
4
11
  from bec_widgets.utils.bec_widget import BECWidget
12
+ from bec_widgets.utils.filter_io import FilterIO
13
+ from bec_widgets.utils.widget_io import WidgetIO
14
+
15
+ logger = bec_logger.logger
16
+
17
+
18
+ class BECDeviceFilter(enum.Enum):
19
+ """Filter for the device classes."""
20
+
21
+ DEVICE = "Device"
22
+ POSITIONER = "Positioner"
23
+ SIGNAL = "Signal"
24
+ COMPUTED_SIGNAL = "ComputedSignal"
5
25
 
6
26
 
7
27
  class DeviceInputConfig(ConnectionConfig):
8
- device_filter: str | list[str] | None = None
28
+ device_filter: list[BECDeviceFilter] = []
29
+ readout_filter: list[ReadoutPriority] = []
30
+ devices: list[str] = []
9
31
  default: str | None = None
10
32
  arg_name: str | None = None
33
+ apply_filter: bool = True
11
34
 
12
35
 
13
36
  class DeviceInputBase(BECWidget):
14
37
  """
15
- Mixin class for device input widgets. This class provides methods to get the device list and device object based
16
- on the current text of the widget.
38
+ Mixin base class for device input widgets.
39
+ It allows to filter devices from BEC based on
40
+ device class and readout priority.
17
41
  """
18
42
 
19
- def __init__(self, client=None, config=None, gui_id=None):
43
+ _device_handler = {
44
+ BECDeviceFilter.DEVICE: Device,
45
+ BECDeviceFilter.POSITIONER: Positioner,
46
+ BECDeviceFilter.SIGNAL: BECSignal,
47
+ BECDeviceFilter.COMPUTED_SIGNAL: ComputedSignal,
48
+ }
49
+
50
+ _filter_handler = {
51
+ BECDeviceFilter.DEVICE: "filter_to_device",
52
+ BECDeviceFilter.POSITIONER: "filter_to_positioner",
53
+ BECDeviceFilter.SIGNAL: "filter_to_signal",
54
+ BECDeviceFilter.COMPUTED_SIGNAL: "filter_to_computed_signal",
55
+ ReadoutPriority.MONITORED: "readout_monitored",
56
+ ReadoutPriority.BASELINE: "readout_baseline",
57
+ ReadoutPriority.ASYNC: "readout_async",
58
+ ReadoutPriority.CONTINUOUS: "readout_continuous",
59
+ ReadoutPriority.ON_REQUEST: "readout_on_request",
60
+ }
61
+
62
+ def __init__(self, client=None, config=None, gui_id: str = None):
63
+
20
64
  if config is None:
21
65
  config = DeviceInputConfig(widget_class=self.__class__.__name__)
22
66
  else:
23
67
  if isinstance(config, dict):
24
68
  config = DeviceInputConfig(**config)
25
69
  self.config = config
26
- super().__init__(client=client, config=config, gui_id=gui_id)
27
-
70
+ super().__init__(client=client, config=config, gui_id=gui_id, theme_update=True)
28
71
  self.get_bec_shortcuts()
29
- self._device_filter = None
72
+ self._device_filter = []
73
+ self._readout_filter = []
30
74
  self._devices = []
31
75
 
32
- @property
76
+ ### QtSlots ###
77
+
78
+ @Slot(str)
79
+ def set_device(self, device: str):
80
+ """
81
+ Set the device.
82
+
83
+ Args:
84
+ device (str): Default name.
85
+ """
86
+ if self.validate_device(device) is True:
87
+ WidgetIO.set_value(widget=self, value=device)
88
+ self.config.default = device
89
+ else:
90
+ logger.warning(f"Device {device} is not in the filtered selection.")
91
+
92
+ @Slot()
93
+ def update_devices_from_filters(self):
94
+ """Update the devices based on the current filter selection
95
+ in self.device_filter and self.readout_filter. If apply_filter is False,
96
+ it will not apply the filters, store the filter settings and return.
97
+ """
98
+ current_device = WidgetIO.get_value(widget=self, as_string=True)
99
+ self.config.device_filter = self.device_filter
100
+ self.config.readout_filter = self.readout_filter
101
+ if self.apply_filter is False:
102
+ return
103
+ all_dev = self.dev.enabled_devices
104
+ # Filter based on device class
105
+ devs = [dev for dev in all_dev if self._check_device_filter(dev)]
106
+ # Filter based on readout priority
107
+ devs = [dev for dev in devs if self._check_readout_filter(dev)]
108
+ self.devices = [device.name for device in devs]
109
+ self.set_device(current_device)
110
+
111
+ @Slot(list)
112
+ def set_available_devices(self, devices: list[str]):
113
+ """
114
+ Set the devices. If a device in the list is not valid, it will not be considered.
115
+
116
+ Args:
117
+ devices (list[str]): List of devices.
118
+ """
119
+ self.apply_filter = False
120
+ self.devices = devices
121
+
122
+ ### QtProperties ###
123
+
124
+ @Property(
125
+ "QStringList",
126
+ doc="List of devices. If updated, it will disable the apply filters property.",
127
+ )
33
128
  def devices(self) -> list[str]:
34
129
  """
35
- Get the list of devices.
130
+ Get the list of devices for the applied filters.
36
131
 
37
132
  Returns:
38
133
  list[str]: List of devices.
@@ -40,84 +135,258 @@ class DeviceInputBase(BECWidget):
40
135
  return self._devices
41
136
 
42
137
  @devices.setter
43
- def devices(self, value: list[str]):
44
- """
45
- Set the list of devices.
46
-
47
- Args:
48
- value: List of devices.
49
- """
138
+ def devices(self, value: list):
50
139
  self._devices = value
140
+ self.config.devices = value
141
+ FilterIO.set_selection(widget=self, selection=value)
142
+
143
+ @Property(str)
144
+ def default(self):
145
+ """Get the default device name. If set through this property, it will update only if the device is within the filtered selection."""
146
+ return self.config.default
147
+
148
+ @default.setter
149
+ def default(self, value: str):
150
+ if self.validate_device(value) is False:
151
+ return
152
+ self.config.default = value
153
+ WidgetIO.set_value(widget=self, value=value)
154
+
155
+ @Property(bool)
156
+ def apply_filter(self):
157
+ """Apply the filters on the devices."""
158
+ return self.config.apply_filter
159
+
160
+ @apply_filter.setter
161
+ def apply_filter(self, value: bool):
162
+ self.config.apply_filter = value
163
+ self.update_devices_from_filters()
51
164
 
52
- def set_device_filter(self, device_filter: str | list[str]):
165
+ @Property(bool)
166
+ def filter_to_device(self):
167
+ """Include devices in filters."""
168
+ return BECDeviceFilter.DEVICE in self.device_filter
169
+
170
+ @filter_to_device.setter
171
+ def filter_to_device(self, value: bool):
172
+ if value is True and BECDeviceFilter.DEVICE not in self.device_filter:
173
+ self._device_filter.append(BECDeviceFilter.DEVICE)
174
+ if value is False and BECDeviceFilter.DEVICE in self.device_filter:
175
+ self._device_filter.remove(BECDeviceFilter.DEVICE)
176
+ self.update_devices_from_filters()
177
+
178
+ @Property(bool)
179
+ def filter_to_positioner(self):
180
+ """Include devices of type Positioner in filters."""
181
+ return BECDeviceFilter.POSITIONER in self.device_filter
182
+
183
+ @filter_to_positioner.setter
184
+ def filter_to_positioner(self, value: bool):
185
+ if value is True and BECDeviceFilter.POSITIONER not in self.device_filter:
186
+ self._device_filter.append(BECDeviceFilter.POSITIONER)
187
+ if value is False and BECDeviceFilter.POSITIONER in self.device_filter:
188
+ self._device_filter.remove(BECDeviceFilter.POSITIONER)
189
+ self.update_devices_from_filters()
190
+
191
+ @Property(bool)
192
+ def filter_to_signal(self):
193
+ """Include devices of type Signal in filters."""
194
+ return BECDeviceFilter.SIGNAL in self.device_filter
195
+
196
+ @filter_to_signal.setter
197
+ def filter_to_signal(self, value: bool):
198
+ if value is True and BECDeviceFilter.SIGNAL not in self.device_filter:
199
+ self._device_filter.append(BECDeviceFilter.SIGNAL)
200
+ if value is False and BECDeviceFilter.SIGNAL in self.device_filter:
201
+ self._device_filter.remove(BECDeviceFilter.SIGNAL)
202
+ self.update_devices_from_filters()
203
+
204
+ @Property(bool)
205
+ def filter_to_computed_signal(self):
206
+ """Include devices of type ComputedSignal in filters."""
207
+ return BECDeviceFilter.COMPUTED_SIGNAL in self.device_filter
208
+
209
+ @filter_to_computed_signal.setter
210
+ def filter_to_computed_signal(self, value: bool):
211
+ if value is True and BECDeviceFilter.COMPUTED_SIGNAL not in self.device_filter:
212
+ self._device_filter.append(BECDeviceFilter.COMPUTED_SIGNAL)
213
+ if value is False and BECDeviceFilter.COMPUTED_SIGNAL in self.device_filter:
214
+ self._device_filter.remove(BECDeviceFilter.COMPUTED_SIGNAL)
215
+ self.update_devices_from_filters()
216
+
217
+ @Property(bool)
218
+ def readout_monitored(self):
219
+ """Include devices with readout priority Monitored in filters."""
220
+ return ReadoutPriority.MONITORED in self.readout_filter
221
+
222
+ @readout_monitored.setter
223
+ def readout_monitored(self, value: bool):
224
+ if value is True and ReadoutPriority.MONITORED not in self.readout_filter:
225
+ self._readout_filter.append(ReadoutPriority.MONITORED)
226
+ if value is False and ReadoutPriority.MONITORED in self.readout_filter:
227
+ self._readout_filter.remove(ReadoutPriority.MONITORED)
228
+ self.update_devices_from_filters()
229
+
230
+ @Property(bool)
231
+ def readout_baseline(self):
232
+ """Include devices with readout priority Baseline in filters."""
233
+ return ReadoutPriority.BASELINE in self.readout_filter
234
+
235
+ @readout_baseline.setter
236
+ def readout_baseline(self, value: bool):
237
+ if value is True and ReadoutPriority.BASELINE not in self.readout_filter:
238
+ self._readout_filter.append(ReadoutPriority.BASELINE)
239
+ if value is False and ReadoutPriority.BASELINE in self.readout_filter:
240
+ self._readout_filter.remove(ReadoutPriority.BASELINE)
241
+ self.update_devices_from_filters()
242
+
243
+ @Property(bool)
244
+ def readout_async(self):
245
+ """Include devices with readout priority Async in filters."""
246
+ return ReadoutPriority.ASYNC in self.readout_filter
247
+
248
+ @readout_async.setter
249
+ def readout_async(self, value: bool):
250
+ if value is True and ReadoutPriority.ASYNC not in self.readout_filter:
251
+ self._readout_filter.append(ReadoutPriority.ASYNC)
252
+ if value is False and ReadoutPriority.ASYNC in self.readout_filter:
253
+ self._readout_filter.remove(ReadoutPriority.ASYNC)
254
+ self.update_devices_from_filters()
255
+
256
+ @Property(bool)
257
+ def readout_continuous(self):
258
+ """Include devices with readout priority continuous in filters."""
259
+ return ReadoutPriority.CONTINUOUS in self.readout_filter
260
+
261
+ @readout_continuous.setter
262
+ def readout_continuous(self, value: bool):
263
+ if value is True and ReadoutPriority.CONTINUOUS not in self.readout_filter:
264
+ self._readout_filter.append(ReadoutPriority.CONTINUOUS)
265
+ if value is False and ReadoutPriority.CONTINUOUS in self.readout_filter:
266
+ self._readout_filter.remove(ReadoutPriority.CONTINUOUS)
267
+ self.update_devices_from_filters()
268
+
269
+ @Property(bool)
270
+ def readout_on_request(self):
271
+ """Include devices with readout priority OnRequest in filters."""
272
+ return ReadoutPriority.ON_REQUEST in self.readout_filter
273
+
274
+ @readout_on_request.setter
275
+ def readout_on_request(self, value: bool):
276
+ if value is True and ReadoutPriority.ON_REQUEST not in self.readout_filter:
277
+ self._readout_filter.append(ReadoutPriority.ON_REQUEST)
278
+ if value is False and ReadoutPriority.ON_REQUEST in self.readout_filter:
279
+ self._readout_filter.remove(ReadoutPriority.ON_REQUEST)
280
+ self.update_devices_from_filters()
281
+
282
+ ### Python Methods and Properties ###
283
+
284
+ @property
285
+ def device_filter(self) -> list[object]:
286
+ """Get the list of filters to apply on the devices."""
287
+ return self._device_filter
288
+
289
+ @property
290
+ def readout_filter(self) -> list[str]:
291
+ """Get the list of filters to apply on the devices"""
292
+ return self._readout_filter
293
+
294
+ def get_available_filters(self) -> list:
295
+ """Get the available filters."""
296
+ return [entry for entry in BECDeviceFilter]
297
+
298
+ def get_readout_priority_filters(self) -> list:
299
+ """Get the available readout priority filters."""
300
+ return [entry for entry in ReadoutPriority]
301
+
302
+ def set_device_filter(
303
+ self, filter_selection: str | BECDeviceFilter | list[str] | list[BECDeviceFilter]
304
+ ):
53
305
  """
54
- Set the device filter.
306
+ Set the device filter. If None is passed, no filters are applied and all devices included.
55
307
 
56
308
  Args:
57
- device_filter(str): Device filter, name of the device class.
309
+ filter_selection (str | list[str]): Device filters. It is recommended to make an enum for the filters.
58
310
  """
59
- self.validate_device_filter(device_filter)
60
- self.config.device_filter = device_filter
61
- self._device_filter = device_filter
311
+ filters = None
312
+ if isinstance(filter_selection, list):
313
+ filters = [self._filter_handler.get(entry) for entry in filter_selection]
314
+ if isinstance(filter_selection, str) or isinstance(filter_selection, BECDeviceFilter):
315
+ filters = [self._filter_handler.get(filter_selection)]
316
+ if filters is None or any([entry is None for entry in filters]):
317
+ logger.warning(f"Device filter {filter_selection} is not in the device filter list.")
318
+ return
319
+ for entry in filters:
320
+ setattr(self, entry, True)
62
321
 
63
- def set_default_device(self, default_device: str):
322
+ def set_readout_priority_filter(
323
+ self, filter_selection: str | ReadoutPriority | list[str] | list[ReadoutPriority]
324
+ ):
64
325
  """
65
- Set the default device.
326
+ Set the readout priority filter. If None is passed, all filters are included.
66
327
 
67
328
  Args:
68
- default_device(str): Default device name.
329
+ filter_selection (str | list[str]): Readout priority filters.
69
330
  """
70
- self.validate_device(default_device)
71
- self.config.default = default_device
331
+ filters = None
332
+ if isinstance(filter_selection, list):
333
+ filters = [self._filter_handler.get(entry) for entry in filter_selection]
334
+ if isinstance(filter_selection, str) or isinstance(filter_selection, ReadoutPriority):
335
+ filters = [self._filter_handler.get(filter_selection)]
336
+ if filters is None or any([entry is None for entry in filters]):
337
+ logger.warning(
338
+ f"Readout priority filter {filter_selection} is not in the readout priority list."
339
+ )
340
+ return
341
+ for entry in filters:
342
+ setattr(self, entry, True)
72
343
 
73
- def get_device_list(self, filter: str | list[str] | None = None) -> list[str]:
74
- """
75
- Get the list of device names based on the filter of current BEC client.
344
+ def _check_device_filter(
345
+ self, device: Device | BECSignal | ComputedSignal | Positioner
346
+ ) -> bool:
347
+ """Check if filter for device type is applied or not.
76
348
 
77
349
  Args:
78
- filter(str|None): Class name filter to apply on the device list.
350
+ device(Device | Signal | ComputedSignal | Positioner): Device object.
351
+ """
352
+ return all(isinstance(device, self._device_handler[entry]) for entry in self.device_filter)
79
353
 
80
- Returns:
81
- devices(list[str]): List of device names.
82
- """
83
- all_devices = self.dev.enabled_devices
84
- if filter is not None:
85
- self.validate_device_filter(filter)
86
- if isinstance(filter, str):
87
- filter = [filter]
88
- devices = [device.name for device in all_devices if device.__class__.__name__ in filter]
89
- else:
90
- devices = [device.name for device in all_devices]
91
- return devices
354
+ def _check_readout_filter(
355
+ self, device: Device | BECSignal | ComputedSignal | Positioner
356
+ ) -> bool:
357
+ """Check if filter for readout priority is applied or not.
92
358
 
93
- def get_available_filters(self):
94
- """
95
- Get the available device classes which can be used as filters.
359
+ Args:
360
+ device(Device | Signal | ComputedSignal | Positioner): Device object.
96
361
  """
97
- all_devices = self.dev.enabled_devices
98
- filters = {device.__class__.__name__ for device in all_devices}
99
- return filters
362
+ return device.readout_priority in self.readout_filter
100
363
 
101
- def validate_device_filter(self, filter: str | list[str]) -> None:
364
+ def get_device_object(self, device: str) -> object:
102
365
  """
103
- Validate the device filter if the class name is present in the current BEC instance.
366
+ Get the device object based on the device name.
104
367
 
105
368
  Args:
106
- filter(str|list[str]): Class name to use as a device filter.
369
+ device(str): Device name.
370
+
371
+ Returns:
372
+ object: Device object, can be device of type Device, Positioner, Signal or ComputedSignal.
107
373
  """
108
- if isinstance(filter, str):
109
- filter = [filter]
110
- available_filters = self.get_available_filters()
111
- for f in filter:
112
- if f not in available_filters:
113
- raise ValueError(f"Device filter {f} is not valid.")
374
+ self.validate_device(device)
375
+ dev = getattr(self.dev, device.lower(), None)
376
+ if dev is None:
377
+ raise ValueError(
378
+ f"Device {device} is not found in the device manager {self.dev} as enabled device."
379
+ )
380
+ return dev
114
381
 
115
- def validate_device(self, device: str) -> None:
382
+ def validate_device(self, device: str) -> bool:
116
383
  """
117
- Validate the device if it is present in current BEC instance.
384
+ Validate the device if it is present in the filtered device selection.
118
385
 
119
386
  Args:
120
387
  device(str): Device to validate.
121
388
  """
122
- if device not in self.get_device_list(self.config.device_filter):
123
- raise ValueError(f"Device {device} is not valid.")
389
+ all_devs = [dev.name for dev in self.dev.enabled_devices]
390
+ if device in self.devices and device in all_devs:
391
+ return True
392
+ return False