bec-widgets 1.25.0__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 +11 -6
- CHANGELOG.md +650 -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 +37 -18
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +28 -4
- 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/spinner/spinner.py +2 -2
- 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.0.dist-info → bec_widgets-2.0.0.dist-info}/METADATA +3 -3
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/RECORD +169 -154
- 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.0.dist-info → bec_widgets-2.0.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/licenses/LICENSE +0 -0
PKG-INFO
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bec_widgets
|
3
|
-
Version:
|
3
|
+
Version: 2.0.0
|
4
4
|
Summary: BEC Widgets
|
5
5
|
Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
|
6
6
|
Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
|
@@ -10,13 +10,13 @@ Classifier: Programming Language :: Python :: 3
|
|
10
10
|
Classifier: Topic :: Scientific/Engineering
|
11
11
|
Requires-Python: >=3.10
|
12
12
|
Requires-Dist: bec-ipython-client<=4.0,>=2.21.4
|
13
|
-
Requires-Dist: bec-lib<=4.0,>=
|
13
|
+
Requires-Dist: bec-lib<=4.0,>=3.29
|
14
14
|
Requires-Dist: bec-qthemes>=0.7,~=0.7
|
15
15
|
Requires-Dist: black~=24.0
|
16
16
|
Requires-Dist: isort>=5.13.2,~=5.13
|
17
17
|
Requires-Dist: pydantic~=2.0
|
18
18
|
Requires-Dist: pyqtgraph~=0.13
|
19
|
-
Requires-Dist: pyside6
|
19
|
+
Requires-Dist: pyside6~=6.8.2
|
20
20
|
Requires-Dist: pyte
|
21
21
|
Requires-Dist: qtconsole>=5.5.1,~=5.5
|
22
22
|
Requires-Dist: qtpy~=2.4
|
bec_widgets/__init__.py
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from bec_widgets.widgets.containers.auto_update.auto_updates import AutoUpdates
|
4
|
+
from bec_widgets.widgets.containers.dock.dock_area import BECDockArea
|
5
|
+
|
6
|
+
|
7
|
+
def dock_area(object_name: str | None = None) -> BECDockArea:
|
8
|
+
_dock_area = BECDockArea(object_name=object_name, root_widget=True)
|
9
|
+
return _dock_area
|
10
|
+
|
11
|
+
|
12
|
+
def auto_update_dock_area(object_name: str | None = None) -> AutoUpdates:
|
13
|
+
"""
|
14
|
+
Create a dock area with auto update enabled.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
object_name(str): The name of the dock area.
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
BECDockArea: The created dock area.
|
21
|
+
"""
|
22
|
+
_auto_update = AutoUpdates(object_name=object_name)
|
23
|
+
return _auto_update
|
@@ -0,0 +1,430 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import os
|
4
|
+
import xml.etree.ElementTree as ET
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from bec_lib.logger import bec_logger
|
8
|
+
from qtpy.QtCore import Qt, Signal
|
9
|
+
from qtpy.QtGui import QPainter, QPainterPath, QPixmap
|
10
|
+
from qtpy.QtWidgets import (
|
11
|
+
QApplication,
|
12
|
+
QComboBox,
|
13
|
+
QFileDialog,
|
14
|
+
QHBoxLayout,
|
15
|
+
QLabel,
|
16
|
+
QPushButton,
|
17
|
+
QSizePolicy,
|
18
|
+
QSpacerItem,
|
19
|
+
QWidget,
|
20
|
+
)
|
21
|
+
|
22
|
+
import bec_widgets
|
23
|
+
from bec_widgets.cli.rpc.rpc_register import RPCRegister
|
24
|
+
from bec_widgets.utils.container_utils import WidgetContainerUtils
|
25
|
+
from bec_widgets.utils.error_popups import SafeSlot
|
26
|
+
from bec_widgets.utils.plugin_utils import get_plugin_auto_updates
|
27
|
+
from bec_widgets.utils.round_frame import RoundedFrame
|
28
|
+
from bec_widgets.utils.toolbar import ModularToolBar
|
29
|
+
from bec_widgets.utils.ui_loader import UILoader
|
30
|
+
from bec_widgets.widgets.containers.auto_update.auto_updates import AutoUpdates
|
31
|
+
from bec_widgets.widgets.containers.dock.dock_area import BECDockArea
|
32
|
+
from bec_widgets.widgets.containers.main_window.main_window import BECMainWindow, UILaunchWindow
|
33
|
+
from bec_widgets.widgets.utility.visual.dark_mode_button.dark_mode_button import DarkModeButton
|
34
|
+
|
35
|
+
if TYPE_CHECKING: # pragma: no cover
|
36
|
+
from qtpy.QtCore import QObject
|
37
|
+
|
38
|
+
logger = bec_logger.logger
|
39
|
+
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
40
|
+
|
41
|
+
|
42
|
+
class LaunchTile(RoundedFrame):
|
43
|
+
open_signal = Signal()
|
44
|
+
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
parent: QObject | None = None,
|
48
|
+
icon_path: str | None = None,
|
49
|
+
top_label: str | None = None,
|
50
|
+
main_label: str | None = None,
|
51
|
+
description: str | None = None,
|
52
|
+
show_selector: bool = False,
|
53
|
+
):
|
54
|
+
super().__init__(parent=parent, orientation="vertical")
|
55
|
+
|
56
|
+
self.icon_label = QLabel(parent=self)
|
57
|
+
self.icon_label.setFixedSize(100, 100)
|
58
|
+
self.icon_label.setScaledContents(True)
|
59
|
+
pixmap = QPixmap(icon_path)
|
60
|
+
if not pixmap.isNull():
|
61
|
+
size = 100
|
62
|
+
circular_pixmap = QPixmap(size, size)
|
63
|
+
circular_pixmap.fill(Qt.transparent)
|
64
|
+
|
65
|
+
painter = QPainter(circular_pixmap)
|
66
|
+
painter.setRenderHints(QPainter.Antialiasing, True)
|
67
|
+
path = QPainterPath()
|
68
|
+
path.addEllipse(0, 0, size, size)
|
69
|
+
painter.setClipPath(path)
|
70
|
+
pixmap = pixmap.scaled(size, size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
71
|
+
painter.drawPixmap(0, 0, pixmap)
|
72
|
+
painter.end()
|
73
|
+
|
74
|
+
self.icon_label.setPixmap(circular_pixmap)
|
75
|
+
self.layout.addWidget(self.icon_label, alignment=Qt.AlignCenter)
|
76
|
+
|
77
|
+
# Top label
|
78
|
+
self.top_label = QLabel(top_label.upper())
|
79
|
+
font_top = self.top_label.font()
|
80
|
+
font_top.setPointSize(10)
|
81
|
+
self.top_label.setFont(font_top)
|
82
|
+
self.layout.addWidget(self.top_label, alignment=Qt.AlignCenter)
|
83
|
+
|
84
|
+
# Main label
|
85
|
+
self.main_label = QLabel(main_label)
|
86
|
+
font_main = self.main_label.font()
|
87
|
+
font_main.setPointSize(14)
|
88
|
+
font_main.setBold(True)
|
89
|
+
self.main_label.setFont(font_main)
|
90
|
+
self.main_label.setWordWrap(True)
|
91
|
+
self.main_label.setAlignment(Qt.AlignCenter)
|
92
|
+
self.layout.addWidget(self.main_label)
|
93
|
+
|
94
|
+
self.spacer_top = QSpacerItem(0, 10, QSizePolicy.Fixed, QSizePolicy.Fixed)
|
95
|
+
self.layout.addItem(self.spacer_top)
|
96
|
+
|
97
|
+
# Description
|
98
|
+
self.description_label = QLabel(description)
|
99
|
+
self.description_label.setWordWrap(True)
|
100
|
+
self.description_label.setAlignment(Qt.AlignCenter)
|
101
|
+
self.layout.addWidget(self.description_label)
|
102
|
+
|
103
|
+
# Selector
|
104
|
+
if show_selector:
|
105
|
+
self.selector = QComboBox(self)
|
106
|
+
self.layout.addWidget(self.selector)
|
107
|
+
else:
|
108
|
+
self.selector = None
|
109
|
+
|
110
|
+
self.spacer_bottom = QSpacerItem(0, 0, QSizePolicy.Fixed, QSizePolicy.Expanding)
|
111
|
+
self.layout.addItem(self.spacer_bottom)
|
112
|
+
|
113
|
+
# Action button
|
114
|
+
self.action_button = QPushButton("Open")
|
115
|
+
self.action_button.setStyleSheet(
|
116
|
+
"""
|
117
|
+
QPushButton {
|
118
|
+
background-color: #007AFF;
|
119
|
+
border: none;
|
120
|
+
padding: 8px 16px;
|
121
|
+
color: white;
|
122
|
+
border-radius: 6px;
|
123
|
+
font-weight: bold;
|
124
|
+
}
|
125
|
+
QPushButton:hover {
|
126
|
+
background-color: #005BB5;
|
127
|
+
}
|
128
|
+
"""
|
129
|
+
)
|
130
|
+
self.layout.addWidget(self.action_button, alignment=Qt.AlignCenter)
|
131
|
+
|
132
|
+
|
133
|
+
class LaunchWindow(BECMainWindow):
|
134
|
+
RPC = True
|
135
|
+
TILE_SIZE = (250, 300)
|
136
|
+
USER_ACCESS = ["show_launcher", "hide_launcher"]
|
137
|
+
|
138
|
+
def __init__(
|
139
|
+
self, parent=None, gui_id: str = None, window_title="BEC Launcher", *args, **kwargs
|
140
|
+
):
|
141
|
+
super().__init__(parent=parent, gui_id=gui_id, window_title=window_title, **kwargs)
|
142
|
+
|
143
|
+
self.app = QApplication.instance()
|
144
|
+
|
145
|
+
# Toolbar
|
146
|
+
self.dark_mode_button = DarkModeButton(parent=self, toolbar=True)
|
147
|
+
self.toolbar = ModularToolBar(parent=self)
|
148
|
+
self.addToolBar(Qt.TopToolBarArea, self.toolbar)
|
149
|
+
self.spacer = QWidget(self)
|
150
|
+
self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
151
|
+
self.toolbar.addWidget(self.spacer)
|
152
|
+
self.toolbar.addWidget(self.dark_mode_button)
|
153
|
+
|
154
|
+
# Main Widget
|
155
|
+
self.central_widget = QWidget(self)
|
156
|
+
self.central_widget.layout = QHBoxLayout(self.central_widget)
|
157
|
+
self.setCentralWidget(self.central_widget)
|
158
|
+
|
159
|
+
self.tile_dock_area = LaunchTile(
|
160
|
+
icon_path=os.path.join(MODULE_PATH, "assets", "app_icons", "bec_widgets_icon.png"),
|
161
|
+
top_label="Get started",
|
162
|
+
main_label="BEC Dock Area",
|
163
|
+
description="Highly flexible and customizable dock area application with modular widgets.",
|
164
|
+
)
|
165
|
+
self.tile_dock_area.setFixedSize(*self.TILE_SIZE)
|
166
|
+
|
167
|
+
self.tile_auto_update = LaunchTile(
|
168
|
+
icon_path=os.path.join(MODULE_PATH, "assets", "app_icons", "auto_update.png"),
|
169
|
+
top_label="Get automated",
|
170
|
+
main_label="BEC Auto Update Dock Area",
|
171
|
+
description="Dock area with auto update functionality for BEC widgets plotting.",
|
172
|
+
show_selector=True,
|
173
|
+
)
|
174
|
+
self.tile_auto_update.setFixedSize(*self.TILE_SIZE)
|
175
|
+
|
176
|
+
self.tile_ui_file = LaunchTile(
|
177
|
+
icon_path=os.path.join(MODULE_PATH, "assets", "app_icons", "ui_loader_tile.png"),
|
178
|
+
top_label="Get customized",
|
179
|
+
main_label="Launch Custom UI File",
|
180
|
+
description="GUI application with custom UI file.",
|
181
|
+
)
|
182
|
+
self.tile_ui_file.setFixedSize(*self.TILE_SIZE)
|
183
|
+
|
184
|
+
# Add tiles to the main layout
|
185
|
+
self.central_widget.layout.addWidget(self.tile_dock_area)
|
186
|
+
self.central_widget.layout.addWidget(self.tile_auto_update)
|
187
|
+
self.central_widget.layout.addWidget(self.tile_ui_file)
|
188
|
+
|
189
|
+
# hacky solution no time to waste
|
190
|
+
self.tiles = [self.tile_dock_area, self.tile_auto_update, self.tile_ui_file]
|
191
|
+
|
192
|
+
# Connect signals
|
193
|
+
self.tile_dock_area.action_button.clicked.connect(lambda: self.launch("dock_area"))
|
194
|
+
self.tile_auto_update.action_button.clicked.connect(self._open_auto_update)
|
195
|
+
self.tile_ui_file.action_button.clicked.connect(self._open_custom_ui_file)
|
196
|
+
self._update_theme()
|
197
|
+
|
198
|
+
# Auto updates
|
199
|
+
self.available_auto_updates: dict[str, type[AutoUpdates]] = (
|
200
|
+
self._update_available_auto_updates()
|
201
|
+
)
|
202
|
+
if self.tile_auto_update.selector is not None:
|
203
|
+
self.tile_auto_update.selector.addItems(
|
204
|
+
list(self.available_auto_updates.keys()) + ["Default"]
|
205
|
+
)
|
206
|
+
|
207
|
+
self.register = RPCRegister()
|
208
|
+
self.register.callbacks.append(self._turn_off_the_lights)
|
209
|
+
self.register.broadcast()
|
210
|
+
|
211
|
+
def launch(
|
212
|
+
self,
|
213
|
+
launch_script: str,
|
214
|
+
name: str | None = None,
|
215
|
+
geometry: tuple[int, int, int, int] | None = None,
|
216
|
+
**kwargs,
|
217
|
+
) -> QWidget | None:
|
218
|
+
"""Launch the specified script. If the launch script creates a QWidget, it will be
|
219
|
+
embedded in a BECMainWindow. If the launch script creates a BECMainWindow, it will be shown
|
220
|
+
as a separate window.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
launch_script(str): The name of the script to be launched.
|
224
|
+
name(str): The name of the dock area.
|
225
|
+
geometry(tuple): The geometry parameters to be passed to the dock area.
|
226
|
+
Returns:
|
227
|
+
QWidget: The created dock area.
|
228
|
+
"""
|
229
|
+
from bec_widgets.applications import bw_launch
|
230
|
+
|
231
|
+
with RPCRegister.delayed_broadcast() as rpc_register:
|
232
|
+
existing_dock_areas = rpc_register.get_names_of_rpc_by_class_type(BECDockArea)
|
233
|
+
if name is not None:
|
234
|
+
if name in existing_dock_areas:
|
235
|
+
raise ValueError(
|
236
|
+
f"Name {name} must be unique for dock areas, but already exists: {existing_dock_areas}."
|
237
|
+
)
|
238
|
+
if not WidgetContainerUtils.has_name_valid_chars(name):
|
239
|
+
raise ValueError(
|
240
|
+
f"Name {name} contains invalid characters. Only alphanumeric characters, underscores, and dashes are allowed."
|
241
|
+
)
|
242
|
+
else:
|
243
|
+
name = "dock_area"
|
244
|
+
name = WidgetContainerUtils.generate_unique_name(name, existing_dock_areas)
|
245
|
+
|
246
|
+
if launch_script is None:
|
247
|
+
launch_script = "dock_area"
|
248
|
+
if not isinstance(launch_script, str):
|
249
|
+
raise ValueError(f"Launch script must be a string, but got {type(launch_script)}.")
|
250
|
+
|
251
|
+
if launch_script == "custom_ui_file":
|
252
|
+
ui_file = kwargs.pop("ui_file", None)
|
253
|
+
if not ui_file:
|
254
|
+
return None
|
255
|
+
return self._launch_custom_ui_file(ui_file)
|
256
|
+
|
257
|
+
if launch_script == "auto_update":
|
258
|
+
auto_update = kwargs.pop("auto_update", None)
|
259
|
+
return self._launch_auto_update(auto_update)
|
260
|
+
|
261
|
+
launch = getattr(bw_launch, launch_script, None)
|
262
|
+
if launch is None:
|
263
|
+
raise ValueError(f"Launch script {launch_script} not found.")
|
264
|
+
|
265
|
+
result_widget = launch(name)
|
266
|
+
result_widget.resize(result_widget.minimumSizeHint())
|
267
|
+
# TODO Should we simply use the specified name as title here?
|
268
|
+
result_widget.window().setWindowTitle(f"BEC - {name}")
|
269
|
+
logger.info(f"Created new dock area: {name}")
|
270
|
+
|
271
|
+
if geometry is not None:
|
272
|
+
result_widget.setGeometry(*geometry)
|
273
|
+
if isinstance(result_widget, BECMainWindow):
|
274
|
+
result_widget.show()
|
275
|
+
else:
|
276
|
+
window = BECMainWindow()
|
277
|
+
window.setCentralWidget(result_widget)
|
278
|
+
window.show()
|
279
|
+
return result_widget
|
280
|
+
|
281
|
+
def _launch_custom_ui_file(self, ui_file: str | None) -> BECMainWindow:
|
282
|
+
# Load the custom UI file
|
283
|
+
if ui_file is None:
|
284
|
+
raise ValueError("UI file must be provided for custom UI file launch.")
|
285
|
+
filename = os.path.basename(ui_file).split(".")[0]
|
286
|
+
|
287
|
+
tree = ET.parse(ui_file)
|
288
|
+
root = tree.getroot()
|
289
|
+
# Check if the top-level widget is a QMainWindow
|
290
|
+
widget = root.find("widget")
|
291
|
+
if widget is None:
|
292
|
+
raise ValueError("No widget found in the UI file.")
|
293
|
+
|
294
|
+
if widget.attrib.get("class") == "QMainWindow":
|
295
|
+
raise ValueError(
|
296
|
+
"Loading a QMainWindow from a UI file is currently not supported. "
|
297
|
+
"If you need this, please contact the BEC team or create a ticket on gitlab.psi.ch/bec/bec_widgets."
|
298
|
+
)
|
299
|
+
|
300
|
+
window = UILaunchWindow(object_name=filename)
|
301
|
+
QApplication.processEvents()
|
302
|
+
result_widget = UILoader(window).loader(ui_file)
|
303
|
+
window.setCentralWidget(result_widget)
|
304
|
+
window.setWindowTitle(f"BEC - {window.object_name}")
|
305
|
+
window.show()
|
306
|
+
logger.info(f"Object name of new instance: {result_widget.objectName()}, {window.gui_id}")
|
307
|
+
return window
|
308
|
+
|
309
|
+
def _launch_auto_update(self, auto_update: str) -> AutoUpdates:
|
310
|
+
if auto_update in self.available_auto_updates:
|
311
|
+
auto_update_cls = self.available_auto_updates[auto_update]
|
312
|
+
window = auto_update_cls()
|
313
|
+
else:
|
314
|
+
|
315
|
+
auto_update = "auto_updates"
|
316
|
+
window = AutoUpdates()
|
317
|
+
|
318
|
+
window.resize(window.minimumSizeHint())
|
319
|
+
QApplication.processEvents()
|
320
|
+
window.setWindowTitle(f"BEC - {window.objectName()}")
|
321
|
+
window.show()
|
322
|
+
return window
|
323
|
+
|
324
|
+
def apply_theme(self, theme: str):
|
325
|
+
"""
|
326
|
+
Change the theme of the application.
|
327
|
+
"""
|
328
|
+
for tile in self.tiles:
|
329
|
+
tile.apply_theme(theme)
|
330
|
+
|
331
|
+
super().apply_theme(theme)
|
332
|
+
|
333
|
+
def _open_auto_update(self):
|
334
|
+
"""
|
335
|
+
Open the auto update window.
|
336
|
+
"""
|
337
|
+
if self.tile_auto_update.selector is None:
|
338
|
+
auto_update = None
|
339
|
+
else:
|
340
|
+
auto_update = self.tile_auto_update.selector.currentText()
|
341
|
+
if auto_update == "Default":
|
342
|
+
auto_update = None
|
343
|
+
return self.launch("auto_update", auto_update=auto_update)
|
344
|
+
|
345
|
+
@SafeSlot(popup_error=True)
|
346
|
+
def _open_custom_ui_file(self):
|
347
|
+
"""
|
348
|
+
Open a file dialog to select a custom UI file and launch it.
|
349
|
+
"""
|
350
|
+
ui_file, _ = QFileDialog.getOpenFileName(
|
351
|
+
self, "Select UI File", "", "UI Files (*.ui);;All Files (*)"
|
352
|
+
)
|
353
|
+
self.launch("custom_ui_file", ui_file=ui_file)
|
354
|
+
|
355
|
+
@staticmethod
|
356
|
+
def _update_available_auto_updates() -> dict[str, type[AutoUpdates]]:
|
357
|
+
"""
|
358
|
+
Load all available auto updates from the plugin repository.
|
359
|
+
"""
|
360
|
+
try:
|
361
|
+
auto_updates = get_plugin_auto_updates()
|
362
|
+
logger.info(f"Available auto updates: {auto_updates.keys()}")
|
363
|
+
except Exception as exc:
|
364
|
+
logger.error(f"Failed to load auto updates: {exc}")
|
365
|
+
return {}
|
366
|
+
return auto_updates
|
367
|
+
|
368
|
+
def show_launcher(self):
|
369
|
+
"""
|
370
|
+
Show the launcher window.
|
371
|
+
"""
|
372
|
+
self.show()
|
373
|
+
|
374
|
+
def hide_launcher(self):
|
375
|
+
"""
|
376
|
+
Hide the launcher window.
|
377
|
+
"""
|
378
|
+
self.hide()
|
379
|
+
|
380
|
+
def showEvent(self, event):
|
381
|
+
super().showEvent(event)
|
382
|
+
self.setFixedSize(self.size())
|
383
|
+
|
384
|
+
def _launcher_is_last_widget(self, connections: dict) -> bool:
|
385
|
+
"""
|
386
|
+
Check if the launcher is the last widget in the application.
|
387
|
+
"""
|
388
|
+
|
389
|
+
remaining_connections = [
|
390
|
+
connection for connection in connections.values() if connection.parent_id != self.gui_id
|
391
|
+
]
|
392
|
+
return len(remaining_connections) <= 1
|
393
|
+
|
394
|
+
def _turn_off_the_lights(self, connections: dict):
|
395
|
+
"""
|
396
|
+
If there is only one connection remaining, it is the launcher, so we show it.
|
397
|
+
Once the launcher is closed as the last window, we quit the application.
|
398
|
+
"""
|
399
|
+
if self._launcher_is_last_widget(connections):
|
400
|
+
self.show()
|
401
|
+
self.activateWindow()
|
402
|
+
self.raise_()
|
403
|
+
if self.app:
|
404
|
+
self.app.setQuitOnLastWindowClosed(True) # type: ignore
|
405
|
+
return
|
406
|
+
|
407
|
+
self.hide()
|
408
|
+
if self.app:
|
409
|
+
self.app.setQuitOnLastWindowClosed(False) # type: ignore
|
410
|
+
|
411
|
+
def closeEvent(self, event):
|
412
|
+
"""
|
413
|
+
Close the launcher window.
|
414
|
+
"""
|
415
|
+
connections = self.register.list_all_connections()
|
416
|
+
if self._launcher_is_last_widget(connections):
|
417
|
+
event.accept()
|
418
|
+
return
|
419
|
+
|
420
|
+
event.ignore()
|
421
|
+
self.hide()
|
422
|
+
|
423
|
+
|
424
|
+
if __name__ == "__main__":
|
425
|
+
import sys
|
426
|
+
|
427
|
+
app = QApplication(sys.argv)
|
428
|
+
launcher = LaunchWindow()
|
429
|
+
launcher.show()
|
430
|
+
sys.exit(app.exec())
|
Binary file
|
Binary file
|
bec_widgets/cli/__init__.py
CHANGED
@@ -1 +0,0 @@
|
|
1
|
-
from .client import *
|