0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

refactor(rpc/server): rpc server can accept BEC container class as a argument; the widget container of BECFigure container property s added to BECFigure; isort applied

This commit is contained in:
2024-04-22 21:31:12 +02:00
parent 4fa9f3f1d1
commit c0356b94db
9 changed files with 117 additions and 69 deletions

View File

@ -248,11 +248,11 @@ class BECWaveform(RPCBase):
"""
@rpc_call
def apply_config(self, config: "dict | WidgetConfig", replot_last_scan: "bool" = False):
def apply_config(self, config: "dict | SubplotConfig", replot_last_scan: "bool" = False):
"""
Apply the configuration to the 1D waveform widget.
Args:
config(dict|WidgetConfig): Configuration settings.
config(dict|SubplotConfig): Configuration settings.
replot_last_scan(bool, optional): If True, replot the last scan. Defaults to False.
"""
@ -614,6 +614,13 @@ class BECFigure(RPCBase, BECFigureClientMixin):
Clear all widgets from the figure and reset to default state
"""
@property
@rpc_call
def containers(self) -> "dict":
"""
None
"""
class BECCurve(RPCBase):
@property

View File

@ -1,10 +1,12 @@
import inspect
from typing import Literal
from bec_lib import MessageEndpoints, messages
from qtpy.QtCore import QTimer
from bec_widgets.utils import BECDispatcher
from bec_widgets.utils.bec_connector import BECConnector
from bec_widgets.widgets.dock.dock_area import BECDockArea
from bec_widgets.widgets.figure import BECFigure
from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
@ -12,12 +14,18 @@ from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
class BECWidgetsCLIServer:
WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]
def __init__(self, gui_id: str = None, dispatcher: BECDispatcher = None, client=None) -> None:
def __init__(
self,
gui_id: str = None,
dispatcher: BECDispatcher = None,
client=None,
gui_class: BECFigure | BECDockArea = BECFigure,
) -> None:
self.dispatcher = BECDispatcher() if dispatcher is None else dispatcher
self.client = self.dispatcher.client if client is None else client
self.client.start()
self.gui_id = gui_id
self.fig = BECFigure(gui_id=self.gui_id)
self.gui = gui_class(gui_id=self.gui_id)
self.dispatcher.connect_slot(
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
@ -53,14 +61,14 @@ class BECWidgetsCLIServer:
def get_object_from_config(self, config: dict):
gui_id = config.get("gui_id")
# check if the object is the figure
if gui_id == self.fig.gui_id:
return self.fig
if gui_id == self.gui.gui_id:
return self.gui
# check if the object is a widget
if gui_id in self.fig._widgets:
obj = self.fig._widgets[config["gui_id"]]
if gui_id in self.gui.containers:
obj = self.gui.containers[config["gui_id"]]
return obj
if self.fig._widgets:
for widget in self.fig._widgets.values():
if self.gui.containers:
for widget in self.gui.containers.values():
item = widget.find_widget_by_id(gui_id)
if item:
return item
@ -123,13 +131,29 @@ if __name__ == "__main__": # pragma: no cover
parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
parser.add_argument("--id", type=str, help="The id of the server")
parser.add_argument(
"--gui_class",
type=str,
help="Name of the gui class to be rendered. Possible values: \n- BECFigure\n- BECDockArea",
)
args = parser.parse_args()
server = BECWidgetsCLIServer(gui_id=args.id)
# server = BECWidgetsCLIServer(gui_id="test")
if args.gui_class == "BECFigure":
gui_class = BECFigure
elif args.gui_class == "BECDockArea":
gui_class = BECDockArea
else:
print(
"Please specify a valid gui_class to run. Use -h for help."
"\n Starting with default gui_class BECFigure."
)
gui_class = BECFigure
fig = server.fig
# server = BECWidgetsCLIServer(gui_id=args.id, gui_class=gui_class)
server = BECWidgetsCLIServer(gui_id="test", gui_class=gui_class)
fig = server.gui
win.setCentralWidget(fig)
win.show()

View File

@ -5,13 +5,13 @@ import pyqtgraph as pg
from PyQt6.QtCore import QSize
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QMainWindow
from pyqtgraph.Qt import uic, QtWidgets
from pyqtgraph.Qt import QtWidgets, uic
from qtconsole.inprocess import QtInProcessKernelManager
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from bec_widgets.utils import BECDispatcher
from bec_widgets.widgets import BECFigure, BECDockArea
from bec_widgets.widgets import BECDockArea, BECFigure
class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:

View File

@ -11,4 +11,3 @@ from .motor_control import (
from .motor_map import MotorMap
from .plots import BECCurve, BECMotorMap, BECWaveform
from .scan_control import ScanControl
from .dock import BECDockArea, BECDock

View File

@ -1 +0,0 @@
from .dock_area import BECDockArea, BECDock

View File

@ -0,0 +1,50 @@
from typing import Literal, Optional
from pydantic import Field
from pyqtgraph.dockarea import Dock
from qtpy.QtWidgets import QWidget
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
class DockConfig(ConnectionConfig):
widgets: dict[str, ConnectionConfig] = Field({}, description="The widgets in the dock.")
position: Literal["bottom", "top", "left", "right", "above", "below"] = Field(
"bottom", description="The position of the dock."
)
parent_dock_area: Optional[str] = Field(
None, description="The GUI ID of parent dock area of the dock."
)
class BECDock(BECConnector, Dock):
def __init__(
self,
parent: Optional[QWidget] = None,
parent_dock_area: Optional["BECDockArea"] = None,
config: Optional[
DockConfig
] = None, # TODO ATM connection config -> will be changed when I will know what I want to use there
name: Optional[str] = None,
client=None,
gui_id: Optional[str] = None,
**kwargs,
) -> None:
if config is None:
config = DockConfig(
widget_class=self.__class__.__name__, parent_dock_area=parent_dock_area.gui_id
)
else:
if isinstance(config, dict):
config = DockConfig(**config)
self.config = config
super().__init__(client=client, config=config, gui_id=gui_id)
Dock.__init__(self, name=name, **kwargs)
self.parent_dock_area = parent_dock_area
self.sigClosed.connect(self._remove_from_dock_area)
def _remove_from_dock_area(self):
"""Remove this dock from the DockArea it lives inside."""
self.parent_dock_area.docks.pop(self.name())

View File

@ -1,62 +1,22 @@
from collections import defaultdict
from typing import Optional, Literal
from typing import Literal, Optional
import pyqtgraph as pg
from qtpy.QtWidgets import QWidget
from pydantic import Field
from pyqtgraph.dockarea.DockArea import DockArea, Dock
from pyqtgraph.dockarea.DockArea import Dock, DockArea
from qtpy.QtWidgets import QWidget
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
from bec_widgets.widgets import BECWaveform, BECFigure, BECMotorMap
from bec_widgets.widgets import BECFigure, BECMotorMap, BECWaveform
from bec_widgets.widgets.plots import BECImageShow
class DockConfig(ConnectionConfig):
widgets: dict[str, ConnectionConfig] = Field({}, description="The widgets in the dock.")
position: Literal["bottom", "top", "left", "right", "above", "below"] = Field(
"bottom", description="The position of the dock."
)
parent_dock_area: Optional[str] = Field(
None, description="The GUI ID of parent dock area of the dock."
)
from .dock import BECDock, DockConfig
class DockAreaConfig(ConnectionConfig):
docks: dict[str, DockConfig] = Field({}, description="The docks in the dock area.")
class BECDock(BECConnector, Dock):
def __init__(
self,
parent: Optional[QWidget] = None,
parent_dock_area: Optional["BECDockArea"] = None,
config: Optional[
DockConfig
] = None, # TODO ATM connection config -> will be changed when I will know what I want to use there
name: Optional[str] = None,
client=None,
gui_id: Optional[str] = None,
**kwargs,
) -> None:
if config is None:
config = DockConfig(
widget_class=self.__class__.__name__, parent_dock_area=parent_dock_area.gui_id
)
else:
if isinstance(config, dict):
config = DockConfig(**config)
self.config = config
super().__init__(client=client, config=config, gui_id=gui_id)
Dock.__init__(self, name=name, **kwargs)
self.parent_dock_area = parent_dock_area
self.sigClosed.connect(self._remove_from_dock_area)
def _remove_from_dock_area(self):
"""Remove this dock from the DockArea it lives inside."""
self.parent_dock_area.docks.pop(self.name())
class BECDockArea(BECConnector, DockArea):
USER_ACCESS = ["figure", "plot", "image", "motor_map", "add_dock", "remove_dock_by_id", "clear"]

View File

@ -20,8 +20,8 @@ from bec_widgets.widgets.plots import (
BECMotorMap,
BECPlotBase,
BECWaveform,
Waveform1DConfig,
SubplotConfig,
Waveform1DConfig,
)
from bec_widgets.widgets.plots.image import ImageConfig
from bec_widgets.widgets.plots.motor_map import MotorMapConfig
@ -110,6 +110,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
"change_layout",
"change_theme",
"clear_all",
"containers",
]
clean_signal = pyqtSignal()
@ -160,6 +161,14 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
def widgets(self, value: dict):
self._widgets = value
@property
def containers(self) -> dict:
return self._widgets
@containers.setter
def containers(self, value: dict):
self._widgets = value
def add_plot(
self,
x_name: str = None,

View File

@ -11,8 +11,8 @@ from bec_widgets.utils import BECDispatcher
def rpc_server(qtbot, bec_client_lib, threads_check):
dispatcher = BECDispatcher(client=bec_client_lib) # Has to init singleton with fixture client
server = BECWidgetsCLIServer(gui_id="id_test")
qtbot.addWidget(server.fig)
qtbot.waitExposed(server.fig)
qtbot.addWidget(server.gui)
qtbot.waitExposed(server.gui)
qtbot.wait(1000) # 1s long to wait until gui is ready
yield server
dispatcher.disconnect_all()
@ -23,7 +23,7 @@ def rpc_server(qtbot, bec_client_lib, threads_check):
def test_rpc_waveform1d_custom_curve(rpc_server, qtbot):
fig = BECFigure(rpc_server.gui_id)
fig_server = rpc_server.fig
fig_server = rpc_server.gui
ax = fig.add_plot()
curve = ax.add_curve_custom([1, 2, 3], [1, 2, 3])
@ -37,7 +37,7 @@ def test_rpc_waveform1d_custom_curve(rpc_server, qtbot):
def test_rpc_plotting_shortcuts_init_configs(rpc_server, qtbot):
fig = BECFigure(rpc_server.gui_id)
fig_server = rpc_server.fig
fig_server = rpc_server.gui
plt = fig.plot("samx", "bpm4i")
im = fig.image("eiger")
@ -151,7 +151,7 @@ def test_rpc_image(rpc_server, qtbot):
def test_rpc_motor_map(rpc_server, qtbot):
fig = BECFigure(rpc_server.gui_id)
fig_server = rpc_server.fig
fig_server = rpc_server.gui
motor_map = fig.motor_map("samx", "samy")