diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index 4c1370c5..ff7eea71 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -16,6 +16,7 @@ class Widgets(str, enum.Enum): BECDock = "BECDock" BECDockArea = "BECDockArea" BECFigure = "BECFigure" + RoundStatusIndicator = "RoundStatusIndicator" SpiralProgressBar = "SpiralProgressBar" TextBox = "TextBox" WebsiteWidget = "WebsiteWidget" @@ -1719,6 +1720,17 @@ class Ring(RPCBase): """ +class RoundStatusIndicator(RPCBase): + @rpc_call + def set_state(self, state: Literal["success", "failure", "warning"]) -> None: + """ + Set the state of the indicator. + + Args: + state (str): The state of the indicator. Can be "success", "failure", or "warning" + """ + + class SpiralProgressBar(RPCBase): @rpc_call def get_all_rpc(self) -> "dict": @@ -1920,7 +1932,7 @@ class TextBox(RPCBase): @rpc_call def set_color(self, background_color: str, font_color: str) -> None: """ - Set the background color of the Widget. + Set the background color of the widget. Args: background_color (str): The color to set the background in HEX. @@ -1930,13 +1942,19 @@ class TextBox(RPCBase): @rpc_call def set_text(self, text: str) -> None: """ - Set the text of the Widget + Set the text of the widget. + + Args: + text (str): The text to set. """ @rpc_call def set_font_size(self, size: int) -> None: """ - Set the font size of the text in the Widget. + Set the font size of the text in the widget. + + Args: + size (int): The font size to set. """ diff --git a/bec_widgets/cli/rpc_wigdet_handler.py b/bec_widgets/cli/rpc_wigdet_handler.py index ff2f372e..a310d4ae 100644 --- a/bec_widgets/cli/rpc_wigdet_handler.py +++ b/bec_widgets/cli/rpc_wigdet_handler.py @@ -1,5 +1,6 @@ from bec_widgets.utils import BECConnector from bec_widgets.widgets.figure import BECFigure +from bec_widgets.widgets.round_status_indicator.round_status_indicator import RoundStatusIndicator from bec_widgets.widgets.spiral_progress_bar.spiral_progress_bar import SpiralProgressBar from bec_widgets.widgets.text_box.text_box import TextBox from bec_widgets.widgets.website.website import WebsiteWidget @@ -13,6 +14,7 @@ class RPCWidgetHandler: "SpiralProgressBar": SpiralProgressBar, "Website": WebsiteWidget, "TextBox": TextBox, + "RoundStatusIndicator": RoundStatusIndicator, } @staticmethod diff --git a/bec_widgets/widgets/round_status_indicator/__init__.py b/bec_widgets/widgets/round_status_indicator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bec_widgets/widgets/round_status_indicator/round_status_indicator.py b/bec_widgets/widgets/round_status_indicator/round_status_indicator.py new file mode 100644 index 00000000..c7372e1c --- /dev/null +++ b/bec_widgets/widgets/round_status_indicator/round_status_indicator.py @@ -0,0 +1,108 @@ +from enum import Enum +from typing import Literal + +from PyQt6.QtGui import QPaintEvent +from qtpy import QtGui +from qtpy.QtCore import QRect, Qt +from qtpy.QtWidgets import QApplication, QMainWindow, QWidget + +from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig + + +class RoundStatusIndicatorConfig(ConnectionConfig): + """ + Configuration for the RoundStatusIndicator + """ + + state: Literal["success", "failure", "warning"] = "success" + + +class RoundStatusIndicator(BECConnector, QWidget): + + indicator_config = { + "success": {"color": "#24a148", "text": "✔", "offset": 0.25}, + "failure": {"color": "#da1e28", "text": "✘", "offset": 0.28}, + "warning": {"color": "#ffcc00", "text": "!", "offset": 0.28}, + } + + USER_ACCESS = ["set_state"] + + def __init__( + self, + client=None, + config: RoundStatusIndicatorConfig | dict | None = None, + gui_id=None, + parent=None, + ): + super().__init__(client=client, config=config, gui_id=gui_id) + QWidget.__init__(self, parent=parent) + self.config = config or RoundStatusIndicatorConfig(widget_class=self.__class__.__name__) + self.active_state_config = self.indicator_config[self.config.state] + + def paintEvent(self, _event: QPaintEvent) -> None: + """ + Paint the widget. + + Args: + _event (QPaintEvent): The paint event + """ + + color = QtGui.QColor(self.active_state_config["color"]) # Red color as default + text_color = QtGui.QColor("#ffffff") # White color for text + text = self.active_state_config["text"] + offset = self.active_state_config["offset"] + + painter = QtGui.QPainter(self) + painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing) + + # Determine the size of the widget + size = min(self.width(), self.height()) + rect = QRect(0, 0, size, size) + + # Set the color and draw the disk + painter.setBrush(color) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawEllipse(rect) + + # Draw the exclamation mark "!" + painter.setPen(text_color) + font = painter.font() + font.setPixelSize(int(size * 0.9)) # Adjust font size based on widget size + painter.setFont(font) + + # text_rect = painter.boundingRect(rect, Qt.AlignmentFlag.AlignCenter, text) + + font_metrics = QtGui.QFontMetrics(font) + text_width = font_metrics.horizontalAdvance(text) + text_height = font_metrics.height() + + text_x = int(rect.center().x() - text_width / 2) + text_y = int(rect.center().y() + text_height * offset) + painter.drawText(text_x, text_y, text) + + def set_state(self, state: Literal["success", "failure", "warning"]) -> None: + """ + Set the state of the indicator. + + Args: + state (str): The state of the indicator. Can be "success", "failure", or "warning" + + """ + self.config.state = state + self.active_state_config = self.indicator_config[state] + self.update() + + +if __name__ == "__main__": + app = QApplication([]) + + window = QMainWindow() + status_indicator = RoundStatusIndicator() + window.setCentralWidget(status_indicator) + window.resize(200, 200) + window.show() + + # Example of changing the color + status_indicator.set_state("warning") # Change to a different color + + app.exec()