From 57132a472165c55bf99e1994d09f5fe3586c24da Mon Sep 17 00:00:00 2001 From: wyzula-jan <133381102+wyzula-jan@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:26:55 +0100 Subject: [PATCH] fix(cli/rpc): server access children widget.find_widget_by_id(gui_id) --- bec_widgets/cli/client.py | 21 +++++++++- bec_widgets/cli/server.py | 17 +++++---- bec_widgets/widgets/plots/waveform1d.py | 51 +++++++++++++++++++++++-- tests/test_waveform1d.py | 5 ++- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/bec_widgets/cli/client.py b/bec_widgets/cli/client.py index 8a1957cf..efae38ac 100644 --- a/bec_widgets/cli/client.py +++ b/bec_widgets/cli/client.py @@ -1,9 +1,8 @@ # This file was automatically generated by generate_cli.py +from bec_widgets.cli.client_utils import rpc_call, RPCBase, BECFigureClientMixin from typing import Literal, Optional, overload -from bec_widgets.cli.client_utils import BECFigureClientMixin, RPCBase, rpc_call - class BECPlotBase(RPCBase): @rpc_call @@ -233,6 +232,16 @@ class BECWaveform1D(RPCBase): replot_last_scan(bool, optional): If True, replot the last scan. Defaults to False. """ + @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. + """ + class BECFigure(RPCBase, BECFigureClientMixin): @rpc_call @@ -367,3 +376,11 @@ class BECCurve(RPCBase): Args: pen_style(Literal["solid", "dash", "dot", "dashdot"]): Style of the pen. """ + + @rpc_call + def get_data(self) -> "tuple[np.ndarray, np.ndarray]": + """ + Get the data of the curve. + Returns: + tuple[np.ndarray,np.ndarray]: X and Y data of the curve. + """ diff --git a/bec_widgets/cli/server.py b/bec_widgets/cli/server.py index aab80e6f..8a0f624c 100644 --- a/bec_widgets/cli/server.py +++ b/bec_widgets/cli/server.py @@ -5,14 +5,14 @@ from bec_lib import MessageEndpoints, messages from bec_widgets.utils import BECDispatcher from bec_widgets.utils.bec_connector import BECConnector from bec_widgets.widgets.figure import BECFigure -from bec_widgets.widgets.plots import BECCurve, BECPlotBase, BECWaveform1D +from bec_widgets.widgets.plots import BECCurve, BECWaveform1D class BECWidgetsCLIServer: WIDGETS = [BECWaveform1D, BECFigure, BECCurve] - def __init__(self, gui_id: str = None) -> None: - self.dispatcher = BECDispatcher() + def __init__(self, gui_id: str = None, dispatcher: BECDispatcher = None) -> None: + self.dispatcher = BECDispatcher() if dispatcher is None else dispatcher self.client = self.dispatcher.client self.client.start() self.gui_id = gui_id @@ -22,6 +22,9 @@ class BECWidgetsCLIServer: self.dispatcher.connect_slot( self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id) ) + + def start(self): + """Start the figure window.""" self.fig.start() @staticmethod @@ -59,10 +62,9 @@ class BECWidgetsCLIServer: return obj if self.fig.widgets: for widget in self.fig.widgets.values(): - if isinstance(widget, BECWaveform1D): - for curve in widget.curves: - if curve.gui_id == gui_id: - return curve + item = widget.find_widget_by_id(gui_id) + if item: + return item raise NotImplementedError( f"gui_id lookup for widget of type {widget.__class__.__name__} not implemented" ) @@ -107,3 +109,4 @@ if __name__ == "__main__": server = BECWidgetsCLIServer(gui_id=args.id) # server = BECWidgetsCLIServer(gui_id="test") + server.start() diff --git a/bec_widgets/widgets/plots/waveform1d.py b/bec_widgets/widgets/plots/waveform1d.py index d1e91c55..33201601 100644 --- a/bec_widgets/widgets/plots/waveform1d.py +++ b/bec_widgets/widgets/plots/waveform1d.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Any, Literal, Optional +from typing import Literal, Optional, Any import numpy as np import pyqtgraph as pg @@ -72,6 +72,7 @@ class BECCurve(BECConnector, pg.PlotDataItem): "set_symbol_size", "set_pen_width", "set_pen_style", + "get_data", ] def __init__( @@ -204,6 +205,15 @@ class BECCurve(BECConnector, pg.PlotDataItem): self.config.pen_style = pen_style self.apply_config() + def get_data(self) -> tuple[np.ndarray, np.ndarray]: + """ + Get the data of the curve. + Returns: + tuple[np.ndarray,np.ndarray]: X and Y data of the curve. + """ + x_data, y_data = self.getData() + return x_data, y_data + class BECWaveform1D(BECPlotBase): USER_ACCESS = [ @@ -216,6 +226,7 @@ class BECWaveform1D(BECPlotBase): "get_curve", "get_curve_config", "apply_config", + "get_all_data", ] scan_signal_update = pyqtSignal() @@ -254,6 +265,13 @@ class BECWaveform1D(BECPlotBase): # TODO check config assigning # TODO check the functionality of config generator + def find_widget_by_id( + self, item_id: str + ): # TODO implement this on level of BECConnector and all other widgets + for curve in self.curves: + if curve.gui_id == item_id: + return curve + def apply_config(self, config: dict | WidgetConfig, replot_last_scan: bool = False): """ Apply the configuration to the 1D waveform widget. @@ -273,8 +291,8 @@ class BECWaveform1D(BECPlotBase): self.apply_axis_config() # Reset curves - self.curves_data = defaultdict(dict) - self.curves = [] + self._curves_data = defaultdict(dict) + self._curves = [] for curve_id, curve_config in self.config.curves.items(): self.add_curve_by_config(curve_config) if replot_last_scan: @@ -291,7 +309,7 @@ class BECWaveform1D(BECPlotBase): self.gui_id = new_gui_id self.config.gui_id = new_gui_id - for curve_id, curve in self.curves_data.items(): + for curve in self.curves: curve.config.parent_id = new_gui_id def add_curve_by_config(self, curve_config: CurveConfig | dict) -> BECCurve: @@ -324,6 +342,31 @@ class BECWaveform1D(BECPlotBase): else: return curves[curve_id].config + @property + def curves(self) -> list[BECCurve]: # TODO discuss if it should be marked as @property for RPC + """ + Get the curves of the plot widget as a list + Returns: + list: List of curves. + """ + return self._curves + + @curves.setter + def curves(self, value: list[BECCurve]): + self._curves = value + + @property + def curves_data(self) -> dict: # TODO discuss if it should be marked as @property for RPC + """ + Get the curves data of the plot widget as a dictionary + Returns: + dict: Dictionary of curves data. + """ + return self._curves_data + + @curves_data.setter + def curves_data(self, value: dict): + self._curves_data = value def get_curve(self, identifier) -> BECCurve: """ diff --git a/tests/test_waveform1d.py b/tests/test_waveform1d.py index 4cfa82aa..1205ba53 100644 --- a/tests/test_waveform1d.py +++ b/tests/test_waveform1d.py @@ -140,8 +140,9 @@ def test_getting_curve(bec_figure): y=SignalData(name="bpm4i", entry="bpm4i", unit=None, modifier=None), ), ) - assert w1.get_curves()[0].config == c1_expected_config - assert w1.get_curves_data()["scan_segment"]["bpm4i-bpm4i"].config == c1_expected_config + + assert w1.curves[0].config == c1_expected_config + assert w1.curves_data["scan_segment"]["bpm4i-bpm4i"].config == c1_expected_config assert w1.get_curve(0).config == c1_expected_config assert w1.get_curve("bpm4i-bpm4i").config == c1_expected_config assert c1.get_config(False) == c1_expected_config