bec-widgets 2.17.0__py3-none-any.whl → 2.19.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.
@@ -21,7 +21,7 @@ jobs:
21
21
  isort --check --diff ./
22
22
 
23
23
  - name: Check for disallowed imports from PySide
24
- run: '! grep -re "from PySide6\." bec_widgets/ | grep -v -e "PySide6.QtDesigner" -e "PySide6.scripts"'
24
+ run: '! grep -re "from PySide6\." bec_widgets/ tests/ | grep -v -e "PySide6.QtDesigner" -e "PySide6.scripts"'
25
25
 
26
26
  Pylint:
27
27
  runs-on: ubuntu-latest
CHANGELOG.md CHANGED
@@ -1,6 +1,55 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.19.0 (2025-06-23)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **ci**: Extend check for pyside import to tests
9
+ ([`d5a40da`](https://github.com/bec-project/bec_widgets/commit/d5a40dabc74753acad05e3eb6b121499fc1e03d7))
10
+
11
+ ### Features
12
+
13
+ - (#494) add signal display to device browser
14
+ ([`f3da6e9`](https://github.com/bec-project/bec_widgets/commit/f3da6e959e0416827ee5d02e34e6ad0ecfc8e5e7))
15
+
16
+ - (#494) add tabbed layout for device item
17
+ ([`3378051`](https://github.com/bec-project/bec_widgets/commit/337805125098c3e028a17b74ef6d9ae4b9ba3d6d))
18
+
19
+ - (#494) display device signals
20
+ ([`3a10341`](https://github.com/bec-project/bec_widgets/commit/3a103410e7448256a56b59bb3276fee056ec42a0))
21
+
22
+
23
+ ## v2.18.0 (2025-06-22)
24
+
25
+ ### Bug Fixes
26
+
27
+ - Make settings dialog resizable
28
+ ([`5a564a5`](https://github.com/bec-project/bec_widgets/commit/5a564a5f3f3229e6407ea52a59d3e63319dc214a))
29
+
30
+ - **curve settings**: Add initial size hint
31
+ ([`a9708f6`](https://github.com/bec-project/bec_widgets/commit/a9708f6d8f15c42b142488da1e392a8f3179932a))
32
+
33
+ ### Features
34
+
35
+ - **curve settings**: Add combobox selection for device and signal
36
+ ([`eea5f7e`](https://github.com/bec-project/bec_widgets/commit/eea5f7ebbd2b477b3ed19c7efcc76390dd391f26))
37
+
38
+ - **device combobox**: Emit reset event if validation fails
39
+ ([`4c2c0c5`](https://github.com/bec-project/bec_widgets/commit/4c2c0c5525d593d8ec7fd554336cb11adbe32de2))
40
+
41
+ - **FilterIO**: Add support for item data
42
+ ([`8e8acd6`](https://github.com/bec-project/bec_widgets/commit/8e8acd672c0deb8dcd928886fb574452ac956de7))
43
+
44
+ - **signal combobox**: Add reset_selection slot
45
+ ([`b51de1a`](https://github.com/bec-project/bec_widgets/commit/b51de1a00e4b17c44cab23e5097391c6fa8ea0e2))
46
+
47
+ ### Refactoring
48
+
49
+ - **device input**: Refactor to SafeProperty and SafeSlot
50
+ ([`6e2f2ce`](https://github.com/bec-project/bec_widgets/commit/6e2f2cea91ba3af33e9891532506f4b0b65b90c8))
51
+
52
+
4
53
  ## v2.17.0 (2025-06-22)
5
54
 
6
55
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.17.0
3
+ Version: 2.19.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
@@ -96,9 +96,21 @@ class FakePositioner(BECPositioner):
96
96
  }
97
97
  self._info = {
98
98
  "signals": {
99
- "readback": {"kind_str": "hinted"}, # hinted
100
- "setpoint": {"kind_str": "normal"}, # normal
101
- "velocity": {"kind_str": "config"}, # config
99
+ "readback": {
100
+ "kind_str": "hinted",
101
+ "component_name": "readback",
102
+ "obj_name": self.name,
103
+ }, # hinted
104
+ "setpoint": {
105
+ "kind_str": "normal",
106
+ "component_name": "setpoint",
107
+ "obj_name": f"{self.name}_setpoint",
108
+ }, # normal
109
+ "velocity": {
110
+ "kind_str": "config",
111
+ "component_name": "velocity",
112
+ "obj_name": f"{self.name}_velocity",
113
+ }, # config
102
114
  }
103
115
  }
104
116
  self.signals = {
@@ -8,6 +8,8 @@ from bec_lib.logger import bec_logger
8
8
  from qtpy.QtCore import QStringListModel
9
9
  from qtpy.QtWidgets import QComboBox, QCompleter, QLineEdit
10
10
 
11
+ from bec_widgets.utils.ophyd_kind_util import Kind
12
+
11
13
  logger = bec_logger.logger
12
14
 
13
15
 
@@ -15,11 +17,13 @@ class WidgetFilterHandler(ABC):
15
17
  """Abstract base class for widget filter handlers"""
16
18
 
17
19
  @abstractmethod
18
- def set_selection(self, widget, selection: list) -> None:
20
+ def set_selection(self, widget, selection: list[str | tuple]) -> None:
19
21
  """Set the filtered_selection for the widget
20
22
 
21
23
  Args:
22
- selection (list): Filtered selection of items
24
+ widget: Widget instance
25
+ selection (list[str | tuple]): Filtered selection of items.
26
+ If tuple, it contains (text, data) pairs.
23
27
  """
24
28
 
25
29
  @abstractmethod
@@ -34,17 +38,37 @@ class WidgetFilterHandler(ABC):
34
38
  bool: True if the input text is in the filtered selection
35
39
  """
36
40
 
41
+ @abstractmethod
42
+ def update_with_kind(
43
+ self, kind: Kind, signal_filter: set, device_info: dict, device_name: str
44
+ ) -> list[str | tuple]:
45
+ """Update the selection based on the kind of signal.
46
+
47
+ Args:
48
+ kind (Kind): The kind of signal to filter.
49
+ signal_filter (set): Set of signal kinds to filter.
50
+ device_info (dict): Dictionary containing device information.
51
+ device_name (str): Name of the device.
52
+
53
+ Returns:
54
+ list[str | tuple]: A list of filtered signals based on the kind.
55
+ """
56
+ # This method should be implemented in subclasses or extended as needed
57
+
37
58
 
38
59
  class LineEditFilterHandler(WidgetFilterHandler):
39
60
  """Handler for QLineEdit widget"""
40
61
 
41
- def set_selection(self, widget: QLineEdit, selection: list) -> None:
62
+ def set_selection(self, widget: QLineEdit, selection: list[str | tuple]) -> None:
42
63
  """Set the selection for the widget to the completer model
43
64
 
44
65
  Args:
45
66
  widget (QLineEdit): The QLineEdit widget
46
- selection (list): Filtered selection of items
67
+ selection (list[str | tuple]): Filtered selection of items. If tuple, it contains (text, data) pairs.
47
68
  """
69
+ if isinstance(selection, tuple):
70
+ # If selection is a tuple, it contains (text, data) pairs
71
+ selection = [text for text, _ in selection]
48
72
  if not isinstance(widget.completer, QCompleter):
49
73
  completer = QCompleter(widget)
50
74
  widget.setCompleter(completer)
@@ -64,19 +88,47 @@ class LineEditFilterHandler(WidgetFilterHandler):
64
88
  model_data = [model.data(model.index(i)) for i in range(model.rowCount())]
65
89
  return text in model_data
66
90
 
91
+ def update_with_kind(
92
+ self, kind: Kind, signal_filter: set, device_info: dict, device_name: str
93
+ ) -> list[str | tuple]:
94
+ """Update the selection based on the kind of signal.
95
+
96
+ Args:
97
+ kind (Kind): The kind of signal to filter.
98
+ signal_filter (set): Set of signal kinds to filter.
99
+ device_info (dict): Dictionary containing device information.
100
+ device_name (str): Name of the device.
101
+
102
+ Returns:
103
+ list[str | tuple]: A list of filtered signals based on the kind.
104
+ """
105
+
106
+ return [
107
+ signal
108
+ for signal, signal_info in device_info.items()
109
+ if kind in signal_filter and (signal_info.get("kind_str", None) == str(kind.name))
110
+ ]
111
+
67
112
 
68
113
  class ComboBoxFilterHandler(WidgetFilterHandler):
69
114
  """Handler for QComboBox widget"""
70
115
 
71
- def set_selection(self, widget: QComboBox, selection: list) -> None:
116
+ def set_selection(self, widget: QComboBox, selection: list[str | tuple]) -> None:
72
117
  """Set the selection for the widget to the completer model
73
118
 
74
119
  Args:
75
120
  widget (QComboBox): The QComboBox widget
76
- selection (list): Filtered selection of items
121
+ selection (list[str | tuple]): Filtered selection of items. If tuple, it contains (text, data) pairs.
77
122
  """
78
123
  widget.clear()
79
- widget.addItems(selection)
124
+ if len(selection) == 0:
125
+ return
126
+ for element in selection:
127
+ if isinstance(element, str):
128
+ widget.addItem(element)
129
+ elif isinstance(element, tuple):
130
+ # If element is a tuple, it contains (text, data) pairs
131
+ widget.addItem(*element)
80
132
 
81
133
  def check_input(self, widget: QComboBox, text: str) -> bool:
82
134
  """Check if the input text is in the filtered selection
@@ -90,6 +142,40 @@ class ComboBoxFilterHandler(WidgetFilterHandler):
90
142
  """
91
143
  return text in [widget.itemText(i) for i in range(widget.count())]
92
144
 
145
+ def update_with_kind(
146
+ self, kind: Kind, signal_filter: set, device_info: dict, device_name: str
147
+ ) -> list[str | tuple]:
148
+ """Update the selection based on the kind of signal.
149
+
150
+ Args:
151
+ kind (Kind): The kind of signal to filter.
152
+ signal_filter (set): Set of signal kinds to filter.
153
+ device_info (dict): Dictionary containing device information.
154
+ device_name (str): Name of the device.
155
+
156
+ Returns:
157
+ list[str | tuple]: A list of filtered signals based on the kind.
158
+ """
159
+ out = []
160
+ for signal, signal_info in device_info.items():
161
+ if kind not in signal_filter or (signal_info.get("kind_str", None) != str(kind.name)):
162
+ continue
163
+ obj_name = signal_info.get("obj_name", "")
164
+ component_name = signal_info.get("component_name", "")
165
+ signal_wo_device = obj_name.removeprefix(f"{device_name}_")
166
+ if not signal_wo_device:
167
+ signal_wo_device = obj_name
168
+
169
+ if signal_wo_device != signal and component_name.replace(".", "_") != signal_wo_device:
170
+ # If the object name is not the same as the signal name, we use the object name
171
+ # to display in the combobox.
172
+ out.append((f"{signal_wo_device} ({signal})", signal_info))
173
+ else:
174
+ # If the object name is the same as the signal name, we do not change it.
175
+ out.append((signal, signal_info))
176
+
177
+ return out
178
+
93
179
 
94
180
  class FilterIO:
95
181
  """Public interface to set filters for input widgets.
@@ -99,13 +185,14 @@ class FilterIO:
99
185
  _handlers = {QLineEdit: LineEditFilterHandler, QComboBox: ComboBoxFilterHandler}
100
186
 
101
187
  @staticmethod
102
- def set_selection(widget, selection: list, ignore_errors=True):
188
+ def set_selection(widget, selection: list[str | tuple], ignore_errors=True):
103
189
  """
104
190
  Retrieve value from the widget instance.
105
191
 
106
192
  Args:
107
193
  widget: Widget instance.
108
- selection(list): List of filtered selection items.
194
+ selection (list[str | tuple]): Filtered selection of items.
195
+ If tuple, it contains (text, data) pairs.
109
196
  ignore_errors(bool, optional): Whether to ignore if no handler is found.
110
197
  """
111
198
  handler_class = FilterIO._find_handler(widget)
@@ -139,6 +226,35 @@ class FilterIO:
139
226
  )
140
227
  return None
141
228
 
229
+ @staticmethod
230
+ def update_with_kind(
231
+ widget, kind: Kind, signal_filter: set, device_info: dict, device_name: str
232
+ ) -> list[str | tuple]:
233
+ """
234
+ Update the selection based on the kind of signal.
235
+
236
+ Args:
237
+ widget: Widget instance.
238
+ kind (Kind): The kind of signal to filter.
239
+ signal_filter (set): Set of signal kinds to filter.
240
+ device_info (dict): Dictionary containing device information.
241
+ device_name (str): Name of the device.
242
+
243
+ Returns:
244
+ list[str | tuple]: A list of filtered signals based on the kind.
245
+ """
246
+ handler_class = FilterIO._find_handler(widget)
247
+ if handler_class:
248
+ return handler_class().update_with_kind(
249
+ kind=kind,
250
+ signal_filter=signal_filter,
251
+ device_info=device_info,
252
+ device_name=device_name,
253
+ )
254
+ raise ValueError(
255
+ f"No matching handler for widget type: {type(widget)} in handler list {FilterIO._handlers}"
256
+ )
257
+
142
258
  @staticmethod
143
259
  def _find_handler(widget):
144
260
  """
@@ -6,10 +6,10 @@ from bec_lib.device import ComputedSignal, Device, Positioner, ReadoutPriority
6
6
  from bec_lib.device import Signal as BECSignal
7
7
  from bec_lib.logger import bec_logger
8
8
  from pydantic import field_validator
9
- from qtpy.QtCore import Property, Signal, Slot
10
9
 
11
10
  from bec_widgets.utils import ConnectionConfig
12
11
  from bec_widgets.utils.bec_widget import BECWidget
12
+ from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
13
13
  from bec_widgets.utils.filter_io import FilterIO
14
14
  from bec_widgets.utils.widget_io import WidgetIO
15
15
 
@@ -100,7 +100,7 @@ class DeviceInputBase(BECWidget):
100
100
 
101
101
  ### QtSlots ###
102
102
 
103
- @Slot(str)
103
+ @SafeSlot(str)
104
104
  def set_device(self, device: str):
105
105
  """
106
106
  Set the device.
@@ -114,7 +114,7 @@ class DeviceInputBase(BECWidget):
114
114
  else:
115
115
  logger.warning(f"Device {device} is not in the filtered selection.")
116
116
 
117
- @Slot()
117
+ @SafeSlot()
118
118
  def update_devices_from_filters(self):
119
119
  """Update the devices based on the current filter selection
120
120
  in self.device_filter and self.readout_filter. If apply_filter is False,
@@ -133,7 +133,7 @@ class DeviceInputBase(BECWidget):
133
133
  self.devices = [device.name for device in devs]
134
134
  self.set_device(current_device)
135
135
 
136
- @Slot(list)
136
+ @SafeSlot(list)
137
137
  def set_available_devices(self, devices: list[str]):
138
138
  """
139
139
  Set the devices. If a device in the list is not valid, it will not be considered.
@@ -146,7 +146,7 @@ class DeviceInputBase(BECWidget):
146
146
 
147
147
  ### QtProperties ###
148
148
 
149
- @Property(
149
+ @SafeProperty(
150
150
  "QStringList",
151
151
  doc="List of devices. If updated, it will disable the apply filters property.",
152
152
  )
@@ -165,7 +165,7 @@ class DeviceInputBase(BECWidget):
165
165
  self.config.devices = value
166
166
  FilterIO.set_selection(widget=self, selection=value)
167
167
 
168
- @Property(str)
168
+ @SafeProperty(str)
169
169
  def default(self):
170
170
  """Get the default device name. If set through this property, it will update only if the device is within the filtered selection."""
171
171
  return self.config.default
@@ -177,7 +177,7 @@ class DeviceInputBase(BECWidget):
177
177
  self.config.default = value
178
178
  WidgetIO.set_value(widget=self, value=value)
179
179
 
180
- @Property(bool)
180
+ @SafeProperty(bool)
181
181
  def apply_filter(self):
182
182
  """Apply the filters on the devices."""
183
183
  return self.config.apply_filter
@@ -187,7 +187,7 @@ class DeviceInputBase(BECWidget):
187
187
  self.config.apply_filter = value
188
188
  self.update_devices_from_filters()
189
189
 
190
- @Property(bool)
190
+ @SafeProperty(bool)
191
191
  def filter_to_device(self):
192
192
  """Include devices in filters."""
193
193
  return BECDeviceFilter.DEVICE in self.device_filter
@@ -200,7 +200,7 @@ class DeviceInputBase(BECWidget):
200
200
  self._device_filter.remove(BECDeviceFilter.DEVICE)
201
201
  self.update_devices_from_filters()
202
202
 
203
- @Property(bool)
203
+ @SafeProperty(bool)
204
204
  def filter_to_positioner(self):
205
205
  """Include devices of type Positioner in filters."""
206
206
  return BECDeviceFilter.POSITIONER in self.device_filter
@@ -213,7 +213,7 @@ class DeviceInputBase(BECWidget):
213
213
  self._device_filter.remove(BECDeviceFilter.POSITIONER)
214
214
  self.update_devices_from_filters()
215
215
 
216
- @Property(bool)
216
+ @SafeProperty(bool)
217
217
  def filter_to_signal(self):
218
218
  """Include devices of type Signal in filters."""
219
219
  return BECDeviceFilter.SIGNAL in self.device_filter
@@ -226,7 +226,7 @@ class DeviceInputBase(BECWidget):
226
226
  self._device_filter.remove(BECDeviceFilter.SIGNAL)
227
227
  self.update_devices_from_filters()
228
228
 
229
- @Property(bool)
229
+ @SafeProperty(bool)
230
230
  def filter_to_computed_signal(self):
231
231
  """Include devices of type ComputedSignal in filters."""
232
232
  return BECDeviceFilter.COMPUTED_SIGNAL in self.device_filter
@@ -239,7 +239,7 @@ class DeviceInputBase(BECWidget):
239
239
  self._device_filter.remove(BECDeviceFilter.COMPUTED_SIGNAL)
240
240
  self.update_devices_from_filters()
241
241
 
242
- @Property(bool)
242
+ @SafeProperty(bool)
243
243
  def readout_monitored(self):
244
244
  """Include devices with readout priority Monitored in filters."""
245
245
  return ReadoutPriority.MONITORED in self.readout_filter
@@ -252,7 +252,7 @@ class DeviceInputBase(BECWidget):
252
252
  self._readout_filter.remove(ReadoutPriority.MONITORED)
253
253
  self.update_devices_from_filters()
254
254
 
255
- @Property(bool)
255
+ @SafeProperty(bool)
256
256
  def readout_baseline(self):
257
257
  """Include devices with readout priority Baseline in filters."""
258
258
  return ReadoutPriority.BASELINE in self.readout_filter
@@ -265,7 +265,7 @@ class DeviceInputBase(BECWidget):
265
265
  self._readout_filter.remove(ReadoutPriority.BASELINE)
266
266
  self.update_devices_from_filters()
267
267
 
268
- @Property(bool)
268
+ @SafeProperty(bool)
269
269
  def readout_async(self):
270
270
  """Include devices with readout priority Async in filters."""
271
271
  return ReadoutPriority.ASYNC in self.readout_filter
@@ -278,7 +278,7 @@ class DeviceInputBase(BECWidget):
278
278
  self._readout_filter.remove(ReadoutPriority.ASYNC)
279
279
  self.update_devices_from_filters()
280
280
 
281
- @Property(bool)
281
+ @SafeProperty(bool)
282
282
  def readout_continuous(self):
283
283
  """Include devices with readout priority continuous in filters."""
284
284
  return ReadoutPriority.CONTINUOUS in self.readout_filter
@@ -291,7 +291,7 @@ class DeviceInputBase(BECWidget):
291
291
  self._readout_filter.remove(ReadoutPriority.CONTINUOUS)
292
292
  self.update_devices_from_filters()
293
293
 
294
- @Property(bool)
294
+ @SafeProperty(bool)
295
295
  def readout_on_request(self):
296
296
  """Include devices with readout priority OnRequest in filters."""
297
297
  return ReadoutPriority.ON_REQUEST in self.readout_filter
@@ -6,7 +6,7 @@ from qtpy.QtCore import Property
6
6
  from bec_widgets.utils import ConnectionConfig
7
7
  from bec_widgets.utils.bec_widget import BECWidget
8
8
  from bec_widgets.utils.error_popups import SafeSlot
9
- from bec_widgets.utils.filter_io import FilterIO
9
+ from bec_widgets.utils.filter_io import FilterIO, LineEditFilterHandler
10
10
  from bec_widgets.utils.ophyd_kind_util import Kind
11
11
  from bec_widgets.utils.widget_io import WidgetIO
12
12
 
@@ -108,25 +108,32 @@ class DeviceSignalInputBase(BECWidget):
108
108
  if not self.validate_device(self._device):
109
109
  self._device = None
110
110
  self.config.device = self._device
111
+ self._signals = []
112
+ self._hinted_signals = []
113
+ self._normal_signals = []
114
+ self._config_signals = []
115
+ FilterIO.set_selection(widget=self, selection=self._signals)
111
116
  return
112
117
  device = self.get_device_object(self._device)
118
+ device_info = device._info.get("signals", {})
119
+
113
120
  # See above convention for Signals and ComputedSignals
114
121
  if isinstance(device, Signal):
115
- self._signals = [self._device]
116
- self._hinted_signals = [self._device]
122
+ self._signals = [(self._device, {})]
123
+ self._hinted_signals = [(self._device, {})]
117
124
  self._normal_signals = []
118
125
  self._config_signals = []
119
126
  FilterIO.set_selection(widget=self, selection=self._signals)
120
127
  return
121
- device_info = device._info.get("signals", {})
122
128
 
123
129
  def _update(kind: Kind):
124
- return [
125
- signal
126
- for signal, signal_info in device_info.items()
127
- if kind in self.signal_filter
128
- and (signal_info.get("kind_str", None) == str(kind.name))
129
- ]
130
+ return FilterIO.update_with_kind(
131
+ widget=self,
132
+ kind=kind,
133
+ signal_filter=self.signal_filter,
134
+ device_info=device_info,
135
+ device_name=self._device,
136
+ )
130
137
 
131
138
  self._hinted_signals = _update(Kind.hinted)
132
139
  self._normal_signals = _update(Kind.normal)
@@ -271,8 +278,11 @@ class DeviceSignalInputBase(BECWidget):
271
278
  Args:
272
279
  signal(str): Signal to validate.
273
280
  """
274
- if signal in self.signals:
275
- return True
281
+ for entry in self.signals:
282
+ if isinstance(entry, tuple):
283
+ entry = entry[0]
284
+ if entry == signal:
285
+ return True
276
286
  return False
277
287
 
278
288
  def _process_config_input(self, config: DeviceSignalInputBaseConfig | dict | None):
@@ -34,6 +34,7 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
34
34
  PLUGIN = True
35
35
 
36
36
  device_selected = Signal(str)
37
+ device_reset = Signal()
37
38
  device_config_update = Signal()
38
39
 
39
40
  def __init__(
@@ -147,6 +148,7 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
147
148
  self.device_selected.emit(input_text)
148
149
  else:
149
150
  self._is_valid_input = False
151
+ self.device_reset.emit()
150
152
  self.update()
151
153
 
152
154
  def validate_device(self, device: str) -> bool: # type: ignore[override]
@@ -90,6 +90,14 @@ class SignalComboBox(DeviceSignalInputBase, QComboBox):
90
90
  self.insertItem(0, "Hinted Signals")
91
91
  self.model().item(0).setEnabled(False)
92
92
 
93
+ @SafeSlot()
94
+ def reset_selection(self):
95
+ """Reset the selection of the combobox."""
96
+ self.clear()
97
+ self.setItemText(0, "Select a device")
98
+ self.update_signals_from_filters()
99
+ self.device_signal_changed.emit("")
100
+
93
101
  @SafeSlot(str)
94
102
  def on_text_changed(self, text: str):
95
103
  """Slot for text changed. If a device is selected and the signal is changed and valid it emits a signal.
@@ -102,11 +110,7 @@ class SignalComboBox(DeviceSignalInputBase, QComboBox):
102
110
  return
103
111
  if self.validate_signal(text) is False:
104
112
  return
105
- if text == "readback" and isinstance(self.get_device_object(self.device), Positioner):
106
- device_signal = self.device
107
- else:
108
- device_signal = f"{self.device}_{text}"
109
- self.device_signal_changed.emit(device_signal)
113
+ self.device_signal_changed.emit(text)
110
114
 
111
115
 
112
116
  if __name__ == "__main__": # pragma: no cover
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ from qtpy.QtCore import QSize
5
6
  from qtpy.QtWidgets import (
6
7
  QComboBox,
7
8
  QGroupBox,
@@ -35,7 +36,11 @@ class CurveSetting(SettingWidget):
35
36
  self._init_x_box()
36
37
  self._init_y_box()
37
38
 
38
- self.setFixedWidth(580) # TODO height is still debate
39
+ def sizeHint(self) -> QSize:
40
+ """
41
+ Returns the size hint for the settings widget.
42
+ """
43
+ return QSize(800, 500)
39
44
 
40
45
  def _init_x_box(self):
41
46
  self.x_axis_box = QGroupBox("X Axis")
@@ -97,7 +102,7 @@ class CurveSetting(SettingWidget):
97
102
 
98
103
  self.layout.addWidget(self.y_axis_box)
99
104
 
100
- @SafeSlot()
105
+ @SafeSlot(popup_error=True)
101
106
  def accept_changes(self):
102
107
  """
103
108
  Accepts the changes made in the settings widget and applies them to the target widget.
@@ -5,13 +5,12 @@ from typing import TYPE_CHECKING
5
5
 
6
6
  from bec_lib.logger import bec_logger
7
7
  from bec_qthemes._icon.material_icons import material_icon
8
- from qtpy.QtGui import QColor
8
+ from qtpy.QtCore import Qt
9
9
  from qtpy.QtWidgets import (
10
- QColorDialog,
11
10
  QComboBox,
12
11
  QHBoxLayout,
12
+ QHeaderView,
13
13
  QLabel,
14
- QLineEdit,
15
14
  QPushButton,
16
15
  QSizePolicy,
17
16
  QSpinBox,
@@ -27,9 +26,8 @@ from bec_widgets.utils import ConnectionConfig, EntryValidator
27
26
  from bec_widgets.utils.bec_widget import BECWidget
28
27
  from bec_widgets.utils.colors import Colors
29
28
  from bec_widgets.utils.toolbar import MaterialIconAction, ModularToolBar
30
- from bec_widgets.widgets.control.device_input.device_line_edit.device_line_edit import (
31
- DeviceLineEdit,
32
- )
29
+ from bec_widgets.widgets.control.device_input.device_combobox.device_combobox import DeviceComboBox
30
+ from bec_widgets.widgets.control.device_input.signal_combobox.signal_combobox import SignalComboBox
33
31
  from bec_widgets.widgets.dap.dap_combo_box.dap_combo_box import DapComboBox
34
32
  from bec_widgets.widgets.plots.waveform.curve import CurveConfig, DeviceSignal
35
33
  from bec_widgets.widgets.utility.visual.color_button_native.color_button_native import (
@@ -125,11 +123,40 @@ class CurveRow(QTreeWidgetItem):
125
123
  """Create columns 1 and 2. For device rows, we have device/entry edits; for dap rows, label/model combo."""
126
124
  if self.source == "device":
127
125
  # Device row: columns 1..2 are device line edits
128
- self.device_edit = DeviceLineEdit(parent=self.tree)
129
- self.entry_edit = QLineEdit(parent=self.tree) # TODO in future will be signal line edit
126
+ self.device_edit = DeviceComboBox(parent=self.tree)
127
+ self.device_edit.insertItem(0, "")
128
+ self.device_edit.setEditable(True)
129
+ self.entry_edit = SignalComboBox(parent=self.tree)
130
+ self.entry_edit.include_config_signals = False
131
+ self.entry_edit.insertItem(0, "")
132
+ self.entry_edit.setEditable(True)
133
+ self.device_edit.currentTextChanged.connect(self.entry_edit.set_device)
134
+ self.device_edit.device_reset.connect(self.entry_edit.reset_selection)
130
135
  if self.config.signal:
131
- self.device_edit.setText(self.config.signal.name or "")
132
- self.entry_edit.setText(self.config.signal.entry or "")
136
+ device_index = self.device_edit.findText(self.config.signal.name or "")
137
+ if device_index >= 0:
138
+ self.device_edit.setCurrentIndex(device_index)
139
+ # Force the entry_edit to update based on the device name
140
+ self.device_edit.currentTextChanged.emit(self.device_edit.currentText())
141
+ else:
142
+ # If the device name is not found, set the first enabled item
143
+ self.device_edit.setCurrentIndex(0)
144
+
145
+ for i in range(self.entry_edit.count()):
146
+ entry_data = self.entry_edit.itemData(i)
147
+ if entry_data and entry_data.get("obj_name") == self.config.signal.entry:
148
+ # If the device name matches an object name, set it
149
+ self.entry_edit.setCurrentIndex(i)
150
+ break
151
+ else:
152
+ # If no match found, set the first enabled item
153
+ for i in range(self.entry_edit.count()):
154
+ model = self.entry_edit.model()
155
+ if model.flags(model.index(i, 0)) & Qt.ItemIsEnabled:
156
+ self.entry_edit.setCurrentIndex(i)
157
+ break
158
+ else:
159
+ self.entry_edit.setCurrentIndex(0)
133
160
 
134
161
  self.tree.setItemWidget(self, 1, self.device_edit)
135
162
  self.tree.setItemWidget(self, 2, self.entry_edit)
@@ -268,13 +295,22 @@ class CurveRow(QTreeWidgetItem):
268
295
  # Gather device name/entry
269
296
  device_name = ""
270
297
  device_entry = ""
298
+
299
+ ## TODO: Move this to itemData
271
300
  if hasattr(self, "device_edit"):
272
- device_name = self.device_edit.text()
301
+ device_name = self.device_edit.currentText()
273
302
  if hasattr(self, "entry_edit"):
274
- device_entry = self.entry_validator.validate_signal(
275
- name=device_name, entry=self.entry_edit.text()
276
- )
277
- self.entry_edit.setText(device_entry)
303
+ device_entry = self.entry_edit.currentText()
304
+ index = self.entry_edit.findText(device_entry)
305
+ if index > -1:
306
+ device_entry_info = self.entry_edit.itemData(index)
307
+ if device_entry_info:
308
+ device_entry = device_entry_info.get("obj_name", device_entry)
309
+ else:
310
+ device_entry = self.entry_validator.validate_signal(
311
+ name=device_name, entry=device_entry
312
+ )
313
+
278
314
  self.config.signal = DeviceSignal(name=device_name, entry=device_entry)
279
315
  self.config.source = "device"
280
316
  self.config.label = f"{device_name}-{device_entry}"
@@ -390,13 +426,20 @@ class CurveTree(BECWidget, QWidget):
390
426
  self.tree = QTreeWidget()
391
427
  self.tree.setColumnCount(7)
392
428
  self.tree.setHeaderLabels(["Actions", "Name", "Entry", "Color", "Style", "Width", "Symbol"])
429
+
430
+ header = self.tree.header()
431
+ for idx in range(self.tree.columnCount()):
432
+ if idx in (1, 2): # Device name and entry should stretch
433
+ header.setSectionResizeMode(idx, QHeaderView.Stretch)
434
+ else:
435
+ header.setSectionResizeMode(idx, QHeaderView.Fixed)
436
+ header.setStretchLastSection(False)
393
437
  self.tree.setColumnWidth(0, 90)
394
- self.tree.setColumnWidth(1, 100)
395
- self.tree.setColumnWidth(2, 100)
396
438
  self.tree.setColumnWidth(3, 70)
397
439
  self.tree.setColumnWidth(4, 80)
398
- self.tree.setColumnWidth(5, 40)
399
- self.tree.setColumnWidth(6, 40)
440
+ self.tree.setColumnWidth(5, 50)
441
+ self.tree.setColumnWidth(6, 50)
442
+
400
443
  self.layout.addWidget(self.tree)
401
444
 
402
445
  def _init_color_buffer(self, size: int):
@@ -330,7 +330,6 @@ class Waveform(PlotBase):
330
330
  self.curve_settings_dialog = SettingsDialog(
331
331
  self, settings_widget=curve_setting, window_title="Curve Settings", modal=False
332
332
  )
333
- self.curve_settings_dialog.setFixedWidth(580)
334
333
  # When the dialog is closed, update the toolbar icon and clear the reference
335
334
  self.curve_settings_dialog.finished.connect(self._curve_settings_closed)
336
335
  self.curve_settings_dialog.show()
@@ -8,7 +8,7 @@ from bec_lib.logger import bec_logger
8
8
  from bec_qthemes import material_icon
9
9
  from qtpy.QtCore import QMimeData, QSize, Qt, Signal
10
10
  from qtpy.QtGui import QDrag
11
- from qtpy.QtWidgets import QApplication, QHBoxLayout, QToolButton, QWidget
11
+ from qtpy.QtWidgets import QApplication, QHBoxLayout, QTabWidget, QToolButton, QVBoxLayout, QWidget
12
12
 
13
13
  from bec_widgets.utils.error_popups import SafeSlot
14
14
  from bec_widgets.utils.expandable_frame import ExpandableGroupFrame
@@ -18,6 +18,9 @@ from bec_widgets.widgets.services.device_browser.device_item.device_config_dialo
18
18
  from bec_widgets.widgets.services.device_browser.device_item.device_config_form import (
19
19
  DeviceConfigForm,
20
20
  )
21
+ from bec_widgets.widgets.services.device_browser.device_item.device_signal_display import (
22
+ SignalDisplay,
23
+ )
21
24
 
22
25
  if TYPE_CHECKING: # pragma: no cover
23
26
  from qtpy.QtGui import QMouseEvent
@@ -38,10 +41,25 @@ class DeviceItem(ExpandableGroupFrame):
38
41
  self._expanded_first_time = False
39
42
  self._data = None
40
43
  self.device = device
41
- layout = QHBoxLayout()
42
- layout.setContentsMargins(0, 0, 0, 0)
43
- self.set_layout(layout)
44
44
 
45
+ self._layout = QHBoxLayout()
46
+ self._layout.setContentsMargins(0, 0, 0, 0)
47
+ self._tab_widget = QTabWidget(tabShape=QTabWidget.TabShape.Rounded)
48
+ self._tab_widget.setDocumentMode(True)
49
+ self._layout.addWidget(self._tab_widget)
50
+
51
+ self.set_layout(self._layout)
52
+
53
+ self._form_page = QWidget()
54
+ self._form_page_layout = QVBoxLayout()
55
+ self._form_page.setLayout(self._form_page_layout)
56
+
57
+ self._signal_page = QWidget()
58
+ self._signal_page_layout = QVBoxLayout()
59
+ self._signal_page.setLayout(self._signal_page_layout)
60
+
61
+ self._tab_widget.addTab(self._form_page, "Configuration")
62
+ self._tab_widget.addTab(self._signal_page, "Signals")
45
63
  self.adjustSize()
46
64
 
47
65
  def _create_title_layout(self, title: str, icon: str):
@@ -64,7 +82,9 @@ class DeviceItem(ExpandableGroupFrame):
64
82
  if not self.expanded and not self._expanded_first_time:
65
83
  self._expanded_first_time = True
66
84
  self.form = DeviceConfigForm(parent=self, pretty_display=True)
67
- self._contents.layout().addWidget(self.form)
85
+ self._form_page_layout.addWidget(self.form)
86
+ self.signals = SignalDisplay(parent=self, device=self.device)
87
+ self._signal_page_layout.addWidget(self.signals)
68
88
  self._reload_config()
69
89
  self.broadcast_size_hint.emit(self.sizeHint())
70
90
  super().switch_expanded_state()
@@ -0,0 +1,102 @@
1
+ from bec_qthemes import material_icon
2
+ from qtpy.QtCore import Qt
3
+ from qtpy.QtWidgets import QHBoxLayout, QLabel, QToolButton, QVBoxLayout, QWidget
4
+
5
+ from bec_widgets.utils.bec_connector import ConnectionConfig
6
+ from bec_widgets.utils.bec_widget import BECWidget
7
+ from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
8
+ from bec_widgets.widgets.containers.dock.dock import BECDock
9
+ from bec_widgets.widgets.utility.signal_label.signal_label import SignalLabel
10
+
11
+
12
+ class SignalDisplay(BECWidget, QWidget):
13
+ RPC = False
14
+
15
+ def __init__(
16
+ self,
17
+ client=None,
18
+ device: str = "",
19
+ config: ConnectionConfig = None,
20
+ gui_id: str | None = None,
21
+ theme_update: bool = False,
22
+ parent_dock: BECDock | None = None,
23
+ **kwargs,
24
+ ):
25
+ """A widget to display all the signals from a given device, and allow getting
26
+ a fresh reading."""
27
+ super().__init__(client, config, gui_id, theme_update, parent_dock, **kwargs)
28
+ self.get_bec_shortcuts()
29
+ self._layout = QVBoxLayout()
30
+ self.setLayout(self._layout)
31
+ self._content = QWidget()
32
+ self._layout.addWidget(self._content)
33
+ self._device = device
34
+ self.device = device
35
+
36
+ @SafeSlot()
37
+ def _refresh(self):
38
+ if self.device in self.dev:
39
+ self.dev.get(self.device).read(cached=False)
40
+ self.dev.get(self.device).read_configuration(cached=False)
41
+
42
+ def _add_refresh_button(self):
43
+ button_holder = QWidget()
44
+ button_holder.setLayout(QHBoxLayout())
45
+ button_holder.layout().setAlignment(Qt.AlignmentFlag.AlignRight)
46
+ button_holder.layout().setContentsMargins(0, 0, 0, 0)
47
+ refresh_button = QToolButton()
48
+ refresh_button.setIcon(
49
+ material_icon(icon_name="refresh", size=(20, 20), convert_to_pixmap=False)
50
+ )
51
+ refresh_button.clicked.connect(self._refresh)
52
+ button_holder.layout().addWidget(refresh_button)
53
+ self._content_layout.addWidget(button_holder)
54
+
55
+ def _populate(self):
56
+ self._content.deleteLater()
57
+ self._content = QWidget()
58
+ self._layout.addWidget(self._content)
59
+ self._content_layout = QVBoxLayout()
60
+ self._content_layout.setContentsMargins(0, 0, 0, 0)
61
+ self._content.setLayout(self._content_layout)
62
+
63
+ self._add_refresh_button()
64
+
65
+ if self._device in self.dev:
66
+ for sig in self.dev[self.device]._info.get("signals", {}).keys():
67
+ self._content_layout.addWidget(
68
+ SignalLabel(
69
+ device=self._device,
70
+ signal=sig,
71
+ show_select_button=False,
72
+ show_default_units=True,
73
+ )
74
+ )
75
+ self._content_layout.addStretch(1)
76
+ else:
77
+ self._content_layout.addWidget(
78
+ QLabel(f"Device {self.device} not found in device manager!")
79
+ )
80
+
81
+ @SafeProperty(str)
82
+ def device(self):
83
+ return self._device
84
+
85
+ @device.setter
86
+ def device(self, value: str):
87
+ self._device = value
88
+ self._populate()
89
+
90
+
91
+ if __name__ == "__main__": # pragma: no cover
92
+ import sys
93
+
94
+ from qtpy.QtWidgets import QApplication
95
+
96
+ from bec_widgets.utils.colors import set_theme
97
+
98
+ app = QApplication(sys.argv)
99
+ set_theme("light")
100
+ widget = SignalDisplay(device="samx")
101
+ widget.show()
102
+ sys.exit(app.exec_())
@@ -16,7 +16,6 @@ from qtpy.QtWidgets import (
16
16
  QGroupBox,
17
17
  QHBoxLayout,
18
18
  QLabel,
19
- QLineEdit,
20
19
  QToolButton,
21
20
  QVBoxLayout,
22
21
  QWidget,
@@ -180,6 +179,7 @@ class SignalLabel(BECWidget, QWidget):
180
179
  self._custom_units: str = custom_units
181
180
  self._show_default_units: bool = show_default_units
182
181
  self._decimal_places = 3
182
+ self._dtype = None
183
183
 
184
184
  self._show_hinted_signals: bool = True
185
185
  self._show_normal_signals: bool = False
@@ -241,8 +241,10 @@ class SignalLabel(BECWidget, QWidget):
241
241
  """Subscribe to the Redis topic for the device to display"""
242
242
  if not self._connected and self._device and self._device in self.dev:
243
243
  self._connected = True
244
- self._readback_endpoint = MessageEndpoints.device_readback(self._device)
245
- self.bec_dispatcher.connect_slot(self.on_device_readback, self._readback_endpoint)
244
+ self._read_endpoint = MessageEndpoints.device_read(self._device)
245
+ self._read_config_endpoint = MessageEndpoints.device_read_configuration(self._device)
246
+ self.bec_dispatcher.connect_slot(self.on_device_readback, self._read_endpoint)
247
+ self.bec_dispatcher.connect_slot(self.on_device_readback, self._read_config_endpoint)
246
248
  self._manual_read()
247
249
  self.set_display_value(self._value)
248
250
 
@@ -250,7 +252,8 @@ class SignalLabel(BECWidget, QWidget):
250
252
  """Unsubscribe from the Redis topic for the device to display"""
251
253
  if self._connected:
252
254
  self._connected = False
253
- self.bec_dispatcher.disconnect_slot(self.on_device_readback, self._readback_endpoint)
255
+ self.bec_dispatcher.disconnect_slot(self.on_device_readback, self._read_endpoint)
256
+ self.bec_dispatcher.disconnect_slot(self.on_device_readback, self._read_config_endpoint)
254
257
 
255
258
  def _manual_read(self):
256
259
  if self._device is None or not isinstance(
@@ -259,8 +262,13 @@ class SignalLabel(BECWidget, QWidget):
259
262
  self._units = ""
260
263
  self._value = "__"
261
264
  return
262
- signal: Signal = (
263
- getattr(device, self.signal, None) if isinstance(device, Device) else device
265
+ signal, info = (
266
+ (
267
+ getattr(device, self.signal, None),
268
+ device._info.get("signals", {}).get(self._signal, {}).get("describe", {}),
269
+ )
270
+ if isinstance(device, Device)
271
+ else (device, device.describe().get(self._device))
264
272
  )
265
273
  if not isinstance(signal, Signal): # Avoid getting other attributes of device, e.g. methods
266
274
  signal = None
@@ -269,7 +277,8 @@ class SignalLabel(BECWidget, QWidget):
269
277
  self._value = "__"
270
278
  return
271
279
  self._value = signal.get()
272
- self._units = signal.get_device_config().get("egu", "")
280
+ self._units = info.get("egu", "")
281
+ self._dtype = info.get("dtype", "float")
273
282
 
274
283
  @SafeSlot(dict, dict)
275
284
  def on_device_readback(self, msg: dict, metadata: dict) -> None:
@@ -278,8 +287,10 @@ class SignalLabel(BECWidget, QWidget):
278
287
  """
279
288
  try:
280
289
  signal_to_read = self._patch_hinted_signal()
281
- self._value = msg["signals"][signal_to_read]["value"]
282
- self.set_display_value(self._value)
290
+ _value = msg["signals"].get(signal_to_read, {}).get("value")
291
+ if _value is not None:
292
+ self._value = _value
293
+ self.set_display_value(self._value)
283
294
  except Exception as e:
284
295
  self._display.setText("ERROR!")
285
296
  self._display.setToolTip(
@@ -401,7 +412,10 @@ class SignalLabel(BECWidget, QWidget):
401
412
  if self._decimal_places == 0:
402
413
  return value
403
414
  try:
404
- return f"{float(value):0.{self._decimal_places}f}"
415
+ if self._dtype in ("integer", "float"):
416
+ return f"{float(value):0.{self._decimal_places}f}"
417
+ else:
418
+ return str(value)
405
419
  except ValueError:
406
420
  return value
407
421
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.17.0
3
+ Version: 2.19.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
@@ -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=OsL5qn-jyLsJ8q_sMsvh6ZFLLDWrcxxcnH_96oFMAMM,306711
5
+ CHANGELOG.md,sha256=TiLkAVElyNh0hZUpQhRMUL2ia_4XUKViiXQ1tXGbUQc,308560
6
6
  LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
7
- PKG-INFO,sha256=clCfYvQ6Ev5P27kqZEpFhw1D3sry8xBjBBfHmEV29uQ,1256
7
+ PKG-INFO,sha256=FfdkFV0tAjMMbo_ISSEu8ttPkPCUnvuyTKflfEfOibY,1256
8
8
  README.md,sha256=oY5Jc1uXehRASuwUJ0umin2vfkFh7tHF-LLruHTaQx0,3560
9
- pyproject.toml,sha256=WtICNGG17S1KW6kBrJDyfxvmwRA9jldVw_eVyMyX8B0,2837
9
+ pyproject.toml,sha256=lynGhtdSMDjC80at55Wf9uEKKQFkvXfd6S1Gb20bQ70,2837
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
@@ -18,7 +18,7 @@ pyproject.toml,sha256=WtICNGG17S1KW6kBrJDyfxvmwRA9jldVw_eVyMyX8B0,2837
18
18
  .github/workflows/check_pr.yml,sha256=jKMtYBJTNRAdw_MAS4JzdKVSoNrv8nxUQMqobsYFAXw,903
19
19
  .github/workflows/ci.yml,sha256=OVZt0UxN4wQInuMucuQcKsvHDiz27sLeuQRskxjtkY0,1863
20
20
  .github/workflows/end2end-conda.yml,sha256=yTH-CS8xxQ7kMo4BDpWwOeef1xmsV6gyrgqnFPHTo30,2278
21
- .github/workflows/formatter.yml,sha256=CrYpMfUaZmFFvCk8sK-_7dTtngVxob0Cb1PaJZAZYWw,1918
21
+ .github/workflows/formatter.yml,sha256=yldGjVF8zckPXho9bgN2_YNCq-5ZWN8pxTYrJNDvCbY,1925
22
22
  .github/workflows/generate-cli-check.yml,sha256=b6TcK8F5Hy0sFjgXpk0w3BO9eMDZw9WysTl3P7zEPuQ,1742
23
23
  .github/workflows/pytest-matrix.yml,sha256=0gL5wNPJKJF1JapqstlYNYiJ44ko05uaTD7epa7smVw,1834
24
24
  .github/workflows/pytest.yml,sha256=hYOB7XK_79MaiELaTH7zDT-WRw-pRDe4mHyB_WfcGDc,1747
@@ -59,7 +59,7 @@ bec_widgets/examples/plugin_example_pyside/tictactoe.py,sha256=s3rCurXloVcmMdzZi
59
59
  bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=MFMwONn4EZ3V8DboEG4I3BXpURE9JDbKB7XTzzfZl5w,1978
60
60
  bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=SiJaoX3OYA8YMkSwU1d7KEfSUjQQUsQgpRAxSSlr8oQ,2376
61
61
  bec_widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
- bec_widgets/tests/utils.py,sha256=O4WwfqC8Ga6-TeyBCdU08TrwH7jcRdN2vJY6_hL4qq8,8546
62
+ bec_widgets/tests/utils.py,sha256=T_8ycpWxtI8gdXHD21r4ua-mjdHhavSVdnTVfm-zecw,8970
63
63
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
64
64
  bec_widgets/utils/bec_connector.py,sha256=ATOSyZqryn1QHPc7aotiDnUtzFhlj_gmcukMT_pqjHQ,19272
65
65
  bec_widgets/utils/bec_designer.py,sha256=ehNl_i743rijmhPiIGNd1bihE7-l4oJzTVoa4yjPjls,5426
@@ -77,7 +77,7 @@ bec_widgets/utils/crosshair.py,sha256=nqBPQqWzoTLZ-sPBR6ONm7M1TtGGD2EpRwm2iSNpoF
77
77
  bec_widgets/utils/entry_validator.py,sha256=lwT8HP0RDG1FXENIeZ3IDEF2DQmD8KXGkRxPoMXbryk,1817
78
78
  bec_widgets/utils/error_popups.py,sha256=UBAmD1YlAgKodpihudyf0VWtI59KGFiLgnjiKmKGjgk,13254
79
79
  bec_widgets/utils/expandable_frame.py,sha256=BvR5kyALWPcOz7l5zarqG_M-EWFY2LMw0jfdDWC5-hI,4025
80
- bec_widgets/utils/filter_io.py,sha256=GKO6GOTiKxTuTp_SHp5Ewou54N7wIZXgWWvlFvhbBmw,5114
80
+ bec_widgets/utils/filter_io.py,sha256=DVWonV3ibc4eCB5SPNGG9EWKX68_flnhMBA3OBPvjHo,9995
81
81
  bec_widgets/utils/fps_counter.py,sha256=seuCWwiNP5q2e2OEztloa66pNb3Sygh-0lEHAcYaDfc,2612
82
82
  bec_widgets/utils/generate_designer_plugin.py,sha256=d_-cWQJ2s8Ff-XD_YQUgnVsRCMHBPcszXOAuJSeIWHM,5676
83
83
  bec_widgets/utils/layout_manager.py,sha256=H0nKsIMaPxRkof1MEXlSmW6w1dFxA6astaGzf4stI84,4727
@@ -180,12 +180,12 @@ bec_widgets/widgets/control/device_control/positioner_group/positioner_group_plu
180
180
  bec_widgets/widgets/control/device_control/positioner_group/register_positioner_group.py,sha256=vjoIwn1CQbg3gEwKzgY2MuscfGab2agM5YV0r1_UL0k,531
181
181
  bec_widgets/widgets/control/device_input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
182
  bec_widgets/widgets/control/device_input/base_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
183
- bec_widgets/widgets/control/device_input/base_classes/device_input_base.py,sha256=r4DwWQz2wwNQ3Uswzdy12MGycV7pFrE_Zv4h_2G5IRA,15915
184
- bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py,sha256=wLEzPfEqaCPL3EpvMgFL5aZqSwuGj1OEkiyKaOQ7M1I,9330
183
+ bec_widgets/widgets/control/device_input/base_classes/device_input_base.py,sha256=WxTPrZm54JRDckrVXQ4XBcUwOsad8ZyF5suZcyF_hyQ,15994
184
+ bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py,sha256=YAVhvOGj2RLNKmkAI6DbbK6BilBM845u84sQ4NKic1s,9702
185
185
  bec_widgets/widgets/control/device_input/device_combobox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
186
186
  bec_widgets/widgets/control/device_input/device_combobox/device_combo_box.pyproject,sha256=wI2eXR5ky_IM9-BCHJnH_9CEqYcZwIuLcgitSEr8OJU,40
187
187
  bec_widgets/widgets/control/device_input/device_combobox/device_combo_box_plugin.py,sha256=E8LD9T4O2w621q25uHqBqZLDiQ6zpMR25ZDuf51jrPw,1434
188
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py,sha256=8ANCTgPtO_Fhhy1ZBUfr9Yx_aArnePgkNORYwnXE-Fw,7251
188
+ bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py,sha256=-alAbGia6RA3EBaDiQC4yxc6sB6wHXBsVa8InBnemTQ,7316
189
189
  bec_widgets/widgets/control/device_input/device_combobox/register_device_combo_box.py,sha256=elw4M4xfIFWe8C0MkdqqqyfnyOVrdl0g0j6bqwOU1GE,526
190
190
  bec_widgets/widgets/control/device_input/device_line_edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
191
191
  bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py,sha256=k87NtUD2TUB4c6-Ks-eZgafWbjTwbk0RKiqf9-OkjMk,7415
@@ -196,7 +196,7 @@ bec_widgets/widgets/control/device_input/signal_combobox/__init__.py,sha256=47DE
196
196
  bec_widgets/widgets/control/device_input/signal_combobox/register_signal_combo_box.py,sha256=VEdFRUfLph7JE2arcnzHw8etsE-4wZkwyzlNLMJBsZk,526
197
197
  bec_widgets/widgets/control/device_input/signal_combobox/signal_combo_box.pyproject,sha256=xod6iyRD-WD0Uk6LWXjSxFJCQy-831pvTkKcw2FAdnM,33
198
198
  bec_widgets/widgets/control/device_input/signal_combobox/signal_combo_box_plugin.py,sha256=sstqm2KtyR5wwOIYJRbzOqHMq5_9ExKP-YS5qV5ACrA,1373
199
- bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py,sha256=vM4PsYDl8RBHb9g083fRpeUZZkW1u1m7uXfaVMGUpyY,4869
199
+ bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py,sha256=ihZYx1wN8vXtOco3_WqTOgz6J7wxd3n3rIL-tWWyCIA,4913
200
200
  bec_widgets/widgets/control/device_input/signal_line_edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
201
  bec_widgets/widgets/control/device_input/signal_line_edit/register_signal_line_edit.py,sha256=aQLTy_3gbji0vq5VvvAddHFimpwGGaMYJy5iGgX23aM,527
202
202
  bec_widgets/widgets/control/device_input/signal_line_edit/signal_line_edit.py,sha256=-y_Oy8A7pQVQbzjvHznGxTX-wCisP-4l5py7WOm1_EY,6008
@@ -315,13 +315,13 @@ bec_widgets/widgets/plots/toolbar_bundles/save_state.py,sha256=H3fu-bRzNIycCUFb2
315
315
  bec_widgets/widgets/plots/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
316
316
  bec_widgets/widgets/plots/waveform/curve.py,sha256=KlcGbd60lPO9BYcca08fMhWkfx5qu4O9IbSNTwvajGM,10401
317
317
  bec_widgets/widgets/plots/waveform/register_waveform.py,sha256=pdcLCYKkLkSb-5DqbJdC6M3JyoXQBRVAKf7BZVCto80,467
318
- bec_widgets/widgets/plots/waveform/waveform.py,sha256=9IoWV3BmLe5Ap9fESqiJ6OzV6TcoEd7UDgNPJpTXoFU,77238
318
+ bec_widgets/widgets/plots/waveform/waveform.py,sha256=XOajidVbmrYeaybAH-ic5EzH3gbx_chlIet-j3akxuE,77180
319
319
  bec_widgets/widgets/plots/waveform/waveform.pyproject,sha256=X2T6d4JGt9YSI28e-myjXh1YkUM4Yr3kNb0-F84KvUA,26
320
320
  bec_widgets/widgets/plots/waveform/waveform_plugin.py,sha256=2AZPtBHs75l9cdhwQnY3jpIMRPUUqK3RNvQbTvrFyvg,1237
321
321
  bec_widgets/widgets/plots/waveform/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
322
322
  bec_widgets/widgets/plots/waveform/settings/curve_settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
323
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py,sha256=Q-wgtzsllExbeOxQoCPmcCQfKRjp_hJeYcinSSy_0HI,4454
324
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py,sha256=Os5u5ZP4L7erClXtkuHMHMvf-a1dTO7ZL8W9Rk3nnWY,20742
323
+ bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py,sha256=jnhd_FqSvLI8xqeXWe1DizP3o_JRfU64swXxqP9Sk6U,4580
324
+ bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py,sha256=XQXIfo7eVMc4P5iafh-BQifG9qcI8Ql5-TfIWKrwWHk,23066
325
325
  bec_widgets/widgets/plots/waveform/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
326
326
  bec_widgets/widgets/plots/waveform/utils/roi_manager.py,sha256=zCl3-p3qP02zi837Udz8VUzsbUAwiP-26K3VpLsvaUU,2964
327
327
  bec_widgets/widgets/progress/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -365,7 +365,8 @@ bec_widgets/widgets/services/device_browser/util.py,sha256=aDtRa53L4-CGn4rM9IKqU
365
365
  bec_widgets/widgets/services/device_browser/device_item/__init__.py,sha256=VGY-uNVCnpcY-q-gijteB2N8KxFNgYR-qQ209MVu1QI,36
366
366
  bec_widgets/widgets/services/device_browser/device_item/device_config_dialog.py,sha256=183LABi4767PptQHy8mfV1I6MFlDGmC1vekFqjtkOuk,8748
367
367
  bec_widgets/widgets/services/device_browser/device_item/device_config_form.py,sha256=Bw2sxvszs2qPe5WCUV_J4P6iuWSEQc0I37cyr9Wat30,2138
368
- bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=LxkeUphf6BjzQadOlyF080TUoPtFdivO7b7c-SjdK94,5251
368
+ bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=OdBPwIzqKSeeOWZ86sct5d_m17HdIy5aQlCL-Hic754,6135
369
+ bec_widgets/widgets/services/device_browser/device_item/device_signal_display.py,sha256=Olt5AHl7qEHKAiNvjYanA_3ZuZyC0ZMswmlwGGoOPkI,3457
369
370
  bec_widgets/widgets/utility/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
370
371
  bec_widgets/widgets/utility/logpanel/__init__.py,sha256=HldSvPLYgrqBjCgIQj0f7Wa4slkSMksk4bsRJOQi__Y,91
371
372
  bec_widgets/widgets/utility/logpanel/_util.py,sha256=GqzHbdOTmWBru9OR4weeYdziWj_cWxqSJhS4_6W3Qjg,1836
@@ -374,7 +375,7 @@ bec_widgets/widgets/utility/logpanel/log_panel_plugin.py,sha256=KY7eS1uGZzLYtDAd
374
375
  bec_widgets/widgets/utility/logpanel/logpanel.py,sha256=5c59r1Z368mqIZhS_0075P4gg2G1sK5NvPFMK5B1DuQ,20861
375
376
  bec_widgets/widgets/utility/logpanel/register_log_panel.py,sha256=LFUE5JzCYvIwJQtTqZASLVAHYy3gO1nrHzPVH_kpCEY,470
376
377
  bec_widgets/widgets/utility/signal_label/register_signal_label.py,sha256=wDB4Q3dSbZ51hsxnuB74oXdMRoLgDRd-XfhaomYY2OA,483
377
- bec_widgets/widgets/utility/signal_label/signal_label.py,sha256=bht1zpHKxrslfFCknnLe3Q9FeF8Do0j6onWAiLXZan0,15875
378
+ bec_widgets/widgets/utility/signal_label/signal_label.py,sha256=ErIyoqKmmga65wBJeXhvWt1FhTdybfkKkqMFzdVl_ig,16571
378
379
  bec_widgets/widgets/utility/signal_label/signal_label.pyproject,sha256=DXgt__AGnPCqXo5A92aTPH0SxbWlvgyNzKraWUuumWg,30
379
380
  bec_widgets/widgets/utility/signal_label/signal_label_plugin.py,sha256=ZXR8oYl4NkPcXJ8pgDcdXcg3teB0MHtoNZoiGDmgCoU,1298
380
381
  bec_widgets/widgets/utility/spinbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -418,8 +419,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=O
418
419
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
419
420
  bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
420
421
  bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
421
- bec_widgets-2.17.0.dist-info/METADATA,sha256=clCfYvQ6Ev5P27kqZEpFhw1D3sry8xBjBBfHmEV29uQ,1256
422
- bec_widgets-2.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
423
- bec_widgets-2.17.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
424
- bec_widgets-2.17.0.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
425
- bec_widgets-2.17.0.dist-info/RECORD,,
422
+ bec_widgets-2.19.0.dist-info/METADATA,sha256=FfdkFV0tAjMMbo_ISSEu8ttPkPCUnvuyTKflfEfOibY,1256
423
+ bec_widgets-2.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
424
+ bec_widgets-2.19.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
425
+ bec_widgets-2.19.0.dist-info/licenses/LICENSE,sha256=Daeiu871NcAp8uYi4eB_qHgvypG-HX0ioRQyQxFwjeg,1531
426
+ bec_widgets-2.19.0.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.17.0"
7
+ version = "2.19.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [