mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat: figure.py create widget factory
This commit is contained in:
@ -17,7 +17,7 @@ from bec_widgets.utils import (
|
|||||||
register_rpc_methods,
|
register_rpc_methods,
|
||||||
rpc_public,
|
rpc_public,
|
||||||
)
|
)
|
||||||
from bec_widgets.widgets.plots import WidgetConfig, BECPlotBase
|
from bec_widgets.widgets.plots import WidgetConfig, BECPlotBase, Waveform1DConfig, BECWaveform1D
|
||||||
|
|
||||||
|
|
||||||
class FigureConfig(ConnectionConfig):
|
class FigureConfig(ConnectionConfig):
|
||||||
@ -30,6 +30,55 @@ class FigureConfig(ConnectionConfig):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WidgetHandler:
|
||||||
|
"""Factory for creating and configuring BEC widgets for BECFigure."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.widget_factory = {
|
||||||
|
"PlotBase": (BECPlotBase, WidgetConfig),
|
||||||
|
"Waveform1D": (BECWaveform1D, Waveform1DConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_widget(
|
||||||
|
self,
|
||||||
|
widget_type: str,
|
||||||
|
widget_id: str,
|
||||||
|
parent_figure_id: str,
|
||||||
|
config: dict = None,
|
||||||
|
**axis_kwargs,
|
||||||
|
) -> BECPlotBase:
|
||||||
|
"""
|
||||||
|
Create and configure a widget based on its type.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
widget_type (str): The type of the widget to create.
|
||||||
|
widget_id (str): Unique identifier for the widget.
|
||||||
|
parent_figure_id (str): Identifier of the parent figure.
|
||||||
|
config (dict, optional): Additional configuration for the widget.
|
||||||
|
**axis_kwargs: Additional axis properties to set on the widget after creation.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BECPlotBase: The created and configured widget instance.
|
||||||
|
"""
|
||||||
|
entry = self.widget_factory.get(widget_type)
|
||||||
|
if not entry:
|
||||||
|
raise ValueError(f"Unsupported widget type: {widget_type}")
|
||||||
|
|
||||||
|
widget_class, config_class = entry
|
||||||
|
widget_config_dict = {
|
||||||
|
"parent_figure_id": parent_figure_id,
|
||||||
|
"gui_id": widget_id,
|
||||||
|
**(config if config is not None else {}),
|
||||||
|
}
|
||||||
|
widget_config = config_class(**widget_config_dict)
|
||||||
|
widget = widget_class(config=widget_config)
|
||||||
|
|
||||||
|
if axis_kwargs:
|
||||||
|
widget.set(**axis_kwargs)
|
||||||
|
|
||||||
|
return widget
|
||||||
|
|
||||||
|
|
||||||
@register_rpc_methods
|
@register_rpc_methods
|
||||||
class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -46,10 +95,11 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|||||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||||
pg.GraphicsLayoutWidget.__init__(self, parent) # in case of inheritance
|
pg.GraphicsLayoutWidget.__init__(self, parent) # in case of inheritance
|
||||||
|
|
||||||
|
self.widget_handler = WidgetHandler()
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
|
|
||||||
# TODO just testing adding plot
|
# TODO just testing adding plot
|
||||||
self.add_widget("widget_1", row=0, col=0, title="Plot 1")
|
self.add_widget(widget_id="widget_1", row=0, col=0, title="Plot 1")
|
||||||
self.widgets["widget_1"].plot_data(
|
self.widgets["widget_1"].plot_data(
|
||||||
np.linspace(0, 10, 100), np.sin(np.linspace(0, 10, 100)), label="sin(x)"
|
np.linspace(0, 10, 100), np.sin(np.linspace(0, 10, 100)), label="sin(x)"
|
||||||
)
|
)
|
||||||
@ -64,48 +114,58 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|||||||
# self.window.close()
|
# self.window.close()
|
||||||
|
|
||||||
@rpc_public
|
@rpc_public
|
||||||
def add_widget(self, widget_id: str = None, row: int = None, col: int = None, **kwargs):
|
def add_widget(
|
||||||
# Generate unique widget_id if not provided
|
self,
|
||||||
|
widget_type: str = "PlotBase",
|
||||||
|
widget_id: str = None,
|
||||||
|
row: int = None,
|
||||||
|
col: int = None,
|
||||||
|
config: dict = None,
|
||||||
|
**axis_kwargs,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Add a widget to the figure at the specified position.
|
||||||
|
Args:
|
||||||
|
widget_type(str): The type of the widget to add.
|
||||||
|
widget_id(str): The unique identifier of the widget. If not provided, a unique ID will be generated.
|
||||||
|
row(int): The row coordinate of the widget in the figure. If not provided, the next empty row will be used.
|
||||||
|
col(int): The column coordinate of the widget in the figure. If not provided, the next empty column will be used.
|
||||||
|
config(dict): Additional configuration for the widget.
|
||||||
|
**axis_kwargs(dict): Additional axis properties to set on the widget after creation.
|
||||||
|
"""
|
||||||
if not widget_id:
|
if not widget_id:
|
||||||
widget_id = self._generate_unique_widget_id()
|
widget_id = self._generate_unique_widget_id()
|
||||||
|
|
||||||
# Check if id is available
|
|
||||||
if widget_id in self.widgets:
|
if widget_id in self.widgets:
|
||||||
print(f"Widget with ID {widget_id} already exists.") # TODO change to raise error)
|
raise ValueError(f"Widget with ID {widget_id} already exists.")
|
||||||
return
|
|
||||||
|
|
||||||
# Crete widget instance and its config
|
widget = self.widget_handler.create_widget(
|
||||||
widget_config = WidgetConfig(
|
widget_type=widget_type,
|
||||||
parent_figure_id=self.gui_id, widget_class="BECPlotBase", gui_id=widget_id
|
widget_id=widget_id,
|
||||||
|
parent_figure_id=self.gui_id,
|
||||||
|
config=config,
|
||||||
|
**axis_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
widget = BECPlotBase(config=widget_config)
|
|
||||||
widget.set(**kwargs) # TODO can be done througt config somehow
|
|
||||||
|
|
||||||
# Check if position is occupied
|
# Check if position is occupied
|
||||||
if row is not None and col is not None:
|
if row is not None and col is not None:
|
||||||
print("adding plot")
|
|
||||||
if self.getItem(row, col):
|
if self.getItem(row, col):
|
||||||
print(
|
raise ValueError(f"Position at row {row} and column {col} is already occupied.")
|
||||||
f"Position at row {row} and column {col} is already occupied."
|
|
||||||
) # TODO change to raise error
|
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
widget_config.row = row
|
widget.config.row = row
|
||||||
widget_config.column = col
|
widget.config.column = col
|
||||||
|
|
||||||
# Add widget to the figure
|
# Add widget to the figure
|
||||||
self.addItem(widget, row=row, col=col)
|
self.addItem(widget, row=row, col=col)
|
||||||
else:
|
else:
|
||||||
row, col = self._find_next_empty_position()
|
row, col = self._find_next_empty_position()
|
||||||
widget_config.row = row
|
widget.config.row = row
|
||||||
widget_config.column = col
|
widget.config.column = col
|
||||||
|
|
||||||
# Add widget to the figure
|
# Add widget to the figure
|
||||||
self.addItem(widget, row=row, col=col)
|
self.addItem(widget, row=row, col=col)
|
||||||
|
|
||||||
# Saving config for future referencing
|
# Saving config for future referencing
|
||||||
self.config.widgets[widget_id] = widget_config
|
self.config.widgets[widget_id] = widget.config
|
||||||
self.widgets[widget_id] = widget
|
self.widgets[widget_id] = widget
|
||||||
|
|
||||||
def __getitem__(self, key: tuple | str):
|
def __getitem__(self, key: tuple | str):
|
||||||
@ -118,7 +178,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|||||||
return self.widgets.get(key)
|
return self.widgets.get(key)
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"Key must be a string (widget id) or a tuple of two integers (coordinates)"
|
"Key must be a string (widget id) or a tuple of two integers (grid coordinates)"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_widget_by_coordinates(self, row: int, col: int) -> BECPlotBase:
|
def _get_widget_by_coordinates(self, row: int, col: int) -> BECPlotBase:
|
||||||
@ -136,19 +196,6 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|||||||
raise KeyError(f"No widget at coordinates ({row}, {col})")
|
raise KeyError(f"No widget at coordinates ({row}, {col})")
|
||||||
return widget
|
return widget
|
||||||
|
|
||||||
def _add_waveform1d(self, widget_id: str = None, row: int = None, col: int = None, **kwargs):
|
|
||||||
"""
|
|
||||||
Add a 1D waveform widget to the figure.
|
|
||||||
Args:
|
|
||||||
widget_id:
|
|
||||||
row:
|
|
||||||
col:
|
|
||||||
**kwargs:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _find_next_empty_position(self):
|
def _find_next_empty_position(self):
|
||||||
"""Find the next empty position (new row) in the figure."""
|
"""Find the next empty position (new row) in the figure."""
|
||||||
row, col = 0, 0
|
row, col = 0, 0
|
||||||
|
Reference in New Issue
Block a user