diff --git a/bec_widgets/applications/views/device_manager_view/device_manager_display_widget.py b/bec_widgets/applications/views/device_manager_view/device_manager_display_widget.py index 3ae9c1bb..b2db6bbd 100644 --- a/bec_widgets/applications/views/device_manager_view/device_manager_display_widget.py +++ b/bec_widgets/applications/views/device_manager_view/device_manager_display_widget.py @@ -13,6 +13,7 @@ from bec_lib.messages import ConfigAction from bec_lib.plugin_helper import plugin_package_name, plugin_repo_path from bec_qthemes import apply_theme, material_icon from qtpy.QtCore import QMetaObject, Qt, QThreadPool, Signal +from qtpy.QtGui import QColor from qtpy.QtWidgets import ( QApplication, QFileDialog, @@ -77,12 +78,13 @@ class CustomBusyWidget(QWidget): # Widgets progress = DeviceInitializationProgressBar(parent=self, client=client) + progress.setMinimumWidth(320) # Spinner spinner = SpinnerWidget(parent=self) scale = self._ui_scale() spinner_size = int(scale * 0.12) if scale else 1 - spinner_size = max(32, min(spinner_size, 64)) + spinner_size = max(32, min(spinner_size, 96)) spinner.setFixedSize(spinner_size, spinner_size) # Cancel button @@ -116,6 +118,17 @@ class CustomBusyWidget(QWidget): content_layout.addStretch() content_layout.addWidget(cancel_button, 0, Qt.AlignmentFlag.AlignHCenter) + if hasattr(color, "_colors"): + bg_color = color._colors.get("BG", None) + if bg_color is None: # Fallback if missing + bg_color = QColor(50, 50, 50, 255) + self.setStyleSheet( + f""" + background-color: {bg_color.name()}; + border-radius: 12px; + """ + ) + def _ui_scale(self) -> int: parent = self.parent() if not parent: @@ -248,7 +261,7 @@ class DeviceManagerDisplayWidget(DockAreaWidget): def _set_busy_wrapper(self, enabled: bool): """Thin wrapper around set_busy to flip the state variable.""" - self._busy_overlay.set_opacity(0.8) + self._busy_overlay.set_opacity(0.92) self._config_upload_active = enabled self.set_busy(enabled=enabled) diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index eef2ac49..c0c0bdf7 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -36,7 +36,6 @@ _Widgets = { "DarkModeButton": "DarkModeButton", "DeviceBrowser": "DeviceBrowser", "DeviceComboBox": "DeviceComboBox", - "DeviceInitializationProgressBar": "DeviceInitializationProgressBar", "DeviceLineEdit": "DeviceLineEdit", "Heatmap": "Heatmap", "Image": "Image", @@ -1076,72 +1075,21 @@ class DeviceInitializationProgressBar(RPCBase): """A progress bar that displays the progress of device initialization.""" @rpc_call - def set_value(self, value): + def remove(self): """ - Set the value of the progress bar. - - Args: - value (float): The value to set. + Cleanup the BECConnector """ @rpc_call - def set_maximum(self, maximum: float): - """ - Set the maximum value of the progress bar. - - Args: - maximum (float): The maximum value. - """ - - @rpc_call - def set_minimum(self, minimum: float): - """ - Set the minimum value of the progress bar. - - Args: - minimum (float): The minimum value. - """ - - @property - @rpc_call - def label_template(self): - """ - The template for the center label. Use $value, $maximum, and $percentage to insert the values. - - Examples: - >>> progressbar.label_template = "$value / $maximum - $percentage %" - >>> progressbar.label_template = "$value / $percentage %" - """ - - @label_template.setter - @rpc_call - def label_template(self): - """ - The template for the center label. Use $value, $maximum, and $percentage to insert the values. - - Examples: - >>> progressbar.label_template = "$value / $maximum - $percentage %" - >>> progressbar.label_template = "$value / $percentage %" - """ - - @property - @rpc_call - def state(self): - """ - None - """ - - @state.setter - @rpc_call - def state(self): + def attach(self): """ None """ @rpc_call - def _get_label(self) -> str: + def detach(self): """ - Return the label text. mostly used for testing rpc. + Detach the widget from its parent dock widget (if widget is in the dock), making it a floating widget. """ diff --git a/bec_widgets/widgets/progress/device_initialization_progress_bar/device_initialization_progress_bar.py b/bec_widgets/widgets/progress/device_initialization_progress_bar/device_initialization_progress_bar.py index de18bbeb..07fe5ba3 100644 --- a/bec_widgets/widgets/progress/device_initialization_progress_bar/device_initialization_progress_bar.py +++ b/bec_widgets/widgets/progress/device_initialization_progress_bar/device_initialization_progress_bar.py @@ -1,12 +1,17 @@ +"""Module for a ProgressBar for device initialization progress.""" + from bec_lib.endpoints import MessageEndpoints from bec_lib.messages import DeviceInitializationProgressMessage from qtpy.QtCore import Signal +from qtpy.QtGui import QColor +from qtpy.QtWidgets import QApplication, QGroupBox, QHBoxLayout, QLabel, QVBoxLayout, QWidget +from bec_widgets.utils.bec_widget import BECWidget from bec_widgets.utils.error_popups import SafeProperty, SafeSlot from bec_widgets.widgets.progress.bec_progressbar.bec_progressbar import BECProgressBar -class DeviceInitializationProgressBar(BECProgressBar): +class DeviceInitializationProgressBar(BECWidget, QWidget): """A progress bar that displays the progress of device initialization.""" # Signal emitted for failed device initializations @@ -15,12 +20,58 @@ class DeviceInitializationProgressBar(BECProgressBar): def __init__(self, parent=None, client=None, **kwargs): super().__init__(parent=parent, client=client, **kwargs) self._failed_devices: list[str] = [] + + # Main Layout with Group Box + main_layout = QVBoxLayout(self) + main_layout.setContentsMargins(4, 4, 4, 4) + main_layout.setSpacing(0) + self.group_box = QGroupBox(self) + self.group_box.setTitle("Config Update Progress") + main_layout.addWidget(self.group_box) + lay = QVBoxLayout(self.group_box) + lay.setContentsMargins(25, 25, 25, 25) + lay.setSpacing(5) + + # Progress Bar and Label in Layout + self.progress_bar = BECProgressBar(parent=parent, client=client, **kwargs) + self.progress_bar.label_template = "$value / $maximum - $percentage %" + self.progress_label = QLabel("Initializing devices...", self) + + self.progress_label.setStyleSheet("font-size: 12px; font-weight: cursive;") + content_layout = QVBoxLayout() + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(0) + content_layout.addWidget(self.progress_bar) + + # Layout for label, to place label properly below progress bar + # Adjust 10px left margin for aesthetic alignment + hor_layout = QHBoxLayout() + hor_layout.setContentsMargins(12, 0, 0, 0) + hor_layout.addWidget(self.progress_label) + content_layout.addLayout(hor_layout) + content_layout.addStretch() + + # Add content layout to main layout + lay.addLayout(content_layout) + self.bec_dispatcher.connect_slot( slot=self._update_device_initialization_progress, topics=MessageEndpoints.device_initialization_progress(), ) self._reset_progress_bar() + def _update_palette(self) -> None: + """Update theme palette for the widget.""" + _app = QApplication.instance() + if hasattr(_app, "theme"): + theme = _app.theme # type: ignore[attr-defined] + text_color = theme.color("FG") + else: + text_color = QColor(230, 230, 230) + self.progress_label.setStyleSheet( + f"color: {text_color.name()}; font-size: 12px; font-weight: cursive;" + ) + @SafeProperty(list) def failed_devices(self) -> list[str]: """Get the list of devices that failed to initialize. @@ -62,39 +113,26 @@ class DeviceInitializationProgressBar(BECProgressBar): msg: DeviceInitializationProgressMessage = ( DeviceInitializationProgressMessage.model_validate(msg) ) + # Reset progress bar if index has gone backwards, this indicates a new initialization sequence + old_value = self.progress_bar._user_value + if msg.index < old_value: + self._reset_progress_bar() + # Update progress based on message content if msg.finished is False: - self.label_template = "\n".join( - [ - f"Device initialization for '{msg.device}' is in progress...", - "$value / $maximum - $percentage %", - ] - ) + self.progress_label.setText(f"{msg.device} initialization in progress...") elif msg.finished is True and msg.success is False: self.add_failed_device(msg.device) - self.label_template = "\n".join( - [ - f"Device initialization for '{msg.device}' failed!", - "$value / $maximum - $percentage %", - ] - ) + self.progress_label.setText(f"{msg.device} initialization failed!") else: - self.label_template = "\n".join( - [ - f"Device initialization for '{msg.device}' succeeded!", - "$value / $maximum - $percentage %", - ] - ) - self.set_maximum(msg.total) - self.set_value(msg.index) + self.progress_label.setText(f"{msg.device} initialization succeeded!") + self.progress_bar.set_maximum(msg.total) + self.progress_bar.set_value(msg.index) self._update_tool_tip() def _reset_progress_bar(self) -> None: """Reset the progress bar to its initial state.""" - self.label_template = "\n".join( - ["Waiting for device initialization...", "$value / $maximum - $percentage %"] - ) - self.set_value(0) - self.set_maximum(1) + self.progress_bar.set_value(0) + self.progress_bar.set_maximum(100) self.reset_failed_devices() self._update_tool_tip() @@ -110,9 +148,11 @@ class DeviceInitializationProgressBar(BECProgressBar): if __name__ == "__main__": # pragma: no cover import sys + from bec_qthemes import apply_theme from qtpy.QtWidgets import QApplication app = QApplication(sys.argv) + apply_theme("dark") progressBar = DeviceInitializationProgressBar()