1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-05-11 01:02:17 +02:00

Compare commits

...

4 Commits

6 changed files with 244 additions and 25 deletions
@@ -2,6 +2,8 @@ import os
import numpy as np import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
import qdarktheme
from PyQt6.QtWidgets import QPushButton, QTextEdit
from pyqtgraph.Qt import QtWidgets, 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
@@ -13,6 +15,7 @@ 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 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 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") self.console.set_default_style("linux")
def _init_figure(self): 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.motor_map("samx", "samy")
self.figure.image("eiger", color_map="viridis", vrange=(0, 100)) 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() self.c1 = self.w1.get_config()
def _init_dock(self): 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.d0 = self.dock.add_dock(name="dock_0")
self.label_3 = QtWidgets.QLabel("Label above figure") 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 = self.dock.add_dock(name="dock_1", position="right")
self.d1.addWidget(self.label_2) self.fig1 = self.d1.add_widget_bec("BECFigure")
self.d2 = self.dock.add_dock(widget=self.bar, position="right") self.fig1.plot("samx", "bpm4i")
self.d3 = self.dock.add_dock(name="figure") self.fig1.plot("samx", "bpm3a")
self.fig_dock3 = BECFigure()
self.fig_dock3.plot(x_name="samx", y_name="bpm4d") self.d2 = self.dock.add_dock(name="dock_2", position="bottom")
self.d3.add_widget(self.label_3) self.fi2 = self.d2.add_widget_bec("BECFigure", row=0, col=0)
self.d3.add_widget(self.button_3) self.fi2.motor_map("samx", "samy")
self.d3.add_widget(self.fig_dock3) self.bar2 = self.d2.add_widget_bec("SpiralProgressBar", row=0, col=1)
self.bar2.set_diameter(200)
self.dock.save_state() 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): def closeEvent(self, event):
"""Override to handle things when main window is closed.""" """Override to handle things when main window is closed."""
self.dock.cleanup() self.dock.cleanup()
@@ -144,10 +159,6 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
if __name__ == "__main__": # pragma: no cover if __name__ == "__main__": # pragma: no cover
import sys import sys
import bec_widgets
module_path = os.path.dirname(bec_widgets.__file__)
bec_dispatcher = BECDispatcher() bec_dispatcher = BECDispatcher()
client = bec_dispatcher.client client = bec_dispatcher.client
client.start() client.start()
@@ -155,8 +166,9 @@ 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") app.setApplicationDisplayName("Jupyter Console")
qdarktheme.setup_theme("auto")
icon = QIcon() 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) app.setWindowIcon(icon)
win = JupyterConsoleWindow() win = JupyterConsoleWindow()
win.show() win.show()
@@ -21,11 +21,11 @@
</property> </property>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="tab_1"> <widget class="QWidget" name="tab_1">
<attribute name="title"> <attribute name="title">
<string>BECDock</string> <string>Dock - Pyqtgraph</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
@@ -33,6 +33,16 @@
</item> </item>
</layout> </layout>
</widget> </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"> <widget class="QWidget" name="tab_2">
<attribute name="title"> <attribute name="title">
<string>BECFigure</string> <string>BECFigure</string>
+115
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()
@@ -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