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.
- CHANGELOG.md +19 -20
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +56 -0
- bec_widgets/tests/__init__.py +0 -0
- bec_widgets/tests/utils.py +226 -0
- bec_widgets/utils/filter_io.py +156 -0
- bec_widgets/utils/widget_io.py +12 -9
- bec_widgets/widgets/base_classes/device_input_base.py +331 -62
- bec_widgets/widgets/base_classes/device_signal_input_base.py +280 -0
- bec_widgets/widgets/dap_combo_box/dap_combo_box_plugin.py +1 -1
- bec_widgets/widgets/device_combobox/device_combo_box_plugin.py +1 -1
- bec_widgets/widgets/device_combobox/device_combobox.py +118 -41
- bec_widgets/widgets/device_line_edit/device_line_edit.py +122 -59
- bec_widgets/widgets/device_line_edit/device_line_edit_plugin.py +1 -1
- bec_widgets/widgets/image/image_widget.py +7 -1
- bec_widgets/widgets/motor_map/motor_map_widget.py +4 -2
- bec_widgets/widgets/positioner_box/positioner_box.py +4 -1
- bec_widgets/widgets/scan_control/scan_group_box.py +3 -1
- bec_widgets/widgets/signal_combobox/__init__.py +0 -0
- bec_widgets/widgets/signal_combobox/register_signal_combobox.py +15 -0
- bec_widgets/widgets/signal_combobox/signal_combobox.py +115 -0
- bec_widgets/widgets/signal_combobox/signal_combobox.pyproject +1 -0
- bec_widgets/widgets/signal_combobox/signal_combobox_plugin.py +54 -0
- bec_widgets/widgets/signal_line_edit/__init__.py +0 -0
- bec_widgets/widgets/signal_line_edit/register_signal_line_edit.py +15 -0
- bec_widgets/widgets/signal_line_edit/signal_line_edit.py +140 -0
- bec_widgets/widgets/signal_line_edit/signal_line_edit.pyproject +1 -0
- bec_widgets/widgets/signal_line_edit/signal_line_edit_plugin.py +54 -0
- {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/METADATA +1 -1
- {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/RECORD +34 -20
- pyproject.toml +1 -1
- {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.0.2.dist-info → bec_widgets-1.1.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## v1.1.0 (2024-10-25)
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* feat: add filter i/o utility class ([`0350833`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0350833f36e0a7cadce4173f9b1d1fbfdf985375))
|
9
|
+
|
10
|
+
### Refactoring
|
11
|
+
|
12
|
+
* refactor: do not flush selection upon receiving config update; allow widgetIO to receive kwargs to be able to use get_value to receive string instead of int for QComboBox ([`91959e8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/91959e82de8586934af3ebb5aaa0923930effc51))
|
13
|
+
|
14
|
+
* refactor: allow to set selection in DeviceInput; automatic update of selection on device config update; cleanup ([`5eb15b7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5eb15b785f12e30eb8ccbc56d4ad9e759a4cf5eb))
|
15
|
+
|
16
|
+
* refactor: cleanup, added device_signal for signal inputs ([`6fb2055`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6fb20552ff57978f4aeb79fd7f062f8d6b5581e7))
|
17
|
+
|
18
|
+
### Testing
|
19
|
+
|
20
|
+
* test(scan_control): tests added for grid_scan to ensure scan_args signal validity ([`acb7902`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/acb79020d4be546efc001ff47b6f5cdba2ee9375))
|
21
|
+
|
22
|
+
|
4
23
|
## v1.0.2 (2024-10-22)
|
5
24
|
|
6
25
|
### Bug Fixes
|
@@ -151,26 +170,6 @@ is emitted multiple times. ([`f084e25`](https://gitlab.psi.ch/bec/bec_widgets/-/
|
|
151
170
|
|
152
171
|
## v0.113.0 (2024-10-02)
|
153
172
|
|
154
|
-
### Bug Fixes
|
155
|
-
|
156
|
-
* fix: add is_log checks and functionality to plot_indicator_items ([`0f9953e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0f9953e8fdcf3f9b5a09f994c69edb6b34756df9))
|
157
|
-
|
158
|
-
### Features
|
159
|
-
|
160
|
-
* feat: add first draft for alignment_1d GUI ([`63c24f9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/63c24f97a355edaa928b6e222909252b276bcada))
|
161
|
-
|
162
|
-
* feat: add move to position button to lmfit dialog ([`281cb27`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/281cb27d8b5433e27a7ba0ca0a19e4b45b9c544f))
|
163
|
-
|
164
|
-
### Refactoring
|
165
|
-
|
166
|
-
* refactor: various minor improvements for the alignment gui ([`f554f3c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f554f3c1672c4fe32968a5991dc98802556a6f3b))
|
167
|
-
|
168
|
-
* refactor: allow hiding of arg/kwarg boxes ([`efe90eb`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/efe90eb163e2123a5b4d0bb59f66025a569336ad))
|
169
|
-
|
170
173
|
### Testing
|
171
174
|
|
172
175
|
* test: add tests for scan_status_callback ([`dc0c825`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/dc0c825fd594c093a24543ff803d6c6564010e92))
|
173
|
-
|
174
|
-
### Unknown
|
175
|
-
|
176
|
-
* feat : Add bec_signal_proxy to handle signals with option to unblock them manually. ([`1dcfeb6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1dcfeb6cfce3c69f0c5401731d4d3f9a1981b22e))
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -38,6 +38,8 @@ class Widgets(str, enum.Enum):
|
|
38
38
|
ResumeButton = "ResumeButton"
|
39
39
|
RingProgressBar = "RingProgressBar"
|
40
40
|
ScanControl = "ScanControl"
|
41
|
+
SignalComboBox = "SignalComboBox"
|
42
|
+
SignalLineEdit = "SignalLineEdit"
|
41
43
|
StopButton = "StopButton"
|
42
44
|
TextBox = "TextBox"
|
43
45
|
VSCodeEditor = "VSCodeEditor"
|
@@ -2591,6 +2593,24 @@ class DeviceLineEdit(RPCBase):
|
|
2591
2593
|
"""
|
2592
2594
|
|
2593
2595
|
|
2596
|
+
class DeviceSignalInputBase(RPCBase):
|
2597
|
+
@property
|
2598
|
+
@rpc_call
|
2599
|
+
def _config_dict(self) -> "dict":
|
2600
|
+
"""
|
2601
|
+
Get the configuration of the widget.
|
2602
|
+
|
2603
|
+
Returns:
|
2604
|
+
dict: The configuration of the widget.
|
2605
|
+
"""
|
2606
|
+
|
2607
|
+
@rpc_call
|
2608
|
+
def _get_all_rpc(self) -> "dict":
|
2609
|
+
"""
|
2610
|
+
Get all registered RPC objects.
|
2611
|
+
"""
|
2612
|
+
|
2613
|
+
|
2594
2614
|
class LMFitDialog(RPCBase):
|
2595
2615
|
@property
|
2596
2616
|
@rpc_call
|
@@ -3003,6 +3023,42 @@ class ScanControl(RPCBase):
|
|
3003
3023
|
"""
|
3004
3024
|
|
3005
3025
|
|
3026
|
+
class SignalComboBox(RPCBase):
|
3027
|
+
@property
|
3028
|
+
@rpc_call
|
3029
|
+
def _config_dict(self) -> "dict":
|
3030
|
+
"""
|
3031
|
+
Get the configuration of the widget.
|
3032
|
+
|
3033
|
+
Returns:
|
3034
|
+
dict: The configuration of the widget.
|
3035
|
+
"""
|
3036
|
+
|
3037
|
+
@rpc_call
|
3038
|
+
def _get_all_rpc(self) -> "dict":
|
3039
|
+
"""
|
3040
|
+
Get all registered RPC objects.
|
3041
|
+
"""
|
3042
|
+
|
3043
|
+
|
3044
|
+
class SignalLineEdit(RPCBase):
|
3045
|
+
@property
|
3046
|
+
@rpc_call
|
3047
|
+
def _config_dict(self) -> "dict":
|
3048
|
+
"""
|
3049
|
+
Get the configuration of the widget.
|
3050
|
+
|
3051
|
+
Returns:
|
3052
|
+
dict: The configuration of the widget.
|
3053
|
+
"""
|
3054
|
+
|
3055
|
+
@rpc_call
|
3056
|
+
def _get_all_rpc(self) -> "dict":
|
3057
|
+
"""
|
3058
|
+
Get all registered RPC objects.
|
3059
|
+
"""
|
3060
|
+
|
3061
|
+
|
3006
3062
|
class StopButton(RPCBase):
|
3007
3063
|
@property
|
3008
3064
|
@rpc_call
|
File without changes
|
@@ -0,0 +1,226 @@
|
|
1
|
+
from unittest.mock import MagicMock
|
2
|
+
|
3
|
+
from bec_lib.device import Device as BECDevice
|
4
|
+
from bec_lib.device import Positioner as BECPositioner
|
5
|
+
from bec_lib.device import ReadoutPriority
|
6
|
+
from bec_lib.devicemanager import DeviceContainer
|
7
|
+
|
8
|
+
|
9
|
+
class FakeDevice(BECDevice):
|
10
|
+
"""Fake minimal positioner class for testing."""
|
11
|
+
|
12
|
+
def __init__(self, name, enabled=True, readout_priority=ReadoutPriority.MONITORED):
|
13
|
+
super().__init__(name=name)
|
14
|
+
self._enabled = enabled
|
15
|
+
self.signals = {self.name: {"value": 1.0}}
|
16
|
+
self.description = {self.name: {"source": self.name, "dtype": "number", "shape": []}}
|
17
|
+
self._readout_priority = readout_priority
|
18
|
+
self._config = {
|
19
|
+
"readoutPriority": "baseline",
|
20
|
+
"deviceClass": "ophyd.Device",
|
21
|
+
"deviceConfig": {},
|
22
|
+
"deviceTags": ["user device"],
|
23
|
+
"enabled": enabled,
|
24
|
+
"readOnly": False,
|
25
|
+
"name": self.name,
|
26
|
+
}
|
27
|
+
|
28
|
+
@property
|
29
|
+
def readout_priority(self):
|
30
|
+
return self._readout_priority
|
31
|
+
|
32
|
+
@readout_priority.setter
|
33
|
+
def readout_priority(self, value):
|
34
|
+
self._readout_priority = value
|
35
|
+
|
36
|
+
@property
|
37
|
+
def limits(self) -> tuple[float, float]:
|
38
|
+
return self._limits
|
39
|
+
|
40
|
+
@limits.setter
|
41
|
+
def limits(self, value: tuple[float, float]):
|
42
|
+
self._limits = value
|
43
|
+
|
44
|
+
def __contains__(self, item):
|
45
|
+
return item == self.name
|
46
|
+
|
47
|
+
@property
|
48
|
+
def _hints(self):
|
49
|
+
return [self.name]
|
50
|
+
|
51
|
+
def set_value(self, fake_value: float = 1.0) -> None:
|
52
|
+
"""
|
53
|
+
Setup fake value for device readout
|
54
|
+
Args:
|
55
|
+
fake_value(float): Desired fake value
|
56
|
+
"""
|
57
|
+
self.signals[self.name]["value"] = fake_value
|
58
|
+
|
59
|
+
def describe(self) -> dict:
|
60
|
+
"""
|
61
|
+
Get the description of the device
|
62
|
+
Returns:
|
63
|
+
dict: Description of the device
|
64
|
+
"""
|
65
|
+
return self.description
|
66
|
+
|
67
|
+
|
68
|
+
class FakePositioner(BECPositioner):
|
69
|
+
|
70
|
+
def __init__(
|
71
|
+
self,
|
72
|
+
name,
|
73
|
+
enabled=True,
|
74
|
+
limits=None,
|
75
|
+
read_value=1.0,
|
76
|
+
readout_priority=ReadoutPriority.MONITORED,
|
77
|
+
):
|
78
|
+
super().__init__(name=name)
|
79
|
+
# self.limits = limits if limits is not None else [0.0, 0.0]
|
80
|
+
self.read_value = read_value
|
81
|
+
self.setpoint_value = read_value
|
82
|
+
self.motor_is_moving_value = 0
|
83
|
+
self._enabled = enabled
|
84
|
+
self._limits = limits
|
85
|
+
self._readout_priority = readout_priority
|
86
|
+
self.signals = {self.name: {"value": 1.0}}
|
87
|
+
self.description = {self.name: {"source": self.name, "dtype": "number", "shape": []}}
|
88
|
+
self._config = {
|
89
|
+
"readoutPriority": "baseline",
|
90
|
+
"deviceClass": "ophyd_devices.SimPositioner",
|
91
|
+
"deviceConfig": {"delay": 1, "tolerance": 0.01, "update_frequency": 400},
|
92
|
+
"deviceTags": ["user motors"],
|
93
|
+
"enabled": enabled,
|
94
|
+
"readOnly": False,
|
95
|
+
"name": self.name,
|
96
|
+
}
|
97
|
+
self._info = {
|
98
|
+
"signals": {
|
99
|
+
"readback": {"kind_str": "5"}, # hinted
|
100
|
+
"setpoint": {"kind_str": "1"}, # normal
|
101
|
+
"velocity": {"kind_str": "2"}, # config
|
102
|
+
}
|
103
|
+
}
|
104
|
+
self.signals = {
|
105
|
+
self.name: {"value": self.read_value},
|
106
|
+
f"{self.name}_setpoint": {"value": self.setpoint_value},
|
107
|
+
f"{self.name}_motor_is_moving": {"value": self.motor_is_moving_value},
|
108
|
+
}
|
109
|
+
|
110
|
+
@property
|
111
|
+
def readout_priority(self):
|
112
|
+
return self._readout_priority
|
113
|
+
|
114
|
+
@readout_priority.setter
|
115
|
+
def readout_priority(self, value):
|
116
|
+
self._readout_priority = value
|
117
|
+
|
118
|
+
@property
|
119
|
+
def enabled(self) -> bool:
|
120
|
+
return self._enabled
|
121
|
+
|
122
|
+
@enabled.setter
|
123
|
+
def enabled(self, value: bool):
|
124
|
+
self._enabled = value
|
125
|
+
|
126
|
+
@property
|
127
|
+
def limits(self) -> tuple[float, float]:
|
128
|
+
return self._limits
|
129
|
+
|
130
|
+
@limits.setter
|
131
|
+
def limits(self, value: tuple[float, float]):
|
132
|
+
self._limits = value
|
133
|
+
|
134
|
+
def __contains__(self, item):
|
135
|
+
return item == self.name
|
136
|
+
|
137
|
+
@property
|
138
|
+
def _hints(self):
|
139
|
+
return [self.name]
|
140
|
+
|
141
|
+
def set_value(self, fake_value: float = 1.0) -> None:
|
142
|
+
"""
|
143
|
+
Setup fake value for device readout
|
144
|
+
Args:
|
145
|
+
fake_value(float): Desired fake value
|
146
|
+
"""
|
147
|
+
self.read_value = fake_value
|
148
|
+
|
149
|
+
def describe(self) -> dict:
|
150
|
+
"""
|
151
|
+
Get the description of the device
|
152
|
+
Returns:
|
153
|
+
dict: Description of the device
|
154
|
+
"""
|
155
|
+
return self.description
|
156
|
+
|
157
|
+
@property
|
158
|
+
def precision(self):
|
159
|
+
return 3
|
160
|
+
|
161
|
+
def set_read_value(self, value):
|
162
|
+
self.read_value = value
|
163
|
+
|
164
|
+
def read(self):
|
165
|
+
return self.signals
|
166
|
+
|
167
|
+
def set_limits(self, limits):
|
168
|
+
self.limits = limits
|
169
|
+
|
170
|
+
def move(self, value, relative=False):
|
171
|
+
"""Simulates moving the device to a new position."""
|
172
|
+
if relative:
|
173
|
+
self.read_value += value
|
174
|
+
else:
|
175
|
+
self.read_value = value
|
176
|
+
# Respect the limits
|
177
|
+
self.read_value = max(min(self.read_value, self.limits[1]), self.limits[0])
|
178
|
+
|
179
|
+
@property
|
180
|
+
def readback(self):
|
181
|
+
return MagicMock(get=MagicMock(return_value=self.read_value))
|
182
|
+
|
183
|
+
|
184
|
+
class Positioner(FakePositioner):
|
185
|
+
"""just placeholder for testing embedded isinstance check in DeviceCombobox"""
|
186
|
+
|
187
|
+
def __init__(self, name="test", limits=None, read_value=1.0):
|
188
|
+
super().__init__(name, limits, read_value)
|
189
|
+
|
190
|
+
|
191
|
+
class Device(FakeDevice):
|
192
|
+
"""just placeholder for testing embedded isinstance check in DeviceCombobox"""
|
193
|
+
|
194
|
+
def __init__(self, name, enabled=True):
|
195
|
+
super().__init__(name, enabled)
|
196
|
+
|
197
|
+
|
198
|
+
class DMMock:
|
199
|
+
def __init__(self):
|
200
|
+
self.devices = DeviceContainer()
|
201
|
+
self.enabled_devices = [device for device in self.devices if device.enabled]
|
202
|
+
|
203
|
+
def add_devives(self, devices: list):
|
204
|
+
for device in devices:
|
205
|
+
self.devices[device.name] = device
|
206
|
+
|
207
|
+
|
208
|
+
DEVICES = [
|
209
|
+
FakePositioner("samx", limits=[-10, 10], read_value=2.0),
|
210
|
+
FakePositioner("samy", limits=[-5, 5], read_value=3.0),
|
211
|
+
FakePositioner("samz", limits=[-8, 8], read_value=4.0),
|
212
|
+
FakePositioner("aptrx", limits=None, read_value=4.0),
|
213
|
+
FakePositioner("aptry", limits=None, read_value=5.0),
|
214
|
+
FakeDevice("gauss_bpm"),
|
215
|
+
FakeDevice("gauss_adc1"),
|
216
|
+
FakeDevice("gauss_adc2"),
|
217
|
+
FakeDevice("gauss_adc3"),
|
218
|
+
FakeDevice("bpm4i"),
|
219
|
+
FakeDevice("bpm3a"),
|
220
|
+
FakeDevice("bpm3i"),
|
221
|
+
FakeDevice("eiger", readout_priority=ReadoutPriority.ASYNC),
|
222
|
+
FakeDevice("waveform1d"),
|
223
|
+
FakeDevice("async_device", readout_priority=ReadoutPriority.ASYNC),
|
224
|
+
Positioner("test", limits=[-10, 10], read_value=2.0),
|
225
|
+
Device("test_device"),
|
226
|
+
]
|
@@ -0,0 +1,156 @@
|
|
1
|
+
"""Module for handling filter I/O operations in BEC Widgets for input fields.
|
2
|
+
These operations include filtering device/signal names and/or device types.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
|
7
|
+
from bec_lib.logger import bec_logger
|
8
|
+
from qtpy.QtCore import QStringListModel
|
9
|
+
from qtpy.QtWidgets import QComboBox, QCompleter, QLineEdit
|
10
|
+
|
11
|
+
logger = bec_logger.logger
|
12
|
+
|
13
|
+
|
14
|
+
class WidgetFilterHandler(ABC):
|
15
|
+
"""Abstract base class for widget filter handlers"""
|
16
|
+
|
17
|
+
@abstractmethod
|
18
|
+
def set_selection(self, widget, selection: list) -> None:
|
19
|
+
"""Set the filtered_selection for the widget
|
20
|
+
|
21
|
+
Args:
|
22
|
+
selection (list): Filtered selection of items
|
23
|
+
"""
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
def check_input(self, widget, text: str) -> bool:
|
27
|
+
"""Check if the input text is in the filtered selection
|
28
|
+
|
29
|
+
Args:
|
30
|
+
widget: Widget instance
|
31
|
+
text (str): Input text
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
bool: True if the input text is in the filtered selection
|
35
|
+
"""
|
36
|
+
|
37
|
+
|
38
|
+
class LineEditFilterHandler(WidgetFilterHandler):
|
39
|
+
"""Handler for QLineEdit widget"""
|
40
|
+
|
41
|
+
def set_selection(self, widget: QLineEdit, selection: list) -> None:
|
42
|
+
"""Set the selection for the widget to the completer model
|
43
|
+
|
44
|
+
Args:
|
45
|
+
widget (QLineEdit): The QLineEdit widget
|
46
|
+
selection (list): Filtered selection of items
|
47
|
+
"""
|
48
|
+
if not isinstance(widget.completer, QCompleter):
|
49
|
+
completer = QCompleter(widget)
|
50
|
+
widget.setCompleter(completer)
|
51
|
+
widget.completer.setModel(QStringListModel(selection, widget))
|
52
|
+
|
53
|
+
def check_input(self, widget: QLineEdit, text: str) -> bool:
|
54
|
+
"""Check if the input text is in the filtered selection
|
55
|
+
|
56
|
+
Args:
|
57
|
+
widget (QLineEdit): The QLineEdit widget
|
58
|
+
text (str): Input text
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
bool: True if the input text is in the filtered selection
|
62
|
+
"""
|
63
|
+
model = widget.completer.model()
|
64
|
+
model_data = [model.data(model.index(i)) for i in range(model.rowCount())]
|
65
|
+
return text in model_data
|
66
|
+
|
67
|
+
|
68
|
+
class ComboBoxFilterHandler(WidgetFilterHandler):
|
69
|
+
"""Handler for QComboBox widget"""
|
70
|
+
|
71
|
+
def set_selection(self, widget: QComboBox, selection: list) -> None:
|
72
|
+
"""Set the selection for the widget to the completer model
|
73
|
+
|
74
|
+
Args:
|
75
|
+
widget (QComboBox): The QComboBox widget
|
76
|
+
selection (list): Filtered selection of items
|
77
|
+
"""
|
78
|
+
widget.clear()
|
79
|
+
widget.addItems(selection)
|
80
|
+
|
81
|
+
def check_input(self, widget: QComboBox, text: str) -> bool:
|
82
|
+
"""Check if the input text is in the filtered selection
|
83
|
+
|
84
|
+
Args:
|
85
|
+
widget (QComboBox): The QComboBox widget
|
86
|
+
text (str): Input text
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
bool: True if the input text is in the filtered selection
|
90
|
+
"""
|
91
|
+
return text in [widget.itemText(i) for i in range(widget.count())]
|
92
|
+
|
93
|
+
|
94
|
+
class FilterIO:
|
95
|
+
"""Public interface to set filters for input widgets.
|
96
|
+
It supports the list of widgets stored in class attribute _handlers.
|
97
|
+
"""
|
98
|
+
|
99
|
+
_handlers = {QLineEdit: LineEditFilterHandler, QComboBox: ComboBoxFilterHandler}
|
100
|
+
|
101
|
+
@staticmethod
|
102
|
+
def set_selection(widget, selection: list, ignore_errors=True):
|
103
|
+
"""
|
104
|
+
Retrieve value from the widget instance.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
widget: Widget instance.
|
108
|
+
selection(list): List of filtered selection items.
|
109
|
+
ignore_errors(bool, optional): Whether to ignore if no handler is found.
|
110
|
+
"""
|
111
|
+
handler_class = FilterIO._find_handler(widget)
|
112
|
+
if handler_class:
|
113
|
+
return handler_class().set_selection(widget=widget, selection=selection)
|
114
|
+
if not ignore_errors:
|
115
|
+
raise ValueError(
|
116
|
+
f"No matching handler for widget type: {type(widget)} in handler list {FilterIO._handlers}"
|
117
|
+
)
|
118
|
+
return None
|
119
|
+
|
120
|
+
@staticmethod
|
121
|
+
def check_input(widget, text: str, ignore_errors=True):
|
122
|
+
"""
|
123
|
+
Check if the input text is in the filtered selection.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
widget: Widget instance.
|
127
|
+
text(str): Input text.
|
128
|
+
ignore_errors(bool, optional): Whether to ignore if no handler is found.
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
bool: True if the input text is in the filtered selection.
|
132
|
+
"""
|
133
|
+
handler_class = FilterIO._find_handler(widget)
|
134
|
+
if handler_class:
|
135
|
+
return handler_class().check_input(widget=widget, text=text)
|
136
|
+
if not ignore_errors:
|
137
|
+
raise ValueError(
|
138
|
+
f"No matching handler for widget type: {type(widget)} in handler list {FilterIO._handlers}"
|
139
|
+
)
|
140
|
+
return None
|
141
|
+
|
142
|
+
@staticmethod
|
143
|
+
def _find_handler(widget):
|
144
|
+
"""
|
145
|
+
Find the appropriate handler for the widget by checking its base classes.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
widget: Widget instance.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
handler_class: The handler class if found, otherwise None.
|
152
|
+
"""
|
153
|
+
for base in type(widget).__mro__:
|
154
|
+
if base in FilterIO._handlers:
|
155
|
+
return FilterIO._handlers[base]
|
156
|
+
return None
|
bec_widgets/utils/widget_io.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# pylint: disable=no-name-in-module
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
+
from typing import Literal
|
3
4
|
|
4
5
|
from qtpy.QtWidgets import (
|
5
6
|
QApplication,
|
@@ -20,7 +21,7 @@ class WidgetHandler(ABC):
|
|
20
21
|
"""Abstract base class for all widget handlers."""
|
21
22
|
|
22
23
|
@abstractmethod
|
23
|
-
def get_value(self, widget: QWidget):
|
24
|
+
def get_value(self, widget: QWidget, **kwargs):
|
24
25
|
"""Retrieve value from the widget instance."""
|
25
26
|
|
26
27
|
@abstractmethod
|
@@ -31,7 +32,7 @@ class WidgetHandler(ABC):
|
|
31
32
|
class LineEditHandler(WidgetHandler):
|
32
33
|
"""Handler for QLineEdit widgets."""
|
33
34
|
|
34
|
-
def get_value(self, widget: QLineEdit) -> str:
|
35
|
+
def get_value(self, widget: QLineEdit, **kwargs) -> str:
|
35
36
|
return widget.text()
|
36
37
|
|
37
38
|
def set_value(self, widget: QLineEdit, value: str) -> None:
|
@@ -41,7 +42,9 @@ class LineEditHandler(WidgetHandler):
|
|
41
42
|
class ComboBoxHandler(WidgetHandler):
|
42
43
|
"""Handler for QComboBox widgets."""
|
43
44
|
|
44
|
-
def get_value(self, widget: QComboBox) -> int:
|
45
|
+
def get_value(self, widget: QComboBox, as_string: bool = False, **kwargs) -> int | str:
|
46
|
+
if as_string is True:
|
47
|
+
return widget.currentText()
|
45
48
|
return widget.currentIndex()
|
46
49
|
|
47
50
|
def set_value(self, widget: QComboBox, value: int | str) -> None:
|
@@ -54,7 +57,7 @@ class ComboBoxHandler(WidgetHandler):
|
|
54
57
|
class TableWidgetHandler(WidgetHandler):
|
55
58
|
"""Handler for QTableWidget widgets."""
|
56
59
|
|
57
|
-
def get_value(self, widget: QTableWidget) -> list:
|
60
|
+
def get_value(self, widget: QTableWidget, **kwargs) -> list:
|
58
61
|
return [
|
59
62
|
[
|
60
63
|
widget.item(row, col).text() if widget.item(row, col) else ""
|
@@ -73,7 +76,7 @@ class TableWidgetHandler(WidgetHandler):
|
|
73
76
|
class SpinBoxHandler(WidgetHandler):
|
74
77
|
"""Handler for QSpinBox and QDoubleSpinBox widgets."""
|
75
78
|
|
76
|
-
def get_value(self, widget):
|
79
|
+
def get_value(self, widget, **kwargs):
|
77
80
|
return widget.value()
|
78
81
|
|
79
82
|
def set_value(self, widget, value):
|
@@ -83,7 +86,7 @@ class SpinBoxHandler(WidgetHandler):
|
|
83
86
|
class CheckBoxHandler(WidgetHandler):
|
84
87
|
"""Handler for QCheckBox widgets."""
|
85
88
|
|
86
|
-
def get_value(self, widget):
|
89
|
+
def get_value(self, widget, **kwargs):
|
87
90
|
return widget.isChecked()
|
88
91
|
|
89
92
|
def set_value(self, widget, value):
|
@@ -93,7 +96,7 @@ class CheckBoxHandler(WidgetHandler):
|
|
93
96
|
class LabelHandler(WidgetHandler):
|
94
97
|
"""Handler for QLabel widgets."""
|
95
98
|
|
96
|
-
def get_value(self, widget):
|
99
|
+
def get_value(self, widget, **kwargs):
|
97
100
|
return widget.text()
|
98
101
|
|
99
102
|
def set_value(self, widget, value):
|
@@ -114,7 +117,7 @@ class WidgetIO:
|
|
114
117
|
}
|
115
118
|
|
116
119
|
@staticmethod
|
117
|
-
def get_value(widget, ignore_errors=False):
|
120
|
+
def get_value(widget, ignore_errors=False, **kwargs):
|
118
121
|
"""
|
119
122
|
Retrieve value from the widget instance.
|
120
123
|
|
@@ -124,7 +127,7 @@ class WidgetIO:
|
|
124
127
|
"""
|
125
128
|
handler_class = WidgetIO._find_handler(widget)
|
126
129
|
if handler_class:
|
127
|
-
return handler_class().get_value(widget) # Instantiate the handler
|
130
|
+
return handler_class().get_value(widget, **kwargs) # Instantiate the handler
|
128
131
|
if not ignore_errors:
|
129
132
|
raise ValueError(f"No handler for widget type: {type(widget)}")
|
130
133
|
return None
|