0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

feat(progressbar): state setting and dynamic corner radius

This commit is contained in:
2025-06-20 14:59:26 +02:00
committed by Jan Wyzula
parent 3bbb8daa24
commit d3a9e0903a
2 changed files with 91 additions and 7 deletions

View File

@ -474,6 +474,20 @@ class BECProgressBar(RPCBase):
>>> progressbar.label_template = "$value / $percentage %" >>> progressbar.label_template = "$value / $percentage %"
""" """
@property
@rpc_call
def state(self):
"""
None
"""
@state.setter
@rpc_call
def state(self):
"""
None
"""
@rpc_call @rpc_call
def _get_label(self) -> str: def _get_label(self) -> str:
""" """

View File

@ -1,8 +1,26 @@
import sys import sys
from enum import Enum
from string import Template from string import Template
from qtpy.QtCore import QEasingCurve, QPropertyAnimation, QRectF, Qt, QTimer from qtpy.QtCore import QEasingCurve, QPropertyAnimation, QRectF, Qt, QTimer
from qtpy.QtGui import QColor, QPainter, QPainterPath from qtpy.QtGui import QColor, QPainter, QPainterPath
class ProgressState(Enum):
NORMAL = "normal"
PAUSED = "paused"
INTERRUPTED = "interrupted"
COMPLETED = "completed"
PROGRESS_STATE_COLORS = {
ProgressState.NORMAL: QColor("#2979ff"), # blue normal progress
ProgressState.PAUSED: QColor("#ffca28"), # orange/amber paused
ProgressState.INTERRUPTED: QColor("#ff5252"), # red interrupted
ProgressState.COMPLETED: QColor("#00e676"), # green finished
}
# ---------------------------------------------------------------------------
from qtpy.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget from qtpy.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget
from bec_widgets.utils.bec_widget import BECWidget from bec_widgets.utils.bec_widget import BECWidget
@ -22,6 +40,8 @@ class BECProgressBar(BECWidget, QWidget):
"set_minimum", "set_minimum",
"label_template", "label_template",
"label_template.setter", "label_template.setter",
"state",
"state.setter",
"_get_label", "_get_label",
] ]
ICON_NAME = "page_control" ICON_NAME = "page_control"
@ -49,6 +69,12 @@ class BECProgressBar(BECWidget, QWidget):
self._completed_color = accent_colors.success self._completed_color = accent_colors.success
self._border_color = QColor(50, 50, 50) self._border_color = QColor(50, 50, 50)
# Cornerrounding: base radius in pixels (autoreduced if bar is small)
self._corner_radius = 10
# Progressbar state handling
self._state = ProgressState.NORMAL
self._state_colors = dict(PROGRESS_STATE_COLORS)
# layout settings # layout settings
self._padding_left_right = 10 self._padding_left_right = 10
@ -141,8 +167,43 @@ class BECProgressBar(BECWidget, QWidget):
self._target_value = self.map_value(value) self._target_value = self.map_value(value)
self._user_value = value self._user_value = value
self.center_label.setText(self._update_template()) self.center_label.setText(self._update_template())
# Update state automatically unless paused or interrupted
if self._state not in (ProgressState.PAUSED, ProgressState.INTERRUPTED):
self._state = (
ProgressState.COMPLETED
if self._user_value >= self._user_maximum
else ProgressState.NORMAL
)
self.animate_progress() self.animate_progress()
@SafeProperty(object, doc="Current visual state of the progress bar.")
def state(self):
return self._state
@state.setter
def state(self, state):
"""
Set the visual state of the progress bar.
Args:
state(ProgressState | str): The state to set. Can be one of the
"""
if isinstance(state, str):
state = ProgressState(state.lower())
if not isinstance(state, ProgressState):
raise ValueError("state must be a ProgressState or its value")
self._state = state
self.update()
@SafeProperty(float, doc="Base corner radius in pixels (autoscaled down on small bars).")
def corner_radius(self) -> float:
return self._corner_radius
@corner_radius.setter
def corner_radius(self, radius: float):
self._corner_radius = max(0.0, radius)
self.update()
@SafeProperty(float) @SafeProperty(float)
def padding_left_right(self) -> float: def padding_left_right(self) -> float:
return self._padding_left_right return self._padding_left_right
@ -157,28 +218,37 @@ class BECProgressBar(BECWidget, QWidget):
painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.Antialiasing)
rect = self.rect().adjusted(self._padding_left_right, 0, -self._padding_left_right, -1) rect = self.rect().adjusted(self._padding_left_right, 0, -self._padding_left_right, -1)
# Corner radius adapts to widget height so it never exceeds half the bars thickness
radius = min(self._corner_radius, rect.height() / 2)
# Draw background # Draw background
painter.setBrush(self._background_color) painter.setBrush(self._background_color)
painter.setPen(Qt.NoPen) painter.setPen(Qt.NoPen)
painter.drawRoundedRect(rect, 10, 10) # Rounded corners painter.drawRoundedRect(rect, radius, radius) # Rounded corners
# Draw border # Draw border
painter.setBrush(Qt.NoBrush) painter.setBrush(Qt.NoBrush)
painter.setPen(self._border_color) painter.setPen(self._border_color)
painter.drawRoundedRect(rect, 10, 10) painter.drawRoundedRect(rect, radius, radius)
# Determine progress color based on completion # Determine progress colour based on current state
if self._value >= self._maximum: if self._state == ProgressState.PAUSED:
current_color = self._completed_color current_color = self._state_colors[ProgressState.PAUSED]
elif self._state == ProgressState.INTERRUPTED:
current_color = self._state_colors[ProgressState.INTERRUPTED]
elif self._state == ProgressState.COMPLETED or self._value >= self._maximum:
current_color = self._state_colors[ProgressState.COMPLETED]
else: else:
current_color = self._progress_color current_color = self._state_colors[ProgressState.NORMAL]
# Set clipping region to preserve the background's rounded corners # Set clipping region to preserve the background's rounded corners
progress_rect = rect.adjusted( progress_rect = rect.adjusted(
0, 0, int(-rect.width() + (self._value / self._maximum) * rect.width()), 0 0, 0, int(-rect.width() + (self._value / self._maximum) * rect.width()), 0
) )
clip_path = QPainterPath() clip_path = QPainterPath()
clip_path.addRoundedRect(QRectF(rect), 10, 10) # Clip to the background's rounded corners clip_path.addRoundedRect(
QRectF(rect), radius, radius
) # Clip to the background's rounded corners
painter.setClipPath(clip_path) painter.setClipPath(clip_path)
# Draw progress bar # Draw progress bar