mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-05-05 14:24:21 +02:00
270 lines
8.3 KiB
Python
270 lines
8.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Literal, Optional
|
|
|
|
from pydantic import Field
|
|
from pyqtgraph.dockarea import Dock
|
|
|
|
from bec_widgets.cli.rpc_wigdet_handler import RPCWidgetHandler
|
|
from bec_widgets.utils import BECConnector, ConnectionConfig, GridLayoutManager
|
|
|
|
if TYPE_CHECKING:
|
|
from qtpy.QtWidgets import QWidget
|
|
|
|
from bec_widgets.widgets import BECDockArea
|
|
|
|
|
|
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 = [
|
|
"rpc_id",
|
|
"widget_list",
|
|
"show_title_bar",
|
|
"hide_title_bar",
|
|
"get_widgets_positions",
|
|
"set_title",
|
|
"add_widget_bec",
|
|
"list_eligible_widgets",
|
|
"move_widget",
|
|
"remove_widget",
|
|
"remove",
|
|
"attach",
|
|
"detach",
|
|
]
|
|
|
|
def __init__(
|
|
self,
|
|
parent: QWidget | None = None,
|
|
parent_dock_area: BECDockArea | None = None,
|
|
config: DockConfig | None = None,
|
|
name: str | None = None,
|
|
client=None,
|
|
gui_id: str | None = 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
|
|
|
|
# Layout Manager
|
|
self.layout_manager = GridLayoutManager(self.layout)
|
|
|
|
def dropEvent(self, event):
|
|
source = event.source()
|
|
old_area = source.area
|
|
self.setOrientation("horizontal", force=True)
|
|
super().dropEvent(event)
|
|
if old_area in self.parent_dock_area.tempAreas and old_area != self.parent_dock_area:
|
|
self.parent_dock_area.removeTempArea(old_area)
|
|
|
|
def float(self):
|
|
"""
|
|
Float the dock.
|
|
Overwrites the default pyqtgraph dock float.
|
|
"""
|
|
|
|
# need to check if the dock is temporary and if it is the only dock in the area
|
|
# fixes bug in pyqtgraph detaching
|
|
if self.area.temporary == True and len(self.area.docks) <= 1:
|
|
return
|
|
elif self.area.temporary == True and len(self.area.docks) > 1:
|
|
self.area.docks.pop(self.name(), None)
|
|
super().float()
|
|
else:
|
|
super().float()
|
|
|
|
@property
|
|
def widget_list(self) -> list:
|
|
"""
|
|
Get the widgets in the dock.
|
|
|
|
Returns:
|
|
widgets(list): The widgets in the dock.
|
|
"""
|
|
return self.widgets
|
|
|
|
@widget_list.setter
|
|
def widget_list(self, value: list):
|
|
self.widgets = value
|
|
|
|
def hide_title_bar(self):
|
|
"""
|
|
Hide the title bar of the dock.
|
|
"""
|
|
# self.hideTitleBar() #TODO pyqtgraph looks bugged ATM, doing my implementation
|
|
self.label.hide()
|
|
self.labelHidden = True
|
|
|
|
def show_title_bar(self):
|
|
"""
|
|
Hide the title bar of the dock.
|
|
"""
|
|
# self.showTitleBar() #TODO pyqtgraph looks bugged ATM, doing my implementation
|
|
self.label.show()
|
|
self.labelHidden = False
|
|
|
|
def set_title(self, title: str):
|
|
"""
|
|
Set the title of the dock.
|
|
|
|
Args:
|
|
title(str): The title of the dock.
|
|
"""
|
|
self.parent_dock_area.docks[title] = self.parent_dock_area.docks.pop(self.name())
|
|
self.setTitle(title)
|
|
|
|
def get_widgets_positions(self) -> dict:
|
|
"""
|
|
Get the positions of the widgets in the dock.
|
|
|
|
Returns:
|
|
dict: The positions of the widgets in the dock as dict -> {(row, col, rowspan, colspan):widget}
|
|
"""
|
|
return self.layout_manager.get_widgets_positions()
|
|
|
|
def list_eligible_widgets(
|
|
self,
|
|
) -> list: # TODO can be moved to some util mixin like container class for rpc widgets
|
|
"""
|
|
List all widgets that can be added to the dock.
|
|
|
|
Returns:
|
|
list: The list of eligible widgets.
|
|
"""
|
|
return list(RPCWidgetHandler.widget_classes.keys())
|
|
|
|
def add_widget_bec(
|
|
self,
|
|
widget_type: str,
|
|
row=None,
|
|
col=0,
|
|
rowspan=1,
|
|
colspan=1,
|
|
shift: Literal["down", "up", "left", "right"] = "down",
|
|
):
|
|
"""
|
|
Add a widget to the dock.
|
|
|
|
Args:
|
|
widget_type(str): The widget to add. Only BEC RPC widgets from RPCWidgetHandler are allowed.
|
|
row(int): The row to add the widget to. If None, the widget will be added to the next available row.
|
|
col(int): The column to add the widget to.
|
|
rowspan(int): The number of rows the widget should span.
|
|
colspan(int): The number of columns the widget should span.
|
|
shift(Literal["down", "up", "left", "right"]): The direction to shift the widgets if the position is occupied.
|
|
"""
|
|
if row is None:
|
|
row = self.layout.rowCount()
|
|
|
|
if self.layout_manager.is_position_occupied(row, col):
|
|
self.layout_manager.shift_widgets(shift, start_row=row)
|
|
|
|
widget = RPCWidgetHandler.create_widget(widget_type)
|
|
self.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
|
|
|
|
return widget
|
|
|
|
def add_widget(
|
|
self,
|
|
widget: QWidget,
|
|
row=None,
|
|
col=0,
|
|
rowspan=1,
|
|
colspan=1,
|
|
shift: Literal["down", "up", "left", "right"] = "down",
|
|
):
|
|
"""
|
|
Add a widget to the dock.
|
|
|
|
Args:
|
|
widget(QWidget): The widget to add.
|
|
row(int): The row to add the widget to. If None, the widget will be added to the next available row.
|
|
col(int): The column to add the widget to.
|
|
rowspan(int): The number of rows the widget should span.
|
|
colspan(int): The number of columns the widget should span.
|
|
shift(Literal["down", "up", "left", "right"]): The direction to shift the widgets if the position is occupied.
|
|
"""
|
|
if row is None:
|
|
row = self.layout.rowCount()
|
|
|
|
if self.layout_manager.is_position_occupied(row, col):
|
|
self.layout_manager.shift_widgets(shift, start_row=row)
|
|
|
|
self.addWidget(widget, row=row, col=col, rowspan=rowspan, colspan=colspan)
|
|
|
|
def move_widget(self, widget: QWidget, new_row: int, new_col: int):
|
|
"""
|
|
Move a widget to a new position in the layout.
|
|
|
|
Args:
|
|
widget(QWidget): The widget to move.
|
|
new_row(int): The new row to move the widget to.
|
|
new_col(int): The new column to move the widget to.
|
|
"""
|
|
self.layout_manager.move_widget(widget, new_row, new_col)
|
|
|
|
def attach(self):
|
|
"""
|
|
Attach the dock to the parent dock area.
|
|
"""
|
|
self.parent_dock_area.removeTempArea(self.area)
|
|
|
|
def detach(self):
|
|
"""
|
|
Detach the dock from the parent dock area.
|
|
"""
|
|
self.float()
|
|
|
|
def remove_widget(self, widget_rpc_id: str):
|
|
"""
|
|
Remove a widget from the dock.
|
|
|
|
Args:
|
|
widget_rpc_id(str): The ID of the widget to remove.
|
|
"""
|
|
widget = self.rpc_register.get_rpc_by_id(widget_rpc_id)
|
|
self.layout.removeWidget(widget)
|
|
widget.close()
|
|
|
|
def remove(self):
|
|
"""
|
|
Remove the dock from the parent dock area.
|
|
"""
|
|
# self.cleanup()
|
|
self.parent_dock_area.remove_dock(self.name())
|
|
|
|
def cleanup(self):
|
|
"""
|
|
Clean up the dock, including all its widgets.
|
|
"""
|
|
for widget in self.widgets:
|
|
if hasattr(widget, "cleanup"):
|
|
widget.cleanup()
|
|
super().cleanup()
|
|
|
|
def close(self):
|
|
"""
|
|
Close the dock area and cleanup.
|
|
Has to be implemented to overwrite pyqtgraph event accept in Container close.
|
|
"""
|
|
self.cleanup()
|
|
super().close()
|