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:
@ -248,11 +248,11 @@ class BECWaveform(RPCBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@rpc_call
|
@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.
|
Apply the configuration to the 1D waveform widget.
|
||||||
Args:
|
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.
|
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
|
Clear all widgets from the figure and reset to default state
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@rpc_call
|
||||||
|
def containers(self) -> "dict":
|
||||||
|
"""
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class BECCurve(RPCBase):
|
class BECCurve(RPCBase):
|
||||||
@property
|
@property
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import inspect
|
import inspect
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
from bec_lib import MessageEndpoints, messages
|
from bec_lib import MessageEndpoints, messages
|
||||||
from qtpy.QtCore import QTimer
|
from qtpy.QtCore import QTimer
|
||||||
|
|
||||||
from bec_widgets.utils import BECDispatcher
|
from bec_widgets.utils import BECDispatcher
|
||||||
from bec_widgets.utils.bec_connector import BECConnector
|
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.figure import BECFigure
|
||||||
from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
|
from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
|
||||||
|
|
||||||
@ -12,12 +14,18 @@ from bec_widgets.widgets.plots import BECCurve, BECImageShow, BECWaveform
|
|||||||
class BECWidgetsCLIServer:
|
class BECWidgetsCLIServer:
|
||||||
WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]
|
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.dispatcher = BECDispatcher() if dispatcher is None else dispatcher
|
||||||
self.client = self.dispatcher.client if client is None else client
|
self.client = self.dispatcher.client if client is None else client
|
||||||
self.client.start()
|
self.client.start()
|
||||||
self.gui_id = gui_id
|
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.dispatcher.connect_slot(
|
||||||
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
self.on_rpc_update, MessageEndpoints.gui_instructions(self.gui_id)
|
||||||
@ -53,14 +61,14 @@ class BECWidgetsCLIServer:
|
|||||||
def get_object_from_config(self, config: dict):
|
def get_object_from_config(self, config: dict):
|
||||||
gui_id = config.get("gui_id")
|
gui_id = config.get("gui_id")
|
||||||
# check if the object is the figure
|
# check if the object is the figure
|
||||||
if gui_id == self.fig.gui_id:
|
if gui_id == self.gui.gui_id:
|
||||||
return self.fig
|
return self.gui
|
||||||
# check if the object is a widget
|
# check if the object is a widget
|
||||||
if gui_id in self.fig._widgets:
|
if gui_id in self.gui.containers:
|
||||||
obj = self.fig._widgets[config["gui_id"]]
|
obj = self.gui.containers[config["gui_id"]]
|
||||||
return obj
|
return obj
|
||||||
if self.fig._widgets:
|
if self.gui.containers:
|
||||||
for widget in self.fig._widgets.values():
|
for widget in self.gui.containers.values():
|
||||||
item = widget.find_widget_by_id(gui_id)
|
item = widget.find_widget_by_id(gui_id)
|
||||||
if item:
|
if item:
|
||||||
return item
|
return item
|
||||||
@ -123,13 +131,29 @@ if __name__ == "__main__": # pragma: no cover
|
|||||||
|
|
||||||
parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
|
parser = argparse.ArgumentParser(description="BEC Widgets CLI Server")
|
||||||
parser.add_argument("--id", type=str, help="The id of the 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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
server = BECWidgetsCLIServer(gui_id=args.id)
|
if args.gui_class == "BECFigure":
|
||||||
# server = BECWidgetsCLIServer(gui_id="test")
|
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.setCentralWidget(fig)
|
||||||
win.show()
|
win.show()
|
||||||
|
|
||||||
|
@ -5,13 +5,13 @@ import pyqtgraph as pg
|
|||||||
from PyQt6.QtCore import QSize
|
from PyQt6.QtCore import QSize
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
from PyQt6.QtWidgets import QMainWindow
|
from PyQt6.QtWidgets import QMainWindow
|
||||||
from pyqtgraph.Qt import uic, QtWidgets
|
from pyqtgraph.Qt import QtWidgets, uic
|
||||||
from qtconsole.inprocess import QtInProcessKernelManager
|
from qtconsole.inprocess import QtInProcessKernelManager
|
||||||
from qtconsole.rich_jupyter_widget import RichJupyterWidget
|
from qtconsole.rich_jupyter_widget import RichJupyterWidget
|
||||||
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
|
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
|
||||||
|
|
||||||
from bec_widgets.utils import BECDispatcher
|
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:
|
class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:
|
||||||
|
@ -11,4 +11,3 @@ from .motor_control import (
|
|||||||
from .motor_map import MotorMap
|
from .motor_map import MotorMap
|
||||||
from .plots import BECCurve, BECMotorMap, BECWaveform
|
from .plots import BECCurve, BECMotorMap, BECWaveform
|
||||||
from .scan_control import ScanControl
|
from .scan_control import ScanControl
|
||||||
from .dock import BECDockArea, BECDock
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from .dock_area import BECDockArea, BECDock
|
|
||||||
|
50
bec_widgets/widgets/dock/dock.py
Normal file
50
bec_widgets/widgets/dock/dock.py
Normal 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())
|
@ -1,62 +1,22 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Optional, Literal
|
from typing import Literal, Optional
|
||||||
|
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from qtpy.QtWidgets import QWidget
|
|
||||||
from pydantic import Field
|
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.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
|
from bec_widgets.widgets.plots import BECImageShow
|
||||||
|
|
||||||
|
from .dock import BECDock, DockConfig
|
||||||
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 DockAreaConfig(ConnectionConfig):
|
class DockAreaConfig(ConnectionConfig):
|
||||||
docks: dict[str, DockConfig] = Field({}, description="The docks in the dock area.")
|
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):
|
class BECDockArea(BECConnector, DockArea):
|
||||||
USER_ACCESS = ["figure", "plot", "image", "motor_map", "add_dock", "remove_dock_by_id", "clear"]
|
USER_ACCESS = ["figure", "plot", "image", "motor_map", "add_dock", "remove_dock_by_id", "clear"]
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ from bec_widgets.widgets.plots import (
|
|||||||
BECMotorMap,
|
BECMotorMap,
|
||||||
BECPlotBase,
|
BECPlotBase,
|
||||||
BECWaveform,
|
BECWaveform,
|
||||||
Waveform1DConfig,
|
|
||||||
SubplotConfig,
|
SubplotConfig,
|
||||||
|
Waveform1DConfig,
|
||||||
)
|
)
|
||||||
from bec_widgets.widgets.plots.image import ImageConfig
|
from bec_widgets.widgets.plots.image import ImageConfig
|
||||||
from bec_widgets.widgets.plots.motor_map import MotorMapConfig
|
from bec_widgets.widgets.plots.motor_map import MotorMapConfig
|
||||||
@ -110,6 +110,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|||||||
"change_layout",
|
"change_layout",
|
||||||
"change_theme",
|
"change_theme",
|
||||||
"clear_all",
|
"clear_all",
|
||||||
|
"containers",
|
||||||
]
|
]
|
||||||
|
|
||||||
clean_signal = pyqtSignal()
|
clean_signal = pyqtSignal()
|
||||||
@ -160,6 +161,14 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|||||||
def widgets(self, value: dict):
|
def widgets(self, value: dict):
|
||||||
self._widgets = value
|
self._widgets = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def containers(self) -> dict:
|
||||||
|
return self._widgets
|
||||||
|
|
||||||
|
@containers.setter
|
||||||
|
def containers(self, value: dict):
|
||||||
|
self._widgets = value
|
||||||
|
|
||||||
def add_plot(
|
def add_plot(
|
||||||
self,
|
self,
|
||||||
x_name: str = None,
|
x_name: str = None,
|
||||||
|
@ -11,8 +11,8 @@ from bec_widgets.utils import BECDispatcher
|
|||||||
def rpc_server(qtbot, bec_client_lib, threads_check):
|
def rpc_server(qtbot, bec_client_lib, threads_check):
|
||||||
dispatcher = BECDispatcher(client=bec_client_lib) # Has to init singleton with fixture client
|
dispatcher = BECDispatcher(client=bec_client_lib) # Has to init singleton with fixture client
|
||||||
server = BECWidgetsCLIServer(gui_id="id_test")
|
server = BECWidgetsCLIServer(gui_id="id_test")
|
||||||
qtbot.addWidget(server.fig)
|
qtbot.addWidget(server.gui)
|
||||||
qtbot.waitExposed(server.fig)
|
qtbot.waitExposed(server.gui)
|
||||||
qtbot.wait(1000) # 1s long to wait until gui is ready
|
qtbot.wait(1000) # 1s long to wait until gui is ready
|
||||||
yield server
|
yield server
|
||||||
dispatcher.disconnect_all()
|
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):
|
def test_rpc_waveform1d_custom_curve(rpc_server, qtbot):
|
||||||
fig = BECFigure(rpc_server.gui_id)
|
fig = BECFigure(rpc_server.gui_id)
|
||||||
fig_server = rpc_server.fig
|
fig_server = rpc_server.gui
|
||||||
|
|
||||||
ax = fig.add_plot()
|
ax = fig.add_plot()
|
||||||
curve = ax.add_curve_custom([1, 2, 3], [1, 2, 3])
|
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):
|
def test_rpc_plotting_shortcuts_init_configs(rpc_server, qtbot):
|
||||||
fig = BECFigure(rpc_server.gui_id)
|
fig = BECFigure(rpc_server.gui_id)
|
||||||
fig_server = rpc_server.fig
|
fig_server = rpc_server.gui
|
||||||
|
|
||||||
plt = fig.plot("samx", "bpm4i")
|
plt = fig.plot("samx", "bpm4i")
|
||||||
im = fig.image("eiger")
|
im = fig.image("eiger")
|
||||||
@ -151,7 +151,7 @@ def test_rpc_image(rpc_server, qtbot):
|
|||||||
|
|
||||||
def test_rpc_motor_map(rpc_server, qtbot):
|
def test_rpc_motor_map(rpc_server, qtbot):
|
||||||
fig = BECFigure(rpc_server.gui_id)
|
fig = BECFigure(rpc_server.gui_id)
|
||||||
fig_server = rpc_server.fig
|
fig_server = rpc_server.gui
|
||||||
|
|
||||||
motor_map = fig.motor_map("samx", "samy")
|
motor_map = fig.motor_map("samx", "samy")
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user