bec-widgets 1.25.1__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- .gitlab-ci.yml +3 -5
- CHANGELOG.md +631 -0
- PKG-INFO +3 -3
- bec_widgets/__init__.py +4 -0
- bec_widgets/applications/bw_launch.py +23 -0
- bec_widgets/applications/launch_window.py +430 -0
- bec_widgets/assets/app_icons/auto_update.png +0 -0
- bec_widgets/assets/app_icons/ui_loader_tile.png +0 -0
- bec_widgets/cli/__init__.py +0 -1
- bec_widgets/cli/client.py +1779 -2064
- bec_widgets/cli/client_utils.py +346 -174
- bec_widgets/cli/generate_cli.py +143 -37
- bec_widgets/cli/rpc/rpc_base.py +152 -21
- bec_widgets/cli/rpc/rpc_register.py +113 -6
- bec_widgets/cli/rpc/rpc_widget_handler.py +13 -11
- bec_widgets/cli/server.py +125 -239
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +97 -145
- bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +1 -1
- bec_widgets/utils/bec_connector.py +190 -21
- bec_widgets/utils/bec_designer.py +7 -0
- bec_widgets/utils/bec_dispatcher.py +71 -4
- bec_widgets/utils/bec_plugin_helper.py +89 -0
- bec_widgets/utils/bec_signal_proxy.py +1 -1
- bec_widgets/utils/bec_widget.py +26 -10
- bec_widgets/utils/colors.py +1 -1
- bec_widgets/{qt_utils → utils}/compact_popup.py +2 -0
- bec_widgets/utils/container_utils.py +37 -12
- bec_widgets/utils/crosshair.py +25 -8
- bec_widgets/utils/entry_validator.py +3 -1
- bec_widgets/{qt_utils → utils}/error_popups.py +18 -0
- bec_widgets/{qt_utils → utils}/expandable_frame.py +2 -2
- bec_widgets/utils/forms_from_types/forms.py +182 -0
- bec_widgets/{widgets/editors/scan_metadata/_metadata_widgets.py → utils/forms_from_types/items.py} +41 -30
- bec_widgets/utils/generate_designer_plugin.py +40 -36
- bec_widgets/utils/linear_region_selector.py +2 -0
- bec_widgets/utils/name_utils.py +16 -0
- bec_widgets/{qt_utils → utils}/palette_viewer.py +2 -2
- bec_widgets/utils/plot_indicator_items.py +2 -5
- bec_widgets/utils/plugin_utils.py +47 -1
- bec_widgets/{qt_utils → utils}/round_frame.py +14 -14
- bec_widgets/utils/rpc_server.py +277 -0
- bec_widgets/utils/serialization.py +44 -0
- bec_widgets/{qt_utils → utils}/settings_dialog.py +26 -1
- bec_widgets/{qt_utils → utils}/side_panel.py +17 -10
- bec_widgets/{qt_utils → utils}/toolbar.py +69 -25
- bec_widgets/utils/ui_loader.py +8 -8
- bec_widgets/utils/widget_io.py +166 -25
- bec_widgets/widgets/containers/auto_update/auto_updates.py +364 -0
- bec_widgets/widgets/containers/dock/dock.py +157 -49
- bec_widgets/widgets/containers/dock/dock_area.py +186 -138
- bec_widgets/widgets/containers/layout_manager/layout_manager.py +2 -1
- bec_widgets/widgets/containers/main_window/addons/web_links.py +15 -0
- bec_widgets/widgets/containers/main_window/main_window.py +189 -41
- bec_widgets/widgets/control/buttons/button_abort/button_abort.py +3 -4
- bec_widgets/widgets/control/buttons/button_reset/button_reset.py +3 -4
- bec_widgets/widgets/control/buttons/button_resume/button_resume.py +3 -3
- bec_widgets/widgets/control/buttons/stop_button/stop_button.py +18 -7
- bec_widgets/widgets/control/device_control/position_indicator/position_indicator.py +22 -3
- bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +31 -13
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +3 -1
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.ui +27 -4
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +5 -2
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +97 -31
- bec_widgets/widgets/control/device_control/positioner_box/positioner_control_line/positioner_control_line.ui +11 -4
- bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +2 -3
- bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +29 -4
- bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +1 -0
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +2 -2
- bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py +2 -2
- bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py +1 -2
- bec_widgets/widgets/control/device_input/signal_line_edit/signal_line_edit.py +1 -2
- bec_widgets/widgets/control/scan_control/scan_control.py +7 -5
- bec_widgets/widgets/control/scan_control/scan_group_box.py +28 -5
- bec_widgets/widgets/dap/dap_combo_box/dap_combo_box.py +1 -2
- bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog.py +3 -4
- bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui +14 -8
- bec_widgets/widgets/editors/console/console.py +1 -1
- bec_widgets/widgets/editors/{scan_metadata/additional_metadata_table.py → dict_backed_table.py} +29 -6
- bec_widgets/widgets/editors/scan_metadata/__init__.py +0 -7
- bec_widgets/widgets/editors/scan_metadata/_util.py +1 -1
- bec_widgets/widgets/{plots/motor_map/register_bec_motor_map_widget.py → editors/scan_metadata/register_scan_metadata.py} +2 -4
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.py +42 -136
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.pyproject +1 -0
- bec_widgets/widgets/{plots/multi_waveform/bec_multi_waveform_widget_plugin.py → editors/scan_metadata/scan_metadata_plugin.py} +9 -9
- bec_widgets/widgets/editors/text_box/text_box.py +2 -3
- bec_widgets/widgets/editors/website/website.py +2 -2
- bec_widgets/widgets/games/minesweeper.py +3 -2
- bec_widgets/widgets/plots/image/image.py +960 -0
- bec_widgets/widgets/plots/image/image.pyproject +1 -0
- bec_widgets/widgets/plots/image/image_item.py +279 -0
- bec_widgets/widgets/plots/{motor_map/bec_motor_map_widget_plugin.py → image/image_plugin.py} +11 -13
- bec_widgets/widgets/{containers/figure/plots → plots}/image/image_processor.py +31 -64
- bec_widgets/widgets/plots/image/{register_bec_image_widget.py → register_image.py} +2 -2
- bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +59 -0
- bec_widgets/widgets/plots/image/toolbar_bundles/processing.py +79 -0
- bec_widgets/widgets/plots/motor_map/motor_map.py +832 -0
- bec_widgets/widgets/plots/motor_map/motor_map.pyproject +1 -0
- bec_widgets/widgets/plots/motor_map/motor_map_plugin.py +54 -0
- bec_widgets/widgets/plots/{multi_waveform/register_bec_multi_waveform_widget.py → motor_map/register_motor_map.py} +2 -4
- bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.py +129 -0
- bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.ui +120 -0
- bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py +70 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform.py +508 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform.pyproject +1 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_plugin.py +54 -0
- bec_widgets/widgets/plots/multi_waveform/register_multi_waveform.py +15 -0
- bec_widgets/widgets/plots/multi_waveform/settings/control_panel.py +144 -0
- bec_widgets/widgets/plots/multi_waveform/settings/multi_waveform_controls.ui +164 -0
- bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py +65 -0
- bec_widgets/widgets/{plots_next_gen → plots}/plot_base.py +321 -40
- bec_widgets/widgets/plots/{waveform/register_bec_waveform_widget.py → scatter_waveform/register_scatter_waveform.py} +3 -3
- bec_widgets/widgets/plots/scatter_waveform/scatter_curve.py +197 -0
- bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py +553 -0
- bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.pyproject +1 -0
- bec_widgets/widgets/plots/{image/bec_image_widget_plugin.py → scatter_waveform/scatter_waveform_plugin.py} +9 -13
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py +138 -0
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_horizontal.ui +195 -0
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_vertical.ui +204 -0
- bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings.py +8 -8
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/mouse_interactions.py +4 -18
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/plot_export.py +14 -3
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/roi_bundle.py +6 -1
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/save_state.py +2 -2
- bec_widgets/widgets/{containers/figure/plots/waveform/waveform_curve.py → plots/waveform/curve.py} +119 -49
- bec_widgets/widgets/plots/waveform/register_waveform.py +15 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +125 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +576 -0
- bec_widgets/widgets/plots/waveform/utils/__init__.py +0 -0
- bec_widgets/widgets/plots/waveform/utils/roi_manager.py +84 -0
- bec_widgets/widgets/plots/waveform/waveform.py +1794 -0
- bec_widgets/widgets/plots/waveform/waveform.pyproject +1 -0
- bec_widgets/widgets/plots/waveform/{bec_waveform_widget_plugin.py → waveform_plugin.py} +9 -13
- bec_widgets/widgets/progress/bec_progressbar/bec_progressbar.py +1 -2
- bec_widgets/widgets/progress/ring_progress_bar/ring.py +11 -10
- bec_widgets/widgets/progress/ring_progress_bar/ring_progress_bar.py +24 -14
- bec_widgets/widgets/services/bec_queue/bec_queue.py +13 -11
- bec_widgets/widgets/services/bec_status_box/bec_status_box.py +3 -4
- bec_widgets/widgets/services/device_browser/device_browser.py +5 -2
- bec_widgets/widgets/services/device_browser/device_item/device_item.py +1 -1
- bec_widgets/widgets/utility/logpanel/logpanel.py +36 -17
- bec_widgets/widgets/utility/spinbox/decimal_spinbox.py +3 -3
- bec_widgets/widgets/utility/visual/color_button/color_button.py +1 -1
- bec_widgets/widgets/utility/visual/colormap_widget/colormap_widget.py +4 -6
- bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py +4 -8
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/METADATA +3 -3
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/RECORD +168 -153
- pyproject.toml +3 -3
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +0 -198
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +0 -615
- bec_widgets/applications/bec_app.py +0 -84
- bec_widgets/cli/auto_updates.py +0 -168
- bec_widgets/widgets/containers/figure/__init__.py +0 -1
- bec_widgets/widgets/containers/figure/figure.py +0 -796
- bec_widgets/widgets/containers/figure/plots/axis_settings.py +0 -91
- bec_widgets/widgets/containers/figure/plots/axis_settings.ui +0 -256
- bec_widgets/widgets/containers/figure/plots/image/image.py +0 -772
- bec_widgets/widgets/containers/figure/plots/image/image_item.py +0 -337
- bec_widgets/widgets/containers/figure/plots/motor_map/motor_map.py +0 -525
- bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py +0 -340
- bec_widgets/widgets/containers/figure/plots/plot_base.py +0 -505
- bec_widgets/widgets/containers/figure/plots/waveform/waveform.py +0 -1563
- bec_widgets/widgets/plots/image/bec_image_widget.pyproject +0 -1
- bec_widgets/widgets/plots/image/image_widget.py +0 -515
- bec_widgets/widgets/plots/motor_map/bec_motor_map_widget.pyproject +0 -1
- bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.py +0 -56
- bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.ui +0 -108
- bec_widgets/widgets/plots/motor_map/motor_map_widget.py +0 -234
- bec_widgets/widgets/plots/multi_waveform/bec_multi_waveform_widget.pyproject +0 -1
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_controls.ui +0 -99
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_widget.py +0 -536
- bec_widgets/widgets/plots/waveform/bec_waveform_widget.pyproject +0 -1
- bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py +0 -336
- bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui +0 -372
- bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +0 -25
- bec_widgets/widgets/plots/waveform/waveform_widget.py +0 -751
- /bec_widgets/{qt_utils → utils}/collapsible_panel_manager.py +0 -0
- /bec_widgets/{applications/alignment → utils/forms_from_types}/__init__.py +0 -0
- /bec_widgets/{qt_utils → utils}/redis_message_waiter.py +0 -0
- /bec_widgets/{applications/alignment/alignment_1d → widgets/containers/auto_update}/__init__.py +0 -0
- /bec_widgets/{qt_utils → widgets/containers/main_window/addons}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots → plots/image/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/image → plots/motor_map/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/motor_map → plots/motor_map/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/multi_waveform → plots/multi_waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/waveform → plots/multi_waveform/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{motor_map/motor_map_dialog → scatter_waveform}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups → scatter_waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups/curve_dialog → setting_menus}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_horizontal.ui +0 -0
- /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_vertical.ui +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups/dap_summary_dialog → toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen/setting_menus → plots/waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen/toolbar_bundles → plots/waveform/settings/curve_settings}/__init__.py +0 -0
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,36 +3,43 @@ from __future__ import annotations
|
|
3
3
|
from typing import Literal, Optional
|
4
4
|
from weakref import WeakValueDictionary
|
5
5
|
|
6
|
-
from bec_lib.
|
6
|
+
from bec_lib.logger import bec_logger
|
7
7
|
from pydantic import Field
|
8
8
|
from pyqtgraph.dockarea.DockArea import DockArea
|
9
9
|
from qtpy.QtCore import QSize, Qt
|
10
10
|
from qtpy.QtGui import QPainter, QPaintEvent
|
11
11
|
from qtpy.QtWidgets import QApplication, QSizePolicy, QVBoxLayout, QWidget
|
12
12
|
|
13
|
-
from bec_widgets.
|
14
|
-
from bec_widgets.
|
13
|
+
from bec_widgets.cli.rpc.rpc_register import RPCRegister
|
14
|
+
from bec_widgets.utils import ConnectionConfig, WidgetContainerUtils
|
15
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
16
|
+
from bec_widgets.utils.error_popups import SafeSlot
|
17
|
+
from bec_widgets.utils.name_utils import pascal_to_snake
|
18
|
+
from bec_widgets.utils.toolbar import (
|
15
19
|
ExpandableMenuAction,
|
16
20
|
MaterialIconAction,
|
17
21
|
ModularToolBar,
|
18
22
|
SeparatorAction,
|
19
23
|
)
|
20
|
-
from bec_widgets.utils import
|
21
|
-
from bec_widgets.utils.bec_widget import BECWidget
|
24
|
+
from bec_widgets.utils.widget_io import WidgetHierarchy
|
22
25
|
from bec_widgets.widgets.containers.dock.dock import BECDock, DockConfig
|
26
|
+
from bec_widgets.widgets.containers.main_window.main_window import BECMainWindow
|
23
27
|
from bec_widgets.widgets.control.device_control.positioner_box import PositionerBox
|
24
28
|
from bec_widgets.widgets.control.scan_control.scan_control import ScanControl
|
25
29
|
from bec_widgets.widgets.editors.vscode.vscode import VSCodeEditor
|
26
|
-
from bec_widgets.widgets.plots.image.
|
27
|
-
from bec_widgets.widgets.plots.motor_map.
|
28
|
-
from bec_widgets.widgets.plots.multi_waveform.
|
29
|
-
from bec_widgets.widgets.plots.
|
30
|
+
from bec_widgets.widgets.plots.image.image import Image
|
31
|
+
from bec_widgets.widgets.plots.motor_map.motor_map import MotorMap
|
32
|
+
from bec_widgets.widgets.plots.multi_waveform.multi_waveform import MultiWaveform
|
33
|
+
from bec_widgets.widgets.plots.scatter_waveform.scatter_waveform import ScatterWaveform
|
34
|
+
from bec_widgets.widgets.plots.waveform.waveform import Waveform
|
30
35
|
from bec_widgets.widgets.progress.ring_progress_bar.ring_progress_bar import RingProgressBar
|
31
36
|
from bec_widgets.widgets.services.bec_queue.bec_queue import BECQueue
|
32
37
|
from bec_widgets.widgets.services.bec_status_box.bec_status_box import BECStatusBox
|
33
38
|
from bec_widgets.widgets.utility.logpanel.logpanel import LogPanel
|
34
39
|
from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import DarkModeButton
|
35
40
|
|
41
|
+
logger = bec_logger.logger
|
42
|
+
|
36
43
|
|
37
44
|
class DockAreaConfig(ConnectionConfig):
|
38
45
|
docks: dict[str, DockConfig] = Field({}, description="The docks in the dock area.")
|
@@ -42,23 +49,27 @@ class DockAreaConfig(ConnectionConfig):
|
|
42
49
|
|
43
50
|
|
44
51
|
class BECDockArea(BECWidget, QWidget):
|
52
|
+
"""
|
53
|
+
Container for other widgets. Widgets can be added to the dock area and arranged in a grid layout.
|
54
|
+
"""
|
55
|
+
|
45
56
|
PLUGIN = True
|
46
57
|
USER_ACCESS = [
|
58
|
+
"_rpc_id",
|
47
59
|
"_config_dict",
|
48
|
-
"selected_device",
|
49
|
-
"panels",
|
50
|
-
"save_state",
|
51
|
-
"remove_dock",
|
52
|
-
"restore_state",
|
53
|
-
"add_dock",
|
54
|
-
"clear_all",
|
55
|
-
"detach_dock",
|
56
|
-
"attach_all",
|
57
60
|
"_get_all_rpc",
|
58
|
-
"
|
61
|
+
"new",
|
59
62
|
"show",
|
60
63
|
"hide",
|
64
|
+
"panels",
|
65
|
+
"panel_list",
|
61
66
|
"delete",
|
67
|
+
"delete_all",
|
68
|
+
"remove",
|
69
|
+
"detach_dock",
|
70
|
+
"attach_all",
|
71
|
+
"save_state",
|
72
|
+
"restore_state",
|
62
73
|
]
|
63
74
|
|
64
75
|
def __init__(
|
@@ -67,6 +78,8 @@ class BECDockArea(BECWidget, QWidget):
|
|
67
78
|
config: DockAreaConfig | None = None,
|
68
79
|
client=None,
|
69
80
|
gui_id: str = None,
|
81
|
+
object_name: str = None,
|
82
|
+
**kwargs,
|
70
83
|
) -> None:
|
71
84
|
if config is None:
|
72
85
|
config = DockAreaConfig(widget_class=self.__class__.__name__)
|
@@ -74,37 +87,47 @@ class BECDockArea(BECWidget, QWidget):
|
|
74
87
|
if isinstance(config, dict):
|
75
88
|
config = DockAreaConfig(**config)
|
76
89
|
self.config = config
|
77
|
-
super().__init__(
|
78
|
-
|
90
|
+
super().__init__(
|
91
|
+
parent=parent,
|
92
|
+
object_name=object_name,
|
93
|
+
client=client,
|
94
|
+
gui_id=gui_id,
|
95
|
+
config=config,
|
96
|
+
**kwargs,
|
97
|
+
)
|
98
|
+
self._parent = parent # TODO probably not needed
|
79
99
|
self.layout = QVBoxLayout(self)
|
80
100
|
self.layout.setSpacing(5)
|
81
101
|
self.layout.setContentsMargins(0, 0, 0, 0)
|
82
102
|
|
83
103
|
self._instructions_visible = True
|
84
104
|
|
85
|
-
self.
|
105
|
+
self.dark_mode_button = DarkModeButton(parent=self, toolbar=True)
|
106
|
+
self.dock_area = DockArea(parent=self)
|
86
107
|
self.toolbar = ModularToolBar(
|
108
|
+
parent=self,
|
87
109
|
actions={
|
88
110
|
"menu_plots": ExpandableMenuAction(
|
89
111
|
label="Add Plot ",
|
90
112
|
actions={
|
91
113
|
"waveform": MaterialIconAction(
|
92
|
-
icon_name=
|
93
|
-
|
114
|
+
icon_name=Waveform.ICON_NAME, tooltip="Add Waveform", filled=True
|
115
|
+
),
|
116
|
+
"scatter_waveform": MaterialIconAction(
|
117
|
+
icon_name=ScatterWaveform.ICON_NAME,
|
118
|
+
tooltip="Add Scatter Waveform",
|
94
119
|
filled=True,
|
95
120
|
),
|
96
121
|
"multi_waveform": MaterialIconAction(
|
97
|
-
icon_name=
|
122
|
+
icon_name=MultiWaveform.ICON_NAME,
|
98
123
|
tooltip="Add Multi Waveform",
|
99
124
|
filled=True,
|
100
125
|
),
|
101
126
|
"image": MaterialIconAction(
|
102
|
-
icon_name=
|
127
|
+
icon_name=Image.ICON_NAME, tooltip="Add Image", filled=True
|
103
128
|
),
|
104
129
|
"motor_map": MaterialIconAction(
|
105
|
-
icon_name=
|
106
|
-
tooltip="Add Motor Map",
|
107
|
-
filled=True,
|
130
|
+
icon_name=MotorMap.ICON_NAME, tooltip="Add Motor Map", filled=True
|
108
131
|
),
|
109
132
|
},
|
110
133
|
),
|
@@ -159,10 +182,10 @@ class BECDockArea(BECWidget, QWidget):
|
|
159
182
|
|
160
183
|
self.layout.addWidget(self.toolbar)
|
161
184
|
self.layout.addWidget(self.dock_area)
|
162
|
-
self.spacer = QWidget()
|
185
|
+
self.spacer = QWidget(parent=self)
|
163
186
|
self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
164
187
|
self.toolbar.addWidget(self.spacer)
|
165
|
-
self.toolbar.addWidget(
|
188
|
+
self.toolbar.addWidget(self.dark_mode_button)
|
166
189
|
self._hook_toolbar()
|
167
190
|
|
168
191
|
def minimumSizeHint(self):
|
@@ -171,41 +194,44 @@ class BECDockArea(BECWidget, QWidget):
|
|
171
194
|
def _hook_toolbar(self):
|
172
195
|
# Menu Plot
|
173
196
|
self.toolbar.widgets["menu_plots"].widgets["waveform"].triggered.connect(
|
174
|
-
lambda: self.
|
197
|
+
lambda: self._create_widget_from_toolbar(widget_name="Waveform")
|
198
|
+
)
|
199
|
+
self.toolbar.widgets["menu_plots"].widgets["scatter_waveform"].triggered.connect(
|
200
|
+
lambda: self._create_widget_from_toolbar(widget_name="ScatterWaveform")
|
175
201
|
)
|
176
202
|
self.toolbar.widgets["menu_plots"].widgets["multi_waveform"].triggered.connect(
|
177
|
-
lambda: self.
|
203
|
+
lambda: self._create_widget_from_toolbar(widget_name="MultiWaveform")
|
178
204
|
)
|
179
205
|
self.toolbar.widgets["menu_plots"].widgets["image"].triggered.connect(
|
180
|
-
lambda: self.
|
206
|
+
lambda: self._create_widget_from_toolbar(widget_name="Image")
|
181
207
|
)
|
182
208
|
self.toolbar.widgets["menu_plots"].widgets["motor_map"].triggered.connect(
|
183
|
-
lambda: self.
|
209
|
+
lambda: self._create_widget_from_toolbar(widget_name="MotorMap")
|
184
210
|
)
|
185
211
|
|
186
212
|
# Menu Devices
|
187
213
|
self.toolbar.widgets["menu_devices"].widgets["scan_control"].triggered.connect(
|
188
|
-
lambda: self.
|
214
|
+
lambda: self._create_widget_from_toolbar(widget_name="ScanControl")
|
189
215
|
)
|
190
216
|
self.toolbar.widgets["menu_devices"].widgets["positioner_box"].triggered.connect(
|
191
|
-
lambda: self.
|
217
|
+
lambda: self._create_widget_from_toolbar(widget_name="PositionerBox")
|
192
218
|
)
|
193
219
|
|
194
220
|
# Menu Utils
|
195
221
|
self.toolbar.widgets["menu_utils"].widgets["queue"].triggered.connect(
|
196
|
-
lambda: self.
|
222
|
+
lambda: self._create_widget_from_toolbar(widget_name="BECQueue")
|
197
223
|
)
|
198
224
|
self.toolbar.widgets["menu_utils"].widgets["status"].triggered.connect(
|
199
|
-
lambda: self.
|
225
|
+
lambda: self._create_widget_from_toolbar(widget_name="BECStatusBox")
|
200
226
|
)
|
201
227
|
self.toolbar.widgets["menu_utils"].widgets["vs_code"].triggered.connect(
|
202
|
-
lambda: self.
|
228
|
+
lambda: self._create_widget_from_toolbar(widget_name="VSCodeEditor")
|
203
229
|
)
|
204
230
|
self.toolbar.widgets["menu_utils"].widgets["progress_bar"].triggered.connect(
|
205
|
-
lambda: self.
|
231
|
+
lambda: self._create_widget_from_toolbar(widget_name="RingProgressBar")
|
206
232
|
)
|
207
233
|
self.toolbar.widgets["menu_utils"].widgets["log_panel"].triggered.connect(
|
208
|
-
lambda: self.
|
234
|
+
lambda: self._create_widget_from_toolbar(widget_name="LogPanel")
|
209
235
|
)
|
210
236
|
|
211
237
|
# Icons
|
@@ -213,6 +239,14 @@ class BECDockArea(BECWidget, QWidget):
|
|
213
239
|
self.toolbar.widgets["save_state"].action.triggered.connect(self.save_state)
|
214
240
|
self.toolbar.widgets["restore_state"].action.triggered.connect(self.restore_state)
|
215
241
|
|
242
|
+
@SafeSlot()
|
243
|
+
def _create_widget_from_toolbar(self, widget_name: str) -> None:
|
244
|
+
# Run with RPC broadcast to namespace of all widgets
|
245
|
+
with RPCRegister.delayed_broadcast():
|
246
|
+
name = pascal_to_snake(widget_name)
|
247
|
+
dock_name = WidgetContainerUtils.generate_unique_name(name, self.panels.keys())
|
248
|
+
self.new(name=dock_name, widget=widget_name)
|
249
|
+
|
216
250
|
def paintEvent(self, event: QPaintEvent): # TODO decide if we want any default instructions
|
217
251
|
super().paintEvent(event)
|
218
252
|
if self._instructions_visible:
|
@@ -220,20 +254,9 @@ class BECDockArea(BECWidget, QWidget):
|
|
220
254
|
painter.drawText(
|
221
255
|
self.rect(),
|
222
256
|
Qt.AlignCenter,
|
223
|
-
"Add docks using '
|
257
|
+
"Add docks using 'new' method from CLI\n or \n Add widget docks using the toolbar",
|
224
258
|
)
|
225
259
|
|
226
|
-
@property
|
227
|
-
def selected_device(self) -> str:
|
228
|
-
gui_id = QApplication.instance().gui_id
|
229
|
-
auto_update_config = self.client.connector.get(
|
230
|
-
MessageEndpoints.gui_auto_update_config(gui_id)
|
231
|
-
)
|
232
|
-
try:
|
233
|
-
return auto_update_config.selected_device
|
234
|
-
except AttributeError:
|
235
|
-
return None
|
236
|
-
|
237
260
|
@property
|
238
261
|
def panels(self) -> dict[str, BECDock]:
|
239
262
|
"""
|
@@ -245,7 +268,17 @@ class BECDockArea(BECWidget, QWidget):
|
|
245
268
|
|
246
269
|
@panels.setter
|
247
270
|
def panels(self, value: dict[str, BECDock]):
|
248
|
-
self.dock_area.docks = WeakValueDictionary(value)
|
271
|
+
self.dock_area.docks = WeakValueDictionary(value) # This can not work can it?
|
272
|
+
|
273
|
+
@property
|
274
|
+
def panel_list(self) -> list[BECDock]:
|
275
|
+
"""
|
276
|
+
Get the docks in the dock area.
|
277
|
+
|
278
|
+
Returns:
|
279
|
+
list: The docks in the dock area.
|
280
|
+
"""
|
281
|
+
return list(self.dock_area.docks.values())
|
249
282
|
|
250
283
|
@property
|
251
284
|
def temp_areas(self) -> list:
|
@@ -289,36 +322,17 @@ class BECDockArea(BECWidget, QWidget):
|
|
289
322
|
self.config.docks_state = last_state
|
290
323
|
return last_state
|
291
324
|
|
292
|
-
def remove_dock(self, name: str):
|
293
|
-
"""
|
294
|
-
Remove a dock by name and ensure it is properly closed and cleaned up.
|
295
|
-
|
296
|
-
Args:
|
297
|
-
name(str): The name of the dock to remove.
|
298
|
-
"""
|
299
|
-
dock = self.dock_area.docks.pop(name, None)
|
300
|
-
self.config.docks.pop(name, None)
|
301
|
-
if dock:
|
302
|
-
dock.close()
|
303
|
-
dock.deleteLater()
|
304
|
-
if len(self.dock_area.docks) <= 1:
|
305
|
-
for dock in self.dock_area.docks.values():
|
306
|
-
dock.hide_title_bar()
|
307
|
-
|
308
|
-
else:
|
309
|
-
raise ValueError(f"Dock with name {name} does not exist.")
|
310
|
-
|
311
325
|
@SafeSlot(popup_error=True)
|
312
|
-
def
|
326
|
+
def new(
|
313
327
|
self,
|
314
|
-
name: str = None,
|
315
|
-
|
328
|
+
name: str | None = None,
|
329
|
+
widget: str | QWidget | None = None,
|
330
|
+
widget_name: str | None = None,
|
331
|
+
position: Literal["bottom", "top", "left", "right", "above", "below"] = "bottom",
|
316
332
|
relative_to: BECDock | None = None,
|
317
333
|
closable: bool = True,
|
318
334
|
floating: bool = False,
|
319
|
-
|
320
|
-
widget: str | QWidget | None = None,
|
321
|
-
row: int = None,
|
335
|
+
row: int | None = None,
|
322
336
|
col: int = 0,
|
323
337
|
rowspan: int = 1,
|
324
338
|
colspan: int = 1,
|
@@ -328,12 +342,11 @@ class BECDockArea(BECWidget, QWidget):
|
|
328
342
|
|
329
343
|
Args:
|
330
344
|
name(str): The name of the dock to be displayed and for further references. Has to be unique.
|
345
|
+
widget(str|QWidget|None): The widget to be added to the dock. While using RPC, only BEC RPC widgets from RPCWidgetHandler are allowed.
|
331
346
|
position(Literal["bottom", "top", "left", "right", "above", "below"]): The position of the dock.
|
332
347
|
relative_to(BECDock): The dock to which the new dock should be added relative to.
|
333
348
|
closable(bool): Whether the dock is closable.
|
334
349
|
floating(bool): Whether the dock is detached after creating.
|
335
|
-
prefix(str): The prefix for the dock name if no name is provided.
|
336
|
-
widget(str|QWidget|None): The widget to be added to the dock. While using RPC, only BEC RPC widgets from RPCWidgetHandler are allowed.
|
337
350
|
row(int): The row of the added widget.
|
338
351
|
col(int): The column of the added widget.
|
339
352
|
rowspan(int): The rowspan of the added widget.
|
@@ -342,21 +355,33 @@ class BECDockArea(BECWidget, QWidget):
|
|
342
355
|
Returns:
|
343
356
|
BECDock: The created dock.
|
344
357
|
"""
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
358
|
+
dock_names = [
|
359
|
+
dock.object_name for dock in self.panel_list
|
360
|
+
] # pylint: disable=protected-access
|
361
|
+
if name is not None: # Name is provided
|
362
|
+
if name in dock_names:
|
363
|
+
raise ValueError(
|
364
|
+
f"Name {name} must be unique for docks, but already exists in DockArea "
|
365
|
+
f"with name: {self.object_name} and id {self.gui_id}."
|
366
|
+
)
|
367
|
+
if not WidgetContainerUtils.has_name_valid_chars(name):
|
368
|
+
raise ValueError(
|
369
|
+
f"Name {name} contains invalid characters. "
|
370
|
+
f"Only alphanumeric characters and underscores are allowed."
|
371
|
+
)
|
372
|
+
else: # Name is not provided
|
373
|
+
name = WidgetContainerUtils.generate_unique_name(name="dock", list_of_names=dock_names)
|
374
|
+
|
375
|
+
dock = BECDock(
|
376
|
+
parent=self,
|
377
|
+
name=name, # this is dock name pyqtgraph property, this is displayed on label
|
378
|
+
object_name=name, # this is a real qt object name passed to BECConnector
|
379
|
+
parent_dock_area=self,
|
380
|
+
closable=closable,
|
381
|
+
)
|
357
382
|
dock.config.position = position
|
358
|
-
self.config.docks[name] = dock.config
|
359
|
-
|
383
|
+
self.config.docks[dock.name()] = dock.config
|
384
|
+
# The dock.name is equal to the name passed to BECDock
|
360
385
|
self.dock_area.addDock(dock=dock, position=position, relativeTo=relative_to)
|
361
386
|
|
362
387
|
if len(self.dock_area.docks) <= 1:
|
@@ -365,10 +390,11 @@ class BECDockArea(BECWidget, QWidget):
|
|
365
390
|
for dock in self.dock_area.docks.values():
|
366
391
|
dock.show_title_bar()
|
367
392
|
|
368
|
-
if widget is not None
|
369
|
-
|
370
|
-
|
371
|
-
|
393
|
+
if widget is not None:
|
394
|
+
# Check if widget name exists.
|
395
|
+
dock.new(
|
396
|
+
widget=widget, name=widget_name, row=row, col=col, rowspan=rowspan, colspan=colspan
|
397
|
+
)
|
372
398
|
if (
|
373
399
|
self._instructions_visible
|
374
400
|
): # TODO still decide how initial instructions should be handled
|
@@ -406,49 +432,24 @@ class BECDockArea(BECWidget, QWidget):
|
|
406
432
|
Remove a temporary area from the dock area.
|
407
433
|
This is a patched method of pyqtgraph's removeTempArea
|
408
434
|
"""
|
435
|
+
if area not in self.dock_area.tempAreas:
|
436
|
+
# FIXME add some context for the logging, I am not sure which object is passed.
|
437
|
+
# It looks like a pyqtgraph.DockArea
|
438
|
+
logger.info(f"Attempted to remove dock_area, but was not floating.")
|
439
|
+
return
|
409
440
|
self.dock_area.tempAreas.remove(area)
|
410
441
|
area.window().close()
|
411
442
|
area.window().deleteLater()
|
412
443
|
|
413
|
-
def clear_all(self):
|
414
|
-
"""
|
415
|
-
Close all docks and remove all temp areas.
|
416
|
-
"""
|
417
|
-
self.attach_all()
|
418
|
-
for dock in dict(self.dock_area.docks).values():
|
419
|
-
dock.remove()
|
420
|
-
self.dock_area.docks.clear()
|
421
|
-
|
422
444
|
def cleanup(self):
|
423
445
|
"""
|
424
446
|
Cleanup the dock area.
|
425
447
|
"""
|
426
|
-
self.
|
427
|
-
self.
|
428
|
-
self.
|
429
|
-
self.dock_area.close()
|
430
|
-
self.dock_area.deleteLater()
|
448
|
+
self.delete_all()
|
449
|
+
self.dark_mode_button.close()
|
450
|
+
self.dark_mode_button.deleteLater()
|
431
451
|
super().cleanup()
|
432
452
|
|
433
|
-
def closeEvent(self, event):
|
434
|
-
if self.parent() is None:
|
435
|
-
# we are at top-level (independent window)
|
436
|
-
if self.isVisible():
|
437
|
-
# we are visible => user clicked on [X]
|
438
|
-
# (when closeEvent is called from shutdown procedure,
|
439
|
-
# everything is hidden first)
|
440
|
-
# so, let's ignore "close", and do hide instead
|
441
|
-
event.ignore()
|
442
|
-
self.setVisible(False)
|
443
|
-
|
444
|
-
def close(self):
|
445
|
-
"""
|
446
|
-
Close the dock area and cleanup.
|
447
|
-
Has to be implemented to overwrite pyqtgraph event accept in Container close.
|
448
|
-
"""
|
449
|
-
self.cleanup()
|
450
|
-
super().close()
|
451
|
-
|
452
453
|
def show(self):
|
453
454
|
"""Show all windows including floating docks."""
|
454
455
|
super().show()
|
@@ -467,18 +468,65 @@ class BECDockArea(BECWidget, QWidget):
|
|
467
468
|
continue
|
468
469
|
docks.window().hide()
|
469
470
|
|
470
|
-
def
|
471
|
-
|
472
|
-
|
471
|
+
def delete_all(self) -> None:
|
472
|
+
"""
|
473
|
+
Delete all docks.
|
474
|
+
"""
|
475
|
+
self.attach_all()
|
476
|
+
for dock_name in self.panels.keys():
|
477
|
+
self.delete(dock_name)
|
478
|
+
|
479
|
+
def delete(self, dock_name: str):
|
480
|
+
"""
|
481
|
+
Delete a dock by name.
|
482
|
+
|
483
|
+
Args:
|
484
|
+
dock_name(str): The name of the dock to delete.
|
485
|
+
"""
|
486
|
+
dock = self.dock_area.docks.pop(dock_name, None)
|
487
|
+
self.config.docks.pop(dock_name, None)
|
488
|
+
if dock:
|
489
|
+
dock.close()
|
490
|
+
dock.deleteLater()
|
491
|
+
if len(self.dock_area.docks) <= 1:
|
492
|
+
for dock in self.dock_area.docks.values():
|
493
|
+
dock.hide_title_bar()
|
494
|
+
else:
|
495
|
+
raise ValueError(f"Dock with name {dock_name} does not exist.")
|
496
|
+
# self._broadcast_update()
|
497
|
+
|
498
|
+
def remove(self) -> None:
|
499
|
+
"""
|
500
|
+
Remove the dock area. If the dock area is embedded in a BECMainWindow and
|
501
|
+
is set as the central widget, the main window will be closed.
|
502
|
+
"""
|
503
|
+
parent = self.parent()
|
504
|
+
if isinstance(parent, BECMainWindow):
|
505
|
+
central_widget = parent.centralWidget()
|
506
|
+
if central_widget is self:
|
507
|
+
# Closing the parent will also close the dock area
|
508
|
+
parent.close()
|
509
|
+
return
|
510
|
+
|
511
|
+
self.close()
|
512
|
+
|
473
513
|
|
514
|
+
if __name__ == "__main__": # pragma: no cover
|
474
515
|
|
475
|
-
|
476
|
-
from qtpy.QtWidgets import QApplication
|
516
|
+
import sys
|
477
517
|
|
478
518
|
from bec_widgets.utils.colors import set_theme
|
479
519
|
|
480
520
|
app = QApplication([])
|
481
521
|
set_theme("auto")
|
482
522
|
dock_area = BECDockArea()
|
523
|
+
dock_1 = dock_area.new(name="dock_0", widget="DarkModeButton")
|
524
|
+
dock_1.new(widget="DarkModeButton")
|
525
|
+
# dock_1 = dock_area.new(name="dock_0", widget="Waveform")
|
526
|
+
dock_area.new(widget="DarkModeButton")
|
483
527
|
dock_area.show()
|
528
|
+
dock_area.setGeometry(100, 100, 800, 600)
|
529
|
+
app.topLevelWidgets()
|
530
|
+
WidgetHierarchy.print_becconnector_hierarchy_from_app()
|
484
531
|
app.exec_()
|
532
|
+
sys.exit(app.exec_())
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import math
|
2
4
|
import sys
|
3
5
|
from typing import Dict, Literal, Optional, Set, Tuple, Union
|
@@ -34,7 +36,6 @@ class LayoutManagerWidget(QWidget):
|
|
34
36
|
|
35
37
|
def __init__(self, parent=None, auto_reindex=True):
|
36
38
|
super().__init__(parent)
|
37
|
-
self.setObjectName("LayoutManagerWidget")
|
38
39
|
self.layout = QGridLayout(self)
|
39
40
|
self.auto_reindex = auto_reindex
|
40
41
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import webbrowser
|
2
|
+
|
3
|
+
|
4
|
+
class BECWebLinksMixin:
|
5
|
+
@staticmethod
|
6
|
+
def open_bec_docs():
|
7
|
+
webbrowser.open("https://beamline-experiment-control.readthedocs.io/en/latest/")
|
8
|
+
|
9
|
+
@staticmethod
|
10
|
+
def open_bec_widgets_docs():
|
11
|
+
webbrowser.open("https://bec.readthedocs.io/projects/bec-widgets/en/latest/")
|
12
|
+
|
13
|
+
@staticmethod
|
14
|
+
def open_bec_bug_report():
|
15
|
+
webbrowser.open("https://gitlab.psi.ch/groups/bec/-/issues/")
|