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
bec_widgets/widgets/editors/{scan_metadata/additional_metadata_table.py → dict_backed_table.py}
RENAMED
@@ -13,15 +13,23 @@ from qtpy.QtWidgets import (
|
|
13
13
|
QWidget,
|
14
14
|
)
|
15
15
|
|
16
|
-
from bec_widgets.
|
16
|
+
from bec_widgets.utils.error_popups import SafeSlot
|
17
17
|
|
18
18
|
|
19
|
-
class
|
19
|
+
class DictBackedTableModel(QAbstractTableModel):
|
20
20
|
def __init__(self, data):
|
21
|
+
"""A model to go with DictBackedTable, which represents key-value pairs
|
22
|
+
to be displayed in a TreeWidget.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
data (list[list[str]]): list of key-value pairs to initialise with"""
|
21
26
|
super().__init__()
|
22
27
|
self._data: list[list[str]] = data
|
23
28
|
self._disallowed_keys: list[str] = []
|
24
29
|
|
30
|
+
# pylint: disable=missing-function-docstring
|
31
|
+
# see QAbstractTableModel documentation for these methods
|
32
|
+
|
25
33
|
def headerData(
|
26
34
|
self, section: int, orientation: Qt.Orientation, role: int = Qt.ItemDataRole()
|
27
35
|
) -> Any:
|
@@ -49,6 +57,10 @@ class AdditionalMetadataTableModel(QAbstractTableModel):
|
|
49
57
|
return False
|
50
58
|
|
51
59
|
def update_disallowed_keys(self, keys: list[str]):
|
60
|
+
"""Set the list of keys which may not be used.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
keys (list[str]): list of keys which are forbidden."""
|
52
64
|
self._disallowed_keys = keys
|
53
65
|
for i, item in enumerate(self._data):
|
54
66
|
if item[0] in self._disallowed_keys:
|
@@ -95,16 +107,21 @@ class AdditionalMetadataTableModel(QAbstractTableModel):
|
|
95
107
|
return dict(self._data)
|
96
108
|
|
97
109
|
|
98
|
-
class
|
99
|
-
|
110
|
+
class DictBackedTable(QWidget):
|
100
111
|
delete_rows = Signal(list)
|
101
112
|
|
102
113
|
def __init__(self, initial_data: list[list[str]]):
|
114
|
+
"""Widget which uses a DictBackedTableModel to display an editable table
|
115
|
+
which can be extracted as a dict.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
initial_data (list[list[str]]): list of key-value pairs to initialise with
|
119
|
+
"""
|
103
120
|
super().__init__()
|
104
121
|
|
105
122
|
self._layout = QHBoxLayout()
|
106
123
|
self.setLayout(self._layout)
|
107
|
-
self._table_model =
|
124
|
+
self._table_model = DictBackedTableModel(initial_data)
|
108
125
|
self._table_view = QTreeView()
|
109
126
|
self._table_view.setModel(self._table_model)
|
110
127
|
self._table_view.setSizePolicy(
|
@@ -126,15 +143,21 @@ class AdditionalMetadataTable(QWidget):
|
|
126
143
|
self.delete_rows.connect(self._table_model.delete_rows)
|
127
144
|
|
128
145
|
def delete_selected_rows(self):
|
146
|
+
"""Delete rows which are part of the selection model"""
|
129
147
|
cells: list[QModelIndex] = self._table_view.selectionModel().selectedIndexes()
|
130
148
|
row_indices = list({r.row() for r in cells})
|
131
149
|
if row_indices:
|
132
150
|
self.delete_rows.emit(row_indices)
|
133
151
|
|
134
152
|
def dump_dict(self):
|
153
|
+
"""Get the current content of the table as a dict"""
|
135
154
|
return self._table_model.dump_dict()
|
136
155
|
|
137
156
|
def update_disallowed_keys(self, keys: list[str]):
|
157
|
+
"""Set the list of keys which may not be used.
|
158
|
+
|
159
|
+
Args:
|
160
|
+
keys (list[str]): list of keys which are forbidden."""
|
138
161
|
self._table_model.update_disallowed_keys(keys)
|
139
162
|
|
140
163
|
|
@@ -144,6 +167,6 @@ if __name__ == "__main__": # pragma: no cover
|
|
144
167
|
app = QApplication([])
|
145
168
|
set_theme("dark")
|
146
169
|
|
147
|
-
window =
|
170
|
+
window = DictBackedTable([["key1", "value1"], ["key2", "value2"], ["key3", "value3"]])
|
148
171
|
window.show()
|
149
172
|
app.exec()
|
@@ -1,7 +0,0 @@
|
|
1
|
-
from bec_widgets.widgets.editors.scan_metadata.additional_metadata_table import (
|
2
|
-
AdditionalMetadataTable,
|
3
|
-
AdditionalMetadataTableModel,
|
4
|
-
)
|
5
|
-
from bec_widgets.widgets.editors.scan_metadata.scan_metadata import ScanMetadata
|
6
|
-
|
7
|
-
__all__ = ["ScanMetadata", "AdditionalMetadataTable", "AdditionalMetadataTableModel"]
|
@@ -9,7 +9,7 @@ from annotated_types import Ge, Gt, Le, Lt
|
|
9
9
|
from bec_lib.logger import bec_logger
|
10
10
|
from pydantic_core import PydanticUndefined
|
11
11
|
|
12
|
-
if TYPE_CHECKING:
|
12
|
+
if TYPE_CHECKING: # pragma: no cover
|
13
13
|
from pydantic.fields import FieldInfo
|
14
14
|
|
15
15
|
logger = bec_logger.logger
|
@@ -6,11 +6,9 @@ def main(): # pragma: no cover
|
|
6
6
|
return
|
7
7
|
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
8
8
|
|
9
|
-
from bec_widgets.widgets.
|
10
|
-
BECMotorMapWidgetPlugin,
|
11
|
-
)
|
9
|
+
from bec_widgets.widgets.editors.scan_metadata.scan_metadata_plugin import ScanMetadataPlugin
|
12
10
|
|
13
|
-
QPyDesignerCustomWidgetCollection.addCustomWidget(
|
11
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(ScanMetadataPlugin())
|
14
12
|
|
15
13
|
|
16
14
|
if __name__ == "__main__": # pragma: no cover
|
@@ -1,48 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from decimal import Decimal
|
4
|
-
from types import NoneType
|
5
|
-
from typing import TYPE_CHECKING
|
6
4
|
|
7
5
|
from bec_lib.logger import bec_logger
|
8
6
|
from bec_lib.metadata_schema import get_metadata_schema_for_scan
|
9
|
-
from
|
10
|
-
from
|
11
|
-
from qtpy.QtCore import Signal # type: ignore
|
12
|
-
from qtpy.QtWidgets import (
|
13
|
-
QApplication,
|
14
|
-
QComboBox,
|
15
|
-
QGridLayout,
|
16
|
-
QHBoxLayout,
|
17
|
-
QLabel,
|
18
|
-
QLayout,
|
19
|
-
QVBoxLayout,
|
20
|
-
QWidget,
|
21
|
-
)
|
22
|
-
|
23
|
-
from bec_widgets.qt_utils.compact_popup import CompactPopupWidget
|
24
|
-
from bec_widgets.qt_utils.error_popups import SafeProperty, SafeSlot
|
25
|
-
from bec_widgets.qt_utils.expandable_frame import ExpandableGroupFrame
|
26
|
-
from bec_widgets.utils.bec_widget import BECWidget
|
27
|
-
from bec_widgets.widgets.editors.scan_metadata._metadata_widgets import widget_from_type
|
28
|
-
from bec_widgets.widgets.editors.scan_metadata.additional_metadata_table import (
|
29
|
-
AdditionalMetadataTable,
|
30
|
-
)
|
31
|
-
|
32
|
-
if TYPE_CHECKING:
|
33
|
-
from pydantic.fields import FieldInfo
|
34
|
-
|
35
|
-
logger = bec_logger.logger
|
7
|
+
from pydantic import Field
|
8
|
+
from qtpy.QtWidgets import QApplication, QComboBox, QHBoxLayout, QVBoxLayout, QWidget
|
36
9
|
|
10
|
+
from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
|
11
|
+
from bec_widgets.utils.expandable_frame import ExpandableGroupFrame
|
12
|
+
from bec_widgets.utils.forms_from_types.forms import PydanticModelForm
|
13
|
+
from bec_widgets.widgets.editors.dict_backed_table import DictBackedTable
|
37
14
|
|
38
|
-
|
39
|
-
"""Dynamically generates a form for inclusion of metadata for a scan. Uses the
|
40
|
-
metadata schema registry supplied in the plugin repo to find pydantic models
|
41
|
-
associated with the scan type. Sets limits for numerical values if specified."""
|
15
|
+
logger = bec_logger.logger
|
42
16
|
|
43
|
-
metadata_updated = Signal(dict)
|
44
|
-
metadata_cleared = Signal(NoneType)
|
45
17
|
|
18
|
+
class ScanMetadata(PydanticModelForm):
|
46
19
|
def __init__(
|
47
20
|
self,
|
48
21
|
parent=None,
|
@@ -51,119 +24,36 @@ class ScanMetadata(BECWidget, QWidget):
|
|
51
24
|
initial_extras: list[list[str]] | None = None,
|
52
25
|
**kwargs,
|
53
26
|
):
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
self.set_schema(scan_name)
|
58
|
-
|
59
|
-
self._layout = QVBoxLayout()
|
60
|
-
self._layout.setContentsMargins(0, 0, 0, 0)
|
61
|
-
self.setLayout(self._layout)
|
27
|
+
"""Dynamically generates a form for inclusion of metadata for a scan. Uses the
|
28
|
+
metadata schema registry supplied in the plugin repo to find pydantic models
|
29
|
+
associated with the scan type. Sets limits for numerical values if specified.
|
62
30
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
self._md_grid = QWidget()
|
69
|
-
self._required_md_box_layout.addWidget(self._md_grid)
|
70
|
-
self._grid_container = QVBoxLayout()
|
71
|
-
self._md_grid.setLayout(self._grid_container)
|
72
|
-
self._new_grid_layout()
|
73
|
-
self._grid_container.addLayout(self._md_grid_layout)
|
31
|
+
Args:
|
32
|
+
scan_name (str): The scan for which to generate a metadata form
|
33
|
+
Initial_extras (list[list[str]]): Initial data with which to populate the additional
|
34
|
+
metadata table - inner lists should be key-value pairs
|
35
|
+
"""
|
74
36
|
|
37
|
+
# self.populate() gets called in super().__init__
|
38
|
+
# so make sure self._additional_metadata exists
|
75
39
|
self._additional_md_box = ExpandableGroupFrame("Additional metadata", expanded=False)
|
76
|
-
self._layout.addWidget(self._additional_md_box)
|
77
40
|
self._additional_md_box_layout = QHBoxLayout()
|
78
41
|
self._additional_md_box.set_layout(self._additional_md_box_layout)
|
79
42
|
|
80
|
-
self._additional_metadata =
|
81
|
-
self.
|
43
|
+
self._additional_metadata = DictBackedTable(initial_extras or [])
|
44
|
+
self._scan_name = scan_name or ""
|
45
|
+
self._md_schema = get_metadata_schema_for_scan(self._scan_name)
|
82
46
|
|
83
|
-
self.
|
84
|
-
self._validity.compact_view = True # type: ignore
|
85
|
-
self._validity.label = "Metadata validity" # type: ignore
|
86
|
-
self._validity.compact_show_popup.setIcon(
|
87
|
-
material_icon(icon_name="info", size=(10, 10), convert_to_pixmap=False)
|
88
|
-
)
|
89
|
-
self._validity_message = QLabel("Not yet validated")
|
90
|
-
self._validity.addWidget(self._validity_message)
|
91
|
-
self._layout.addWidget(self._validity)
|
47
|
+
super().__init__(parent=parent, metadata_model=self._md_schema, client=client, **kwargs)
|
92
48
|
|
93
|
-
self.
|
49
|
+
self._layout.addWidget(self._additional_md_box)
|
50
|
+
self._additional_md_box_layout.addWidget(self._additional_metadata)
|
94
51
|
|
95
52
|
@SafeSlot(str)
|
96
53
|
def update_with_new_scan(self, scan_name: str):
|
97
|
-
self.
|
98
|
-
self.populate()
|
54
|
+
self.set_schema_from_scan(scan_name)
|
99
55
|
self.validate_form()
|
100
56
|
|
101
|
-
def validate_form(self, *_) -> bool:
|
102
|
-
"""validate the currently entered metadata against the pydantic schema.
|
103
|
-
If successful, returns on metadata_emitted and returns true.
|
104
|
-
Otherwise, emits on metadata_cleared and returns false."""
|
105
|
-
try:
|
106
|
-
metadata_dict = self.get_full_model_dict()
|
107
|
-
self._md_schema.model_validate(metadata_dict)
|
108
|
-
self._validity.set_global_state("success")
|
109
|
-
self._validity_message.setText("No errors!")
|
110
|
-
self.metadata_updated.emit(metadata_dict)
|
111
|
-
except ValidationError as e:
|
112
|
-
self._validity.set_global_state("emergency")
|
113
|
-
self._validity_message.setText(str(e))
|
114
|
-
self.metadata_cleared.emit(None)
|
115
|
-
|
116
|
-
def get_full_model_dict(self):
|
117
|
-
"""Get the entered metadata as a dict"""
|
118
|
-
return self._additional_metadata.dump_dict() | self._dict_from_grid()
|
119
|
-
|
120
|
-
def set_schema(self, scan_name: str | None = None):
|
121
|
-
self._scan_name = scan_name or ""
|
122
|
-
self._md_schema = get_metadata_schema_for_scan(self._scan_name)
|
123
|
-
|
124
|
-
def populate(self):
|
125
|
-
self._clear_grid()
|
126
|
-
self._populate()
|
127
|
-
|
128
|
-
def _populate(self):
|
129
|
-
self._additional_metadata.update_disallowed_keys(list(self._md_schema.model_fields.keys()))
|
130
|
-
for i, (field_name, info) in enumerate(self._md_schema.model_fields.items()):
|
131
|
-
self._add_griditem(field_name, info, i)
|
132
|
-
|
133
|
-
def _add_griditem(self, field_name: str, info: FieldInfo, row: int):
|
134
|
-
grid = self._md_grid_layout
|
135
|
-
label = QLabel(info.title or field_name)
|
136
|
-
label.setProperty("_model_field_name", field_name)
|
137
|
-
label.setToolTip(info.description or field_name)
|
138
|
-
grid.addWidget(label, row, 0)
|
139
|
-
widget = widget_from_type(info.annotation)(info)
|
140
|
-
widget.valueChanged.connect(self.validate_form)
|
141
|
-
grid.addWidget(widget, row, 1)
|
142
|
-
|
143
|
-
def _dict_from_grid(self) -> dict[str, str | int | float | Decimal | bool]:
|
144
|
-
grid = self._md_grid_layout
|
145
|
-
return {
|
146
|
-
grid.itemAtPosition(i, 0).widget().property("_model_field_name"): grid.itemAtPosition(i, 1).widget().getValue() # type: ignore # we only add 'MetadataWidget's here
|
147
|
-
for i in range(grid.rowCount())
|
148
|
-
}
|
149
|
-
|
150
|
-
def _clear_grid(self):
|
151
|
-
while self._md_grid_layout.count():
|
152
|
-
item = self._md_grid_layout.takeAt(0)
|
153
|
-
widget = item.widget()
|
154
|
-
if widget is not None:
|
155
|
-
widget.deleteLater()
|
156
|
-
self._md_grid_layout.deleteLater()
|
157
|
-
self._new_grid_layout()
|
158
|
-
self._grid_container.addLayout(self._md_grid_layout)
|
159
|
-
self._md_grid.adjustSize()
|
160
|
-
self.adjustSize()
|
161
|
-
|
162
|
-
def _new_grid_layout(self):
|
163
|
-
self._md_grid_layout = QGridLayout()
|
164
|
-
self._md_grid_layout.setContentsMargins(0, 0, 0, 0)
|
165
|
-
self._md_grid_layout.setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
|
166
|
-
|
167
57
|
@SafeProperty(bool)
|
168
58
|
def hide_optional_metadata(self): # type: ignore
|
169
59
|
"""Property to hide the optional metadata table."""
|
@@ -178,8 +68,25 @@ class ScanMetadata(BECWidget, QWidget):
|
|
178
68
|
"""
|
179
69
|
self._additional_md_box.setVisible(not hide)
|
180
70
|
|
71
|
+
def get_form_data(self):
|
72
|
+
"""Get the entered metadata as a dict"""
|
73
|
+
return self._additional_metadata.dump_dict() | self._dict_from_grid()
|
74
|
+
|
75
|
+
def populate(self):
|
76
|
+
self._additional_metadata.update_disallowed_keys(list(self._md_schema.model_fields.keys()))
|
77
|
+
super().populate()
|
78
|
+
|
79
|
+
def set_schema_from_scan(self, scan_name: str | None):
|
80
|
+
self._scan_name = scan_name or ""
|
81
|
+
self.set_schema(get_metadata_schema_for_scan(self._scan_name))
|
82
|
+
self.populate()
|
83
|
+
|
181
84
|
|
182
85
|
if __name__ == "__main__": # pragma: no cover
|
86
|
+
# pylint: disable=redefined-outer-name
|
87
|
+
# pylint: disable=protected-access
|
88
|
+
# pylint: disable=disallowed-name
|
89
|
+
|
183
90
|
from unittest.mock import patch
|
184
91
|
|
185
92
|
from bec_lib.metadata_schema import BasicScanMetadata
|
@@ -210,7 +117,6 @@ if __name__ == "__main__": # pragma: no cover
|
|
210
117
|
"bec_lib.metadata_schema._get_metadata_schema_registry",
|
211
118
|
lambda: {"scan1": ExampleSchema1, "scan2": ExampleSchema2, "scan3": ExampleSchema3},
|
212
119
|
):
|
213
|
-
|
214
120
|
app = QApplication([])
|
215
121
|
w = QWidget()
|
216
122
|
selection = QComboBox()
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['scan_metadata.py']}
|
@@ -4,36 +4,36 @@
|
|
4
4
|
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
5
5
|
|
6
6
|
from bec_widgets.utils.bec_designer import designer_material_icon
|
7
|
-
from bec_widgets.widgets.
|
7
|
+
from bec_widgets.widgets.editors.scan_metadata.scan_metadata import ScanMetadata
|
8
8
|
|
9
9
|
DOM_XML = """
|
10
10
|
<ui language='c++'>
|
11
|
-
<widget class='
|
11
|
+
<widget class='ScanMetadata' name='scan_metadata'>
|
12
12
|
</widget>
|
13
13
|
</ui>
|
14
14
|
"""
|
15
15
|
|
16
16
|
|
17
|
-
class
|
17
|
+
class ScanMetadataPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
18
|
def __init__(self):
|
19
19
|
super().__init__()
|
20
20
|
self._form_editor = None
|
21
21
|
|
22
22
|
def createWidget(self, parent):
|
23
|
-
t =
|
23
|
+
t = ScanMetadata(parent)
|
24
24
|
return t
|
25
25
|
|
26
26
|
def domXml(self):
|
27
27
|
return DOM_XML
|
28
28
|
|
29
29
|
def group(self):
|
30
|
-
return "
|
30
|
+
return ""
|
31
31
|
|
32
32
|
def icon(self):
|
33
|
-
return designer_material_icon(
|
33
|
+
return designer_material_icon(ScanMetadata.ICON_NAME)
|
34
34
|
|
35
35
|
def includeFile(self):
|
36
|
-
return "
|
36
|
+
return "scan_metadata"
|
37
37
|
|
38
38
|
def initialize(self, form_editor):
|
39
39
|
self._form_editor = form_editor
|
@@ -45,10 +45,10 @@ class BECMultiWaveformWidgetPlugin(QDesignerCustomWidgetInterface): # pragma: n
|
|
45
45
|
return self._form_editor is not None
|
46
46
|
|
47
47
|
def name(self):
|
48
|
-
return "
|
48
|
+
return "ScanMetadata"
|
49
49
|
|
50
50
|
def toolTip(self):
|
51
|
-
return "
|
51
|
+
return "Dynamically generates a form for inclusion of metadata for a scan."
|
52
52
|
|
53
53
|
def whatsThis(self):
|
54
54
|
return self.toolTip()
|
@@ -7,9 +7,9 @@ from bec_lib.logger import bec_logger
|
|
7
7
|
from pydantic import Field
|
8
8
|
from qtpy.QtWidgets import QTextEdit, QVBoxLayout, QWidget
|
9
9
|
|
10
|
-
from bec_widgets.qt_utils.error_popups import SafeProperty, SafeSlot
|
11
10
|
from bec_widgets.utils.bec_connector import ConnectionConfig
|
12
11
|
from bec_widgets.utils.bec_widget import BECWidget
|
12
|
+
from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
|
13
13
|
|
14
14
|
logger = bec_logger.logger
|
15
15
|
|
@@ -49,8 +49,7 @@ class TextBox(BECWidget, QWidget):
|
|
49
49
|
if isinstance(config, dict):
|
50
50
|
config = TextBoxConfig(**config)
|
51
51
|
self.config = config
|
52
|
-
super().__init__(
|
53
|
-
QWidget.__init__(self, parent)
|
52
|
+
super().__init__(parent=parent, client=client, gui_id=gui_id, config=config, **kwargs)
|
54
53
|
self.layout = QVBoxLayout(self)
|
55
54
|
self.text_box_text_edit = QTextEdit(parent=self)
|
56
55
|
self.layout.addWidget(self.text_box_text_edit)
|
@@ -26,8 +26,7 @@ class WebsiteWidget(BECWidget, QWidget):
|
|
26
26
|
def __init__(
|
27
27
|
self, parent=None, url: str = None, config=None, client=None, gui_id=None, **kwargs
|
28
28
|
):
|
29
|
-
super().__init__(
|
30
|
-
QWidget.__init__(self, parent=parent)
|
29
|
+
super().__init__(parent=parent, client=client, gui_id=gui_id, config=config, **kwargs)
|
31
30
|
layout = QVBoxLayout()
|
32
31
|
layout.setContentsMargins(0, 0, 0, 0)
|
33
32
|
self.website = QWebEngineView()
|
@@ -118,6 +117,7 @@ class WebsiteWidget(BECWidget, QWidget):
|
|
118
117
|
Cleanup the widget
|
119
118
|
"""
|
120
119
|
self.website.page().deleteLater()
|
120
|
+
super().cleanup()
|
121
121
|
|
122
122
|
|
123
123
|
if __name__ == "__main__":
|
@@ -144,10 +144,10 @@ class Minesweeper(BECWidget, QWidget):
|
|
144
144
|
PLUGIN = True
|
145
145
|
ICON_NAME = "videogame_asset"
|
146
146
|
USER_ACCESS = []
|
147
|
+
RPC = True
|
147
148
|
|
148
149
|
def __init__(self, parent=None, *args, **kwargs):
|
149
|
-
super().__init__(*args, **kwargs)
|
150
|
-
QWidget.__init__(self, parent=parent)
|
150
|
+
super().__init__(parent=parent, *args, **kwargs)
|
151
151
|
|
152
152
|
self._ui_initialised = False
|
153
153
|
self._timer_start_num_seconds = 0
|
@@ -403,6 +403,7 @@ class Minesweeper(BECWidget, QWidget):
|
|
403
403
|
|
404
404
|
def cleanup(self):
|
405
405
|
self._timer.stop()
|
406
|
+
super().cleanup()
|
406
407
|
|
407
408
|
|
408
409
|
if __name__ == "__main__":
|