From 755b394c1c4d7c443c442d89c630d08ce5415554 Mon Sep 17 00:00:00 2001 From: wyzula-jan Date: Wed, 3 Jul 2024 14:35:57 +0200 Subject: [PATCH] feat(waveform_widget): BECWaveformWidget added with toolbar --- bec_widgets/cli/client.py | 278 ++++++++++-- .../plugin_example_pyside/tictactoe.py | 1 + .../widgets/figure/plots/axis_settings.py | 66 ++- bec_widgets/widgets/waveform/__init__.py | 0 .../widgets/waveform/assets/settings.svg | 4 + .../waveform/waveform_dialog/__init__.py | 0 .../waveform_dialog/waveform_toolbar.py | 16 + .../widgets/waveform/waveform_widget.py | 406 ++++++++++++++++++ 8 files changed, 722 insertions(+), 49 deletions(-) create mode 100644 bec_widgets/widgets/waveform/__init__.py create mode 100644 bec_widgets/widgets/waveform/assets/settings.svg create mode 100644 bec_widgets/widgets/waveform/waveform_dialog/__init__.py create mode 100644 bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py create mode 100644 bec_widgets/widgets/waveform/waveform_widget.py diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index c2a01d55..b052e742 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -27,6 +27,7 @@ class Widgets(str, enum.Enum): StopButton = "StopButton" TextBox = "TextBox" VSCodeEditor = "VSCodeEditor" + BECWaveformWidget = "BECWaveformWidget" WebsiteWidget = "WebsiteWidget" @@ -468,9 +469,8 @@ class BECFigure(RPCBase): @rpc_call def plot( self, - arg1: "list | np.ndarray | str | None" = None, - y: "list | np.ndarray | None" = None, x: "list | np.ndarray | None" = None, + y: "list | np.ndarray | None" = None, x_name: "str | None" = None, y_name: "str | None" = None, z_name: "str | None" = None, @@ -492,9 +492,8 @@ class BECFigure(RPCBase): Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure. Args: - arg1(list | np.ndarray | str | None): First argument which can be x data, y data, or y_name. - y(list | np.ndarray): Custom y data to plot. x(list | np.ndarray): Custom x data to plot. + y(list | np.ndarray): Custom y data to plot. x_name(str): The name of the device for the x-axis. y_name(str): The name of the device for the y-axis. z_name(str): The name of the device for the z-axis. @@ -1499,9 +1498,8 @@ class BECWaveform(RPCBase): @rpc_call def plot( self, - arg1: "list | np.ndarray | str | None" = None, - y: "list | np.ndarray | None" = None, x: "list | np.ndarray | None" = None, + y: "list | np.ndarray | None" = None, x_name: "str | None" = None, y_name: "str | None" = None, z_name: "str | None" = None, @@ -1513,20 +1511,13 @@ class BECWaveform(RPCBase): label: "str | None" = None, validate: "bool" = True, dap: "str | None" = None, - **kwargs, ) -> "BECCurve": """ Plot a curve to the plot widget. - Args: - arg1(list | np.ndarray | str | None): First argument which can be x data, y data, or y_name. + x(list | np.ndarray): Custom x data to plot. y(list | np.ndarray): Custom y data to plot. - x(list | np.ndarray): Custom y data to plot. - x_name(str): Name of the x signal. - - "best_effort": Use the best effort signal. - - "timestamp": Use the timestamp signal. - - "index": Use the index signal. - - Custom signal name of device from BEC. + x_name(str): The name of the device for the x-axis. y_name(str): The name of the device for the y-axis. z_name(str): The name of the device for the z-axis. x_entry(str): The name of the entry for the x-axis. @@ -1536,7 +1527,7 @@ class BECWaveform(RPCBase): color_map_z(str): The color map to use for the z-axis. label(str): The label of the curve. validate(bool): If True, validate the device names and entries. - dap(str): The dap model to use for the curve, only available for sync devices. If not specified, none will be added. + dap(str): The dap model to use for the curve. If not specified, none will be added. Returns: BECCurve: The curve object. @@ -1545,13 +1536,12 @@ class BECWaveform(RPCBase): @rpc_call def add_dap( self, - x_name: "str | None" = None, - y_name: "str | None" = None, + x_name: "str", + y_name: "str", x_entry: "Optional[str]" = None, y_entry: "Optional[str]" = None, color: "Optional[str]" = None, dap: "str" = "GaussianModel", - validate_bec: "bool" = True, **kwargs, ) -> "BECCurve": """ @@ -1566,27 +1556,12 @@ class BECWaveform(RPCBase): color_map_z(str): The color map to use for the z-axis. label(str, optional): Label of the curve. Defaults to None. dap(str): The dap model to use for the curve. - validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True. **kwargs: Additional keyword arguments for the curve configuration. Returns: BECCurve: The curve object. """ - @rpc_call - def set_x(self, x_name: "str", x_entry: "str | None" = None): - """ - Change the x axis of the plot widget. - - Args: - x_name(str): Name of the x signal. - - "best_effort": Use the best effort signal. - - "timestamp": Use the timestamp signal. - - "index": Use the index signal. - - Custom signal name of device from BEC. - x_entry(str): Entry of the x signal. - """ - @rpc_call def get_dap_params(self) -> "dict": """ @@ -1771,12 +1746,6 @@ class BECWaveform(RPCBase): Remove the plot widget from the figure. """ - @rpc_call - def clear_all(self): - """ - None - """ - @rpc_call def set_legend_label_size(self, size: "int" = None): """ @@ -1805,6 +1774,235 @@ class DeviceBox(RPCBase): """ +class BECWaveformWidget(RPCBase): + @property + @rpc_call + def curves(self) -> "list[BECCurve]": + """ + Get the curves of the plot widget as a list + Returns: + list: List of curves. + """ + + @rpc_call + def plot( + self, + x: "list | np.ndarray | None" = None, + y: "list | np.ndarray | None" = None, + x_name: "str | None" = None, + y_name: "str | None" = None, + z_name: "str | None" = None, + x_entry: "str | None" = None, + y_entry: "str | None" = None, + z_entry: "str | None" = None, + color: "str | None" = None, + color_map_z: "str | None" = "plasma", + label: "str | None" = None, + validate: "bool" = True, + dap: "str | None" = None, + ) -> "BECCurve": + """ + Plot a curve to the plot widget. + Args: + x(list | np.ndarray): Custom x data to plot. + y(list | np.ndarray): Custom y data to plot. + x_name(str): The name of the device for the x-axis. + y_name(str): The name of the device for the y-axis. + z_name(str): The name of the device for the z-axis. + x_entry(str): The name of the entry for the x-axis. + y_entry(str): The name of the entry for the y-axis. + z_entry(str): The name of the entry for the z-axis. + color(str): The color of the curve. + color_map_z(str): The color map to use for the z-axis. + label(str): The label of the curve. + validate(bool): If True, validate the device names and entries. + dap(str): The dap model to use for the curve. If not specified, none will be added. + + Returns: + BECCurve: The curve object. + """ + + @rpc_call + def add_dap( + self, + x_name: "str", + y_name: "str", + x_entry: "str | None" = None, + y_entry: "str | None" = None, + color: "str | None" = None, + dap: "str" = "GaussianModel", + **kwargs, + ) -> "BECCurve": + """ + Add LMFIT dap model curve to the plot widget. + + Args: + x_name(str): Name of the x signal. + x_entry(str): Entry of the x signal. + y_name(str): Name of the y signal. + y_entry(str): Entry of the y signal. + color(str, optional): Color of the curve. Defaults to None. + color_map_z(str): The color map to use for the z-axis. + label(str, optional): Label of the curve. Defaults to None. + dap(str): The dap model to use for the curve. + **kwargs: Additional keyword arguments for the curve configuration. + + Returns: + BECCurve: The curve object. + """ + + @rpc_call + def get_dap_params(self) -> "dict": + """ + Get the DAP parameters of all DAP curves. + + Returns: + dict: DAP parameters of all DAP curves. + """ + + @rpc_call + def remove_curve(self, *identifiers): + """ + Remove a curve from the plot widget. + + Args: + *identifiers: Identifier of the curve to be removed. Can be either an integer (index) or a string (curve_id). + """ + + @rpc_call + def scan_history(self, scan_index: "int" = None, scan_id: "str" = None): + """ + Update the scan curves with the data from the scan storage. + Provide only one of scan_id or scan_index. + + Args: + scan_id(str, optional): ScanID of the scan to be updated. Defaults to None. + scan_index(int, optional): Index of the scan to be updated. Defaults to None. + """ + + @rpc_call + def get_all_data(self, output: "Literal['dict', 'pandas']" = "dict") -> "dict | pd.DataFrame": + """ + Extract all curve data into a dictionary or a pandas DataFrame. + + Args: + output (Literal["dict", "pandas"]): Format of the output data. + + Returns: + dict | pd.DataFrame: Data of all curves in the specified format. + """ + + @rpc_call + def set(self, **kwargs): + """ + Set the properties of the plot widget. + + Args: + **kwargs: Keyword arguments for the properties to be set. + + Possible properties: + - title: str + - x_label: str + - y_label: str + - x_scale: Literal["linear", "log"] + - y_scale: Literal["linear", "log"] + - x_lim: tuple + - y_lim: tuple + - legend_label_size: int + """ + + @rpc_call + def set_title(self, title: "str"): + """ + Set the title of the plot widget. + + Args: + title(str): Title of the plot. + """ + + @rpc_call + def set_x_label(self, x_label: "str"): + """ + Set the x-axis label of the plot widget. + + Args: + x_label(str): Label of the x-axis. + """ + + @rpc_call + def set_y_label(self, y_label: "str"): + """ + Set the y-axis label of the plot widget. + + Args: + y_label(str): Label of the y-axis. + """ + + @rpc_call + def set_x_scale(self, x_scale: "Literal['linear', 'log']"): + """ + Set the scale of the x-axis of the plot widget. + + Args: + x_scale(Literal["linear", "log"]): Scale of the x-axis. + """ + + @rpc_call + def set_y_scale(self, y_scale: "Literal['linear', 'log']"): + """ + Set the scale of the y-axis of the plot widget. + + Args: + y_scale(Literal["linear", "log"]): Scale of the y-axis. + """ + + @rpc_call + def set_x_lim(self, x_lim: "tuple"): + """ + Set the limits of the x-axis of the plot widget. + + Args: + x_lim(tuple): Limits of the x-axis. + """ + + @rpc_call + def set_y_lim(self, y_lim: "tuple"): + """ + Set the limits of the y-axis of the plot widget. + + Args: + y_lim(tuple): Limits of the y-axis. + """ + + @rpc_call + def set_legend_label_size(self, legend_label_size: "int"): + """ + Set the size of the legend labels of the plot widget. + + Args: + legend_label_size(int): Size of the legend labels. + """ + + @rpc_call + def set_grid(self, x_grid: "bool", y_grid: "bool"): + """ + Set the grid visibility of the plot widget. + + Args: + x_grid(bool): Visibility of the x-axis grid. + y_grid(bool): Visibility of the y-axis grid. + """ + + @rpc_call + def lock_aspect_ratio(self, lock: "bool"): + """ + Lock the aspect ratio of the plot widget. + + Args: + lock(bool): Lock the aspect ratio. + """ + + class DeviceComboBox(RPCBase): @property @rpc_call diff --git a/bec_widgets/examples/plugin_example_pyside/tictactoe.py b/bec_widgets/examples/plugin_example_pyside/tictactoe.py index 9a8e5ac1..ad250254 100644 --- a/bec_widgets/examples/plugin_example_pyside/tictactoe.py +++ b/bec_widgets/examples/plugin_example_pyside/tictactoe.py @@ -16,6 +16,7 @@ class TicTacToe(QWidget): # pragma: no cover super().__init__(parent) self._state = DEFAULT_STATE self._turn_number = 0 + print("TicTac HERE !!!!!!") def minimumSizeHint(self): return QSize(200, 200) diff --git a/bec_widgets/widgets/figure/plots/axis_settings.py b/bec_widgets/widgets/figure/plots/axis_settings.py index 45b492f5..511a2a90 100644 --- a/bec_widgets/widgets/figure/plots/axis_settings.py +++ b/bec_widgets/widgets/figure/plots/axis_settings.py @@ -1,5 +1,6 @@ import os +from PySide6.QtWidgets import QDialog, QDialogButtonBox from qtpy.QtCore import Slot from qtpy.QtWidgets import QVBoxLayout, QWidget @@ -9,11 +10,12 @@ from bec_widgets.utils.widget_io import WidgetIO class AxisSettings(QWidget): - def __init__(self, parent=None, *args, **kwargs): + def __init__(self, parent=None, target_widget: QWidget = None, *args, **kwargs): super().__init__(parent=parent, *args, **kwargs) current_path = os.path.dirname(__file__) self.ui = UILoader().load_ui(os.path.join(current_path, "axis_settings.ui"), self) + self.target_widget = target_widget self.layout = QVBoxLayout(self) self.layout.addWidget(self.ui) @@ -23,8 +25,14 @@ class AxisSettings(QWidget): self.setMaximumHeight(280) self.resize(380, 280) + self.display_current_settings(self.target_widget._config_dict.get("axis", {})) + @Slot(dict) def display_current_settings(self, axis_config: dict): + + if dict == {}: + return + # Top Box WidgetIO.set_value(self.ui.plot_title, axis_config["title"]) @@ -48,14 +56,54 @@ class AxisSettings(QWidget): WidgetIO.set_value(self.ui.y_min, axis_config["y_lim"][0]) WidgetIO.set_value(self.ui.y_max, axis_config["y_lim"][1]) + @Slot() + def accept_changes(self): + title = WidgetIO.get_value(self.ui.plot_title) -if __name__ == "__main__": - import sys + # X Axis + x_label = WidgetIO.get_value(self.ui.x_label) + x_scale = self.ui.x_scale.currentText() + x_grid = WidgetIO.get_value(self.ui.x_grid) + x_lim = (WidgetIO.get_value(self.ui.x_min), WidgetIO.get_value(self.ui.x_max)) - from qtpy.QtWidgets import QApplication + # Y Axis + y_label = WidgetIO.get_value(self.ui.y_label) + y_scale = self.ui.y_scale.currentText() + y_grid = WidgetIO.get_value(self.ui.y_grid) + y_lim = (WidgetIO.get_value(self.ui.y_min), WidgetIO.get_value(self.ui.y_max)) - app = QApplication(sys.argv) - apply_theme("dark") - window = AxisSettings() - window.show() - sys.exit(app.exec_()) + self.target_widget.set( + title=title, + x_label=x_label, + x_scale=x_scale, + x_lim=x_lim, + y_label=y_label, + y_scale=y_scale, + y_lim=y_lim, + ) + self.target_widget.set_grid(x_grid, y_grid) + + +class AxisSettingsDialog(QDialog): + def __init__(self, parent=None, target_widget: QWidget = None, *args, **kwargs): + super().__init__(parent, *args, **kwargs) + + self.setModal(False) + + self.setWindowTitle("Axis Settings") + self.target_widget = target_widget + self.widget = AxisSettings(target_widget=self.target_widget) + # self.widget.display_current_settings(self.target_widget._config_dict) + self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + + self.button_box.accepted.connect(self.accept) + self.button_box.rejected.connect(self.reject) + + self.layout = QVBoxLayout(self) + self.layout.addWidget(self.widget) + self.layout.addWidget(self.button_box) + + @Slot() + def accept(self): + self.widget.accept_changes() + super().accept() diff --git a/bec_widgets/widgets/waveform/__init__.py b/bec_widgets/widgets/waveform/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bec_widgets/widgets/waveform/assets/settings.svg b/bec_widgets/widgets/waveform/assets/settings.svg new file mode 100644 index 00000000..9ce6f411 --- /dev/null +++ b/bec_widgets/widgets/waveform/assets/settings.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/bec_widgets/widgets/waveform/waveform_dialog/__init__.py b/bec_widgets/widgets/waveform/waveform_dialog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py b/bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py new file mode 100644 index 00000000..f60c3434 --- /dev/null +++ b/bec_widgets/widgets/waveform/waveform_dialog/waveform_toolbar.py @@ -0,0 +1,16 @@ +import os + +from qtpy.QtCore import QSize +from qtpy.QtGui import QIcon, QAction + +from bec_widgets.widgets.toolbar.toolbar import ToolBarAction + + +class SettingsAction(ToolBarAction): + def add_to_toolbar(self, toolbar, target): + current_path = os.path.dirname(__file__) + parent_path = os.path.dirname(current_path) + icon = QIcon() + icon.addFile(os.path.join(parent_path, "assets", "settings.svg"), size=QSize(20, 20)) + self.action = QAction(icon, "Open Configuration Dialog", target) + toolbar.addAction(self.action) diff --git a/bec_widgets/widgets/waveform/waveform_widget.py b/bec_widgets/widgets/waveform/waveform_widget.py new file mode 100644 index 00000000..4aa18a11 --- /dev/null +++ b/bec_widgets/widgets/waveform/waveform_widget.py @@ -0,0 +1,406 @@ +from __future__ import annotations + +import sys +from typing import Literal + +import numpy as np +from PySide6.QtWidgets import QWidget, QVBoxLayout +from qtpy import PYSIDE6 + +from bec_widgets.utils import BECConnector +from bec_widgets.widgets.figure import BECFigure +from bec_widgets.widgets.figure.plots.axis_settings import AxisSettingsDialog +from bec_widgets.widgets.figure.plots.waveform.waveform import Waveform1DConfig +from bec_widgets.widgets.figure.plots.waveform.waveform_curve import BECCurve +from bec_widgets.widgets.toolbar import ModularToolBar +from bec_widgets.widgets.waveform.waveform_dialog.waveform_toolbar import SettingsAction + +try: + import pandas as pd +except ImportError: + pd = None + + +class BECWaveformWidget(BECConnector, QWidget): + USER_ACCESS = [ + "curves", + "plot", + "add_dap", + "get_dap_params", + "remove_curve", + "scan_history", + "get_all_data", + "set", + "set_title", + "set_x_label", + "set_y_label", + "set_x_scale", + "set_y_scale", + "set_x_lim", + "set_y_lim", + "set_legend_label_size", + "set_grid", + "lock_aspect_ratio", + ] + + def __init__( + self, + parent: QWidget | None = None, + config: Waveform1DConfig | dict = None, + client=None, + gui_id: str | None = None, + ) -> None: + if not PYSIDE6: + raise RuntimeError( + "PYSIDE6 is not available in the environment. This widget is compatible only with PySide6." + ) + if config is None: + config = Waveform1DConfig(widget_class=self.__class__.__name__) + else: + if isinstance(config, dict): + config = Waveform1DConfig(**config) + super().__init__(client=client, gui_id=gui_id) + QWidget.__init__(self, parent) + + self.layout = QVBoxLayout(self) + self.layout.setSpacing(0) + self.layout.setContentsMargins(0, 0, 0, 0) + + self.fig = BECFigure() + self.toolbar = ModularToolBar( + actions={ + # "connect": ConnectAction(), + # "history": ResetHistoryAction(), + "axis_settings": SettingsAction() + }, + target_widget=self, + ) + + self.layout.addWidget(self.toolbar) + self.layout.addWidget(self.fig) + + self.waveform = self.fig.plot() + self.waveform.apply_config(config) + + self.config = config # TODO not sure if this should be here + + self._hook_actions() + + def _hook_actions(self): + self.toolbar.widgets["axis_settings"].action.triggered.connect(self.show_axis_settings) + + def show_axis_settings(self): + dialog = AxisSettingsDialog(self, target_widget=self) + dialog.exec() + + ################################### + # User Access Methods from Waveform + ################################### + @property + def curves(self) -> list[BECCurve]: + """ + Get the curves of the plot widget as a list + Returns: + list: List of curves. + """ + return self.waveform._curves + + @curves.setter + def curves(self, value: list[BECCurve]): + self.waveform._curves = value + + def get_curve(self, identifier) -> BECCurve: + """ + Get the curve by its index or ID. + + Args: + identifier(int|str): Identifier of the curve. Can be either an integer (index) or a string (curve_id). + + Returns: + BECCurve: The curve object. + """ + return self.waveform.get_curve(identifier) + + def plot( + self, + x: list | np.ndarray | None = None, + y: list | np.ndarray | None = None, + x_name: str | None = None, + y_name: str | None = None, + z_name: str | None = None, + x_entry: str | None = None, + y_entry: str | None = None, + z_entry: str | None = None, + color: str | None = None, + color_map_z: str | None = "plasma", + label: str | None = None, + validate: bool = True, + dap: str | None = None, # TODO add dap custom curve wrapper + ) -> BECCurve: + """ + Plot a curve to the plot widget. + Args: + x(list | np.ndarray): Custom x data to plot. + y(list | np.ndarray): Custom y data to plot. + x_name(str): The name of the device for the x-axis. + y_name(str): The name of the device for the y-axis. + z_name(str): The name of the device for the z-axis. + x_entry(str): The name of the entry for the x-axis. + y_entry(str): The name of the entry for the y-axis. + z_entry(str): The name of the entry for the z-axis. + color(str): The color of the curve. + color_map_z(str): The color map to use for the z-axis. + label(str): The label of the curve. + validate(bool): If True, validate the device names and entries. + dap(str): The dap model to use for the curve. If not specified, none will be added. + + Returns: + BECCurve: The curve object. + """ + return self.waveform.plot( + x=x, + y=y, + x_name=x_name, + y_name=y_name, + z_name=z_name, + x_entry=x_entry, + y_entry=y_entry, + z_entry=z_entry, + color=color, + color_map_z=color_map_z, + label=label, + validate=validate, + dap=dap, + ) + + def add_dap( + self, + x_name: str, + y_name: str, + x_entry: str | None = None, + y_entry: str | None = None, + color: str | None = None, + dap: str = "GaussianModel", + **kwargs, + ) -> BECCurve: + """ + Add LMFIT dap model curve to the plot widget. + + Args: + x_name(str): Name of the x signal. + x_entry(str): Entry of the x signal. + y_name(str): Name of the y signal. + y_entry(str): Entry of the y signal. + color(str, optional): Color of the curve. Defaults to None. + color_map_z(str): The color map to use for the z-axis. + label(str, optional): Label of the curve. Defaults to None. + dap(str): The dap model to use for the curve. + **kwargs: Additional keyword arguments for the curve configuration. + + Returns: + BECCurve: The curve object. + """ + return self.waveform.add_dap( + x_name=x_name, + y_name=y_name, + x_entry=x_entry, + y_entry=y_entry, + color=color, + dap=dap, + **kwargs, + ) + + def get_dap_params(self) -> dict: + """ + Get the DAP parameters of all DAP curves. + + Returns: + dict: DAP parameters of all DAP curves. + """ + + self.waveform.get_dap_params() + + def remove_curve(self, *identifiers): + """ + Remove a curve from the plot widget. + + Args: + *identifiers: Identifier of the curve to be removed. Can be either an integer (index) or a string (curve_id). + """ + self.waveform.remove_curve(*identifiers) + + def scan_history(self, scan_index: int = None, scan_id: str = None): + """ + Update the scan curves with the data from the scan storage. + Provide only one of scan_id or scan_index. + + Args: + scan_id(str, optional): ScanID of the scan to be updated. Defaults to None. + scan_index(int, optional): Index of the scan to be updated. Defaults to None. + """ + self.waveform.scan_history(scan_index, scan_id) + + def get_all_data(self, output: Literal["dict", "pandas"] = "dict") -> dict | pd.DataFrame: + """ + Extract all curve data into a dictionary or a pandas DataFrame. + + Args: + output (Literal["dict", "pandas"]): Format of the output data. + + Returns: + dict | pd.DataFrame: Data of all curves in the specified format. + """ + try: + import pandas as pd + except ImportError: + pd = None + if output == "pandas": + print( + "Pandas is not installed. " + "Please install pandas using 'pip install pandas'." + "Output will be dictionary instead." + ) + output = "dict" + return self.waveform.get_all_data(output) + + ################################### + # User Access Methods from Plotbase + ################################### + + def set(self, **kwargs): + """ + Set the properties of the plot widget. + + Args: + **kwargs: Keyword arguments for the properties to be set. + + Possible properties: + - title: str + - x_label: str + - y_label: str + - x_scale: Literal["linear", "log"] + - y_scale: Literal["linear", "log"] + - x_lim: tuple + - y_lim: tuple + - legend_label_size: int + """ + self.waveform.set(**kwargs) + + def set_title(self, title: str): + """ + Set the title of the plot widget. + + Args: + title(str): Title of the plot. + """ + self.waveform.set_title(title) + + def set_x_label(self, x_label: str): + """ + Set the x-axis label of the plot widget. + + Args: + x_label(str): Label of the x-axis. + """ + self.waveform.set_x_label(x_label) + + def set_y_label(self, y_label: str): + """ + Set the y-axis label of the plot widget. + + Args: + y_label(str): Label of the y-axis. + """ + self.waveform.set_y_label(y_label) + + def set_x_scale(self, x_scale: Literal["linear", "log"]): + """ + Set the scale of the x-axis of the plot widget. + + Args: + x_scale(Literal["linear", "log"]): Scale of the x-axis. + """ + self.waveform.set_x_scale(x_scale) + + def set_y_scale(self, y_scale: Literal["linear", "log"]): + """ + Set the scale of the y-axis of the plot widget. + + Args: + y_scale(Literal["linear", "log"]): Scale of the y-axis. + """ + self.waveform.set_y_scale(y_scale) + + def set_x_lim(self, x_lim: tuple): + """ + Set the limits of the x-axis of the plot widget. + + Args: + x_lim(tuple): Limits of the x-axis. + """ + self.waveform.set_x_lim(x_lim) + + def set_y_lim(self, y_lim: tuple): + """ + Set the limits of the y-axis of the plot widget. + + Args: + y_lim(tuple): Limits of the y-axis. + """ + self.waveform.set_y_lim(y_lim) + + def set_legend_label_size(self, legend_label_size: int): + """ + Set the size of the legend labels of the plot widget. + + Args: + legend_label_size(int): Size of the legend labels. + """ + self.waveform.set_legend_label_size(legend_label_size) + + def set_grid(self, x_grid: bool, y_grid: bool): + """ + Set the grid visibility of the plot widget. + + Args: + x_grid(bool): Visibility of the x-axis grid. + y_grid(bool): Visibility of the y-axis grid. + """ + self.waveform.set_grid(x_grid, y_grid) + + def lock_aspect_ratio(self, lock: bool): + """ + Lock the aspect ratio of the plot widget. + + Args: + lock(bool): Lock the aspect ratio. + """ + self.waveform.lock_aspect_ratio(lock) + + def cleanup(self): + self.fig.cleanup() + return super().cleanup() + + def closeEvent(self, event): + self.cleanup() + QWidget().closeEvent(event) + + +def main(): # pragma: no cover + + if not PYSIDE6: + print( + "PYSIDE6 is not available in the environment. UI files with BEC custom widgets are runnable only with PySide6." + ) + return + + from qtpy.QtWidgets import QApplication + + app = QApplication(sys.argv) + widget = BECWaveformWidget() + widget.show() + sys.exit(app.exec_()) + + +if __name__ == "__main__": # pragma: no cover + main()