bec-widgets 2.16.2__py3-none-any.whl → 2.18.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 +88 -0
- PKG-INFO +3 -3
- bec_widgets/applications/launch_window.py +1 -1
- bec_widgets/cli/client.py +24 -0
- bec_widgets/tests/utils.py +15 -3
- bec_widgets/utils/filter_io.py +125 -9
- bec_widgets/widgets/containers/main_window/main_window.py +122 -7
- bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +16 -16
- bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +22 -12
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +2 -0
- bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py +9 -5
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +7 -2
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +62 -19
- bec_widgets/widgets/plots/waveform/waveform.py +0 -1
- bec_widgets/widgets/progress/bec_progressbar/bec_progressbar.py +135 -21
- bec_widgets/widgets/progress/scan_progressbar/__init__.py +0 -0
- bec_widgets/widgets/progress/scan_progressbar/register_scan_progress_bar.py +17 -0
- bec_widgets/widgets/progress/scan_progressbar/scan_progress_bar.pyproject +1 -0
- bec_widgets/widgets/progress/scan_progressbar/scan_progress_bar_plugin.py +54 -0
- bec_widgets/widgets/progress/scan_progressbar/scan_progressbar.py +320 -0
- bec_widgets/widgets/progress/scan_progressbar/scan_progressbar.ui +141 -0
- bec_widgets/widgets/progress/scan_progressbar/scan_progressbar_one_line.ui +124 -0
- {bec_widgets-2.16.2.dist-info → bec_widgets-2.18.0.dist-info}/METADATA +3 -3
- {bec_widgets-2.16.2.dist-info → bec_widgets-2.18.0.dist-info}/RECORD +28 -21
- pyproject.toml +6 -6
- {bec_widgets-2.16.2.dist-info → bec_widgets-2.18.0.dist-info}/WHEEL +0 -0
- {bec_widgets-2.16.2.dist-info → bec_widgets-2.18.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-2.16.2.dist-info → bec_widgets-2.18.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,6 +1,94 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## v2.18.0 (2025-06-22)
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
- Make settings dialog resizable
|
9
|
+
([`5a564a5`](https://github.com/bec-project/bec_widgets/commit/5a564a5f3f3229e6407ea52a59d3e63319dc214a))
|
10
|
+
|
11
|
+
- **curve settings**: Add initial size hint
|
12
|
+
([`a9708f6`](https://github.com/bec-project/bec_widgets/commit/a9708f6d8f15c42b142488da1e392a8f3179932a))
|
13
|
+
|
14
|
+
### Features
|
15
|
+
|
16
|
+
- **curve settings**: Add combobox selection for device and signal
|
17
|
+
([`eea5f7e`](https://github.com/bec-project/bec_widgets/commit/eea5f7ebbd2b477b3ed19c7efcc76390dd391f26))
|
18
|
+
|
19
|
+
- **device combobox**: Emit reset event if validation fails
|
20
|
+
([`4c2c0c5`](https://github.com/bec-project/bec_widgets/commit/4c2c0c5525d593d8ec7fd554336cb11adbe32de2))
|
21
|
+
|
22
|
+
- **FilterIO**: Add support for item data
|
23
|
+
([`8e8acd6`](https://github.com/bec-project/bec_widgets/commit/8e8acd672c0deb8dcd928886fb574452ac956de7))
|
24
|
+
|
25
|
+
- **signal combobox**: Add reset_selection slot
|
26
|
+
([`b51de1a`](https://github.com/bec-project/bec_widgets/commit/b51de1a00e4b17c44cab23e5097391c6fa8ea0e2))
|
27
|
+
|
28
|
+
### Refactoring
|
29
|
+
|
30
|
+
- **device input**: Refactor to SafeProperty and SafeSlot
|
31
|
+
([`6e2f2ce`](https://github.com/bec-project/bec_widgets/commit/6e2f2cea91ba3af33e9891532506f4b0b65b90c8))
|
32
|
+
|
33
|
+
|
34
|
+
## v2.17.0 (2025-06-22)
|
35
|
+
|
36
|
+
### Bug Fixes
|
37
|
+
|
38
|
+
- **bec_progressbar**: Layout and sizing adjustments
|
39
|
+
([`b02c870`](https://github.com/bec-project/bec_widgets/commit/b02c870dbfecb4bc6921ec4c915dac0e67beb9b4))
|
40
|
+
|
41
|
+
- **launch_window**: Number of remaining connections increase to 2 to include the ScanProgressBar
|
42
|
+
([`3bbb8da`](https://github.com/bec-project/bec_widgets/commit/3bbb8daa24348613f62bde667a446d37dcec8fb0))
|
43
|
+
|
44
|
+
- **main_window**: Labels and sizing of scan progress adopted
|
45
|
+
([`aca6efb`](https://github.com/bec-project/bec_widgets/commit/aca6efb567528eb3c68521a59b4f9903a5616c6f))
|
46
|
+
|
47
|
+
- **scan_progressbar**: Cleanup adjusted
|
48
|
+
([`e8ae972`](https://github.com/bec-project/bec_widgets/commit/e8ae9725fa86b7db52a147ca5a2acc62fa2ccf43))
|
49
|
+
|
50
|
+
- **scan_progressbar**: Mapping of bec progress states to the progressbar enums
|
51
|
+
([`88b42e4`](https://github.com/bec-project/bec_widgets/commit/88b42e49e30a0aa0edc2de4d970408f4be5bde6b))
|
52
|
+
|
53
|
+
### Build System
|
54
|
+
|
55
|
+
- Update min dependency of bec to 3.42.4
|
56
|
+
([`a4274ff`](https://github.com/bec-project/bec_widgets/commit/a4274ff8cd9f3e73a61b2eaf902c172c028d21b0))
|
57
|
+
|
58
|
+
### Features
|
59
|
+
|
60
|
+
- **main_window**: Added scan progress bar to BECMainWindow status bar
|
61
|
+
([`497e394`](https://github.com/bec-project/bec_widgets/commit/497e394deb5cfe36c8fc4f769fef26f109fd1c1f))
|
62
|
+
|
63
|
+
- **main_window**: Timer to show hide scan progress when it is relevant only
|
64
|
+
([`9ff1706`](https://github.com/bec-project/bec_widgets/commit/9ff170660edd9e03f99eccee60b5e20fc1cf5a8d))
|
65
|
+
|
66
|
+
- **progressbar**: Added padding as designer property
|
67
|
+
([`a451625`](https://github.com/bec-project/bec_widgets/commit/a451625a5ab804ca8259f9c9f83c4f9ebbea4a5b))
|
68
|
+
|
69
|
+
- **progressbar**: State setting and dynamic corner radius
|
70
|
+
([`d3a9e09`](https://github.com/bec-project/bec_widgets/commit/d3a9e0903a263d735ecab3a2ad9319c9d5e86092))
|
71
|
+
|
72
|
+
- **scan_progressbar**: Added oneline design for compact applications
|
73
|
+
([`d5ca7b8`](https://github.com/bec-project/bec_widgets/commit/d5ca7b84337cf60aa66f961d357ae66994f53c7a))
|
74
|
+
|
75
|
+
- **scan_progressbar**: Added progressbar with hooks to scan progress and device progress
|
76
|
+
([`c4b8538`](https://github.com/bec-project/bec_widgets/commit/c4b85381a41e4742567680864668ee83d498b1d1))
|
77
|
+
|
78
|
+
### Refactoring
|
79
|
+
|
80
|
+
- **progressbar**: Change slot / property to safeslot / safeproperty
|
81
|
+
([`92d0ffe`](https://github.com/bec-project/bec_widgets/commit/92d0ffee65babc718fafd60131d0a4f291e5ca2b))
|
82
|
+
|
83
|
+
### Testing
|
84
|
+
|
85
|
+
- **scan progress**: Add test for queue update logic
|
86
|
+
([`b2a46e2`](https://github.com/bec-project/bec_widgets/commit/b2a46e284d45e97dd9853d1a3c8e95de7e530267))
|
87
|
+
|
88
|
+
- **scan_progress**: Tests extended
|
89
|
+
([`6c04eac`](https://github.com/bec-project/bec_widgets/commit/6c04eac18c887526b333f58fc1118c3b4029abd8))
|
90
|
+
|
91
|
+
|
4
92
|
## v2.16.2 (2025-06-20)
|
5
93
|
|
6
94
|
### Bug Fixes
|
PKG-INFO
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bec_widgets
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.18.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
|
@@ -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,>=3.
|
13
|
-
Requires-Dist: bec-lib<=4.0,>=3.
|
12
|
+
Requires-Dist: bec-ipython-client<=4.0,>=3.42.4
|
13
|
+
Requires-Dist: bec-lib<=4.0,>=3.42.4
|
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
|
@@ -542,7 +542,7 @@ class LaunchWindow(BECMainWindow):
|
|
542
542
|
remaining_connections = [
|
543
543
|
connection for connection in connections.values() if connection.parent_id != self.gui_id
|
544
544
|
]
|
545
|
-
return len(remaining_connections) <=
|
545
|
+
return len(remaining_connections) <= 2
|
546
546
|
|
547
547
|
def _turn_off_the_lights(self, connections: dict):
|
548
548
|
"""
|
bec_widgets/cli/client.py
CHANGED
@@ -474,6 +474,20 @@ class BECProgressBar(RPCBase):
|
|
474
474
|
>>> progressbar.label_template = "$value / $percentage %"
|
475
475
|
"""
|
476
476
|
|
477
|
+
@property
|
478
|
+
@rpc_call
|
479
|
+
def state(self):
|
480
|
+
"""
|
481
|
+
None
|
482
|
+
"""
|
483
|
+
|
484
|
+
@state.setter
|
485
|
+
@rpc_call
|
486
|
+
def state(self):
|
487
|
+
"""
|
488
|
+
None
|
489
|
+
"""
|
490
|
+
|
477
491
|
@rpc_call
|
478
492
|
def _get_label(self) -> str:
|
479
493
|
"""
|
@@ -3245,6 +3259,16 @@ class ScanControl(RPCBase):
|
|
3245
3259
|
"""
|
3246
3260
|
|
3247
3261
|
|
3262
|
+
class ScanProgressBar(RPCBase):
|
3263
|
+
"""Widget to display a progress bar that is hooked up to the scan progress of a scan."""
|
3264
|
+
|
3265
|
+
@rpc_call
|
3266
|
+
def remove(self):
|
3267
|
+
"""
|
3268
|
+
Cleanup the BECConnector
|
3269
|
+
"""
|
3270
|
+
|
3271
|
+
|
3248
3272
|
class ScatterCurve(RPCBase):
|
3249
3273
|
"""Scatter curve item for the scatter waveform widget."""
|
3250
3274
|
|
bec_widgets/tests/utils.py
CHANGED
@@ -96,9 +96,21 @@ class FakePositioner(BECPositioner):
|
|
96
96
|
}
|
97
97
|
self._info = {
|
98
98
|
"signals": {
|
99
|
-
"readback": {
|
100
|
-
|
101
|
-
|
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 = {
|
bec_widgets/utils/filter_io.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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):
|
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
|
"""
|
@@ -1,9 +1,28 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import os
|
2
4
|
|
3
5
|
from bec_lib.endpoints import MessageEndpoints
|
4
|
-
from qtpy.QtCore import
|
6
|
+
from qtpy.QtCore import (
|
7
|
+
QAbstractAnimation,
|
8
|
+
QEasingCurve,
|
9
|
+
QEvent,
|
10
|
+
QPropertyAnimation,
|
11
|
+
QSize,
|
12
|
+
Qt,
|
13
|
+
QTimer,
|
14
|
+
)
|
5
15
|
from qtpy.QtGui import QAction, QActionGroup, QIcon
|
6
|
-
from qtpy.QtWidgets import
|
16
|
+
from qtpy.QtWidgets import (
|
17
|
+
QApplication,
|
18
|
+
QFrame,
|
19
|
+
QHBoxLayout,
|
20
|
+
QLabel,
|
21
|
+
QMainWindow,
|
22
|
+
QStyle,
|
23
|
+
QVBoxLayout,
|
24
|
+
QWidget,
|
25
|
+
)
|
7
26
|
|
8
27
|
import bec_widgets
|
9
28
|
from bec_widgets.utils import UILoader
|
@@ -13,6 +32,7 @@ from bec_widgets.utils.error_popups import SafeSlot
|
|
13
32
|
from bec_widgets.utils.widget_io import WidgetHierarchy
|
14
33
|
from bec_widgets.widgets.containers.main_window.addons.scroll_label import ScrollLabel
|
15
34
|
from bec_widgets.widgets.containers.main_window.addons.web_links import BECWebLinksMixin
|
35
|
+
from bec_widgets.widgets.progress.scan_progressbar.scan_progressbar import ScanProgressBar
|
16
36
|
|
17
37
|
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
18
38
|
|
@@ -20,6 +40,8 @@ MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
|
20
40
|
class BECMainWindow(BECWidget, QMainWindow):
|
21
41
|
RPC = False
|
22
42
|
PLUGIN = False
|
43
|
+
SCAN_PROGRESS_WIDTH = 100 # px
|
44
|
+
STATUS_BAR_WIDGETS_EXPIRE_TIME = 60_000 # milliseconds
|
23
45
|
|
24
46
|
def __init__(
|
25
47
|
self,
|
@@ -33,6 +55,7 @@ class BECMainWindow(BECWidget, QMainWindow):
|
|
33
55
|
super().__init__(parent=parent, gui_id=gui_id, **kwargs)
|
34
56
|
|
35
57
|
self.app = QApplication.instance()
|
58
|
+
self.status_bar = self.statusBar()
|
36
59
|
self.setWindowTitle(window_title)
|
37
60
|
self._init_ui()
|
38
61
|
self._connect_to_theme_change()
|
@@ -61,14 +84,13 @@ class BECMainWindow(BECWidget, QMainWindow):
|
|
61
84
|
"""
|
62
85
|
Prepare the BEC specific widgets in the status bar.
|
63
86
|
"""
|
64
|
-
status_bar = self.statusBar()
|
65
87
|
|
66
88
|
# Left: App‑ID label
|
67
89
|
self._app_id_label = QLabel()
|
68
90
|
self._app_id_label.setAlignment(
|
69
91
|
Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter
|
70
92
|
)
|
71
|
-
status_bar.addWidget(self._app_id_label)
|
93
|
+
self.status_bar.addWidget(self._app_id_label)
|
72
94
|
|
73
95
|
# Add a separator after the app ID label
|
74
96
|
self._add_separator()
|
@@ -78,16 +100,100 @@ class BECMainWindow(BECWidget, QMainWindow):
|
|
78
100
|
self._client_info_label.setAlignment(
|
79
101
|
Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter
|
80
102
|
)
|
81
|
-
status_bar.addWidget(self._client_info_label, 1)
|
103
|
+
self.status_bar.addWidget(self._client_info_label, 1)
|
82
104
|
|
83
105
|
# Timer to automatically clear client messages once they expire
|
84
106
|
self._client_info_expire_timer = QTimer(self)
|
85
107
|
self._client_info_expire_timer.setSingleShot(True)
|
86
108
|
self._client_info_expire_timer.timeout.connect(lambda: self._client_info_label.setText(""))
|
87
109
|
|
88
|
-
|
110
|
+
# Add scan_progress bar with display logic
|
111
|
+
self._add_scan_progress_bar()
|
112
|
+
|
113
|
+
################################################################################
|
114
|
+
# Progress‑bar helpers
|
115
|
+
def _add_scan_progress_bar(self):
|
116
|
+
|
117
|
+
# --- Progress bar -------------------------------------------------
|
118
|
+
# Scan progress bar minimalistic design setup
|
119
|
+
self._scan_progress_bar = ScanProgressBar(self, one_line_design=True)
|
120
|
+
self._scan_progress_bar.show_elapsed_time = False
|
121
|
+
self._scan_progress_bar.show_remaining_time = False
|
122
|
+
self._scan_progress_bar.show_source_label = False
|
123
|
+
self._scan_progress_bar.progressbar.label_template = ""
|
124
|
+
self._scan_progress_bar.progressbar.setFixedHeight(8)
|
125
|
+
self._scan_progress_bar.progressbar.setFixedWidth(80)
|
126
|
+
|
127
|
+
# Bundle the progress bar with a separator
|
128
|
+
separator = self._add_separator(separate_object=True)
|
129
|
+
self._scan_progress_bar_with_separator = QWidget()
|
130
|
+
self._scan_progress_bar_with_separator.layout = QHBoxLayout(
|
131
|
+
self._scan_progress_bar_with_separator
|
132
|
+
)
|
133
|
+
self._scan_progress_bar_with_separator.layout.setContentsMargins(0, 0, 0, 0)
|
134
|
+
self._scan_progress_bar_with_separator.layout.setSpacing(0)
|
135
|
+
self._scan_progress_bar_with_separator.layout.addWidget(separator)
|
136
|
+
self._scan_progress_bar_with_separator.layout.addWidget(self._scan_progress_bar)
|
137
|
+
|
138
|
+
# Set Size
|
139
|
+
self._scan_progress_bar_target_width = self.SCAN_PROGRESS_WIDTH
|
140
|
+
self._scan_progress_bar_with_separator.setMaximumWidth(self._scan_progress_bar_target_width)
|
141
|
+
|
142
|
+
self.status_bar.addWidget(self._scan_progress_bar_with_separator)
|
143
|
+
|
144
|
+
# Visibility logic
|
145
|
+
self._scan_progress_bar_with_separator.hide()
|
146
|
+
self._scan_progress_bar_with_separator.setMaximumWidth(0)
|
147
|
+
|
148
|
+
# Timer for hiding logic
|
149
|
+
self._scan_progress_hide_timer = QTimer(self)
|
150
|
+
self._scan_progress_hide_timer.setSingleShot(True)
|
151
|
+
self._scan_progress_hide_timer.setInterval(self.STATUS_BAR_WIDGETS_EXPIRE_TIME)
|
152
|
+
self._scan_progress_hide_timer.timeout.connect(self._animate_hide_scan_progress_bar)
|
153
|
+
|
154
|
+
# Show / hide behaviour
|
155
|
+
self._scan_progress_bar.progress_started.connect(self._show_scan_progress_bar)
|
156
|
+
self._scan_progress_bar.progress_finished.connect(self._delay_hide_scan_progress_bar)
|
157
|
+
|
158
|
+
def _show_scan_progress_bar(self):
|
159
|
+
if self._scan_progress_hide_timer.isActive():
|
160
|
+
self._scan_progress_hide_timer.stop()
|
161
|
+
if self._scan_progress_bar_with_separator.isVisible():
|
162
|
+
return
|
163
|
+
|
164
|
+
# Make visible and reset width
|
165
|
+
self._scan_progress_bar_with_separator.show()
|
166
|
+
self._scan_progress_bar_with_separator.setMaximumWidth(0)
|
167
|
+
|
168
|
+
self._show_container_anim = QPropertyAnimation(
|
169
|
+
self._scan_progress_bar_with_separator, b"maximumWidth", self
|
170
|
+
)
|
171
|
+
self._show_container_anim.setDuration(300)
|
172
|
+
self._show_container_anim.setStartValue(0)
|
173
|
+
self._show_container_anim.setEndValue(self._scan_progress_bar_target_width)
|
174
|
+
self._show_container_anim.setEasingCurve(QEasingCurve.OutCubic)
|
175
|
+
self._show_container_anim.start()
|
176
|
+
|
177
|
+
def _delay_hide_scan_progress_bar(self):
|
178
|
+
"""Start the countdown to hide the scan progress bar."""
|
179
|
+
if hasattr(self, "_scan_progress_hide_timer"):
|
180
|
+
self._scan_progress_hide_timer.start()
|
181
|
+
|
182
|
+
def _animate_hide_scan_progress_bar(self):
|
183
|
+
"""Shrink container to the right, then hide."""
|
184
|
+
self._hide_container_anim = QPropertyAnimation(
|
185
|
+
self._scan_progress_bar_with_separator, b"maximumWidth", self
|
186
|
+
)
|
187
|
+
self._hide_container_anim.setDuration(300)
|
188
|
+
self._hide_container_anim.setStartValue(self._scan_progress_bar_with_separator.width())
|
189
|
+
self._hide_container_anim.setEndValue(0)
|
190
|
+
self._hide_container_anim.setEasingCurve(QEasingCurve.InCubic)
|
191
|
+
self._hide_container_anim.finished.connect(self._scan_progress_bar_with_separator.hide)
|
192
|
+
self._hide_container_anim.start()
|
193
|
+
|
194
|
+
def _add_separator(self, separate_object: bool = False) -> QWidget | None:
|
89
195
|
"""
|
90
|
-
Add a vertically centred separator to the status bar.
|
196
|
+
Add a vertically centred separator to the status bar or just return it as a separate object.
|
91
197
|
"""
|
92
198
|
status_bar = self.statusBar()
|
93
199
|
|
@@ -106,6 +212,8 @@ class BECMainWindow(BECWidget, QMainWindow):
|
|
106
212
|
vbox.addStretch()
|
107
213
|
wrapper.setFixedWidth(line.sizeHint().width())
|
108
214
|
|
215
|
+
if separate_object:
|
216
|
+
return wrapper
|
109
217
|
status_bar.addWidget(wrapper)
|
110
218
|
|
111
219
|
def _init_bec_icon(self):
|
@@ -279,10 +387,16 @@ class BECMainWindow(BECWidget, QMainWindow):
|
|
279
387
|
child.close()
|
280
388
|
child.deleteLater()
|
281
389
|
|
390
|
+
# Timer cleanup
|
282
391
|
if hasattr(self, "_client_info_expire_timer") and self._client_info_expire_timer.isActive():
|
283
392
|
self._client_info_expire_timer.stop()
|
393
|
+
if hasattr(self, "_scan_progress_hide_timer") and self._scan_progress_hide_timer.isActive():
|
394
|
+
self._scan_progress_hide_timer.stop()
|
395
|
+
|
284
396
|
# Status bar widgets cleanup
|
285
397
|
self._client_info_label.cleanup()
|
398
|
+
self._scan_progress_bar.close()
|
399
|
+
self._scan_progress_bar.deleteLater()
|
286
400
|
super().cleanup()
|
287
401
|
|
288
402
|
|
@@ -296,4 +410,5 @@ if __name__ == "__main__":
|
|
296
410
|
app = QApplication(sys.argv)
|
297
411
|
main_window = UILaunchWindow()
|
298
412
|
main_window.show()
|
413
|
+
main_window.resize(800, 600)
|
299
414
|
sys.exit(app.exec())
|