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
@@ -0,0 +1,508 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections import deque
|
4
|
+
|
5
|
+
import pyqtgraph as pg
|
6
|
+
from bec_lib.endpoints import MessageEndpoints
|
7
|
+
from bec_lib.logger import bec_logger
|
8
|
+
from pydantic import Field, ValidationError, field_validator
|
9
|
+
from qtpy.QtCore import Signal
|
10
|
+
from qtpy.QtWidgets import QWidget
|
11
|
+
|
12
|
+
from bec_widgets.utils import Colors, ConnectionConfig
|
13
|
+
from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
|
14
|
+
from bec_widgets.utils.side_panel import SidePanel
|
15
|
+
from bec_widgets.widgets.plots.multi_waveform.settings.control_panel import (
|
16
|
+
MultiWaveformControlPanel,
|
17
|
+
)
|
18
|
+
from bec_widgets.widgets.plots.multi_waveform.toolbar_bundles.monitor_selection import (
|
19
|
+
MultiWaveformSelectionToolbarBundle,
|
20
|
+
)
|
21
|
+
from bec_widgets.widgets.plots.plot_base import PlotBase
|
22
|
+
|
23
|
+
logger = bec_logger.logger
|
24
|
+
|
25
|
+
|
26
|
+
class MultiWaveformConfig(ConnectionConfig):
|
27
|
+
color_palette: str | None = Field(
|
28
|
+
"plasma", description="The color palette of the figure widget.", validate_default=True
|
29
|
+
)
|
30
|
+
curve_limit: int | None = Field(
|
31
|
+
200, description="The maximum number of curves to display on the plot."
|
32
|
+
)
|
33
|
+
flush_buffer: bool | None = Field(
|
34
|
+
False, description="Flush the buffer of the plot widget when the curve limit is reached."
|
35
|
+
)
|
36
|
+
monitor: str | None = Field(None, description="The monitor to set for the plot widget.")
|
37
|
+
curve_width: int | None = Field(1, description="The width of the curve on the plot.")
|
38
|
+
opacity: int | None = Field(50, description="The opacity of the curve on the plot.")
|
39
|
+
highlight_last_curve: bool | None = Field(
|
40
|
+
True, description="Highlight the last curve on the plot."
|
41
|
+
)
|
42
|
+
|
43
|
+
model_config: dict = {"validate_assignment": True}
|
44
|
+
_validate_color_map_z = field_validator("color_palette")(Colors.validate_color_map)
|
45
|
+
|
46
|
+
|
47
|
+
class MultiWaveform(PlotBase):
|
48
|
+
"""
|
49
|
+
MultiWaveform widget for displaying multiple waveforms emitted by a single signal.
|
50
|
+
"""
|
51
|
+
|
52
|
+
PLUGIN = True
|
53
|
+
RPC = True
|
54
|
+
ICON_NAME = "ssid_chart"
|
55
|
+
USER_ACCESS = [
|
56
|
+
# General PlotBase Settings
|
57
|
+
"enable_toolbar",
|
58
|
+
"enable_toolbar.setter",
|
59
|
+
"enable_side_panel",
|
60
|
+
"enable_side_panel.setter",
|
61
|
+
"enable_fps_monitor",
|
62
|
+
"enable_fps_monitor.setter",
|
63
|
+
"set",
|
64
|
+
"title",
|
65
|
+
"title.setter",
|
66
|
+
"x_label",
|
67
|
+
"x_label.setter",
|
68
|
+
"y_label",
|
69
|
+
"y_label.setter",
|
70
|
+
"x_limits",
|
71
|
+
"x_limits.setter",
|
72
|
+
"y_limits",
|
73
|
+
"y_limits.setter",
|
74
|
+
"x_grid",
|
75
|
+
"x_grid.setter",
|
76
|
+
"y_grid",
|
77
|
+
"y_grid.setter",
|
78
|
+
"inner_axes",
|
79
|
+
"inner_axes.setter",
|
80
|
+
"outer_axes",
|
81
|
+
"outer_axes.setter",
|
82
|
+
"lock_aspect_ratio",
|
83
|
+
"lock_aspect_ratio.setter",
|
84
|
+
"auto_range_x",
|
85
|
+
"auto_range_x.setter",
|
86
|
+
"auto_range_y",
|
87
|
+
"auto_range_y.setter",
|
88
|
+
"x_log",
|
89
|
+
"x_log.setter",
|
90
|
+
"y_log",
|
91
|
+
"y_log.setter",
|
92
|
+
"legend_label_size",
|
93
|
+
"legend_label_size.setter",
|
94
|
+
# MultiWaveform Specific RPC Access
|
95
|
+
"highlighted_index",
|
96
|
+
"highlighted_index.setter",
|
97
|
+
"highlight_last_curve",
|
98
|
+
"highlight_last_curve.setter",
|
99
|
+
"color_palette",
|
100
|
+
"color_palette.setter",
|
101
|
+
"opacity",
|
102
|
+
"opacity.setter",
|
103
|
+
"flush_buffer",
|
104
|
+
"flush_buffer.setter",
|
105
|
+
"max_trace",
|
106
|
+
"max_trace.setter",
|
107
|
+
"monitor",
|
108
|
+
"monitor.setter",
|
109
|
+
"set_curve_limit",
|
110
|
+
"plot",
|
111
|
+
"set_curve_highlight",
|
112
|
+
"clear_curves",
|
113
|
+
]
|
114
|
+
|
115
|
+
monitor_signal_updated = Signal()
|
116
|
+
highlighted_curve_index_changed = Signal(int)
|
117
|
+
|
118
|
+
def __init__(
|
119
|
+
self,
|
120
|
+
parent: QWidget | None = None,
|
121
|
+
config: MultiWaveformConfig | None = None,
|
122
|
+
client=None,
|
123
|
+
gui_id: str | None = None,
|
124
|
+
popups: bool = True,
|
125
|
+
**kwargs,
|
126
|
+
):
|
127
|
+
if config is None:
|
128
|
+
config = MultiWaveformConfig(widget_class=self.__class__.__name__)
|
129
|
+
super().__init__(
|
130
|
+
parent=parent, config=config, client=client, gui_id=gui_id, popups=popups, **kwargs
|
131
|
+
)
|
132
|
+
|
133
|
+
# Scan Data
|
134
|
+
self.old_scan_id = None
|
135
|
+
self.scan_id = None
|
136
|
+
self.connected = False
|
137
|
+
self._current_highlight_index = 0
|
138
|
+
self._curves = deque()
|
139
|
+
self.visible_curves = []
|
140
|
+
self.number_of_visible_curves = 0
|
141
|
+
|
142
|
+
self._init_control_panel()
|
143
|
+
|
144
|
+
################################################################################
|
145
|
+
# Widget Specific GUI interactions
|
146
|
+
################################################################################
|
147
|
+
def _init_toolbar(self):
|
148
|
+
self.monitor_selection_bundle = MultiWaveformSelectionToolbarBundle(
|
149
|
+
bundle_id="motor_selection", target_widget=self
|
150
|
+
)
|
151
|
+
self.toolbar.add_bundle(self.monitor_selection_bundle, target_widget=self)
|
152
|
+
super()._init_toolbar()
|
153
|
+
self.toolbar.widgets["reset_legend"].action.setVisible(False)
|
154
|
+
|
155
|
+
def _init_control_panel(self):
|
156
|
+
self.control_panel = SidePanel(self, orientation="top", panel_max_width=90)
|
157
|
+
self.layout_manager.add_widget_relative(
|
158
|
+
self.control_panel, self.round_plot_widget, "bottom"
|
159
|
+
)
|
160
|
+
self.controls = MultiWaveformControlPanel(parent=self, target_widget=self)
|
161
|
+
self.control_panel.add_menu(
|
162
|
+
action_id="control",
|
163
|
+
icon_name="tune",
|
164
|
+
tooltip="Show Control panel",
|
165
|
+
widget=self.controls,
|
166
|
+
title=None,
|
167
|
+
)
|
168
|
+
self.control_panel.toolbar.widgets["control"].action.trigger()
|
169
|
+
|
170
|
+
################################################################################
|
171
|
+
# Widget Specific Properties
|
172
|
+
################################################################################
|
173
|
+
|
174
|
+
@property
|
175
|
+
def curves(self) -> deque:
|
176
|
+
"""
|
177
|
+
Get the curves of the plot widget as a deque.
|
178
|
+
Returns:
|
179
|
+
deque: Deque of curves.
|
180
|
+
"""
|
181
|
+
return self._curves
|
182
|
+
|
183
|
+
@curves.setter
|
184
|
+
def curves(self, value: deque):
|
185
|
+
self._curves = value
|
186
|
+
|
187
|
+
@SafeProperty(int, designable=False)
|
188
|
+
def highlighted_index(self):
|
189
|
+
return self._current_highlight_index
|
190
|
+
|
191
|
+
@highlighted_index.setter
|
192
|
+
def highlighted_index(self, value: int):
|
193
|
+
self._current_highlight_index = value
|
194
|
+
self.property_changed.emit("highlighted_index", value)
|
195
|
+
self.set_curve_highlight(value)
|
196
|
+
|
197
|
+
@SafeProperty(bool)
|
198
|
+
def highlight_last_curve(self) -> bool:
|
199
|
+
"""
|
200
|
+
Get the highlight_last_curve property.
|
201
|
+
Returns:
|
202
|
+
bool: The highlight_last_curve property.
|
203
|
+
"""
|
204
|
+
return self.config.highlight_last_curve
|
205
|
+
|
206
|
+
@highlight_last_curve.setter
|
207
|
+
def highlight_last_curve(self, value: bool):
|
208
|
+
self.config.highlight_last_curve = value
|
209
|
+
self.property_changed.emit("highlight_last_curve", value)
|
210
|
+
self.set_curve_highlight(-1)
|
211
|
+
|
212
|
+
@SafeProperty(str)
|
213
|
+
def color_palette(self) -> str:
|
214
|
+
"""
|
215
|
+
The color palette of the figure widget.
|
216
|
+
"""
|
217
|
+
return self.config.color_palette
|
218
|
+
|
219
|
+
@color_palette.setter
|
220
|
+
def color_palette(self, value: str):
|
221
|
+
"""
|
222
|
+
Set the color palette of the figure widget.
|
223
|
+
|
224
|
+
Args:
|
225
|
+
value(str): The color palette to set.
|
226
|
+
"""
|
227
|
+
try:
|
228
|
+
self.config.color_palette = value
|
229
|
+
except ValidationError:
|
230
|
+
return
|
231
|
+
self.set_curve_highlight(self._current_highlight_index)
|
232
|
+
self._sync_monitor_selection_toolbar()
|
233
|
+
|
234
|
+
@SafeProperty(int)
|
235
|
+
def opacity(self) -> int:
|
236
|
+
"""
|
237
|
+
The opacity of the figure widget.
|
238
|
+
"""
|
239
|
+
return self.config.opacity
|
240
|
+
|
241
|
+
@opacity.setter
|
242
|
+
def opacity(self, value: int):
|
243
|
+
"""
|
244
|
+
Set the opacity of the figure widget.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
value(int): The opacity to set.
|
248
|
+
"""
|
249
|
+
self.config.opacity = max(0, min(100, value))
|
250
|
+
self.property_changed.emit("opacity", value)
|
251
|
+
self.set_curve_highlight(self._current_highlight_index)
|
252
|
+
|
253
|
+
@SafeProperty(bool)
|
254
|
+
def flush_buffer(self) -> bool:
|
255
|
+
"""
|
256
|
+
The flush_buffer property.
|
257
|
+
"""
|
258
|
+
return self.config.flush_buffer
|
259
|
+
|
260
|
+
@flush_buffer.setter
|
261
|
+
def flush_buffer(self, value: bool):
|
262
|
+
self.config.flush_buffer = value
|
263
|
+
self.property_changed.emit("flush_buffer", value)
|
264
|
+
self.set_curve_limit(
|
265
|
+
max_trace=self.config.curve_limit, flush_buffer=self.config.flush_buffer
|
266
|
+
)
|
267
|
+
|
268
|
+
@SafeProperty(int)
|
269
|
+
def max_trace(self) -> int:
|
270
|
+
"""
|
271
|
+
The maximum number of traces to display on the plot.
|
272
|
+
"""
|
273
|
+
return self.config.curve_limit
|
274
|
+
|
275
|
+
@max_trace.setter
|
276
|
+
def max_trace(self, value: int):
|
277
|
+
"""
|
278
|
+
Set the maximum number of traces to display on the plot.
|
279
|
+
|
280
|
+
Args:
|
281
|
+
value(int): The maximum number of traces to display.
|
282
|
+
"""
|
283
|
+
self.config.curve_limit = value
|
284
|
+
self.property_changed.emit("max_trace", value)
|
285
|
+
self.set_curve_limit(
|
286
|
+
max_trace=self.config.curve_limit, flush_buffer=self.config.flush_buffer
|
287
|
+
)
|
288
|
+
|
289
|
+
@SafeProperty(str)
|
290
|
+
def monitor(self) -> str:
|
291
|
+
"""
|
292
|
+
The monitor of the figure widget.
|
293
|
+
"""
|
294
|
+
return self.config.monitor
|
295
|
+
|
296
|
+
@monitor.setter
|
297
|
+
def monitor(self, value: str):
|
298
|
+
"""
|
299
|
+
Set the monitor of the figure widget.
|
300
|
+
|
301
|
+
Args:
|
302
|
+
value(str): The monitor to set.
|
303
|
+
"""
|
304
|
+
self.plot(value)
|
305
|
+
|
306
|
+
################################################################################
|
307
|
+
# High Level methods for API
|
308
|
+
################################################################################
|
309
|
+
|
310
|
+
@SafeSlot(popup_error=True)
|
311
|
+
def plot(self, monitor: str, color_palette: str | None = "plasma"):
|
312
|
+
"""
|
313
|
+
Create a plot for the given monitor.
|
314
|
+
Args:
|
315
|
+
monitor (str): The monitor to set.
|
316
|
+
color_palette (str|None): The color palette to use for the plot.
|
317
|
+
"""
|
318
|
+
self.entry_validator.validate_monitor(monitor)
|
319
|
+
self._disconnect_monitor()
|
320
|
+
self.config.monitor = monitor
|
321
|
+
self._connect_monitor()
|
322
|
+
if color_palette is not None:
|
323
|
+
self.color_palette = color_palette
|
324
|
+
self._sync_monitor_selection_toolbar()
|
325
|
+
|
326
|
+
@SafeSlot(int, bool)
|
327
|
+
def set_curve_limit(self, max_trace: int, flush_buffer: bool):
|
328
|
+
"""
|
329
|
+
Set the maximum number of traces to display on the plot.
|
330
|
+
|
331
|
+
Args:
|
332
|
+
max_trace (int): The maximum number of traces to display.
|
333
|
+
flush_buffer (bool): Flush the buffer.
|
334
|
+
"""
|
335
|
+
if max_trace != self.config.curve_limit:
|
336
|
+
self.config.curve_limit = max_trace
|
337
|
+
if flush_buffer != self.config.flush_buffer:
|
338
|
+
self.config.flush_buffer = flush_buffer
|
339
|
+
|
340
|
+
if self.config.curve_limit is None:
|
341
|
+
self.scale_colors()
|
342
|
+
return
|
343
|
+
|
344
|
+
if self.config.flush_buffer:
|
345
|
+
# Remove excess curves from the plot and the deque
|
346
|
+
while len(self.curves) > self.config.curve_limit:
|
347
|
+
curve = self.curves.popleft()
|
348
|
+
self.plot_item.removeItem(curve)
|
349
|
+
else:
|
350
|
+
# Hide or show curves based on the new max_trace
|
351
|
+
num_curves_to_show = min(self.config.curve_limit, len(self.curves))
|
352
|
+
for i, curve in enumerate(self.curves):
|
353
|
+
if i < len(self.curves) - num_curves_to_show:
|
354
|
+
curve.hide()
|
355
|
+
else:
|
356
|
+
curve.show()
|
357
|
+
self.scale_colors()
|
358
|
+
self.monitor_signal_updated.emit()
|
359
|
+
|
360
|
+
################################################################################
|
361
|
+
# BEC Update Methods
|
362
|
+
################################################################################
|
363
|
+
@SafeSlot(dict, dict)
|
364
|
+
def on_monitor_1d_update(self, msg: dict, metadata: dict):
|
365
|
+
"""
|
366
|
+
Update the plot widget with the monitor data.
|
367
|
+
|
368
|
+
Args:
|
369
|
+
msg(dict): The message data.
|
370
|
+
metadata(dict): The metadata of the message.
|
371
|
+
"""
|
372
|
+
data = msg.get("data", None)
|
373
|
+
current_scan_id = metadata.get("scan_id", None)
|
374
|
+
|
375
|
+
if current_scan_id != self.scan_id:
|
376
|
+
self.scan_id = current_scan_id
|
377
|
+
self.clear_curves()
|
378
|
+
self.curves.clear()
|
379
|
+
if self.crosshair:
|
380
|
+
self.crosshair.clear_markers()
|
381
|
+
|
382
|
+
# Always create a new curve and add it
|
383
|
+
curve = pg.PlotDataItem()
|
384
|
+
curve.setData(data)
|
385
|
+
self.plot_item.addItem(curve)
|
386
|
+
self.curves.append(curve)
|
387
|
+
|
388
|
+
# Max Trace and scale colors
|
389
|
+
self.set_curve_limit(self.config.curve_limit, self.config.flush_buffer)
|
390
|
+
|
391
|
+
@SafeSlot(int)
|
392
|
+
def set_curve_highlight(self, index: int):
|
393
|
+
"""
|
394
|
+
Set the curve highlight based on visible curves.
|
395
|
+
|
396
|
+
Args:
|
397
|
+
index (int): The index of the curve to highlight among visible curves.
|
398
|
+
"""
|
399
|
+
self.plot_item.visible_curves = [curve for curve in self.curves if curve.isVisible()]
|
400
|
+
num_visible_curves = len(self.plot_item.visible_curves)
|
401
|
+
self.number_of_visible_curves = num_visible_curves
|
402
|
+
|
403
|
+
if num_visible_curves == 0:
|
404
|
+
return # No curves to highlight
|
405
|
+
|
406
|
+
if index >= num_visible_curves:
|
407
|
+
index = num_visible_curves - 1
|
408
|
+
elif index < 0:
|
409
|
+
index = num_visible_curves + index
|
410
|
+
self._current_highlight_index = index
|
411
|
+
num_colors = num_visible_curves
|
412
|
+
colors = Colors.evenly_spaced_colors(
|
413
|
+
colormap=self.config.color_palette, num=num_colors, format="HEX"
|
414
|
+
)
|
415
|
+
for i, curve in enumerate(self.plot_item.visible_curves):
|
416
|
+
curve.setPen()
|
417
|
+
if i == self._current_highlight_index:
|
418
|
+
curve.setPen(pg.mkPen(color=colors[i], width=5))
|
419
|
+
curve.setAlpha(alpha=1, auto=False)
|
420
|
+
curve.setZValue(1)
|
421
|
+
else:
|
422
|
+
curve.setPen(pg.mkPen(color=colors[i], width=1))
|
423
|
+
curve.setAlpha(alpha=self.config.opacity / 100, auto=False)
|
424
|
+
curve.setZValue(0)
|
425
|
+
|
426
|
+
self.highlighted_curve_index_changed.emit(self._current_highlight_index)
|
427
|
+
|
428
|
+
def _disconnect_monitor(self):
|
429
|
+
try:
|
430
|
+
previous_monitor = self.config.monitor
|
431
|
+
except AttributeError:
|
432
|
+
previous_monitor = None
|
433
|
+
|
434
|
+
if previous_monitor and self.connected is True:
|
435
|
+
self.bec_dispatcher.disconnect_slot(
|
436
|
+
self.on_monitor_1d_update, MessageEndpoints.device_monitor_1d(previous_monitor)
|
437
|
+
)
|
438
|
+
self.connected = False
|
439
|
+
|
440
|
+
def _connect_monitor(self):
|
441
|
+
"""
|
442
|
+
Connect the monitor to the plot widget.
|
443
|
+
"""
|
444
|
+
|
445
|
+
if self.config.monitor and self.connected is False:
|
446
|
+
self.bec_dispatcher.connect_slot(
|
447
|
+
self.on_monitor_1d_update, MessageEndpoints.device_monitor_1d(self.config.monitor)
|
448
|
+
)
|
449
|
+
self.connected = True
|
450
|
+
|
451
|
+
################################################################################
|
452
|
+
# Utility Methods
|
453
|
+
################################################################################
|
454
|
+
def scale_colors(self):
|
455
|
+
"""
|
456
|
+
Scale the colors of the curves based on the current colormap.
|
457
|
+
"""
|
458
|
+
# TODO probably has to be changed to property
|
459
|
+
if self.config.highlight_last_curve:
|
460
|
+
self.set_curve_highlight(-1) # Use -1 to highlight the last visible curve
|
461
|
+
else:
|
462
|
+
self.set_curve_highlight(self._current_highlight_index)
|
463
|
+
|
464
|
+
def hook_crosshair(self) -> None:
|
465
|
+
"""
|
466
|
+
Specific hookfor crosshair, since it is for multiple curves.
|
467
|
+
"""
|
468
|
+
super().hook_crosshair()
|
469
|
+
if self.crosshair:
|
470
|
+
self.highlighted_curve_index_changed.connect(self.crosshair.update_highlighted_curve)
|
471
|
+
if self.curves:
|
472
|
+
self.crosshair.update_highlighted_curve(self._current_highlight_index)
|
473
|
+
|
474
|
+
def clear_curves(self):
|
475
|
+
"""
|
476
|
+
Remove all curves from the plot, excluding crosshair items.
|
477
|
+
"""
|
478
|
+
items_to_remove = []
|
479
|
+
for item in self.plot_item.items:
|
480
|
+
if not getattr(item, "is_crosshair", False) and isinstance(item, pg.PlotDataItem):
|
481
|
+
items_to_remove.append(item)
|
482
|
+
for item in items_to_remove:
|
483
|
+
self.plot_item.removeItem(item)
|
484
|
+
|
485
|
+
def _sync_monitor_selection_toolbar(self):
|
486
|
+
"""
|
487
|
+
Sync the motor map selection toolbar with the current motor map.
|
488
|
+
"""
|
489
|
+
if self.monitor_selection_bundle is not None:
|
490
|
+
monitor = self.monitor_selection_bundle.monitor.currentText()
|
491
|
+
color_palette = self.monitor_selection_bundle.colormap_widget.colormap
|
492
|
+
|
493
|
+
if monitor != self.config.monitor:
|
494
|
+
self.monitor_selection_bundle.monitor.blockSignals(True)
|
495
|
+
self.monitor_selection_bundle.monitor.set_device(self.config.monitor)
|
496
|
+
self.monitor_selection_bundle.monitor.check_validity(self.config.monitor)
|
497
|
+
self.monitor_selection_bundle.monitor.blockSignals(False)
|
498
|
+
|
499
|
+
if color_palette != self.config.color_palette:
|
500
|
+
self.monitor_selection_bundle.colormap_widget.blockSignals(True)
|
501
|
+
self.monitor_selection_bundle.colormap_widget.colormap = self.config.color_palette
|
502
|
+
self.monitor_selection_bundle.colormap_widget.blockSignals(False)
|
503
|
+
|
504
|
+
def cleanup(self):
|
505
|
+
self._disconnect_monitor()
|
506
|
+
self.clear_curves()
|
507
|
+
self.monitor_selection_bundle.cleanup()
|
508
|
+
super().cleanup()
|
@@ -0,0 +1 @@
|
|
1
|
+
{'files': ['multi_waveform.py']}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copyright (C) 2022 The Qt Company Ltd.
|
2
|
+
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
3
|
+
|
4
|
+
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
5
|
+
|
6
|
+
from bec_widgets.utils.bec_designer import designer_material_icon
|
7
|
+
from bec_widgets.widgets.plots.multi_waveform.multi_waveform import MultiWaveform
|
8
|
+
|
9
|
+
DOM_XML = """
|
10
|
+
<ui language='c++'>
|
11
|
+
<widget class='MultiWaveform' name='multi_waveform'>
|
12
|
+
</widget>
|
13
|
+
</ui>
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
class MultiWaveformPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
18
|
+
def __init__(self):
|
19
|
+
super().__init__()
|
20
|
+
self._form_editor = None
|
21
|
+
|
22
|
+
def createWidget(self, parent):
|
23
|
+
t = MultiWaveform(parent)
|
24
|
+
return t
|
25
|
+
|
26
|
+
def domXml(self):
|
27
|
+
return DOM_XML
|
28
|
+
|
29
|
+
def group(self):
|
30
|
+
return "Plot Widgets"
|
31
|
+
|
32
|
+
def icon(self):
|
33
|
+
return designer_material_icon(MultiWaveform.ICON_NAME)
|
34
|
+
|
35
|
+
def includeFile(self):
|
36
|
+
return "multi_waveform"
|
37
|
+
|
38
|
+
def initialize(self, form_editor):
|
39
|
+
self._form_editor = form_editor
|
40
|
+
|
41
|
+
def isContainer(self):
|
42
|
+
return False
|
43
|
+
|
44
|
+
def isInitialized(self):
|
45
|
+
return self._form_editor is not None
|
46
|
+
|
47
|
+
def name(self):
|
48
|
+
return "MultiWaveform"
|
49
|
+
|
50
|
+
def toolTip(self):
|
51
|
+
return "MultiWaveform"
|
52
|
+
|
53
|
+
def whatsThis(self):
|
54
|
+
return self.toolTip()
|
@@ -0,0 +1,15 @@
|
|
1
|
+
def main(): # pragma: no cover
|
2
|
+
from qtpy import PYSIDE6
|
3
|
+
|
4
|
+
if not PYSIDE6:
|
5
|
+
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
6
|
+
return
|
7
|
+
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
8
|
+
|
9
|
+
from bec_widgets.widgets.plots.multi_waveform.multi_waveform_plugin import MultiWaveformPlugin
|
10
|
+
|
11
|
+
QPyDesignerCustomWidgetCollection.addCustomWidget(MultiWaveformPlugin())
|
12
|
+
|
13
|
+
|
14
|
+
if __name__ == "__main__": # pragma: no cover
|
15
|
+
main()
|