1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-04-08 17:57:54 +02:00

Compare commits

...

4 Commits

6 changed files with 244 additions and 25 deletions

View File

@@ -2,6 +2,8 @@ import os
import numpy as np
import pyqtgraph as pg
import qdarktheme
from PyQt6.QtWidgets import QPushButton, QTextEdit
from pyqtgraph.Qt import QtWidgets, uic
from qtconsole.inprocess import QtInProcessKernelManager
from qtconsole.rich_jupyter_widget import RichJupyterWidget
@@ -13,6 +15,7 @@ from bec_widgets.cli.rpc_register import RPCRegister
from bec_widgets.utils import BECDispatcher
from bec_widgets.widgets import BECFigure
from bec_widgets.widgets.dock.dock_area import BECDockArea
from bec_widgets.widgets.dock_area.dock_area import BECDockAreaAlt
from bec_widgets.widgets.spiral_progress_bar.spiral_progress_bar import SpiralProgressBar
@@ -95,7 +98,7 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
self.console.set_default_style("linux")
def _init_figure(self):
self.figure.plot(x_name="samx", y_name="bpm4d")
self.figure.plot("samx", "bpm4d")
self.figure.motor_map("samx", "samy")
self.figure.image("eiger", color_map="viridis", vrange=(0, 100))
@@ -111,28 +114,40 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
self.c1 = self.w1.get_config()
def _init_dock(self):
self.button_1 = QtWidgets.QPushButton("Button 1 ")
self.button_2_a = QtWidgets.QPushButton("Button to be added at place 0,0 in d3")
self.button_2_b = QtWidgets.QPushButton("button after without postions specified")
self.button_2_c = QtWidgets.QPushButton("button super late")
self.button_3 = QtWidgets.QPushButton("Button above Figure ")
self.bar = SpiralProgressBar()
self.label_2 = QtWidgets.QLabel("label which is added separately")
self.label_3 = QtWidgets.QLabel("Label above figure")
self.d0 = self.dock.add_dock(name="dock_0")
self.fig0 = self.d0.add_widget_bec("BECFigure")
self.fig0.image("eiger", vrange=(0, 100))
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.bar, position="right")
self.d3 = self.dock.add_dock(name="figure")
self.fig_dock3 = BECFigure()
self.fig_dock3.plot(x_name="samx", y_name="bpm4d")
self.d3.add_widget(self.label_3)
self.d3.add_widget(self.button_3)
self.d3.add_widget(self.fig_dock3)
self.d1 = self.dock.add_dock(name="dock_1", position="right")
self.fig1 = self.d1.add_widget_bec("BECFigure")
self.fig1.plot("samx", "bpm4i")
self.fig1.plot("samx", "bpm3a")
self.d2 = self.dock.add_dock(name="dock_2", position="bottom")
self.fi2 = self.d2.add_widget_bec("BECFigure", row=0, col=0)
self.fi2.motor_map("samx", "samy")
self.bar2 = self.d2.add_widget_bec("SpiralProgressBar", row=0, col=1)
self.bar2.set_diameter(200)
self.dock.save_state()
def ini_dock_alt(self):
self.d0_alt = self.dock_alt.add_dock(title="dock_0", layout="grid", position="top")
self.fig0_alt = self.d0_alt.add_widget_bec("BECFigure")
self.fig0_alt.image("eiger", vrange=(0, 100))
self.d1_alt = self.dock_alt.add_dock(title="dock_1", layout="grid", position="top")
self.fig1_alt = self.d1_alt.add_widget_bec("BECFigure")
self.fig1_alt.plot("samx", "bpm4i")
self.fig1_alt.plot("samx", "bpm3a")
self.d2_alt = self.dock_alt.add_dock(title="dock_2", layout="grid", position="bottom")
self.fi2_alt = self.d2_alt.add_widget_bec("BECFigure", row=0, col=0)
self.fi2_alt.motor_map("samx", "samy")
self.bar2_alt = self.d2_alt.add_widget_bec("SpiralProgressBar", row=0, col=1)
self.bar2_alt.set_diameter(200)
def closeEvent(self, event):
"""Override to handle things when main window is closed."""
self.dock.cleanup()
@@ -144,10 +159,6 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
if __name__ == "__main__": # pragma: no cover
import sys
import bec_widgets
module_path = os.path.dirname(bec_widgets.__file__)
bec_dispatcher = BECDispatcher()
client = bec_dispatcher.client
client.start()
@@ -155,8 +166,9 @@ if __name__ == "__main__": # pragma: no cover
app = QApplication(sys.argv)
app.setApplicationName("Jupyter Console")
app.setApplicationDisplayName("Jupyter Console")
qdarktheme.setup_theme("auto")
icon = QIcon()
icon.addFile(os.path.join(module_path, "assets", "terminal_icon.png"), size=QSize(48, 48))
icon.addFile("terminal_icon.png", size=QSize(48, 48))
app.setWindowIcon(icon)
win = JupyterConsoleWindow()
win.show()

View File

@@ -21,11 +21,11 @@
</property>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab_1">
<attribute name="title">
<string>BECDock</string>
<string>Dock - Pyqtgraph</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@@ -33,6 +33,16 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Dock - Qt Native</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="dock_alt_placeholder" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>BECFigure</string>

View File

@@ -0,0 +1,115 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Literal, Optional
from weakref import WeakValueDictionary
from pydantic import Field
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QGridLayout, QHBoxLayout, QVBoxLayout
from qtpy.QtWidgets import QDockWidget, QSizePolicy
from bec_widgets.cli.rpc_wigdet_handler import RPCWidgetHandler
from bec_widgets.utils import BECConnector, ConnectionConfig
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()

View File

@@ -0,0 +1,82 @@
from __future__ import annotations
from typing import Literal
from weakref import WeakValueDictionary
from pydantic import Field
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QMainWindow, QWidget
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
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