bec-widgets 1.25.1__py3-none-any.whl → 2.0.1__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 +639 -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 +188 -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.1.dist-info}/METADATA +3 -3
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.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.1.dist-info}/WHEEL +0 -0
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -15,12 +15,16 @@ from qtpy.QtWidgets import (
|
|
15
15
|
)
|
16
16
|
|
17
17
|
from bec_widgets.utils import BECDispatcher
|
18
|
-
from bec_widgets.utils.
|
18
|
+
from bec_widgets.utils.widget_io import WidgetHierarchy as wh
|
19
19
|
from bec_widgets.widgets.containers.dock import BECDockArea
|
20
|
-
from bec_widgets.widgets.containers.figure import BECFigure
|
21
20
|
from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget
|
22
21
|
from bec_widgets.widgets.editors.jupyter_console.jupyter_console import BECJupyterConsole
|
23
|
-
from bec_widgets.widgets.
|
22
|
+
from bec_widgets.widgets.plots.image.image import Image
|
23
|
+
from bec_widgets.widgets.plots.motor_map.motor_map import MotorMap
|
24
|
+
from bec_widgets.widgets.plots.multi_waveform.multi_waveform import MultiWaveform
|
25
|
+
from bec_widgets.widgets.plots.plot_base import PlotBase
|
26
|
+
from bec_widgets.widgets.plots.scatter_waveform.scatter_waveform import ScatterWaveform
|
27
|
+
from bec_widgets.widgets.plots.waveform.waveform import Waveform
|
24
28
|
|
25
29
|
|
26
30
|
class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
@@ -37,34 +41,24 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
37
41
|
{
|
38
42
|
"np": np,
|
39
43
|
"pg": pg,
|
40
|
-
"
|
44
|
+
"wh": wh,
|
41
45
|
"dock": self.dock,
|
42
|
-
"
|
43
|
-
"
|
44
|
-
"
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"
|
51
|
-
"
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"
|
57
|
-
"
|
58
|
-
"mw": self.mw,
|
59
|
-
"lm": self.lm,
|
60
|
-
"btn1": self.btn1,
|
61
|
-
"btn2": self.btn2,
|
62
|
-
"btn3": self.btn3,
|
63
|
-
"btn4": self.btn4,
|
64
|
-
"btn5": self.btn5,
|
65
|
-
"btn6": self.btn6,
|
66
|
-
"pb": self.pb,
|
67
|
-
"pi": self.pi,
|
46
|
+
# "im": self.im,
|
47
|
+
# "mi": self.mi,
|
48
|
+
# "mm": self.mm,
|
49
|
+
# "lm": self.lm,
|
50
|
+
# "btn1": self.btn1,
|
51
|
+
# "btn2": self.btn2,
|
52
|
+
# "btn3": self.btn3,
|
53
|
+
# "btn4": self.btn4,
|
54
|
+
# "btn5": self.btn5,
|
55
|
+
# "btn6": self.btn6,
|
56
|
+
# "pb": self.pb,
|
57
|
+
# "pi": self.pi,
|
58
|
+
# "wf": self.wf,
|
59
|
+
# "scatter": self.scatter,
|
60
|
+
# "scatter_mi": self.scatter,
|
61
|
+
# "mwf": self.mwf,
|
68
62
|
}
|
69
63
|
)
|
70
64
|
|
@@ -83,126 +77,85 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
|
|
83
77
|
first_tab_layout.addWidget(self.dock)
|
84
78
|
tab_widget.addTab(first_tab, "Dock Area")
|
85
79
|
|
86
|
-
|
87
|
-
|
88
|
-
self.
|
89
|
-
|
90
|
-
tab_widget.addTab(
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
self.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
self.pi = self.pb.plot_item
|
102
|
-
fourth_tab_layout.addWidget(self.pb)
|
103
|
-
tab_widget.addTab(fourth_tab, "PltoBase")
|
104
|
-
|
105
|
-
tab_widget.setCurrentIndex(3)
|
106
|
-
|
80
|
+
# third_tab = QWidget()
|
81
|
+
# third_tab_layout = QVBoxLayout(third_tab)
|
82
|
+
# self.lm = LayoutManagerWidget()
|
83
|
+
# third_tab_layout.addWidget(self.lm)
|
84
|
+
# tab_widget.addTab(third_tab, "Layout Manager Widget")
|
85
|
+
#
|
86
|
+
# fourth_tab = QWidget()
|
87
|
+
# fourth_tab_layout = QVBoxLayout(fourth_tab)
|
88
|
+
# self.pb = PlotBase()
|
89
|
+
# self.pi = self.pb.plot_item
|
90
|
+
# fourth_tab_layout.addWidget(self.pb)
|
91
|
+
# tab_widget.addTab(fourth_tab, "PlotBase")
|
92
|
+
#
|
93
|
+
# tab_widget.setCurrentIndex(3)
|
94
|
+
#
|
107
95
|
group_box = QGroupBox("Jupyter Console", splitter)
|
108
96
|
group_box_layout = QVBoxLayout(group_box)
|
109
97
|
self.console = BECJupyterConsole(inprocess=True)
|
110
98
|
group_box_layout.addWidget(self.console)
|
111
|
-
|
112
|
-
# Some buttons for layout testing
|
113
|
-
self.btn1 = QPushButton("Button 1")
|
114
|
-
self.btn2 = QPushButton("Button 2")
|
115
|
-
self.btn3 = QPushButton("Button 3")
|
116
|
-
self.btn4 = QPushButton("Button 4")
|
117
|
-
self.btn5 = QPushButton("Button 5")
|
118
|
-
self.btn6 = QPushButton("Button 6")
|
119
|
-
|
120
|
-
#
|
121
|
-
|
122
|
-
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
self.
|
130
|
-
self.
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
)
|
157
|
-
|
158
|
-
|
159
|
-
)
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
)
|
166
|
-
self.w9 = self.figure.plot(
|
167
|
-
x_name="timestamp",
|
168
|
-
y_name="monitor_async",
|
169
|
-
new=True,
|
170
|
-
title="Async Plot - timestamp - w9",
|
171
|
-
row=2,
|
172
|
-
col=1,
|
173
|
-
)
|
174
|
-
self.w10 = self.figure.plot(
|
175
|
-
x_name="index",
|
176
|
-
y_name="monitor_async",
|
177
|
-
new=True,
|
178
|
-
title="Async Plot - index - w10",
|
179
|
-
row=2,
|
180
|
-
col=2,
|
181
|
-
)
|
182
|
-
|
183
|
-
def _init_dock(self):
|
184
|
-
|
185
|
-
self.d0 = self.dock.add_dock(name="dock_0")
|
186
|
-
self.mm = self.d0.add_widget("BECMotorMapWidget")
|
187
|
-
self.mm.change_motors("samx", "samy")
|
188
|
-
|
189
|
-
self.d1 = self.dock.add_dock(name="dock_1", position="right")
|
190
|
-
self.im = self.d1.add_widget("BECImageWidget")
|
191
|
-
self.im.image("waveform", "1d")
|
192
|
-
|
193
|
-
self.d2 = self.dock.add_dock(name="dock_2", position="bottom")
|
194
|
-
self.wf = self.d2.add_widget("BECFigure", row=0, col=0)
|
195
|
-
|
196
|
-
self.mw = self.wf.multi_waveform(monitor="waveform") # , config=config)
|
197
|
-
|
198
|
-
self.dock.save_state()
|
99
|
+
#
|
100
|
+
# # Some buttons for layout testing
|
101
|
+
# self.btn1 = QPushButton("Button 1")
|
102
|
+
# self.btn2 = QPushButton("Button 2")
|
103
|
+
# self.btn3 = QPushButton("Button 3")
|
104
|
+
# self.btn4 = QPushButton("Button 4")
|
105
|
+
# self.btn5 = QPushButton("Button 5")
|
106
|
+
# self.btn6 = QPushButton("Button 6")
|
107
|
+
#
|
108
|
+
# fifth_tab = QWidget()
|
109
|
+
# fifth_tab_layout = QVBoxLayout(fifth_tab)
|
110
|
+
# self.wf = Waveform()
|
111
|
+
# fifth_tab_layout.addWidget(self.wf)
|
112
|
+
# tab_widget.addTab(fifth_tab, "Waveform Next Gen")
|
113
|
+
# tab_widget.setCurrentIndex(4)
|
114
|
+
#
|
115
|
+
# sixth_tab = QWidget()
|
116
|
+
# sixth_tab_layout = QVBoxLayout(sixth_tab)
|
117
|
+
# self.im = Image()
|
118
|
+
# self.mi = self.im.main_image
|
119
|
+
# sixth_tab_layout.addWidget(self.im)
|
120
|
+
# tab_widget.addTab(sixth_tab, "Image Next Gen")
|
121
|
+
# tab_widget.setCurrentIndex(5)
|
122
|
+
#
|
123
|
+
# seventh_tab = QWidget()
|
124
|
+
# seventh_tab_layout = QVBoxLayout(seventh_tab)
|
125
|
+
# self.scatter = ScatterWaveform()
|
126
|
+
# self.scatter_mi = self.scatter.main_curve
|
127
|
+
# self.scatter.plot("samx", "samy", "bpm4i")
|
128
|
+
# seventh_tab_layout.addWidget(self.scatter)
|
129
|
+
# tab_widget.addTab(seventh_tab, "Scatter Waveform")
|
130
|
+
# tab_widget.setCurrentIndex(6)
|
131
|
+
#
|
132
|
+
# eighth_tab = QWidget()
|
133
|
+
# eighth_tab_layout = QVBoxLayout(eighth_tab)
|
134
|
+
# self.mm = MotorMap()
|
135
|
+
# eighth_tab_layout.addWidget(self.mm)
|
136
|
+
# tab_widget.addTab(eighth_tab, "Motor Map")
|
137
|
+
# tab_widget.setCurrentIndex(7)
|
138
|
+
#
|
139
|
+
# ninth_tab = QWidget()
|
140
|
+
# ninth_tab_layout = QVBoxLayout(ninth_tab)
|
141
|
+
# self.mwf = MultiWaveform()
|
142
|
+
# ninth_tab_layout.addWidget(self.mwf)
|
143
|
+
# tab_widget.addTab(ninth_tab, "MultiWaveform")
|
144
|
+
# tab_widget.setCurrentIndex(8)
|
145
|
+
#
|
146
|
+
# # add stuff to the new Waveform widget
|
147
|
+
# self._init_waveform()
|
148
|
+
#
|
149
|
+
# self.setWindowTitle("Jupyter Console Window")
|
150
|
+
|
151
|
+
def _init_waveform(self):
|
152
|
+
self.wf.plot(y_name="bpm4i", y_entry="bpm4i", dap="GaussianModel")
|
153
|
+
self.wf.plot(y_name="bpm3a", y_entry="bpm3a", dap="GaussianModel")
|
199
154
|
|
200
155
|
def closeEvent(self, event):
|
201
156
|
"""Override to handle things when main window is closed."""
|
202
157
|
self.dock.cleanup()
|
203
158
|
self.dock.close()
|
204
|
-
self.figure.cleanup()
|
205
|
-
self.figure.close()
|
206
159
|
self.console.close()
|
207
160
|
|
208
161
|
super().closeEvent(event)
|
@@ -218,17 +171,16 @@ if __name__ == "__main__": # pragma: no cover
|
|
218
171
|
app = QApplication(sys.argv)
|
219
172
|
app.setApplicationName("Jupyter Console")
|
220
173
|
app.setApplicationDisplayName("Jupyter Console")
|
221
|
-
|
222
|
-
icon = material_icon("terminal", color="#434343", filled=True)
|
174
|
+
icon = material_icon("terminal", color=(255, 255, 255, 255), filled=True)
|
223
175
|
app.setWindowIcon(icon)
|
224
176
|
|
225
|
-
bec_dispatcher = BECDispatcher()
|
177
|
+
bec_dispatcher = BECDispatcher(gui_id="jupyter_console")
|
226
178
|
client = bec_dispatcher.client
|
227
179
|
client.start()
|
228
180
|
|
229
181
|
win = JupyterConsoleWindow()
|
230
182
|
win.show()
|
231
|
-
win.resize(
|
183
|
+
win.resize(1500, 800)
|
232
184
|
|
233
185
|
app.aboutToQuit.connect(win.close)
|
234
186
|
sys.exit(app.exec_())
|
@@ -6,7 +6,7 @@ from qtpy.QtGui import QAction
|
|
6
6
|
from qtpy.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout
|
7
7
|
|
8
8
|
from bec_widgets.examples.plugin_example_pyside.tictactoe import TicTacToe
|
9
|
-
from bec_widgets.
|
9
|
+
from bec_widgets.utils.error_popups import SafeSlot as Slot
|
10
10
|
|
11
11
|
|
12
12
|
class TicTacToeDialog(QDialog): # pragma: no cover
|
@@ -3,25 +3,29 @@ from __future__ import annotations
|
|
3
3
|
|
4
4
|
import os
|
5
5
|
import time
|
6
|
+
import traceback
|
6
7
|
import uuid
|
8
|
+
from datetime import datetime
|
7
9
|
from typing import TYPE_CHECKING, Optional
|
8
10
|
|
9
11
|
from bec_lib.logger import bec_logger
|
10
12
|
from bec_lib.utils.import_utils import lazy_import_from
|
11
13
|
from pydantic import BaseModel, Field, field_validator
|
12
|
-
from qtpy.QtCore import QObject, QRunnable, QThreadPool, Signal
|
14
|
+
from qtpy.QtCore import QObject, QRunnable, QThreadPool, QTimer, Signal
|
13
15
|
from qtpy.QtWidgets import QApplication
|
14
16
|
|
15
17
|
from bec_widgets.cli.rpc.rpc_register import RPCRegister
|
16
|
-
from bec_widgets.
|
17
|
-
from bec_widgets.
|
18
|
+
from bec_widgets.utils.error_popups import ErrorPopupUtility, SafeSlot
|
19
|
+
from bec_widgets.utils.widget_io import WidgetHierarchy
|
18
20
|
from bec_widgets.utils.yaml_dialog import load_yaml, load_yaml_gui, save_yaml, save_yaml_gui
|
19
21
|
|
20
|
-
if TYPE_CHECKING:
|
22
|
+
if TYPE_CHECKING: # pragma: no cover
|
21
23
|
from bec_widgets.utils.bec_dispatcher import BECDispatcher
|
24
|
+
from bec_widgets.widgets.containers.dock import BECDock
|
25
|
+
else:
|
26
|
+
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
|
22
27
|
|
23
28
|
logger = bec_logger.logger
|
24
|
-
BECDispatcher = lazy_import_from("bec_widgets.utils.bec_dispatcher", ("BECDispatcher",))
|
25
29
|
|
26
30
|
|
27
31
|
class ConnectionConfig(BaseModel):
|
@@ -39,8 +43,7 @@ class ConnectionConfig(BaseModel):
|
|
39
43
|
"""Generate a GUI ID if none is provided."""
|
40
44
|
if v is None:
|
41
45
|
widget_class = values.data["widget_class"]
|
42
|
-
v = f"{widget_class}_{
|
43
|
-
return v
|
46
|
+
v = f"{widget_class}_{datetime.now().strftime('%Y_%m_%d_%H_%M_%S_%f')}"
|
44
47
|
return v
|
45
48
|
|
46
49
|
|
@@ -75,15 +78,52 @@ class BECConnector:
|
|
75
78
|
USER_ACCESS = ["_config_dict", "_get_all_rpc", "_rpc_id"]
|
76
79
|
EXIT_HANDLERS = {}
|
77
80
|
|
78
|
-
def __init__(
|
81
|
+
def __init__(
|
82
|
+
self,
|
83
|
+
client=None,
|
84
|
+
config: ConnectionConfig | None = None,
|
85
|
+
gui_id: str | None = None,
|
86
|
+
object_name: str | None = None,
|
87
|
+
parent_dock: BECDock | None = None, # TODO should go away -> issue created #473
|
88
|
+
root_widget: bool = False,
|
89
|
+
**kwargs,
|
90
|
+
):
|
91
|
+
"""
|
92
|
+
BECConnector mixin class to handle BEC client and device manager.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
client(BECClient, optional): The BEC client.
|
96
|
+
config(ConnectionConfig, optional): The connection configuration with specific gui id.
|
97
|
+
gui_id(str, optional): The GUI ID.
|
98
|
+
object_name(str, optional): The object name.
|
99
|
+
parent_dock(BECDock, optional): The parent dock.# TODO should go away -> issue created #473
|
100
|
+
root_widget(bool, optional): If set to True, the parent_id will be always set to None, thus enforcing that the widget is accessible as a root widget of the BECGuiClient object.
|
101
|
+
**kwargs:
|
102
|
+
"""
|
103
|
+
# Extract object_name from kwargs to not pass it to Qt class
|
104
|
+
object_name = object_name or kwargs.pop("objectName", None)
|
105
|
+
# Ensure the parent is always the first argument for QObject
|
106
|
+
parent = kwargs.pop("parent", None)
|
107
|
+
# This initializes the QObject or any qt related class BECConnector has to be used from this line down with QObject, otherwise hierarchy logic will not work
|
108
|
+
super().__init__(parent=parent, **kwargs)
|
109
|
+
|
110
|
+
assert isinstance(
|
111
|
+
self, QObject
|
112
|
+
), "BECConnector must be used with a QObject or any qt related class."
|
113
|
+
|
114
|
+
# flag to check if the object was destroyed and its cleanup was called
|
115
|
+
self._destroyed = False
|
116
|
+
|
79
117
|
# BEC related connections
|
80
118
|
self.bec_dispatcher = BECDispatcher(client=client)
|
81
119
|
self.client = self.bec_dispatcher.client if client is None else client
|
120
|
+
self._parent_dock = parent_dock # TODO also remove at some point -> issue created #473
|
121
|
+
self.rpc_register = RPCRegister()
|
82
122
|
|
83
123
|
if not self.client in BECConnector.EXIT_HANDLERS:
|
84
124
|
# register function to clean connections at exit;
|
85
125
|
# the function depends on BECClient, and BECDispatcher
|
86
|
-
@
|
126
|
+
@SafeSlot()
|
87
127
|
def terminate(client=self.client, dispatcher=self.bec_dispatcher):
|
88
128
|
logger.info("Disconnecting", repr(dispatcher))
|
89
129
|
dispatcher.disconnect_all()
|
@@ -103,17 +143,26 @@ class BECConnector:
|
|
103
143
|
)
|
104
144
|
self.config = ConnectionConfig(widget_class=self.__class__.__name__)
|
105
145
|
|
146
|
+
# If the gui_id is passed, it should be respected. However, this should be revisted since
|
147
|
+
# the gui_id has to be unique, and may no longer be.
|
106
148
|
if gui_id:
|
107
149
|
self.config.gui_id = gui_id
|
108
|
-
self.gui_id = gui_id
|
150
|
+
self.gui_id: str = gui_id # Keep namespace in sync
|
109
151
|
else:
|
110
|
-
self.gui_id = self.config.gui_id
|
152
|
+
self.gui_id: str = self.config.gui_id # type: ignore
|
111
153
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
self.
|
154
|
+
if object_name is not None:
|
155
|
+
self.setObjectName(object_name)
|
156
|
+
|
157
|
+
# 1) If no objectName is set, set the initial name
|
158
|
+
if not self.objectName():
|
159
|
+
self.setObjectName(self.__class__.__name__)
|
160
|
+
self.object_name = self.objectName()
|
161
|
+
|
162
|
+
# 2) Enforce unique objectName among siblings with the same BECConnector parent
|
163
|
+
self.setParent(parent)
|
164
|
+
if isinstance(self.parent(), QObject) and hasattr(self, "cleanup"):
|
165
|
+
self.parent().destroyed.connect(self._run_cleanup_on_deleted_parent)
|
117
166
|
|
118
167
|
# Error popups
|
119
168
|
self.error_utility = ErrorPopupUtility()
|
@@ -122,7 +171,108 @@ class BECConnector:
|
|
122
171
|
# Store references to running workers so they're not garbage collected prematurely.
|
123
172
|
self._workers = []
|
124
173
|
|
125
|
-
|
174
|
+
# If set to True, the parent_id will be always set to None, thus enforcing that the widget is accessible as a root widget of the BECGuiClient object.
|
175
|
+
self.root_widget = root_widget
|
176
|
+
|
177
|
+
QTimer.singleShot(0, self._update_object_name)
|
178
|
+
|
179
|
+
@property
|
180
|
+
def parent_id(self) -> str | None:
|
181
|
+
try:
|
182
|
+
if self.root_widget:
|
183
|
+
return None
|
184
|
+
connector_parent = WidgetHierarchy._get_becwidget_ancestor(self)
|
185
|
+
return connector_parent.gui_id if connector_parent else None
|
186
|
+
except:
|
187
|
+
logger.error(f"Error getting parent_id for {self.__class__.__name__}")
|
188
|
+
|
189
|
+
@SafeSlot()
|
190
|
+
def _run_cleanup_on_deleted_parent(self) -> None:
|
191
|
+
"""
|
192
|
+
Run cleanup on the deleted parent.
|
193
|
+
This method is called when the parent is deleted.
|
194
|
+
"""
|
195
|
+
if not hasattr(self, "cleanup"):
|
196
|
+
return
|
197
|
+
try:
|
198
|
+
if not self._destroyed:
|
199
|
+
self.cleanup()
|
200
|
+
self._destroyed = True
|
201
|
+
except Exception:
|
202
|
+
content = traceback.format_exc()
|
203
|
+
logger.info(
|
204
|
+
"Failed to run cleanup on deleted parent. "
|
205
|
+
f"This is not necessarily an error as the parent may be deleted before the child and includes already a cleanup. The following exception was raised:\n{content}"
|
206
|
+
)
|
207
|
+
|
208
|
+
def _update_object_name(self) -> None:
|
209
|
+
"""
|
210
|
+
Enforce a unique object name among siblings and register the object for RPC.
|
211
|
+
This method is called through a single shot timer kicked off in the constructor.
|
212
|
+
"""
|
213
|
+
# 1) Enforce unique objectName among siblings with the same BECConnector parent
|
214
|
+
self._enforce_unique_sibling_name()
|
215
|
+
# 2) Register the object for RPC
|
216
|
+
self.rpc_register.add_rpc(self)
|
217
|
+
|
218
|
+
def _enforce_unique_sibling_name(self):
|
219
|
+
"""
|
220
|
+
Enforce that this BECConnector has a unique objectName among its siblings.
|
221
|
+
|
222
|
+
Sibling logic:
|
223
|
+
- If there's a nearest BECConnector parent, only compare with children of that parent.
|
224
|
+
- If parent is None (i.e., top-level object), compare with all other top-level BECConnectors.
|
225
|
+
"""
|
226
|
+
QApplication.processEvents()
|
227
|
+
parent_bec = WidgetHierarchy._get_becwidget_ancestor(self)
|
228
|
+
|
229
|
+
if parent_bec:
|
230
|
+
# We have a parent => only compare with siblings under that parent
|
231
|
+
siblings = parent_bec.findChildren(BECConnector)
|
232
|
+
else:
|
233
|
+
# No parent => treat all top-level BECConnectors as siblings
|
234
|
+
# 1) Gather all BECConnectors from QApplication
|
235
|
+
all_widgets = QApplication.allWidgets()
|
236
|
+
all_bec = [w for w in all_widgets if isinstance(w, BECConnector)]
|
237
|
+
# 2) "Top-level" means closest BECConnector parent is None
|
238
|
+
top_level_bec = [
|
239
|
+
w for w in all_bec if WidgetHierarchy._get_becwidget_ancestor(w) is None
|
240
|
+
]
|
241
|
+
# 3) We are among these top-level siblings
|
242
|
+
siblings = top_level_bec
|
243
|
+
|
244
|
+
# Collect used names among siblings
|
245
|
+
used_names = {sib.objectName() for sib in siblings if sib is not self}
|
246
|
+
|
247
|
+
base_name = self.object_name
|
248
|
+
if base_name not in used_names:
|
249
|
+
# Name is already unique among siblings
|
250
|
+
return
|
251
|
+
|
252
|
+
# Need a suffix to avoid collision
|
253
|
+
counter = 0
|
254
|
+
while True:
|
255
|
+
trial_name = f"{base_name}_{counter}"
|
256
|
+
if trial_name not in used_names:
|
257
|
+
self.setObjectName(trial_name)
|
258
|
+
self.object_name = trial_name
|
259
|
+
break
|
260
|
+
counter += 1
|
261
|
+
|
262
|
+
# pylint: disable=invalid-name
|
263
|
+
def setObjectName(self, name: str) -> None:
|
264
|
+
"""
|
265
|
+
Set the object name of the widget.
|
266
|
+
|
267
|
+
Args:
|
268
|
+
name (str): The new object name.
|
269
|
+
"""
|
270
|
+
super().setObjectName(name)
|
271
|
+
self.object_name = name
|
272
|
+
if self.rpc_register.object_is_registered(self):
|
273
|
+
self.rpc_register.broadcast()
|
274
|
+
|
275
|
+
def submit_task(self, fn, *args, on_complete: SafeSlot = None, **kwargs) -> Worker:
|
126
276
|
"""
|
127
277
|
Submit a task to run in a separate thread. The task will run the specified
|
128
278
|
function with the provided arguments and emit the completed signal when done.
|
@@ -195,6 +345,7 @@ class BECConnector:
|
|
195
345
|
"""
|
196
346
|
self.config = config
|
197
347
|
|
348
|
+
# FIXME some thoughts are required to decide how thhis should work with rpc registry
|
198
349
|
def apply_config(self, config: dict, generate_new_id: bool = True) -> None:
|
199
350
|
"""
|
200
351
|
Apply the configuration to the widget.
|
@@ -207,11 +358,12 @@ class BECConnector:
|
|
207
358
|
if generate_new_id is True:
|
208
359
|
gui_id = str(uuid.uuid4())
|
209
360
|
self.rpc_register.remove_rpc(self)
|
210
|
-
self.
|
361
|
+
self._set_gui_id(gui_id)
|
211
362
|
self.rpc_register.add_rpc(self)
|
212
363
|
else:
|
213
364
|
self.gui_id = self.config.gui_id
|
214
365
|
|
366
|
+
# FIXME some thoughts are required to decide how thhis should work with rpc registry
|
215
367
|
def load_config(self, path: str | None = None, gui: bool = False):
|
216
368
|
"""
|
217
369
|
Load the configuration of the widget from YAML.
|
@@ -248,8 +400,8 @@ class BECConnector:
|
|
248
400
|
file_path = os.path.join(path, f"{self.__class__.__name__}_config.yaml")
|
249
401
|
save_yaml(file_path, self._config_dict)
|
250
402
|
|
251
|
-
@
|
252
|
-
def
|
403
|
+
# @SafeSlot(str)
|
404
|
+
def _set_gui_id(self, gui_id: str) -> None:
|
253
405
|
"""
|
254
406
|
Set the GUI ID for the widget.
|
255
407
|
|
@@ -280,7 +432,7 @@ class BECConnector:
|
|
280
432
|
self.client = client
|
281
433
|
self.get_bec_shortcuts()
|
282
434
|
|
283
|
-
@
|
435
|
+
@SafeSlot(ConnectionConfig) # TODO can be also dict
|
284
436
|
def on_config_update(self, config: ConnectionConfig | dict) -> None:
|
285
437
|
"""
|
286
438
|
Update the configuration for the widget.
|
@@ -288,9 +440,26 @@ class BECConnector:
|
|
288
440
|
Args:
|
289
441
|
config (ConnectionConfig | dict): Configuration settings.
|
290
442
|
"""
|
443
|
+
gui_id = getattr(config, "gui_id", None)
|
291
444
|
if isinstance(config, dict):
|
292
445
|
config = ConnectionConfig(**config)
|
293
446
|
self.config = config
|
447
|
+
if gui_id and config.gui_id != gui_id: # Recreating config should not overwrite the gui_id
|
448
|
+
self.config.gui_id = gui_id
|
449
|
+
|
450
|
+
def remove(self):
|
451
|
+
"""Cleanup the BECConnector"""
|
452
|
+
# If the widget is attached to a dock, remove it from the dock.
|
453
|
+
# TODO this should be handled by dock and dock are not by BECConnector -> issue created #473
|
454
|
+
if self._parent_dock is not None:
|
455
|
+
self._parent_dock.delete(self.object_name)
|
456
|
+
# If the widget is from Qt, trigger its close method.
|
457
|
+
elif hasattr(self, "close"):
|
458
|
+
self.close()
|
459
|
+
# If the widget is neither from a Dock nor from Qt, remove it from the RPC registry.
|
460
|
+
# i.e. Curve Item from Waveform
|
461
|
+
else:
|
462
|
+
self.rpc_register.remove_rpc(self)
|
294
463
|
|
295
464
|
def get_config(self, dict_output: bool = True) -> dict | BaseModel:
|
296
465
|
"""
|
@@ -10,6 +10,8 @@ from bec_qthemes import material_icon
|
|
10
10
|
from qtpy import PYSIDE6
|
11
11
|
from qtpy.QtGui import QIcon
|
12
12
|
|
13
|
+
from bec_widgets.utils.bec_plugin_helper import user_widget_plugin
|
14
|
+
|
13
15
|
if PYSIDE6:
|
14
16
|
from PySide6.scripts.pyside_tool import (
|
15
17
|
_extend_path_var,
|
@@ -150,7 +152,12 @@ def main(): # pragma: no cover
|
|
150
152
|
print("PYSIDE6 is not available in the environment. Exiting...")
|
151
153
|
return
|
152
154
|
base_dir = Path(os.path.dirname(bec_widgets.__file__)).resolve()
|
155
|
+
|
153
156
|
plugin_paths = find_plugin_paths(base_dir)
|
157
|
+
if (plugin_repo := user_widget_plugin()) and isinstance(plugin_repo.__file__, str):
|
158
|
+
plugin_repo_dir = Path(os.path.dirname(plugin_repo.__file__)).resolve()
|
159
|
+
plugin_paths.extend(find_plugin_paths(plugin_repo_dir))
|
160
|
+
|
154
161
|
set_plugin_environment_variable(plugin_paths)
|
155
162
|
|
156
163
|
patch_designer()
|