diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index f45fdb56..a477c1a2 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -2914,31 +2914,21 @@ class StopButton(RPCBase): class TextBox(RPCBase): @rpc_call - def set_color(self, background_color: str, font_color: str) -> None: + def set_plain_text(self, text: str) -> None: """ - Set the background color of the widget. - - Args: - background_color (str): The color to set the background in HEX. - font_color (str): The color to set the font in HEX. - """ - - @rpc_call - def set_text(self, text: str) -> None: - """ - Set the text of the widget. + Set the plain text of the widget. Args: text (str): The text to set. """ @rpc_call - def set_font_size(self, size: int) -> None: + def set_html_text(self, text: str) -> None: """ - Set the font size of the text in the widget. + Set the HTML text of the widget. Args: - size (int): The font size to set. + text (str): The text to set. """ diff --git a/bec_widgets/widgets/text_box/text_box.py b/bec_widgets/widgets/text_box/text_box.py index ae099eae..6bc343d0 100644 --- a/bec_widgets/widgets/text_box/text_box.py +++ b/bec_widgets/widgets/text_box/text_box.py @@ -1,39 +1,47 @@ -import re +"""Module for a text box widget that displays text in plain and HTML format and adheres to the BECWidget interface & style.""" -from pydantic import Field, field_validator -from qtpy.QtWidgets import QTextEdit +import re +from html.parser import HTMLParser + +from bec_lib.logger import bec_logger +from pydantic import Field +from qtpy.QtCore import Property, Slot +from qtpy.QtWidgets import QTextEdit, QVBoxLayout, QWidget from bec_widgets.utils.bec_connector import ConnectionConfig from bec_widgets.utils.bec_widget import BECWidget -from bec_widgets.utils.colors import Colors + +logger = bec_logger.logger + +DEFAULT_TEXT = "
A widget that allows user to display text in plain and HTML format.
This is an example of displaying HTML text.
" class TextBoxConfig(ConnectionConfig): + """Configuration for the TextBox widget. - theme: str = Field("dark", description="The theme of the figure widget.") - font_color: str = Field("#FFF", description="The font color of the text") - background_color: str = Field("#000", description="The background color of the widget.") - font_size: int = Field(16, description="The font size of the text in the widget.") - text: str = Field("", description="The text to display in the widget.") + Args: + text (str, optional): The text to display in the widget. Defaults to None. + is_html (bool, optional): Whether the text is in HTML format or not. Defaults to False. + """ - @classmethod - @field_validator("theme") - def validate_theme(cls, v): - """Validate the theme of the figure widget.""" - if v not in ["dark", "light"]: - raise ValueError("Theme must be either 'dark' or 'light'") - return v - - _validate_font_color = field_validator("font_color")(Colors.validate_color) - _validate_background_color = field_validator("background_color")(Colors.validate_color) + text: str | None = Field(None, description="The text to display in the widget.") + is_html: bool = Field(False, description="Whether the text is in HTML format or not.") -class TextBox(BECWidget, QTextEdit): +class TextBox(BECWidget, QWidget): + """A widget that displays text in plain and HTML format - USER_ACCESS = ["set_color", "set_text", "set_font_size"] + Args: + parent (QWidget, optional): The parent widget. Defaults to None. + client ([type], optional): The client to use. Defaults to None. + config ([type], optional): The config to use. Defaults to None. + gui_id ([type], optional): The gui_id to use. Defaults to None. + """ + + USER_ACCESS = ["set_plain_text", "set_html_text"] ICON_NAME = "chat" - def __init__(self, parent=None, text: str = "", client=None, config=None, gui_id=None): + def __init__(self, parent=None, client=None, config=None, gui_id=None): if config is None: config = TextBoxConfig(widget_class=self.__class__.__name__) else: @@ -41,80 +49,79 @@ class TextBox(BECWidget, QTextEdit): config = TextBoxConfig(**config) self.config = config super().__init__(client=client, config=config, gui_id=gui_id) - QTextEdit.__init__(self, parent=parent) - + QWidget.__init__(self, parent) + self.layout = QVBoxLayout(self) + self.text_box_text_edit = QTextEdit(parent=self) + self.layout.addWidget(self.text_box_text_edit) + self.setLayout(self.layout) + self.layout.setContentsMargins(0, 0, 0, 0) self.config = config - self.setReadOnly(True) - self.setGeometry(self.rect()) - self.set_color(self.config.background_color, self.config.font_color) - if not text: - text = "A widget that allows user to display text in plain and HTML format.
This is an example of displaying HTML text.
" - self.set_text(text) - - def change_theme(self) -> None: - """ - Change the theme of the figure widget. - """ - if self.config.theme == "dark": - theme = "light" - font_color = "#000" - background_color = "#FFF" + self.text_box_text_edit.setReadOnly(True) + if self.config.text is not None: + if self.config.is_html: + self.set_html_text(self.config.text) + else: + self.set_plain_text(self.config.text) else: - theme = "dark" - font_color = "#FFF" - background_color = "#000" - self.config.theme = theme - self.set_color(background_color, font_color) + self.set_html_text(DEFAULT_TEXT) - def set_color(self, background_color: str, font_color: str) -> None: - """Set the background color of the widget. + @Slot(str) + def set_plain_text(self, text: str) -> None: + """Set the plain text of the widget. Args: - background_color (str): The color to set the background in HEX. - font_color (str): The color to set the font in HEX. - + text (str): The text to set. """ - self.config.background_color = background_color - self.config.font_color = font_color - self._update_stylesheet() + self.text_box_text_edit.setPlainText(text) + self.config.text = text + self.config.is_html = False - def set_font_size(self, size: int) -> None: - """Set the font size of the text in the widget. + @Slot(str) + def set_html_text(self, text: str) -> None: + """Set the HTML text of the widget. Args: - size (int): The font size to set. + text (str): The text to set. """ - self.config.font_size = size - self._update_stylesheet() + self.text_box_text_edit.setHtml(text) + self.config.text = text + self.config.is_html = True - def _update_stylesheet(self): - """Update the stylesheet of the widget.""" - self.setStyleSheet( - f"background-color: {self.config.background_color}; color: {self.config.font_color}; font-size: {self.config.font_size}px" - ) + @Property(str) + def plain_text(self) -> str: + """Get the text of the widget. - def set_text(self, text: str) -> None: + Returns: + str: The text of the widget. + """ + return self.text_box_text_edit.toPlainText() + + @plain_text.setter + def plain_text(self, text: str) -> None: """Set the text of the widget. Args: text (str): The text to set. """ - if self.is_html(text): - self.setHtml(text) - else: - self.setPlainText(text) - self.config.text = text + self.set_plain_text(text) - def is_html(self, text: str) -> bool: - """Check if the text contains HTML tags. - - Args: - text (str): The text to check. + @Property(str) + def html_text(self) -> str: + """Get the HTML text of the widget. Returns: - bool: True if the text contains HTML tags, False otherwise. + str: The HTML text of the widget. """ - return bool(re.search(r"<[a-zA-Z/][^>]*>", text)) + return self.text_box_text_edit.toHtml() + + @html_text.setter + def html_text(self, text: str) -> None: + """Set the HTML text of the widget. + + Args: + text (str): The HTML text to set. + """ + self.set_html_text(text) if __name__ == "__main__": @@ -123,7 +130,6 @@ if __name__ == "__main__": from qtpy.QtWidgets import QApplication app = QApplication(sys.argv) - widget = TextBox() widget.show() sys.exit(app.exec()) diff --git a/docs/assets/widget_screenshots/text_box_properties.png b/docs/assets/widget_screenshots/text_box_properties.png new file mode 100644 index 00000000..c9100053 Binary files /dev/null and b/docs/assets/widget_screenshots/text_box_properties.png differ diff --git a/docs/user/widgets/lmfit_dialog/lmfit_dialog.md b/docs/user/widgets/lmfit_dialog/lmfit_dialog.md index ae498c92..a3157e0a 100644 --- a/docs/user/widgets/lmfit_dialog/lmfit_dialog.md +++ b/docs/user/widgets/lmfit_dialog/lmfit_dialog.md @@ -36,7 +36,7 @@ waveform.dap_summary_update.connect(lmfit_dialog.update_summary_tree) ```` ````{tab} API ```{eval-rst} -.. include:: /api_reference/_autosummary/bec_widgets.cli.client.LMFitDialog.rst +.. include:: /api_reference/_autosummary/bec_widgets.widgets.lmfit_dialog.lmfit_dialog.LMFitDialog.rst ``` ```` diff --git a/docs/user/widgets/scan_control/scan_control.md b/docs/user/widgets/scan_control/scan_control.md index 5cae6d5a..2e894756 100644 --- a/docs/user/widgets/scan_control/scan_control.md +++ b/docs/user/widgets/scan_control/scan_control.md @@ -30,6 +30,7 @@ Within the BECDesigner's [property editor](https://doc.qt.io/qt-6/designer-widge - **Hide Scan Control**: Allows you to hide the scan control buttons from the widget interface. This is useful when you want to place the control buttons in a different location. - **Hide Scan Selection**: Allows you to hide the scan selection combobox from the widget interface. This is useful when you want to restrict the user to a specific scan type or implement a custom scan selection mechanism. - **Hide Scan Remember Toggle**: Allows you to hide the toggle button that reloads scan parameters from the last executed scan. This is useful if you want to disable or restrict this functionality in specific scenarios. +- **Hide Bundle Buttons**: Allows you to hide the buttons that add or remove argument bundles from the widget interface. This is useful when you want to restrict the user from adding additional motor bundles to the scan by accident. **BEC Designer properties:** ```{figure} ./hide_scan_control.png diff --git a/docs/user/widgets/text_box/text_box.md b/docs/user/widgets/text_box/text_box.md index 24bd1275..083c8087 100644 --- a/docs/user/widgets/text_box/text_box.md +++ b/docs/user/widgets/text_box/text_box.md @@ -8,8 +8,11 @@ The [`Text Box Widget`](/api_reference/_autosummary/bec_widgets.cli.client.TextB ## Key Features: - **Text Display**: Display either plain text or HTML content, with automatic detection of the format. -- **Customizable Appearance**: Set the background and font colors to match the design of your application. -- **Font Size Adjustment**: Customize the font size of the displayed text for better readability. +- **Automatic styling**: The widget automatically adheres to BEC's style guides. No need to worry about background colors, font sizes, or other appearance settings. + +## BEC Designer Properties +```{figure} ../../assets/widget_screenshots/text_box_properties.png +``` ```` @@ -38,24 +41,6 @@ The `TextBox` widget can automatically detect and render HTML content. This exam text_box.set_text("This is an example of displaying HTML text.
") ``` -## Example 3 - Customizing Appearance - -The `TextBox` widget allows you to customize the background and font colors to fit your application's design. Below is an example of how to set these properties. - -```python -# Set the background color to white and the font color to black -text_box.set_color(background_color="#FFF", font_color="#000") -``` - -## Example 4 - Adjusting Font Size - -To improve readability or fit more text within the widget, you can adjust the font size. - -```python -# Set the font size to 14 pixels -text_box.set_font_size(14) -``` - ```` ````{tab} API diff --git a/tests/unit_tests/test_text_box_widget.py b/tests/unit_tests/test_text_box_widget.py index fa95970c..1c706c7e 100644 --- a/tests/unit_tests/test_text_box_widget.py +++ b/tests/unit_tests/test_text_box_widget.py @@ -1,9 +1,6 @@ -import re -from unittest import mock - import pytest -from bec_widgets.widgets.text_box.text_box import TextBox +from bec_widgets.widgets.text_box.text_box import DEFAULT_TEXT, TextBox from .client_mocks import mocked_client @@ -18,37 +15,16 @@ def text_box_widget(qtbot, mocked_client): def test_textbox_widget(text_box_widget): """Test the TextBox widget.""" + # Test default + assert text_box_widget.config.text == DEFAULT_TEXT + # Test set text text = "Hello World!" - text_box_widget.set_text(text) - assert text_box_widget.toPlainText() == text - - text_box_widget.set_color("#FFDDC1", "#123456") - text_box_widget.set_font_size(20) - assert ( - text_box_widget.styleSheet() == "background-color: #FFDDC1; color: #123456; font-size: 20px" - ) - text_box_widget.set_color("white", "blue") - text_box_widget.set_font_size(14) - assert text_box_widget.styleSheet() == "background-color: white; color: blue; font-size: 14px" + text_box_widget.set_plain_text(text) + assert text_box_widget.plain_text == text + # Test set HTML text text = "This is an example of displaying HTML text.
" - with mock.patch.object(text_box_widget, "setHtml") as mocked_set_html: - text_box_widget.set_text(text) - assert mocked_set_html.call_count == 1 - assert mocked_set_html.call_args == mock.call(text) - - -def test_textbox_change_theme(text_box_widget): - """Test change theme functionaility""" - # Default is dark theme - text_box_widget.change_theme() - assert text_box_widget.config.theme == "light" + text_box_widget.set_html_text(text) assert ( - text_box_widget.styleSheet() - == f"background-color: #FFF; color: #000; font-size: {text_box_widget.config.font_size}px" - ) - text_box_widget.change_theme() - assert text_box_widget.config.theme == "dark" - assert ( - text_box_widget.styleSheet() - == f"background-color: #000; color: #FFF; font-size: {text_box_widget.config.font_size}px" + text_box_widget.plain_text + == "Welcome to PyQt6\nThis is an example of displaying HTML text." )