diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index 3f4d9e2f..50f8278a 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -554,17 +554,17 @@ class BECFigure(RPCBase): @rpc_call def plot( self, - x_name: "str" = None, - y_name: "str" = None, - z_name: "str" = None, - x_entry: "str" = None, - y_entry: "str" = None, - z_entry: "str" = None, - x: "list | np.ndarray" = None, - y: "list | np.ndarray" = None, - color: "Optional[str]" = None, - color_map_z: "Optional[str]" = "plasma", - label: "Optional[str]" = 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, + 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, **axis_kwargs, ) -> "BECWaveform": @@ -572,14 +572,14 @@ class BECFigure(RPCBase): Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure. 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. - x(list | np.ndarray): Custom x data to plot. - y(list | np.ndarray): Custom y data to plot. 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. diff --git a/bec_widgets/examples/jupyter_console/jupyter_console_window.py b/bec_widgets/examples/jupyter_console/jupyter_console_window.py index 45fd5761..d95db00a 100644 --- a/bec_widgets/examples/jupyter_console/jupyter_console_window.py +++ b/bec_widgets/examples/jupyter_console/jupyter_console_window.py @@ -93,7 +93,7 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover: self.console.set_default_style("linux") def _init_figure(self): - self.figure.plot("samx", "bpm4d") + self.figure.plot(x_name="samx", y_name="bpm4d") self.figure.motor_map("samx", "samy") self.figure.image("eiger", color_map="viridis", vrange=(0, 100)) @@ -124,7 +124,7 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover: self.d2 = self.dock.add_dock(widget=self.label_1, position="right") self.d3 = self.dock.add_dock(name="figure") self.fig_dock3 = BECFigure() - self.fig_dock3.plot("samx", "bpm4d") + self.fig_dock3.plot(x_name="samx", y_name="bpm4d") self.d3.add_widget(self.label_3) self.d3.add_widget(self.button_3) self.d3.add_widget(self.fig_dock3) diff --git a/bec_widgets/widgets/figure/figure.py b/bec_widgets/widgets/figure/figure.py index 9b7abef7..6bf29c42 100644 --- a/bec_widgets/widgets/figure/figure.py +++ b/bec_widgets/widgets/figure/figure.py @@ -11,6 +11,7 @@ import qdarktheme from pydantic import Field from qtpy.QtCore import Signal as pyqtSignal from qtpy.QtWidgets import QWidget +from typeguard import typechecked from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils from bec_widgets.widgets.figure.plots.image.image import BECImageShow, ImageConfig @@ -261,19 +262,20 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget): return waveform + @typechecked def plot( self, - x_name: str = None, - y_name: str = None, - z_name: str = None, - x_entry: str = None, - y_entry: str = None, - z_entry: str = None, - x: list | np.ndarray = None, - y: list | np.ndarray = None, - color: Optional[str] = None, - color_map_z: Optional[str] = "plasma", - label: Optional[str] = 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, + 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, **axis_kwargs, ) -> BECWaveform: @@ -281,14 +283,14 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget): Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure. 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. - x(list | np.ndarray): Custom x data to plot. - y(list | np.ndarray): Custom y data to plot. 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. @@ -307,6 +309,27 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget): else: waveform = self.add_plot(**axis_kwargs) + if x is not None and y is None: + if isinstance(x, np.ndarray): + if x.ndim == 1: + y = np.arange(x.size) + waveform.add_curve_custom(x=np.arange(x.size), y=x, color=color, label=label) + return waveform + if x.ndim == 2: + waveform.add_curve_custom(x=x[:, 0], y=x[:, 1], color=color, label=label) + return waveform + elif isinstance(x, list): + y = np.arange(len(x)) + waveform.add_curve_custom(x=np.arange(len(x)), y=x, color=color, label=label) + return waveform + else: + raise ValueError( + "Invalid input. Provide either device names (x_name, y_name) or custom data." + ) + if x is not None and y is not None: + waveform.add_curve_custom(x=x, y=y, color=color, label=label) + return waveform + # User wants to add scan curve -> 1D Waveform if x_name is not None and y_name is not None and z_name is None and x is None and y is None: waveform.add_curve_scan( diff --git a/tests/end-2-end/test_bec_dock_rpc_e2e.py b/tests/end-2-end/test_bec_dock_rpc_e2e.py index 8ae4718a..dbb0d86d 100644 --- a/tests/end-2-end/test_bec_dock_rpc_e2e.py +++ b/tests/end-2-end/test_bec_dock_rpc_e2e.py @@ -38,7 +38,7 @@ def test_rpc_add_dock_with_figure_e2e(rpc_server_dock, qtbot): assert fig2.__class__ == BECFigure mm = fig0.motor_map("samx", "samy") - plt = fig1.plot("samx", "bpm4i") + plt = fig1.plot(x_name="samx", y_name="bpm4i") im = fig2.image("eiger") assert mm.__class__.__name__ == "BECMotorMap" diff --git a/tests/end-2-end/test_bec_figure_rpc_e2e.py b/tests/end-2-end/test_bec_figure_rpc_e2e.py index 06eb4d61..098f3411 100644 --- a/tests/end-2-end/test_bec_figure_rpc_e2e.py +++ b/tests/end-2-end/test_bec_figure_rpc_e2e.py @@ -23,7 +23,7 @@ def test_rpc_plotting_shortcuts_init_configs(rpc_server_figure, qtbot): fig = BECFigure(rpc_server_figure.gui_id) fig_server = rpc_server_figure.gui - plt = fig.plot("samx", "bpm4i") + plt = fig.plot(x_name="samx", y_name="bpm4i") im = fig.image("eiger") motor_map = fig.motor_map("samx", "samy") plt_z = fig.add_plot("samx", "samy", "bpm4i") @@ -79,9 +79,9 @@ def test_rpc_waveform_scan(rpc_server_figure, qtbot): fig = BECFigure(rpc_server_figure.gui_id) # add 3 different curves to track - plt = fig.plot("samx", "bpm4i") - fig.plot("samx", "bpm3a") - fig.plot("samx", "bpm4d") + plt = fig.plot(x_name="samx", y_name="bpm4i") + fig.plot(x_name="samx", y_name="bpm3a") + fig.plot(x_name="samx", y_name="bpm4d") client = rpc_server_figure.client dev = client.device_manager.devices diff --git a/tests/end-2-end/test_rpc_register_e2e.py b/tests/end-2-end/test_rpc_register_e2e.py index 983ba468..ced07d5f 100644 --- a/tests/end-2-end/test_rpc_register_e2e.py +++ b/tests/end-2-end/test_rpc_register_e2e.py @@ -22,7 +22,7 @@ def test_rpc_register_list_connections(rpc_server_figure, rpc_register, qtbot): fig = BECFigure(rpc_server_figure.gui_id) fig_server = rpc_server_figure.gui - plt = fig.plot("samx", "bpm4i") + plt = fig.plot(x_name="samx", y_name="bpm4i") im = fig.image("eiger") motor_map = fig.motor_map("samx", "samy") plt_z = fig.add_plot("samx", "samy", "bpm4i") diff --git a/tests/unit_tests/test_bec_dock.py b/tests/unit_tests/test_bec_dock.py index 702eabea..6e150538 100644 --- a/tests/unit_tests/test_bec_dock.py +++ b/tests/unit_tests/test_bec_dock.py @@ -59,7 +59,7 @@ def test_bec_dock_area_add_remove_dock(bec_dock_area, qtbot): def test_add_remove_bec_figure_to_dock(bec_dock_area): d0 = bec_dock_area.add_dock() fig = d0.add_widget_bec("BECFigure") - plt = fig.plot("samx", "bpm4i") + plt = fig.plot(x_name="samx", y_name="bpm4i") im = fig.image("eiger") mm = fig.motor_map("samx", "samy") diff --git a/tests/unit_tests/test_bec_figure.py b/tests/unit_tests/test_bec_figure.py index dd8ab594..2764d7b0 100644 --- a/tests/unit_tests/test_bec_figure.py +++ b/tests/unit_tests/test_bec_figure.py @@ -65,7 +65,7 @@ def test_bec_figure_add_remove_plot(bec_figure): def test_add_different_types_of_widgets(bec_figure): - plt = bec_figure.plot("samx", "bpm4i") + plt = bec_figure.plot(x_name="samx", y_name="bpm4i") im = bec_figure.image("eiger") motor_map = bec_figure.motor_map("samx", "samy") @@ -228,7 +228,7 @@ def test_clear_all(bec_figure): def test_shortcuts(bec_figure): - plt = bec_figure.plot("samx", "bpm4i") + plt = bec_figure.plot(x_name="samx", y_name="bpm4i") im = bec_figure.image("eiger") motor_map = bec_figure.motor_map("samx", "samy") diff --git a/tests/unit_tests/test_waveform1d.py b/tests/unit_tests/test_waveform1d.py index 7ede7a31..6c36d7a2 100644 --- a/tests/unit_tests/test_waveform1d.py +++ b/tests/unit_tests/test_waveform1d.py @@ -304,6 +304,18 @@ def test_set_custom_curve_data(bec_figure, qtbot): assert np.array_equal(y_new, [7, 8, 9]) +def test_custom_data_2D_array(bec_figure, qtbot): + + data = np.random.rand(10, 2) + + plt = bec_figure.plot(data) + + x, y = plt.curves[0].get_data() + + assert np.array_equal(x, data[:, 0]) + assert np.array_equal(y, data[:, 1]) + + def test_get_all_data(bec_figure): w1 = bec_figure.add_plot()