bec-widgets 2.16.2__py3-none-any.whl → 2.17.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 CHANGED
@@ -1,6 +1,64 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v2.17.0 (2025-06-22)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **bec_progressbar**: Layout and sizing adjustments
9
+ ([`b02c870`](https://github.com/bec-project/bec_widgets/commit/b02c870dbfecb4bc6921ec4c915dac0e67beb9b4))
10
+
11
+ - **launch_window**: Number of remaining connections increase to 2 to include the ScanProgressBar
12
+ ([`3bbb8da`](https://github.com/bec-project/bec_widgets/commit/3bbb8daa24348613f62bde667a446d37dcec8fb0))
13
+
14
+ - **main_window**: Labels and sizing of scan progress adopted
15
+ ([`aca6efb`](https://github.com/bec-project/bec_widgets/commit/aca6efb567528eb3c68521a59b4f9903a5616c6f))
16
+
17
+ - **scan_progressbar**: Cleanup adjusted
18
+ ([`e8ae972`](https://github.com/bec-project/bec_widgets/commit/e8ae9725fa86b7db52a147ca5a2acc62fa2ccf43))
19
+
20
+ - **scan_progressbar**: Mapping of bec progress states to the progressbar enums
21
+ ([`88b42e4`](https://github.com/bec-project/bec_widgets/commit/88b42e49e30a0aa0edc2de4d970408f4be5bde6b))
22
+
23
+ ### Build System
24
+
25
+ - Update min dependency of bec to 3.42.4
26
+ ([`a4274ff`](https://github.com/bec-project/bec_widgets/commit/a4274ff8cd9f3e73a61b2eaf902c172c028d21b0))
27
+
28
+ ### Features
29
+
30
+ - **main_window**: Added scan progress bar to BECMainWindow status bar
31
+ ([`497e394`](https://github.com/bec-project/bec_widgets/commit/497e394deb5cfe36c8fc4f769fef26f109fd1c1f))
32
+
33
+ - **main_window**: Timer to show hide scan progress when it is relevant only
34
+ ([`9ff1706`](https://github.com/bec-project/bec_widgets/commit/9ff170660edd9e03f99eccee60b5e20fc1cf5a8d))
35
+
36
+ - **progressbar**: Added padding as designer property
37
+ ([`a451625`](https://github.com/bec-project/bec_widgets/commit/a451625a5ab804ca8259f9c9f83c4f9ebbea4a5b))
38
+
39
+ - **progressbar**: State setting and dynamic corner radius
40
+ ([`d3a9e09`](https://github.com/bec-project/bec_widgets/commit/d3a9e0903a263d735ecab3a2ad9319c9d5e86092))
41
+
42
+ - **scan_progressbar**: Added oneline design for compact applications
43
+ ([`d5ca7b8`](https://github.com/bec-project/bec_widgets/commit/d5ca7b84337cf60aa66f961d357ae66994f53c7a))
44
+
45
+ - **scan_progressbar**: Added progressbar with hooks to scan progress and device progress
46
+ ([`c4b8538`](https://github.com/bec-project/bec_widgets/commit/c4b85381a41e4742567680864668ee83d498b1d1))
47
+
48
+ ### Refactoring
49
+
50
+ - **progressbar**: Change slot / property to safeslot / safeproperty
51
+ ([`92d0ffe`](https://github.com/bec-project/bec_widgets/commit/92d0ffee65babc718fafd60131d0a4f291e5ca2b))
52
+
53
+ ### Testing
54
+
55
+ - **scan progress**: Add test for queue update logic
56
+ ([`b2a46e2`](https://github.com/bec-project/bec_widgets/commit/b2a46e284d45e97dd9853d1a3c8e95de7e530267))
57
+
58
+ - **scan_progress**: Tests extended
59
+ ([`6c04eac`](https://github.com/bec-project/bec_widgets/commit/6c04eac18c887526b333f58fc1118c3b4029abd8))
60
+
61
+
4
62
  ## v2.16.2 (2025-06-20)
5
63
 
6
64
  ### Bug Fixes
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_widgets
3
- Version: 2.16.2
3
+ Version: 2.17.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.38
13
- Requires-Dist: bec-lib<=4.0,>=3.38
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) <= 1
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
 
@@ -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 QEvent, QSize, Qt, QTimer
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 QApplication, QFrame, QLabel, QMainWindow, QStyle, QVBoxLayout, QWidget
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
- def _add_separator(self):
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())
@@ -1,12 +1,46 @@
1
1
  import sys
2
+ from enum import Enum
2
3
  from string import Template
3
4
 
4
- from qtpy.QtCore import Property, QEasingCurve, QPropertyAnimation, QRectF, Qt, QTimer, Slot
5
+ from qtpy.QtCore import QEasingCurve, QPropertyAnimation, QRectF, Qt, QTimer
5
6
  from qtpy.QtGui import QColor, QPainter, QPainterPath
7
+
8
+
9
+ class ProgressState(Enum):
10
+ NORMAL = "normal"
11
+ PAUSED = "paused"
12
+ INTERRUPTED = "interrupted"
13
+ COMPLETED = "completed"
14
+
15
+ @classmethod
16
+ def from_bec_status(cls, status: str) -> "ProgressState":
17
+ """
18
+ Map a BEC status string (open, paused, aborted, halted, closed)
19
+ to the corresponding ProgressState.
20
+ Any unknown status falls back to NORMAL.
21
+ """
22
+ mapping = {
23
+ "open": cls.NORMAL,
24
+ "paused": cls.PAUSED,
25
+ "aborted": cls.INTERRUPTED,
26
+ "halted": cls.PAUSED,
27
+ "closed": cls.COMPLETED,
28
+ }
29
+ return mapping.get(status.lower(), cls.NORMAL)
30
+
31
+
32
+ PROGRESS_STATE_COLORS = {
33
+ ProgressState.NORMAL: QColor("#2979ff"), # blue – normal progress
34
+ ProgressState.PAUSED: QColor("#ffca28"), # orange/amber – paused
35
+ ProgressState.INTERRUPTED: QColor("#ff5252"), # red – interrupted
36
+ ProgressState.COMPLETED: QColor("#00e676"), # green – finished
37
+ }
38
+
6
39
  from qtpy.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
7
40
 
8
41
  from bec_widgets.utils.bec_widget import BECWidget
9
42
  from bec_widgets.utils.colors import get_accent_colors
43
+ from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
10
44
 
11
45
 
12
46
  class BECProgressBar(BECWidget, QWidget):
@@ -21,6 +55,8 @@ class BECProgressBar(BECWidget, QWidget):
21
55
  "set_minimum",
22
56
  "label_template",
23
57
  "label_template.setter",
58
+ "state",
59
+ "state.setter",
24
60
  "_get_label",
25
61
  ]
26
62
  ICON_NAME = "page_control"
@@ -48,27 +84,38 @@ class BECProgressBar(BECWidget, QWidget):
48
84
 
49
85
  self._completed_color = accent_colors.success
50
86
  self._border_color = QColor(50, 50, 50)
87
+ # Corner‑rounding: base radius in pixels (auto‑reduced if bar is small)
88
+ self._corner_radius = 10
89
+
90
+ # Progress‑bar state handling
91
+ self._state = ProgressState.NORMAL
92
+ self._state_colors = dict(PROGRESS_STATE_COLORS)
51
93
 
52
94
  # layout settings
95
+ self._padding_left_right = 10
53
96
  self._value_animation = QPropertyAnimation(self, b"_progressbar_value")
54
97
  self._value_animation.setDuration(200)
55
98
  self._value_animation.setEasingCurve(QEasingCurve.Type.OutCubic)
56
99
 
57
100
  # label on top of the progress bar
58
101
  self.center_label = QLabel(self)
59
- self.center_label.setAlignment(Qt.AlignCenter)
102
+ self.center_label.setAlignment(Qt.AlignHCenter)
60
103
  self.center_label.setStyleSheet("color: white;")
61
104
  self.center_label.setMinimumSize(0, 0)
62
105
 
63
106
  layout = QVBoxLayout(self)
64
- layout.setContentsMargins(0, 0, 0, 0)
107
+ layout.setContentsMargins(10, 0, 10, 0)
65
108
  layout.setSpacing(0)
66
109
  layout.addWidget(self.center_label)
110
+ layout.setAlignment(self.center_label, Qt.AlignCenter)
67
111
  self.setLayout(layout)
68
112
 
69
113
  self.update()
114
+ self._adjust_label_width()
70
115
 
71
- @Property(str, doc="The template for the center label. Use $value, $maximum, and $percentage.")
116
+ @SafeProperty(
117
+ str, doc="The template for the center label. Use $value, $maximum, and $percentage."
118
+ )
72
119
  def label_template(self):
73
120
  """
74
121
  The template for the center label. Use $value, $maximum, and $percentage to insert the values.
@@ -83,10 +130,11 @@ class BECProgressBar(BECWidget, QWidget):
83
130
  @label_template.setter
84
131
  def label_template(self, template):
85
132
  self._label_template = template
133
+ self._adjust_label_width()
86
134
  self.set_value(self._user_value)
87
135
  self.update()
88
136
 
89
- @Property(float, designable=False)
137
+ @SafeProperty(float, designable=False)
90
138
  def _progressbar_value(self):
91
139
  """
92
140
  The current value of the progress bar.
@@ -106,8 +154,20 @@ class BECProgressBar(BECWidget, QWidget):
106
154
  percentage=int((self.map_value(self._user_value) / self._maximum) * 100),
107
155
  )
108
156
 
109
- @Slot(float)
110
- @Slot(int)
157
+ def _adjust_label_width(self):
158
+ """
159
+ Reserve enough horizontal space for the center label so the widget
160
+ doesn't resize as the text grows during progress.
161
+ """
162
+ template = Template(self._label_template)
163
+ sample_text = template.safe_substitute(
164
+ value=self._user_maximum, maximum=self._user_maximum, percentage=100
165
+ )
166
+ width = self.center_label.fontMetrics().horizontalAdvance(sample_text)
167
+ self.center_label.setFixedWidth(width)
168
+
169
+ @SafeSlot(float)
170
+ @SafeSlot(int)
111
171
  def set_value(self, value):
112
172
  """
113
173
  Set the value of the progress bar.
@@ -122,35 +182,88 @@ class BECProgressBar(BECWidget, QWidget):
122
182
  self._target_value = self.map_value(value)
123
183
  self._user_value = value
124
184
  self.center_label.setText(self._update_template())
185
+ # Update state automatically unless paused or interrupted
186
+ if self._state not in (ProgressState.PAUSED, ProgressState.INTERRUPTED):
187
+ self._state = (
188
+ ProgressState.COMPLETED
189
+ if self._user_value >= self._user_maximum
190
+ else ProgressState.NORMAL
191
+ )
125
192
  self.animate_progress()
126
193
 
194
+ @SafeProperty(object, doc="Current visual state of the progress bar.")
195
+ def state(self):
196
+ return self._state
197
+
198
+ @state.setter
199
+ def state(self, state):
200
+ """
201
+ Set the visual state of the progress bar.
202
+
203
+ Args:
204
+ state(ProgressState | str): The state to set. Can be one of the
205
+ """
206
+ if isinstance(state, str):
207
+ state = ProgressState(state.lower())
208
+ if not isinstance(state, ProgressState):
209
+ raise ValueError("state must be a ProgressState or its value")
210
+ self._state = state
211
+ self.update()
212
+
213
+ @SafeProperty(float, doc="Base corner radius in pixels (auto‑scaled down on small bars).")
214
+ def corner_radius(self) -> float:
215
+ return self._corner_radius
216
+
217
+ @corner_radius.setter
218
+ def corner_radius(self, radius: float):
219
+ self._corner_radius = max(0.0, radius)
220
+ self.update()
221
+
222
+ @SafeProperty(float)
223
+ def padding_left_right(self) -> float:
224
+ return self._padding_left_right
225
+
226
+ @padding_left_right.setter
227
+ def padding_left_right(self, padding: float):
228
+ self._padding_left_right = padding
229
+ self.update()
230
+
127
231
  def paintEvent(self, event):
128
232
  painter = QPainter(self)
129
233
  painter.setRenderHint(QPainter.Antialiasing)
130
- rect = self.rect().adjusted(10, 0, -10, -1)
234
+ rect = self.rect().adjusted(self._padding_left_right, 0, -self._padding_left_right, -1)
235
+
236
+ # Corner radius adapts to widget height so it never exceeds half the bar’s thickness
237
+ radius = min(self._corner_radius, rect.height() / 2)
131
238
 
132
239
  # Draw background
133
240
  painter.setBrush(self._background_color)
134
241
  painter.setPen(Qt.NoPen)
135
- painter.drawRoundedRect(rect, 10, 10) # Rounded corners
242
+ painter.drawRoundedRect(rect, radius, radius) # Rounded corners
136
243
 
137
244
  # Draw border
138
245
  painter.setBrush(Qt.NoBrush)
139
246
  painter.setPen(self._border_color)
140
- painter.drawRoundedRect(rect, 10, 10)
141
-
142
- # Determine progress color based on completion
143
- if self._value >= self._maximum:
144
- current_color = self._completed_color
247
+ painter.drawRoundedRect(rect, radius, radius)
248
+
249
+ # Determine progress colour based on current state
250
+ if self._state == ProgressState.PAUSED:
251
+ current_color = self._state_colors[ProgressState.PAUSED]
252
+ elif self._state == ProgressState.INTERRUPTED:
253
+ current_color = self._state_colors[ProgressState.INTERRUPTED]
254
+ elif self._state == ProgressState.COMPLETED or self._value >= self._maximum:
255
+ current_color = self._state_colors[ProgressState.COMPLETED]
145
256
  else:
146
- current_color = self._progress_color
257
+ current_color = self._state_colors[ProgressState.NORMAL]
147
258
 
148
259
  # Set clipping region to preserve the background's rounded corners
149
260
  progress_rect = rect.adjusted(
150
261
  0, 0, int(-rect.width() + (self._value / self._maximum) * rect.width()), 0
151
262
  )
152
263
  clip_path = QPainterPath()
153
- clip_path.addRoundedRect(QRectF(rect), 10, 10) # Clip to the background's rounded corners
264
+ clip_path.addRoundedRect(
265
+ QRectF(rect), radius, radius
266
+ ) # Clip to the background's rounded corners
154
267
  painter.setClipPath(clip_path)
155
268
 
156
269
  # Draw progress bar
@@ -168,7 +281,7 @@ class BECProgressBar(BECWidget, QWidget):
168
281
  self._value_animation.setEndValue(self._target_value)
169
282
  self._value_animation.start()
170
283
 
171
- @Property(float)
284
+ @SafeProperty(float)
172
285
  def maximum(self):
173
286
  """
174
287
  The maximum value of the progress bar.
@@ -182,7 +295,7 @@ class BECProgressBar(BECWidget, QWidget):
182
295
  """
183
296
  self.set_maximum(maximum)
184
297
 
185
- @Property(float)
298
+ @SafeProperty(float)
186
299
  def minimum(self):
187
300
  """
188
301
  The minimum value of the progress bar.
@@ -193,7 +306,7 @@ class BECProgressBar(BECWidget, QWidget):
193
306
  def minimum(self, minimum: float):
194
307
  self.set_minimum(minimum)
195
308
 
196
- @Property(float)
309
+ @SafeProperty(float)
197
310
  def initial_value(self):
198
311
  """
199
312
  The initial value of the progress bar.
@@ -204,7 +317,7 @@ class BECProgressBar(BECWidget, QWidget):
204
317
  def initial_value(self, value: float):
205
318
  self.set_value(value)
206
319
 
207
- @Slot(float)
320
+ @SafeSlot(float)
208
321
  def set_maximum(self, maximum: float):
209
322
  """
210
323
  Set the maximum value of the progress bar.
@@ -213,10 +326,11 @@ class BECProgressBar(BECWidget, QWidget):
213
326
  maximum (float): The maximum value.
214
327
  """
215
328
  self._user_maximum = maximum
329
+ self._adjust_label_width()
216
330
  self.set_value(self._user_value) # Update the value to fit the new range
217
331
  self.update()
218
332
 
219
- @Slot(float)
333
+ @SafeSlot(float)
220
334
  def set_minimum(self, minimum: float):
221
335
  """
222
336
  Set the minimum value of the progress bar.
@@ -0,0 +1,17 @@
1
+ def main(): # pragma: no cover
2
+ from qtpy import PYSIDE6
3
+
4
+ if not PYSIDE6:
5
+ print("PYSIDE6 is not available in the environment. Cannot patch designer.")
6
+ return
7
+ from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
8
+
9
+ from bec_widgets.widgets.progress.scan_progressbar.scan_progress_bar_plugin import (
10
+ ScanProgressBarPlugin,
11
+ )
12
+
13
+ QPyDesignerCustomWidgetCollection.addCustomWidget(ScanProgressBarPlugin())
14
+
15
+
16
+ if __name__ == "__main__": # pragma: no cover
17
+ main()
@@ -0,0 +1 @@
1
+ {'files': ['scan_progressbar.py']}