bec-widgets 2.12.1__py3-none-any.whl → 2.12.3__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 CHANGED
@@ -1,6 +1,34 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.12.3 (2025-06-05)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **crosshair**: Use objectName instead of config for retrieving the monitor name
9
+ ([`edfac87`](https://github.com/bec-project/bec_widgets/commit/edfac87868605b4b755f7732b2841673de53bc3f))
10
+
11
+ - **device_combobox**: Tuple entries of preview signals are checked in DeviceComboBoxes just for the
12
+ relevant device
13
+ ([`12f5233`](https://github.com/bec-project/bec_widgets/commit/12f523374586d55499f80baf56a50b6ef486cd43))
14
+
15
+ - **image**: Preview signals can be used in Image widget; update logic adjusted; closes #683
16
+ ([`2711164`](https://github.com/bec-project/bec_widgets/commit/271116453d1ef5316b19457d04613b6ddc939cdb))
17
+
18
+ ### Build System
19
+
20
+ - Update min dependency of bec to 3.38
21
+ ([`3740ac8`](https://github.com/bec-project/bec_widgets/commit/3740ac8e325a489d59faca648896ffcea29e1a02))
22
+
23
+
24
+ ## v2.12.2 (2025-06-05)
25
+
26
+ ### Bug Fixes
27
+
28
+ - **waveform**: Safeguard for history data access, closes #571; removed return values "none"
29
+ ([`8570538`](https://github.com/bec-project/bec_widgets/commit/85705383e4aff2f83f76d342db0a13380aeca42f))
30
+
31
+
4
32
  ## v2.12.1 (2025-06-05)
5
33
 
6
34
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.12.1
3
+ Version: 2.12.3
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
@@ -9,8 +9,8 @@ Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Topic :: Scientific/Engineering
11
11
  Requires-Python: >=3.10
12
- Requires-Dist: bec-ipython-client<=4.0,>=2.21.4
13
- Requires-Dist: bec-lib<=4.0,>=3.29
12
+ Requires-Dist: bec-ipython-client<=4.0,>=3.38
13
+ Requires-Dist: bec-lib<=4.0,>=3.38
14
14
  Requires-Dist: bec-qthemes>=0.7,~=0.7
15
15
  Requires-Dist: black~=25.0
16
16
  Requires-Dist: isort>=5.13.2,~=5.13
bec_widgets/cli/client.py CHANGED
@@ -1459,12 +1459,12 @@ class Image(RPCBase):
1459
1459
  @rpc_call
1460
1460
  def image(
1461
1461
  self,
1462
- monitor: "str | None" = None,
1462
+ monitor: "str | tuple | None" = None,
1463
1463
  monitor_type: "Literal['auto', '1d', '2d']" = "auto",
1464
1464
  color_map: "str | None" = None,
1465
1465
  color_bar: "Literal['simple', 'full'] | None" = None,
1466
1466
  vrange: "tuple[int, int] | None" = None,
1467
- ) -> "ImageItem":
1467
+ ) -> "ImageItem | None":
1468
1468
  """
1469
1469
  Set the image source and update the image.
1470
1470
 
@@ -458,7 +458,7 @@ class Crosshair(QObject):
458
458
  )
459
459
  self.coordinatesClicked1D.emit(coordinate_to_emit)
460
460
  elif isinstance(item, pg.ImageItem):
461
- name = item.config.monitor or str(id(item))
461
+ name = item.objectName() or str(id(item))
462
462
  x, y = x_snap_values[name], y_snap_values[name]
463
463
  if x is None or y is None:
464
464
  continue
@@ -149,6 +149,25 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
149
149
  self._is_valid_input = False
150
150
  self.update()
151
151
 
152
+ def validate_device(self, device: str) -> bool: # type: ignore[override]
153
+ """
154
+ Extend validation so that preview‑signal pseudo‑devices (labels like
155
+ ``"eiger_preview"``) are accepted as valid choices.
156
+
157
+ The validation run only on device not on the preview‑signal.
158
+
159
+ Args:
160
+ device: The text currently entered/selected.
161
+
162
+ Returns:
163
+ True if the device is a genuine BEC device *or* one of the
164
+ whitelisted preview‑signal entries.
165
+ """
166
+ idx = self.findText(device)
167
+ if idx >= 0 and isinstance(self.itemData(idx), tuple):
168
+ device = self.itemData(idx)[0] # type: ignore[assignment]
169
+ return super().validate_device(device)
170
+
152
171
 
153
172
  if __name__ == "__main__": # pragma: no cover
154
173
  # pylint: disable=import-outside-toplevel
@@ -35,7 +35,7 @@ class ImageConfig(ConnectionConfig):
35
35
 
36
36
 
37
37
  class ImageLayerConfig(BaseModel):
38
- monitor: str | None = Field(None, description="The name of the monitor.")
38
+ monitor: str | tuple | None = Field(None, description="The name of the monitor.")
39
39
  monitor_type: Literal["1d", "2d", "auto"] = Field("auto", description="The type of monitor.")
40
40
  source: Literal["device_monitor_1d", "device_monitor_2d", "auto"] = Field(
41
41
  "auto", description="The source of the image data."
@@ -179,12 +179,12 @@ class Image(ImageBase):
179
179
  @SafeSlot(popup_error=True)
180
180
  def image(
181
181
  self,
182
- monitor: str | None = None,
182
+ monitor: str | tuple | None = None,
183
183
  monitor_type: Literal["auto", "1d", "2d"] = "auto",
184
184
  color_map: str | None = None,
185
185
  color_bar: Literal["simple", "full"] | None = None,
186
186
  vrange: tuple[int, int] | None = None,
187
- ) -> ImageItem:
187
+ ) -> ImageItem | None:
188
188
  """
189
189
  Set the image source and update the image.
190
190
 
@@ -201,21 +201,13 @@ class Image(ImageBase):
201
201
 
202
202
  if self.subscriptions["main"].monitor:
203
203
  self.disconnect_monitor(self.subscriptions["main"].monitor)
204
- self.entry_validator.validate_monitor(monitor)
205
- self.subscriptions["main"].monitor = monitor
206
-
207
- if monitor_type == "1d":
208
- self.subscriptions["main"].source = "device_monitor_1d"
209
- self.subscriptions["main"].monitor_type = "1d"
210
- elif monitor_type == "2d":
211
- self.subscriptions["main"].source = "device_monitor_2d"
212
- self.subscriptions["main"].monitor_type = "2d"
213
- elif monitor_type == "auto":
214
- self.subscriptions["main"].source = "auto"
215
- logger.warning(
216
- f"Updates for '{monitor}' will be fetch from both 1D and 2D monitor endpoints."
217
- )
218
- self.subscriptions["main"].monitor_type = "auto"
204
+ if monitor is None or monitor == "":
205
+ logger.warning(f"No monitor specified, cannot set image, old monitor is unsubscribed")
206
+ return None
207
+ if isinstance(monitor, tuple):
208
+ self.entry_validator.validate_monitor(monitor[0])
209
+ else:
210
+ self.entry_validator.validate_monitor(monitor)
219
211
 
220
212
  self.set_image_update(monitor=monitor, type=monitor_type)
221
213
  if color_map is not None:
@@ -240,7 +232,12 @@ class Image(ImageBase):
240
232
  self.selection_bundle.dim_combo_box,
241
233
  ):
242
234
  combo.blockSignals(True)
243
- self.selection_bundle.device_combo_box.set_device(config.monitor)
235
+ if isinstance(config.monitor, tuple):
236
+ self.selection_bundle.device_combo_box.setCurrentText(
237
+ f"{config.monitor[0]}_{config.monitor[1]}"
238
+ )
239
+ else:
240
+ self.selection_bundle.device_combo_box.setCurrentText(config.monitor)
244
241
  self.selection_bundle.dim_combo_box.setCurrentText(config.monitor_type)
245
242
  for combo in (
246
243
  self.selection_bundle.device_combo_box,
@@ -340,7 +337,8 @@ class Image(ImageBase):
340
337
  ########################################
341
338
  # Connections
342
339
 
343
- def set_image_update(self, monitor: str, type: Literal["1d", "2d", "auto"]):
340
+ @SafeSlot()
341
+ def set_image_update(self, monitor: str | tuple, type: Literal["1d", "2d", "auto"]):
344
342
  """
345
343
  Set the image update method for the given monitor.
346
344
 
@@ -350,37 +348,95 @@ class Image(ImageBase):
350
348
  """
351
349
 
352
350
  # TODO consider moving connecting and disconnecting logic to Image itself if multiple images
353
- if type == "1d":
354
- self.bec_dispatcher.connect_slot(
355
- self.on_image_update_1d, MessageEndpoints.device_monitor_1d(monitor)
356
- )
357
- elif type == "2d":
358
- self.bec_dispatcher.connect_slot(
359
- self.on_image_update_2d, MessageEndpoints.device_monitor_2d(monitor)
360
- )
361
- elif type == "auto":
362
- self.bec_dispatcher.connect_slot(
363
- self.on_image_update_1d, MessageEndpoints.device_monitor_1d(monitor)
364
- )
365
- self.bec_dispatcher.connect_slot(
366
- self.on_image_update_2d, MessageEndpoints.device_monitor_2d(monitor)
367
- )
351
+ if isinstance(monitor, tuple):
352
+ device = self.dev[monitor[0]]
353
+ signal = monitor[1]
354
+ if len(monitor) == 3:
355
+ signal_config = monitor[2]
356
+ else:
357
+ signal_config = device._info["signals"][signal]
358
+ signal_class = signal_config.get("signal_class", None)
359
+ if signal_class != "PreviewSignal":
360
+ logger.warning(f"Signal '{monitor}' is not a PreviewSignal.")
361
+ return
362
+
363
+ ndim = signal_config.get("describe", None).get("signal_info", None).get("ndim", None)
364
+ if ndim is None:
365
+ logger.warning(
366
+ f"Signal '{monitor}' does not have a valid 'ndim' in its signal_info."
367
+ )
368
+ return
369
+
370
+ if ndim == 1:
371
+ self.bec_dispatcher.connect_slot(
372
+ self.on_image_update_1d, MessageEndpoints.device_preview(device.name, signal)
373
+ )
374
+ self.subscriptions["main"].source = "device_monitor_1d"
375
+ self.subscriptions["main"].monitor_type = "1d"
376
+ elif ndim == 2:
377
+ self.bec_dispatcher.connect_slot(
378
+ self.on_image_update_2d, MessageEndpoints.device_preview(device.name, signal)
379
+ )
380
+ self.subscriptions["main"].source = "device_monitor_2d"
381
+ self.subscriptions["main"].monitor_type = "2d"
382
+
383
+ else: # FIXME old monitor 1d/2d endpoint handling, present for backwards compatibility, will be removed in future versions
384
+ if type == "1d":
385
+ self.bec_dispatcher.connect_slot(
386
+ self.on_image_update_1d, MessageEndpoints.device_monitor_1d(monitor)
387
+ )
388
+ self.subscriptions["main"].source = "device_monitor_1d"
389
+ self.subscriptions["main"].monitor_type = "1d"
390
+ elif type == "2d":
391
+ self.bec_dispatcher.connect_slot(
392
+ self.on_image_update_2d, MessageEndpoints.device_monitor_2d(monitor)
393
+ )
394
+ self.subscriptions["main"].source = "device_monitor_2d"
395
+ self.subscriptions["main"].monitor_type = "2d"
396
+ elif type == "auto":
397
+ self.bec_dispatcher.connect_slot(
398
+ self.on_image_update_1d, MessageEndpoints.device_monitor_1d(monitor)
399
+ )
400
+ self.bec_dispatcher.connect_slot(
401
+ self.on_image_update_2d, MessageEndpoints.device_monitor_2d(monitor)
402
+ )
403
+ self.subscriptions["main"].source = "auto"
404
+ logger.warning(
405
+ f"Updates for '{monitor}' will be fetch from both 1D and 2D monitor endpoints."
406
+ )
407
+ self.subscriptions["main"].monitor_type = "auto"
408
+
368
409
  logger.info(f"Connected to {monitor} with type {type}")
369
410
  self.subscriptions["main"].monitor = monitor
370
411
 
371
- def disconnect_monitor(self, monitor: str):
412
+ def disconnect_monitor(self, monitor: str | tuple):
372
413
  """
373
414
  Disconnect the monitor from the image update signals, both 1D and 2D.
374
415
 
375
416
  Args:
376
- monitor(str): The name of the monitor to disconnect.
377
- """
378
- self.bec_dispatcher.disconnect_slot(
379
- self.on_image_update_1d, MessageEndpoints.device_monitor_1d(monitor)
380
- )
381
- self.bec_dispatcher.disconnect_slot(
382
- self.on_image_update_2d, MessageEndpoints.device_monitor_2d(monitor)
383
- )
417
+ monitor(str|tuple): The name of the monitor to disconnect, or a tuple of (device, signal) for preview signals.
418
+ """
419
+ if isinstance(monitor, tuple):
420
+ if self.subscriptions["main"].source == "device_monitor_1d":
421
+ self.bec_dispatcher.disconnect_slot(
422
+ self.on_image_update_1d, MessageEndpoints.device_preview(monitor[0], monitor[1])
423
+ )
424
+ elif self.subscriptions["main"].source == "device_monitor_2d":
425
+ self.bec_dispatcher.disconnect_slot(
426
+ self.on_image_update_2d, MessageEndpoints.device_preview(monitor[0], monitor[1])
427
+ )
428
+ else:
429
+ logger.warning(
430
+ f"Cannot disconnect monitor {monitor} with source {self.subscriptions['main'].source}"
431
+ )
432
+ return
433
+ else: # FIXME old monitor 1d/2d endpoint handling, present for backwards compatibility, will be removed in future versions
434
+ self.bec_dispatcher.disconnect_slot(
435
+ self.on_image_update_1d, MessageEndpoints.device_monitor_1d(monitor)
436
+ )
437
+ self.bec_dispatcher.disconnect_slot(
438
+ self.on_image_update_2d, MessageEndpoints.device_monitor_2d(monitor)
439
+ )
384
440
  self.subscriptions["main"].monitor = None
385
441
  self._sync_device_selection()
386
442
 
@@ -1,5 +1,5 @@
1
1
  from bec_lib.device import ReadoutPriority
2
- from qtpy.QtCore import Qt
2
+ from qtpy.QtCore import Qt, QTimer
3
3
  from qtpy.QtWidgets import QComboBox, QStyledItemDelegate
4
4
 
5
5
  from bec_widgets.utils.error_popups import SafeSlot
@@ -50,11 +50,58 @@ class MonitorSelectionToolbarBundle(ToolbarBundle):
50
50
 
51
51
  self.add_action("dim_combo", WidgetAction(widget=self.dim_combo_box, adjust_size=False))
52
52
 
53
- # Connect slots, a device will be connected upon change of any combobox
54
- self.device_combo_box.currentTextChanged.connect(lambda: self.connect_monitor())
55
- self.dim_combo_box.currentTextChanged.connect(lambda: self.connect_monitor())
53
+ self.device_combo_box.currentTextChanged.connect(self.connect_monitor)
54
+ self.dim_combo_box.currentTextChanged.connect(self.connect_monitor)
55
+
56
+ QTimer.singleShot(0, self._adjust_and_connect)
57
+
58
+ def _adjust_and_connect(self):
59
+ """
60
+ Adjust the size of the device combo box and populate it with preview signals.
61
+ Has to be done with QTimer.singleShot to ensure the UI is fully initialized, needed for testing.
62
+ """
63
+ self._populate_preview_signals()
64
+ self._reverse_device_items()
65
+ self.device_combo_box.setCurrentText("") # set again default to empty string
66
+
67
+ def _populate_preview_signals(self) -> None:
68
+ """
69
+ Populate the device combo box with preview‑signal devices in the
70
+ format '<device>_<signal>' and store the tuple(device, signal) in
71
+ the item's userData for later use.
72
+ """
73
+ preview_signals = self.target_widget.client.device_manager.get_bec_signals("PreviewSignal")
74
+ for device, signal, signal_config in preview_signals:
75
+ label = signal_config.get("obj_name", f"{device}_{signal}")
76
+ self.device_combo_box.addItem(label, (device, signal, signal_config))
77
+
78
+ def _reverse_device_items(self) -> None:
79
+ """
80
+ Reverse the current order of items in the device combo box while
81
+ keeping their userData and restoring the previous selection.
82
+ """
83
+ current_text = self.device_combo_box.currentText()
84
+ items = [
85
+ (self.device_combo_box.itemText(i), self.device_combo_box.itemData(i))
86
+ for i in range(self.device_combo_box.count())
87
+ ]
88
+ self.device_combo_box.clear()
89
+ for text, data in reversed(items):
90
+ self.device_combo_box.addItem(text, data)
91
+ if current_text:
92
+ self.device_combo_box.setCurrentText(current_text)
56
93
 
57
94
  @SafeSlot()
58
- def connect_monitor(self):
95
+ def connect_monitor(self, *args, **kwargs):
96
+ """
97
+ Connect the target widget to the selected monitor based on the current device and dimension.
98
+
99
+ If the selected device is a preview-signal device, it will use the tuple (device, signal) as the monitor.
100
+ """
59
101
  dim = self.dim_combo_box.currentText()
60
- self.target_widget.image(monitor=self.device_combo_box.currentText(), monitor_type=dim)
102
+ data = self.device_combo_box.currentData()
103
+
104
+ if isinstance(data, tuple):
105
+ self.target_widget.image(monitor=data, monitor_type="auto")
106
+ else:
107
+ self.target_widget.image(monitor=self.device_combo_box.currentText(), monitor_type=dim)
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- from typing import Literal
4
+ from typing import Any, Literal
5
5
 
6
6
  import lmfit
7
7
  import numpy as np
@@ -163,7 +163,7 @@ class Waveform(PlotBase):
163
163
  self._async_curves = []
164
164
  self._slice_index = None
165
165
  self._dap_curves = []
166
- self._mode: Literal["none", "sync", "async", "mixed"] = "none"
166
+ self._mode = None
167
167
 
168
168
  # Scan data
169
169
  self._scan_done = True # means scan is not running
@@ -1139,7 +1139,7 @@ class Waveform(PlotBase):
1139
1139
  QTimer.singleShot(100, self.update_sync_curves)
1140
1140
  QTimer.singleShot(300, self.update_sync_curves)
1141
1141
 
1142
- def _fetch_scan_data_and_access(self):
1142
+ def _fetch_scan_data_and_access(self) -> tuple[dict, str] | tuple[None, None]:
1143
1143
  """
1144
1144
  Decide whether the widget is in live or historical mode
1145
1145
  and return the appropriate data dict and access key.
@@ -1153,7 +1153,7 @@ class Waveform(PlotBase):
1153
1153
  self.update_with_scan_history(-1)
1154
1154
  if self.scan_item is None:
1155
1155
  logger.info("No scan executed so far; skipping device curves categorisation.")
1156
- return "none", "none"
1156
+ return None, None
1157
1157
 
1158
1158
  if hasattr(self.scan_item, "live_data"):
1159
1159
  # Live scan
@@ -1169,7 +1169,7 @@ class Waveform(PlotBase):
1169
1169
  """
1170
1170
  if self.scan_item is None:
1171
1171
  logger.info("No scan executed so far; skipping device curves categorisation.")
1172
- return "none"
1172
+ return
1173
1173
  data, access_key = self._fetch_scan_data_and_access()
1174
1174
  for curve in self._sync_curves:
1175
1175
  device_name = curve.config.signal.name
@@ -1177,9 +1177,8 @@ class Waveform(PlotBase):
1177
1177
  if access_key == "val":
1178
1178
  device_data = data.get(device_name, {}).get(device_entry, {}).get(access_key, None)
1179
1179
  else:
1180
- device_data = (
1181
- data.get(device_name, {}).get(device_entry, {}).read().get("value", None)
1182
- )
1180
+ entry_obj = data.get(device_name, {}).get(device_entry)
1181
+ device_data = entry_obj.read()["value"] if entry_obj else None
1183
1182
  x_data = self._get_x_data(device_name, device_entry)
1184
1183
  if x_data is not None:
1185
1184
  if len(x_data) == 1:
@@ -1217,7 +1216,8 @@ class Waveform(PlotBase):
1217
1216
  if self._skip_large_dataset_check is False:
1218
1217
  if not self._check_dataset_size_and_confirm(dataset_obj, device_entry):
1219
1218
  continue # user declined to load; skip this curve
1220
- device_data = dataset_obj.get(device_entry, {}).read().get("value", None)
1219
+ entry_obj = dataset_obj.get(device_entry, None)
1220
+ device_data = entry_obj.read()["value"] if entry_obj else None
1221
1221
 
1222
1222
  # if shape is 2D cast it into 1D and take the last waveform
1223
1223
  if len(np.shape(device_data)) > 1:
@@ -1549,15 +1549,21 @@ class Waveform(PlotBase):
1549
1549
  if access_key == "val": # live data
1550
1550
  x_data = data.get(x_name, {}).get(x_entry, {}).get(access_key, [0])
1551
1551
  else: # history data
1552
- x_data = data.get(x_name, {}).get(x_entry, {}).read().get("value", [0])
1552
+ entry_obj = data.get(x_name, {}).get(x_entry)
1553
+ x_data = entry_obj.read()["value"] if entry_obj else [0]
1553
1554
  new_suffix = f" (custom: {x_name}-{x_entry})"
1554
1555
 
1555
1556
  # 2 User wants timestamp
1556
1557
  if self.x_axis_mode["name"] == "timestamp":
1557
1558
  if access_key == "val": # live
1558
- timestamps = data[device_name][device_entry].timestamps
1559
+ x_data = data.get(device_name, {}).get(device_entry, None)
1560
+ if x_data is None:
1561
+ return None
1562
+ else:
1563
+ timestamps = x_data.timestamps
1559
1564
  else: # history data
1560
- timestamps = data[device_name][device_entry].read().get("timestamp", [0])
1565
+ entry_obj = data.get(device_name, {}).get(device_entry)
1566
+ timestamps = entry_obj.read()["timestamp"] if entry_obj else [0]
1561
1567
  x_data = timestamps
1562
1568
  new_suffix = " (timestamp)"
1563
1569
 
@@ -1584,7 +1590,8 @@ class Waveform(PlotBase):
1584
1590
  if access_key == "val":
1585
1591
  x_data = data.get(x_name, {}).get(x_entry, {}).get(access_key, None)
1586
1592
  else:
1587
- x_data = data.get(x_name, {}).get(x_entry, {}).read().get("value", None)
1593
+ entry_obj = data.get(x_name, {}).get(x_entry)
1594
+ x_data = entry_obj.read()["value"] if entry_obj else None
1588
1595
  new_suffix = f" (auto: {x_name}-{x_entry})"
1589
1596
  self._update_x_label_suffix(new_suffix)
1590
1597
  return x_data
@@ -1637,7 +1644,7 @@ class Waveform(PlotBase):
1637
1644
  self.update_with_scan_history(-1)
1638
1645
  if self.scan_item is None:
1639
1646
  logger.info("No scan executed so far; skipping device curves categorisation.")
1640
- return "none"
1647
+ return None
1641
1648
 
1642
1649
  if hasattr(self.scan_item, "live_data"):
1643
1650
  readout_priority = self.scan_item.status_message.info["readout_priority"] # live data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.12.1
3
+ Version: 2.12.3
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
@@ -9,8 +9,8 @@ Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Topic :: Scientific/Engineering
11
11
  Requires-Python: >=3.10
12
- Requires-Dist: bec-ipython-client<=4.0,>=2.21.4
13
- Requires-Dist: bec-lib<=4.0,>=3.29
12
+ Requires-Dist: bec-ipython-client<=4.0,>=3.38
13
+ Requires-Dist: bec-lib<=4.0,>=3.38
14
14
  Requires-Dist: bec-qthemes>=0.7,~=0.7
15
15
  Requires-Dist: black~=25.0
16
16
  Requires-Dist: isort>=5.13.2,~=5.13
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=1nMYldzVk0tFkBWYTcUjumOrdSADASheWOAc0kOFDYs,9509
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=ivqg3HTaOxNbEW3bzWh9MXAkrekuGoNdj0Mj3SdRYuw,639
5
- CHANGELOG.md,sha256=aibe9NzesiaBxpYGGLEcieGO0X5K2kNdChWbS1Lkz_8,296944
5
+ CHANGELOG.md,sha256=-3WQU4pTRoYhYRYqq3l_pU9H6tevpGKSbPIdIk2DUOY,298015
6
6
  LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
7
- PKG-INFO,sha256=wptvRuS9Q-MU6MikQcSUQChj8FQ8Wm-AQ86iE9P902s,1254
7
+ PKG-INFO,sha256=r3TUzt3yd2hxbLa3PN3y3K3nXlrDOkCkYjQOvuFc_rw,1252
8
8
  README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
9
- pyproject.toml,sha256=YCScLsK5LsAQvYfmpjvE0C6r1rScg7M9VCG9-I7h7tQ,2835
9
+ pyproject.toml,sha256=yNICdyTZSQATRSjrmwHJ2Jh832NbrsObMzGZmc7t2wU,2827
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .github/pull_request_template.md,sha256=F_cJXzooWMFgMGtLK-7KeGcQt0B4AYFse5oN0zQ9p6g,801
12
12
  .github/ISSUE_TEMPLATE/bug_report.yml,sha256=WdRnt7HGxvsIBLzhkaOWNfg8IJQYa_oV9_F08Ym6znQ,1081
@@ -35,7 +35,7 @@ bec_widgets/assets/app_icons/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqg
35
35
  bec_widgets/assets/app_icons/ui_loader_tile.png,sha256=qSK3XHqvnAVGV9Q0ulORcGFbXJ9LDq2uz8l9uTtMsNk,1812476
36
36
  bec_widgets/assets/app_icons/widget_launch_tile.png,sha256=bWsICHFfSe9-ESUj3AwlE95dDOea-f6M-s9fBapsxB4,2252911
37
37
  bec_widgets/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- bec_widgets/cli/client.py,sha256=YcHF_WYmCy4DcgLhYsjVuzESxvjOlG5Ip6E3v2GmBCk,97938
38
+ bec_widgets/cli/client.py,sha256=MIHk1aBnopQ9qc9QIEldvbO2C5xW4I6U7WYjyOvaMZw,97953
39
39
  bec_widgets/cli/client_utils.py,sha256=F2hyt--jL53bN8NoWifNUMqwwx5FbpS6I1apERdTRzM,18114
40
40
  bec_widgets/cli/generate_cli.py,sha256=K_wMxo2XBUn92SnY3dSrlyUn8ax6Y20QBGCuP284DsQ,10986
41
41
  bec_widgets/cli/server.py,sha256=h7QyBOOGjyrP_fxJIIOSEMc4E06cLG0JyaofjNV6oCA,5671
@@ -72,7 +72,7 @@ bec_widgets/utils/collapsible_panel_manager.py,sha256=tvv77-9YTfYpsU6M_Le3bHR6wt
72
72
  bec_widgets/utils/colors.py,sha256=i1DuwwdXzRs7HVq9m5wXSaZOJwLX5mJPvV6GAqylZhM,18341
73
73
  bec_widgets/utils/compact_popup.py,sha256=xVK_lQqL5Hy1ZnUzHXB8GU-Ru-mXevKcdM8ync3ssiA,10269
74
74
  bec_widgets/utils/container_utils.py,sha256=J8YXombOlAPa3M8NGZdhntp2NirBu4raDEQZOgP4elM,3791
75
- bec_widgets/utils/crosshair.py,sha256=d4h3uHh9ZvW0PlI0yWI-kKvgLQP7E6HJoCPzNvojHr0,22306
75
+ bec_widgets/utils/crosshair.py,sha256=nqBPQqWzoTLZ-sPBR6ONm7M1TtGGD2EpRwm2iSNpoFo,22304
76
76
  bec_widgets/utils/entry_validator.py,sha256=lwT8HP0RDG1FXENIeZ3IDEF2DQmD8KXGkRxPoMXbryk,1817
77
77
  bec_widgets/utils/error_popups.py,sha256=UBAmD1YlAgKodpihudyf0VWtI59KGFiLgnjiKmKGjgk,13254
78
78
  bec_widgets/utils/expandable_frame.py,sha256=ynqRFwXsd6jWT5C6qjNwmmHY9Rm9UCALeL32OPQkYgw,3785
@@ -183,7 +183,7 @@ bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.p
183
183
  bec_widgets/widgets/control/device_input/device_combobox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
184
184
  bec_widgets/widgets/control/device_input/device_combobox/device_combo_box.pyproject,sha256=wI2eXR5ky_IM9-BCHJnH_9CEqYcZwIuLcgitSEr8OJU,40
185
185
  bec_widgets/widgets/control/device_input/device_combobox/device_combo_box_plugin.py,sha256=E8LD9T4O2w621q25uHqBqZLDiQ6zpMR25ZDuf51jrPw,1434
186
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py,sha256=babUAPI8St58FV13b4RZJ10DXcj-2mlYcNKKsdpB7Hs,6507
186
+ bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py,sha256=8ANCTgPtO_Fhhy1ZBUfr9Yx_aArnePgkNORYwnXE-Fw,7251
187
187
  bec_widgets/widgets/control/device_input/device_combobox/register_device_combo_box.py,sha256=elw4M4xfIFWe8C0MkdqqqyfnyOVrdl0g0j6bqwOU1GE,526
188
188
  bec_widgets/widgets/control/device_input/device_line_edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
189
189
  bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py,sha256=k87NtUD2TUB4c6-Ks-eZgafWbjTwbk0RKiqf9-OkjMk,7415
@@ -256,7 +256,7 @@ bec_widgets/widgets/games/register_minesweeper.py,sha256=8fgMBD3yB-5_eGqhG_qxpj3
256
256
  bec_widgets/widgets/plots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
257
257
  bec_widgets/widgets/plots/plot_base.py,sha256=GETUsx51BE_Tuop8bC-KiFVrkR82TJ5S0cr7siouSWM,35848
258
258
  bec_widgets/widgets/plots/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
259
- bec_widgets/widgets/plots/image/image.py,sha256=8s9DShQVAz458aPKfJTCWEmg8HoagCX3MkjFJZXSmek,17278
259
+ bec_widgets/widgets/plots/image/image.py,sha256=wrI9C2xNEoQmCWoKjlSdRHroWHffIg_DFT4fNDTgAyE,20299
260
260
  bec_widgets/widgets/plots/image/image.pyproject,sha256=_sRCIu4MNgToaB4D7tUMWq3xKX6T2VoRS3UzGNIseHQ,23
261
261
  bec_widgets/widgets/plots/image/image_base.py,sha256=_c4rYRX2AwGyy0WWUhaAJl3gNfXSq1ts99MG8rsa-6w,36104
262
262
  bec_widgets/widgets/plots/image/image_item.py,sha256=rkL1o35Pgs1zhvv2wpSG1gt_bjP5kO4Z1oy6d2q-Yls,8577
@@ -267,7 +267,7 @@ bec_widgets/widgets/plots/image/register_image.py,sha256=0rvFyAMGRZcknc7nMVwsMGS
267
267
  bec_widgets/widgets/plots/image/setting_widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
268
268
  bec_widgets/widgets/plots/image/setting_widgets/image_roi_tree.py,sha256=nlllg-yTNCjG5DxCEDSGzCNabNSTJ3TZn9mawzIiAq4,14255
269
269
  bec_widgets/widgets/plots/image/toolbar_bundles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
270
- bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py,sha256=gJhDAdHB4cAsPw7E6W6Y2iR3nF_3n_v-ElGqj4TgIGo,2646
270
+ bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py,sha256=ezs2TWZCz-3npbIFEiYuHNiuSvJptJznTEZWD_CX3po,4705
271
271
  bec_widgets/widgets/plots/image/toolbar_bundles/processing.py,sha256=99hgd1q86ZUhQYTTsFCk3Ml8oAEeZJy-WqdmsMO4bzA,3367
272
272
  bec_widgets/widgets/plots/motor_map/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
273
273
  bec_widgets/widgets/plots/motor_map/motor_map.py,sha256=pcQStrIJJOsxC2sW3FDMDIFmFXM94uXQVdAjPi-vBvM,29209
@@ -313,7 +313,7 @@ bec_widgets/widgets/plots/toolbar_bundles/save_state.py,sha256=H3fu-bRzNIycCUFb2
313
313
  bec_widgets/widgets/plots/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
314
314
  bec_widgets/widgets/plots/waveform/curve.py,sha256=KlcGbd60lPO9BYcca08fMhWkfx5qu4O9IbSNTwvajGM,10401
315
315
  bec_widgets/widgets/plots/waveform/register_waveform.py,sha256=pdcLCYKkLkSb-5DqbJdC6M3JyoXQBRVAKf7BZVCto80,467
316
- bec_widgets/widgets/plots/waveform/waveform.py,sha256=_EmQTWiPJmUzDo8PmvLd2gshzE-IECtswfaY7nCTZb4,75499
316
+ bec_widgets/widgets/plots/waveform/waveform.py,sha256=S-9Be3gbzpztZk3GyEI7MDH2m0Xl0l3g7TN5GYcbjpw,75850
317
317
  bec_widgets/widgets/plots/waveform/waveform.pyproject,sha256=X2T6d4JGt9YSI28e-myjXh1YkUM4Yr3kNb0-F84KvUA,26
318
318
  bec_widgets/widgets/plots/waveform/waveform_plugin.py,sha256=2AZPtBHs75l9cdhwQnY3jpIMRPUUqK3RNvQbTvrFyvg,1237
319
319
  bec_widgets/widgets/plots/waveform/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -407,8 +407,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
407
407
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
408
408
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
409
409
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
410
- bec_widgets-2.12.1.dist-info/METADATA,sha256=wptvRuS9Q-MU6MikQcSUQChj8FQ8Wm-AQ86iE9P902s,1254
411
- bec_widgets-2.12.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
412
- bec_widgets-2.12.1.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
413
- bec_widgets-2.12.1.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
414
- bec_widgets-2.12.1.dist-info/RECORD,,
410
+ bec_widgets-2.12.3.dist-info/METADATA,sha256=r3TUzt3yd2hxbLa3PN3y3K3nXlrDOkCkYjQOvuFc_rw,1252
411
+ bec_widgets-2.12.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
412
+ bec_widgets-2.12.3.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
413
+ bec_widgets-2.12.3.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
414
+ bec_widgets-2.12.3.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "2.12.1"
7
+ version = "2.12.3"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -13,15 +13,15 @@ classifiers = [
13
13
  "Topic :: Scientific/Engineering",
14
14
  ]
15
15
  dependencies = [
16
- "bec_ipython_client>=2.21.4, <=4.0", # needed for jupyter console
17
- "bec_lib>=3.29, <=4.0",
16
+ "bec_ipython_client>=3.38, <=4.0", # needed for jupyter console
17
+ "bec_lib>=3.38, <=4.0",
18
18
  "bec_qthemes~=0.7, >=0.7",
19
- "black~=25.0", # needed for bw-generate-cli
20
- "isort~=5.13, >=5.13.2", # needed for bw-generate-cli
19
+ "black~=25.0", # needed for bw-generate-cli
20
+ "isort~=5.13, >=5.13.2", # needed for bw-generate-cli
21
21
  "pydantic~=2.0",
22
22
  "pyqtgraph~=0.13",
23
23
  "PySide6~=6.8.2",
24
- "qtconsole~=5.5, >=5.5.1", # needed for jupyter console
24
+ "qtconsole~=5.5, >=5.5.1", # needed for jupyter console
25
25
  "qtpy~=2.4",
26
26
  ]
27
27