mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 11:41:49 +02:00
feat(widget/dock): BECDock and BECDock area for dockable windows
This commit is contained in:
@ -255,11 +255,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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -1307,3 +1307,70 @@ class BECMotorMap(RPCBase):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: Data of the motor map.
|
dict: Data of the motor map.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BECDock(RPCBase):
|
||||||
|
@rpc_call
|
||||||
|
def add_widget(self, widget: "QWidget", row=None, col=0, rowspan=1, colspan=1):
|
||||||
|
"""
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@rpc_call
|
||||||
|
def widget_list(self) -> "list":
|
||||||
|
"""
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BECDockArea(RPCBase):
|
||||||
|
@rpc_call
|
||||||
|
def add_dock(
|
||||||
|
self,
|
||||||
|
name: "str" = None,
|
||||||
|
position: "Literal['bottom', 'top', 'left', 'right', 'above', 'below']" = None,
|
||||||
|
relative_to: "Optional[BECDock]" = None,
|
||||||
|
prefix: "str" = "dock",
|
||||||
|
widget: "QWidget" = None,
|
||||||
|
row: "int" = None,
|
||||||
|
col: "int" = None,
|
||||||
|
rowspan: "int" = 1,
|
||||||
|
colspan: "int" = 1,
|
||||||
|
) -> "BECDock":
|
||||||
|
"""
|
||||||
|
Add a dock to the dock area. Dock has QGridLayout as layout manager by default.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name(str): The name of the dock to be displayed and for further references. Has to be unique.
|
||||||
|
position(Literal["bottom", "top", "left", "right", "above", "below"]): The position of the dock.
|
||||||
|
relative_to(BECDock): The dock to which the new dock should be added relative to.
|
||||||
|
prefix(str): The prefix for the dock name if no name is provided.
|
||||||
|
widget(QWidget): The widget to be added to the dock.
|
||||||
|
row(int): The row of the added widget.
|
||||||
|
col(int): The column of the added widget.
|
||||||
|
rowspan(int): The rowspan of the added widget.
|
||||||
|
colspan(int): The colspan of the added widget.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BECDock: The created dock.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@rpc_call
|
||||||
|
def remove_dock_by_id(self, dock_id: "str"):
|
||||||
|
"""
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
@rpc_call
|
||||||
|
def clear_all(self):
|
||||||
|
"""
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@rpc_call
|
||||||
|
def dock_dict(self) -> "dict":
|
||||||
|
"""
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
@ -108,6 +108,7 @@ if __name__ == "__main__": # pragma: no cover
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from bec_widgets.utils import BECConnector
|
from bec_widgets.utils import BECConnector
|
||||||
|
from bec_widgets.widgets.dock import BECDock, BECDockArea
|
||||||
from bec_widgets.widgets.figure import BECFigure
|
from bec_widgets.widgets.figure import BECFigure
|
||||||
from bec_widgets.widgets.plots import BECImageShow, BECMotorMap, BECPlotBase, BECWaveform
|
from bec_widgets.widgets.plots import BECImageShow, BECMotorMap, BECPlotBase, BECWaveform
|
||||||
from bec_widgets.widgets.plots.image import BECImageItem
|
from bec_widgets.widgets.plots.image import BECImageItem
|
||||||
@ -124,6 +125,8 @@ if __name__ == "__main__": # pragma: no cover
|
|||||||
BECConnector,
|
BECConnector,
|
||||||
BECImageItem,
|
BECImageItem,
|
||||||
BECMotorMap,
|
BECMotorMap,
|
||||||
|
BECDock,
|
||||||
|
BECDockArea,
|
||||||
]
|
]
|
||||||
generator = ClientGenerator()
|
generator = ClientGenerator()
|
||||||
generator.generate_client(clss)
|
generator.generate_client(clss)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
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
|
||||||
@ -8,6 +9,7 @@ from qtpy.QtCore import QTimer
|
|||||||
from bec_widgets.cli.rpc_register import RPCRegister
|
from bec_widgets.cli.rpc_register import RPCRegister
|
||||||
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
|
||||||
|
|
||||||
@ -16,15 +18,20 @@ class BECWidgetsCLIServer:
|
|||||||
WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]
|
WIDGETS = [BECWaveform, BECFigure, BECCurve, BECImageShow]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, gui_id: str = None, dispatcher: BECDispatcher = None, client=None, config=None
|
self,
|
||||||
|
gui_id: str = None,
|
||||||
|
dispatcher: BECDispatcher = None,
|
||||||
|
client=None,
|
||||||
|
config=None,
|
||||||
|
gui_class: BECFigure | BECDockArea = BECFigure,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.dispatcher = BECDispatcher(config=config) if dispatcher is None else dispatcher
|
self.dispatcher = BECDispatcher(config=config) 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.rpc_register = RPCRegister()
|
self.rpc_register = RPCRegister()
|
||||||
self.rpc_register.add_rpc(self.fig)
|
self.rpc_register.add_rpc(self.gui)
|
||||||
|
|
||||||
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)
|
||||||
@ -127,14 +134,30 @@ 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",
|
||||||
|
)
|
||||||
parser.add_argument("--config", type=str, help="Config to connect to redis.")
|
parser.add_argument("--config", type=str, help="Config to connect to redis.")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
server = BECWidgetsCLIServer(gui_id=args.id, config=args.config)
|
if args.gui_class == "BECFigure":
|
||||||
# server = BECWidgetsCLIServer(gui_id="test",config="awi-bec-dev-01:6379")
|
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, config=args.config, gui_class=gui_class)
|
||||||
|
# server = BECWidgetsCLIServer(gui_id="test", config=args.config, gui_class=gui_class)
|
||||||
|
|
||||||
|
fig = server.gui
|
||||||
win.setCentralWidget(fig)
|
win.setCentralWidget(fig)
|
||||||
win.show()
|
win.show()
|
||||||
|
|
||||||
|
@ -2,14 +2,17 @@ import os
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.Qt import uic
|
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.QtCore import QSize
|
||||||
|
from qtpy.QtGui import QIcon
|
||||||
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
|
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
|
||||||
|
|
||||||
from bec_widgets.cli.rpc_register import RPCRegister
|
from bec_widgets.cli.rpc_register import RPCRegister
|
||||||
from bec_widgets.utils import BECDispatcher
|
from bec_widgets.utils import BECDispatcher
|
||||||
from bec_widgets.widgets import BECFigure
|
from bec_widgets.widgets import BECFigure
|
||||||
|
from bec_widgets.widgets.dock.dock_area import BECDockArea
|
||||||
|
|
||||||
|
|
||||||
class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:
|
class JupyterConsoleWidget(RichJupyterWidget): # pragma: no cover:
|
||||||
@ -52,9 +55,13 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|||||||
{
|
{
|
||||||
"fig": self.figure,
|
"fig": self.figure,
|
||||||
"register": self.register,
|
"register": self.register,
|
||||||
|
"dock": self.dock,
|
||||||
"w1": self.w1,
|
"w1": self.w1,
|
||||||
"w2": self.w2,
|
"w2": self.w2,
|
||||||
"w3": self.w3,
|
"w3": self.w3,
|
||||||
|
"d1": self.d1,
|
||||||
|
"d2": self.d2,
|
||||||
|
"d3": self.d3,
|
||||||
"bec": self.figure.client,
|
"bec": self.figure.client,
|
||||||
"scans": self.figure.client.scans,
|
"scans": self.figure.client.scans,
|
||||||
"dev": self.figure.client.device_manager.devices,
|
"dev": self.figure.client.device_manager.devices,
|
||||||
@ -67,9 +74,16 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|||||||
self.figure = BECFigure(parent=self, gui_id="remote") # Create a new BECDeviceMonitor
|
self.figure = BECFigure(parent=self, gui_id="remote") # Create a new BECDeviceMonitor
|
||||||
self.glw_1_layout.addWidget(self.figure) # Add BECDeviceMonitor to the layout
|
self.glw_1_layout.addWidget(self.figure) # Add BECDeviceMonitor to the layout
|
||||||
|
|
||||||
|
self.dock_layout = QVBoxLayout(self.dock_placeholder)
|
||||||
|
self.dock = BECDockArea(gui_id="remote")
|
||||||
|
self.dock_layout.addWidget(self.dock)
|
||||||
|
|
||||||
# add stuff to figure
|
# add stuff to figure
|
||||||
self._init_figure()
|
self._init_figure()
|
||||||
|
|
||||||
|
# init dock for testing
|
||||||
|
self._init_dock()
|
||||||
|
|
||||||
self.console_layout = QVBoxLayout(self.widget_console)
|
self.console_layout = QVBoxLayout(self.widget_console)
|
||||||
self.console = JupyterConsoleWidget()
|
self.console = JupyterConsoleWidget()
|
||||||
self.console_layout.addWidget(self.console)
|
self.console_layout.addWidget(self.console)
|
||||||
@ -91,6 +105,24 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|||||||
self.w1.add_curve_scan("samx", "samy", "bpm3a", pen_style="dash")
|
self.w1.add_curve_scan("samx", "samy", "bpm3a", pen_style="dash")
|
||||||
self.c1 = self.w1.get_config()
|
self.c1 = self.w1.get_config()
|
||||||
|
|
||||||
|
def _init_dock(self):
|
||||||
|
self.button_1 = QtWidgets.QPushButton("Button 1 ")
|
||||||
|
self.button_3 = QtWidgets.QPushButton("Button above Figure ")
|
||||||
|
self.label_1 = QtWidgets.QLabel("some scan info label with useful information")
|
||||||
|
|
||||||
|
self.label_2 = QtWidgets.QLabel("label which is added separately")
|
||||||
|
self.label_3 = QtWidgets.QLabel("Label above figure")
|
||||||
|
|
||||||
|
self.d1 = self.dock.add_dock(widget=self.button_1, position="left")
|
||||||
|
self.d1.addWidget(self.label_2)
|
||||||
|
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.d3.add_widget(self.label_3)
|
||||||
|
self.d3.add_widget(self.button_3)
|
||||||
|
self.d3.add_widget(self.fig_dock3)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover
|
if __name__ == "__main__": # pragma: no cover
|
||||||
import sys
|
import sys
|
||||||
@ -101,6 +133,10 @@ if __name__ == "__main__": # pragma: no cover
|
|||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
app.setApplicationName("Jupyter Console")
|
app.setApplicationName("Jupyter Console")
|
||||||
|
app.setApplicationDisplayName("Jupyter Console")
|
||||||
|
icon = QIcon()
|
||||||
|
icon.addFile("terminal_icon.png", size=QSize(48, 48))
|
||||||
|
app.setWindowIcon(icon)
|
||||||
win = JupyterConsoleWindow()
|
win = JupyterConsoleWindow()
|
||||||
win.show()
|
win.show()
|
||||||
|
|
||||||
|
@ -13,13 +13,37 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Plotting Console</string>
|
<string>Plotting Console</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSplitter" name="splitter">
|
<widget class="QSplitter" name="splitter">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="glw" native="true"/>
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab_1">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>BECDock</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="dock_placeholder" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>BECFigure</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="glw" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="widget_console" native="true"/>
|
<widget class="QWidget" name="widget_console" native="true"/>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
BIN
bec_widgets/examples/jupyter_console/terminal_icon.png
Normal file
BIN
bec_widgets/examples/jupyter_console/terminal_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
@ -1,3 +1,4 @@
|
|||||||
|
from .dock import BECDock, BECDockArea
|
||||||
from .figure import BECFigure, FigureConfig
|
from .figure import BECFigure, FigureConfig
|
||||||
from .monitor import BECMonitor
|
from .monitor import BECMonitor
|
||||||
from .motor_control import (
|
from .motor_control import (
|
||||||
|
2
bec_widgets/widgets/dock/__init__.py
Normal file
2
bec_widgets/widgets/dock/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .dock import BECDock
|
||||||
|
from .dock_area import BECDockArea
|
65
bec_widgets/widgets/dock/dock.py
Normal file
65
bec_widgets/widgets/dock/dock.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
USER_ACCESS = ["add_widget", "widget_list"]
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def widget_list(self) -> list:
|
||||||
|
return self.widgets
|
||||||
|
|
||||||
|
@widget_list.setter
|
||||||
|
def widget_list(self, value: list):
|
||||||
|
self.widgets = value
|
||||||
|
|
||||||
|
def add_widget(self, widget: QWidget, row=None, col=0, rowspan=1, colspan=1):
|
||||||
|
self.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
|
||||||
|
|
||||||
|
def _remove_from_dock_area(self):
|
||||||
|
"""Remove this dock from the DockArea it lives inside."""
|
||||||
|
self.parent_dock_area.docks.pop(self.name())
|
123
bec_widgets/widgets/dock/dock_area.py
Normal file
123
bec_widgets/widgets/dock/dock_area.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Literal, Optional
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
from pyqtgraph.dockarea.DockArea import DockArea
|
||||||
|
from qtpy.QtWidgets import QWidget
|
||||||
|
|
||||||
|
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
|
||||||
|
|
||||||
|
from .dock import BECDock, DockConfig
|
||||||
|
|
||||||
|
# from bec_widgets.widgets import BECDock
|
||||||
|
|
||||||
|
|
||||||
|
class DockAreaConfig(ConnectionConfig):
|
||||||
|
docks: dict[str, DockConfig] = Field({}, description="The docks in the dock area.")
|
||||||
|
|
||||||
|
|
||||||
|
class BECDockArea(BECConnector, DockArea):
|
||||||
|
USER_ACCESS = [
|
||||||
|
"add_dock",
|
||||||
|
"remove_dock_by_id",
|
||||||
|
"clear_all",
|
||||||
|
"dock_dict",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: Optional[QWidget] = None,
|
||||||
|
config: Optional[DockAreaConfig] = None,
|
||||||
|
client=None,
|
||||||
|
gui_id: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
if config is None:
|
||||||
|
config = DockAreaConfig(widget_class=self.__class__.__name__)
|
||||||
|
else:
|
||||||
|
if isinstance(config, dict):
|
||||||
|
config = DockAreaConfig(**config)
|
||||||
|
self.config = config
|
||||||
|
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||||
|
DockArea.__init__(self, parent=parent)
|
||||||
|
|
||||||
|
self._last_state = None # TODO not sure if this will ever work
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dock_dict(self) -> dict:
|
||||||
|
return dict(self.docks)
|
||||||
|
|
||||||
|
@dock_dict.setter
|
||||||
|
def dock_dict(self, value: dict):
|
||||||
|
from weakref import WeakValueDictionary
|
||||||
|
|
||||||
|
self.docks = WeakValueDictionary(value)
|
||||||
|
|
||||||
|
def remove_dock_by_id(self, dock_id: str):
|
||||||
|
if dock_id in self.docks:
|
||||||
|
dock_to_remove = self.docks[dock_id]
|
||||||
|
dock_to_remove.close()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Dock with id {dock_id} does not exist.")
|
||||||
|
|
||||||
|
def remove_dock(self, name: str):
|
||||||
|
for id, dock in self.docks.items():
|
||||||
|
dock_name = dock.name()
|
||||||
|
if dock_name == name:
|
||||||
|
dock.close()
|
||||||
|
break
|
||||||
|
|
||||||
|
def add_dock(
|
||||||
|
self,
|
||||||
|
name: str = None,
|
||||||
|
position: Literal["bottom", "top", "left", "right", "above", "below"] = None,
|
||||||
|
relative_to: Optional[BECDock] = None,
|
||||||
|
prefix: str = "dock",
|
||||||
|
widget: QWidget = None,
|
||||||
|
row: int = None,
|
||||||
|
col: int = None,
|
||||||
|
rowspan: int = 1,
|
||||||
|
colspan: int = 1,
|
||||||
|
) -> BECDock:
|
||||||
|
"""
|
||||||
|
Add a dock to the dock area. Dock has QGridLayout as layout manager by default.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name(str): The name of the dock to be displayed and for further references. Has to be unique.
|
||||||
|
position(Literal["bottom", "top", "left", "right", "above", "below"]): The position of the dock.
|
||||||
|
relative_to(BECDock): The dock to which the new dock should be added relative to.
|
||||||
|
prefix(str): The prefix for the dock name if no name is provided.
|
||||||
|
widget(QWidget): The widget to be added to the dock.
|
||||||
|
row(int): The row of the added widget.
|
||||||
|
col(int): The column of the added widget.
|
||||||
|
rowspan(int): The rowspan of the added widget.
|
||||||
|
colspan(int): The colspan of the added widget.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BECDock: The created dock.
|
||||||
|
"""
|
||||||
|
if name is None:
|
||||||
|
name = WidgetContainerUtils.generate_unique_widget_id(
|
||||||
|
container=self.docks, prefix=prefix
|
||||||
|
)
|
||||||
|
|
||||||
|
if name in set(self.docks.keys()):
|
||||||
|
raise ValueError(f"Dock with name {name} already exists.")
|
||||||
|
|
||||||
|
if position is None:
|
||||||
|
position = "bottom"
|
||||||
|
|
||||||
|
dock = BECDock(name=name, parent_dock_area=self, closable=True)
|
||||||
|
dock.config.position = position
|
||||||
|
self.config.docks[name] = dock.config
|
||||||
|
|
||||||
|
self.addDock(dock=dock, position=position, relativeTo=relative_to)
|
||||||
|
|
||||||
|
if widget is not None:
|
||||||
|
dock.addWidget(widget) # , row, col, rowspan, colspan)
|
||||||
|
|
||||||
|
return dock
|
||||||
|
|
||||||
|
def clear_all(self):
|
||||||
|
for dock in self.docks.values():
|
||||||
|
dock.close()
|
@ -3,11 +3,27 @@ import pytest
|
|||||||
from bec_lib import MessageEndpoints
|
from bec_lib import MessageEndpoints
|
||||||
|
|
||||||
from bec_widgets.cli.client import BECFigure, BECImageShow, BECMotorMap, BECWaveform
|
from bec_widgets.cli.client import BECFigure, BECImageShow, BECMotorMap, BECWaveform
|
||||||
|
from bec_widgets.cli.server import BECWidgetsCLIServer
|
||||||
|
from bec_widgets.utils import BECDispatcher
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
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.gui)
|
||||||
|
qtbot.waitExposed(server.gui)
|
||||||
|
qtbot.wait(1000) # 1s long to wait until gui is ready
|
||||||
|
yield server
|
||||||
|
dispatcher.disconnect_all()
|
||||||
|
server.client.shutdown()
|
||||||
|
server.shutdown()
|
||||||
|
dispatcher.reset_singleton()
|
||||||
|
|
||||||
|
|
||||||
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])
|
||||||
@ -21,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")
|
||||||
@ -135,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