mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-12-31 03:01:18 +01:00
feat(widget/dock_area): alternative dock area based on qt
This commit is contained in:
0
bec_widgets/widgets/dock_area/__init__.py
Normal file
0
bec_widgets/widgets/dock_area/__init__.py
Normal file
0
bec_widgets/widgets/dock_area/dock/__init__.py
Normal file
0
bec_widgets/widgets/dock_area/dock/__init__.py
Normal file
116
bec_widgets/widgets/dock_area/dock/dock.py
Normal file
116
bec_widgets/widgets/dock_area/dock/dock.py
Normal file
@@ -0,0 +1,116 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Literal, Optional
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from PyQt6.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout
|
||||
from PyQt6.QtCore import Qt
|
||||
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
|
||||
from qtpy.QtWidgets import QWidget, QMainWindow, QDockWidget, QSizePolicy
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qtpy.QtWidgets import QWidget
|
||||
from bec_widgets.widgets.dock_area.dock_area import BECDockAreaAlt
|
||||
|
||||
|
||||
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 BECDockAlt(BECConnector, QDockWidget):
|
||||
USER_ACCESS = []
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget | None = None,
|
||||
parent_dock_area: BECDockAreaAlt | None = None,
|
||||
config: DockConfig | None = None,
|
||||
title: str | None = None, # TODO maybe rename to title
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
layout: Literal["horizontal", "vertical", "grid"] = "vertical",
|
||||
) -> 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)
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
QDockWidget.__init__(self, title, parent)
|
||||
|
||||
self._parent_dock_area = parent_dock_area
|
||||
self._init_setup()
|
||||
self.setup_layout(layout)
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
# self._grid_layout_manager = GridLayoutManager(self)
|
||||
# self._widget_handler.init()
|
||||
# self._grid_layout_manager.init
|
||||
|
||||
def _init_setup(self):
|
||||
self.setAllowedAreas(Qt.DockWidgetArea.AllDockWidgetAreas)
|
||||
self._widget_handler = RPCWidgetHandler()
|
||||
self._base_widget = QWidget()
|
||||
self._widgets = WeakValueDictionary()
|
||||
|
||||
def setup_layout(self, layout: Literal["horizontal", "vertical", "grid"] = "grid"):
|
||||
if layout == "horizontal":
|
||||
self.layout = QHBoxLayout()
|
||||
elif layout == "vertical":
|
||||
self.layout = QVBoxLayout()
|
||||
elif layout == "grid":
|
||||
self.layout = QGridLayout()
|
||||
self.layout.setContentsMargins(0, 0, 0, 0)
|
||||
self._base_widget.setLayout(self.layout)
|
||||
self.setWidget(self._base_widget)
|
||||
|
||||
def add_widget(self, widget: QWidget, row: int = None, col: int = None):
|
||||
|
||||
if isinstance(self.layout, QVBoxLayout) or isinstance(self.layout, QHBoxLayout):
|
||||
self.layout.addWidget(widget)
|
||||
if row or col:
|
||||
print(f"Warning: row and column are not used in this layout - {self.layout}")
|
||||
elif isinstance(self.layout, QGridLayout):
|
||||
if row is None:
|
||||
row = self.layout.rowCount()
|
||||
if col is None:
|
||||
col = self.layout.columnCount()
|
||||
self.layout.addWidget(widget, row, col)
|
||||
|
||||
return widget
|
||||
|
||||
def add_widget_bec(self, widget_type: str, row: int = None, col: int = None):
|
||||
widget = self._widget_handler.create_widget(widget_type)
|
||||
self.add_widget(widget, row, col)
|
||||
return widget
|
||||
|
||||
def remove_widget(self, widget_id: str):
|
||||
widget = self.widgets.pop(widget_id)
|
||||
widget.deleteLater()
|
||||
return widget
|
||||
|
||||
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):
|
||||
self.cleanup()
|
||||
super().close()
|
||||
88
bec_widgets/widgets/dock_area/dock_area.py
Normal file
88
bec_widgets/widgets/dock_area/dock_area.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Literal, Optional
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
import qdarktheme
|
||||
from PyQt6.QtWidgets import QApplication, QVBoxLayout
|
||||
from pydantic import Field
|
||||
from pyqtgraph.dockarea.DockArea import DockArea
|
||||
from qtpy.QtCore import Qt
|
||||
from qtpy.QtGui import QPainter, QPaintEvent
|
||||
from qtpy.QtWidgets import QWidget, QMainWindow, QDockWidget
|
||||
|
||||
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
|
||||
from bec_widgets.widgets import BECFigure
|
||||
from bec_widgets.widgets.dock_area.dock.dock import BECDockAlt, DockConfig
|
||||
|
||||
|
||||
class DockAreaConfig(ConnectionConfig):
|
||||
docks: dict[str, DockConfig] = Field({}, description="The docks in the dock area.")
|
||||
|
||||
|
||||
class BECDockAreaAlt(BECConnector, QMainWindow):
|
||||
USER_ACCESS = []
|
||||
positions = {
|
||||
"top": Qt.DockWidgetArea.TopDockWidgetArea,
|
||||
"bottom": Qt.DockWidgetArea.BottomDockWidgetArea,
|
||||
"left": Qt.DockWidgetArea.LeftDockWidgetArea,
|
||||
"right": Qt.DockWidgetArea.RightDockWidgetArea,
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget | None = None,
|
||||
config: DockAreaConfig | None = None,
|
||||
client=None,
|
||||
gui_id: 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)
|
||||
QMainWindow.__init__(self, parent=parent)
|
||||
# TODO experimetn with the options and features
|
||||
# self.setDockNestingEnabled(True)
|
||||
# self.setDockOptions(
|
||||
# QMainWindow.DockOption.AllowTabbedDocks | QMainWindow.DockOption.AllowNestedDocks
|
||||
# )
|
||||
self._instructions_visible = True # TODO do not know how to translate yet to native qt
|
||||
|
||||
self._docks = WeakValueDictionary()
|
||||
|
||||
@property
|
||||
def docks(self) -> dict:
|
||||
"""
|
||||
Get the docks in the dock area.
|
||||
Returns:
|
||||
dock_dict(dict): The docks in the dock area.
|
||||
"""
|
||||
return dict(self._docks)
|
||||
|
||||
@docks.setter
|
||||
def docks(self, value: dict):
|
||||
self._docks = WeakValueDictionary(value)
|
||||
|
||||
def add_dock(
|
||||
self,
|
||||
title: str = None,
|
||||
layout: Literal["horizontal", "vertical", "grid"] = "vertical",
|
||||
prefix: str = "dock",
|
||||
position: Literal["top", "bottom", "left", "right"] = "bottom",
|
||||
):
|
||||
|
||||
if title is None:
|
||||
title = WidgetContainerUtils.generate_unique_widget_id(self._docks, prefix=prefix)
|
||||
|
||||
if title in set(self.docks.keys()):
|
||||
raise ValueError(f"Dock with name {title} already exists.")
|
||||
|
||||
dock = BECDockAlt(title=title, parent=self, parent_dock_area=self, layout=layout)
|
||||
self.addDockWidget(self.positions[position], dock)
|
||||
self._docks[title] = dock
|
||||
|
||||
return dock
|
||||
Reference in New Issue
Block a user