bec-widgets 1.25.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- .gitlab-ci.yml +11 -6
- CHANGELOG.md +650 -0
- PKG-INFO +3 -3
- bec_widgets/__init__.py +4 -0
- bec_widgets/applications/bw_launch.py +23 -0
- bec_widgets/applications/launch_window.py +430 -0
- bec_widgets/assets/app_icons/auto_update.png +0 -0
- bec_widgets/assets/app_icons/ui_loader_tile.png +0 -0
- bec_widgets/cli/__init__.py +0 -1
- bec_widgets/cli/client.py +1779 -2064
- bec_widgets/cli/client_utils.py +346 -174
- bec_widgets/cli/generate_cli.py +143 -37
- bec_widgets/cli/rpc/rpc_base.py +152 -21
- bec_widgets/cli/rpc/rpc_register.py +113 -6
- bec_widgets/cli/rpc/rpc_widget_handler.py +13 -11
- bec_widgets/cli/server.py +125 -239
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +97 -145
- bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +1 -1
- bec_widgets/utils/bec_connector.py +190 -21
- bec_widgets/utils/bec_designer.py +7 -0
- bec_widgets/utils/bec_dispatcher.py +71 -4
- bec_widgets/utils/bec_plugin_helper.py +89 -0
- bec_widgets/utils/bec_signal_proxy.py +1 -1
- bec_widgets/utils/bec_widget.py +26 -10
- bec_widgets/utils/colors.py +1 -1
- bec_widgets/{qt_utils → utils}/compact_popup.py +2 -0
- bec_widgets/utils/container_utils.py +37 -12
- bec_widgets/utils/crosshair.py +25 -8
- bec_widgets/utils/entry_validator.py +3 -1
- bec_widgets/{qt_utils → utils}/error_popups.py +18 -0
- bec_widgets/{qt_utils → utils}/expandable_frame.py +2 -2
- bec_widgets/utils/forms_from_types/forms.py +182 -0
- bec_widgets/{widgets/editors/scan_metadata/_metadata_widgets.py → utils/forms_from_types/items.py} +41 -30
- bec_widgets/utils/generate_designer_plugin.py +40 -36
- bec_widgets/utils/linear_region_selector.py +2 -0
- bec_widgets/utils/name_utils.py +16 -0
- bec_widgets/{qt_utils → utils}/palette_viewer.py +2 -2
- bec_widgets/utils/plot_indicator_items.py +2 -5
- bec_widgets/utils/plugin_utils.py +47 -1
- bec_widgets/{qt_utils → utils}/round_frame.py +14 -14
- bec_widgets/utils/rpc_server.py +277 -0
- bec_widgets/utils/serialization.py +44 -0
- bec_widgets/{qt_utils → utils}/settings_dialog.py +26 -1
- bec_widgets/{qt_utils → utils}/side_panel.py +17 -10
- bec_widgets/{qt_utils → utils}/toolbar.py +69 -25
- bec_widgets/utils/ui_loader.py +8 -8
- bec_widgets/utils/widget_io.py +166 -25
- bec_widgets/widgets/containers/auto_update/auto_updates.py +364 -0
- bec_widgets/widgets/containers/dock/dock.py +157 -49
- bec_widgets/widgets/containers/dock/dock_area.py +186 -138
- bec_widgets/widgets/containers/layout_manager/layout_manager.py +2 -1
- bec_widgets/widgets/containers/main_window/addons/web_links.py +15 -0
- bec_widgets/widgets/containers/main_window/main_window.py +189 -41
- bec_widgets/widgets/control/buttons/button_abort/button_abort.py +3 -4
- bec_widgets/widgets/control/buttons/button_reset/button_reset.py +3 -4
- bec_widgets/widgets/control/buttons/button_resume/button_resume.py +3 -3
- bec_widgets/widgets/control/buttons/stop_button/stop_button.py +18 -7
- bec_widgets/widgets/control/device_control/position_indicator/position_indicator.py +22 -3
- bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +37 -18
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +28 -4
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.ui +27 -4
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +5 -2
- bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +97 -31
- bec_widgets/widgets/control/device_control/positioner_box/positioner_control_line/positioner_control_line.ui +11 -4
- bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +2 -3
- bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +29 -4
- bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +1 -0
- bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +2 -2
- bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py +2 -2
- bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py +1 -2
- bec_widgets/widgets/control/device_input/signal_line_edit/signal_line_edit.py +1 -2
- bec_widgets/widgets/control/scan_control/scan_control.py +7 -5
- bec_widgets/widgets/control/scan_control/scan_group_box.py +28 -5
- bec_widgets/widgets/dap/dap_combo_box/dap_combo_box.py +1 -2
- bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog.py +3 -4
- bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui +14 -8
- bec_widgets/widgets/editors/console/console.py +1 -1
- bec_widgets/widgets/editors/{scan_metadata/additional_metadata_table.py → dict_backed_table.py} +29 -6
- bec_widgets/widgets/editors/scan_metadata/__init__.py +0 -7
- bec_widgets/widgets/editors/scan_metadata/_util.py +1 -1
- bec_widgets/widgets/{plots/motor_map/register_bec_motor_map_widget.py → editors/scan_metadata/register_scan_metadata.py} +2 -4
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.py +42 -136
- bec_widgets/widgets/editors/scan_metadata/scan_metadata.pyproject +1 -0
- bec_widgets/widgets/{plots/multi_waveform/bec_multi_waveform_widget_plugin.py → editors/scan_metadata/scan_metadata_plugin.py} +9 -9
- bec_widgets/widgets/editors/text_box/text_box.py +2 -3
- bec_widgets/widgets/editors/website/website.py +2 -2
- bec_widgets/widgets/games/minesweeper.py +3 -2
- bec_widgets/widgets/plots/image/image.py +960 -0
- bec_widgets/widgets/plots/image/image.pyproject +1 -0
- bec_widgets/widgets/plots/image/image_item.py +279 -0
- bec_widgets/widgets/plots/{motor_map/bec_motor_map_widget_plugin.py → image/image_plugin.py} +11 -13
- bec_widgets/widgets/{containers/figure/plots → plots}/image/image_processor.py +31 -64
- bec_widgets/widgets/plots/image/{register_bec_image_widget.py → register_image.py} +2 -2
- bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +59 -0
- bec_widgets/widgets/plots/image/toolbar_bundles/processing.py +79 -0
- bec_widgets/widgets/plots/motor_map/motor_map.py +832 -0
- bec_widgets/widgets/plots/motor_map/motor_map.pyproject +1 -0
- bec_widgets/widgets/plots/motor_map/motor_map_plugin.py +54 -0
- bec_widgets/widgets/plots/{multi_waveform/register_bec_multi_waveform_widget.py → motor_map/register_motor_map.py} +2 -4
- bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.py +129 -0
- bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.ui +120 -0
- bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py +70 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform.py +508 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform.pyproject +1 -0
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_plugin.py +54 -0
- bec_widgets/widgets/plots/multi_waveform/register_multi_waveform.py +15 -0
- bec_widgets/widgets/plots/multi_waveform/settings/control_panel.py +144 -0
- bec_widgets/widgets/plots/multi_waveform/settings/multi_waveform_controls.ui +164 -0
- bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py +65 -0
- bec_widgets/widgets/{plots_next_gen → plots}/plot_base.py +321 -40
- bec_widgets/widgets/plots/{waveform/register_bec_waveform_widget.py → scatter_waveform/register_scatter_waveform.py} +3 -3
- bec_widgets/widgets/plots/scatter_waveform/scatter_curve.py +197 -0
- bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py +553 -0
- bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.pyproject +1 -0
- bec_widgets/widgets/plots/{image/bec_image_widget_plugin.py → scatter_waveform/scatter_waveform_plugin.py} +9 -13
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py +138 -0
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_horizontal.ui +195 -0
- bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_vertical.ui +204 -0
- bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings.py +8 -8
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/mouse_interactions.py +4 -18
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/plot_export.py +14 -3
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/roi_bundle.py +6 -1
- bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/save_state.py +2 -2
- bec_widgets/widgets/{containers/figure/plots/waveform/waveform_curve.py → plots/waveform/curve.py} +119 -49
- bec_widgets/widgets/plots/waveform/register_waveform.py +15 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +125 -0
- bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +576 -0
- bec_widgets/widgets/plots/waveform/utils/__init__.py +0 -0
- bec_widgets/widgets/plots/waveform/utils/roi_manager.py +84 -0
- bec_widgets/widgets/plots/waveform/waveform.py +1794 -0
- bec_widgets/widgets/plots/waveform/waveform.pyproject +1 -0
- bec_widgets/widgets/plots/waveform/{bec_waveform_widget_plugin.py → waveform_plugin.py} +9 -13
- bec_widgets/widgets/progress/bec_progressbar/bec_progressbar.py +1 -2
- bec_widgets/widgets/progress/ring_progress_bar/ring.py +11 -10
- bec_widgets/widgets/progress/ring_progress_bar/ring_progress_bar.py +24 -14
- bec_widgets/widgets/services/bec_queue/bec_queue.py +13 -11
- bec_widgets/widgets/services/bec_status_box/bec_status_box.py +3 -4
- bec_widgets/widgets/services/device_browser/device_browser.py +5 -2
- bec_widgets/widgets/services/device_browser/device_item/device_item.py +1 -1
- bec_widgets/widgets/utility/logpanel/logpanel.py +36 -17
- bec_widgets/widgets/utility/spinbox/decimal_spinbox.py +3 -3
- bec_widgets/widgets/utility/spinner/spinner.py +2 -2
- bec_widgets/widgets/utility/visual/color_button/color_button.py +1 -1
- bec_widgets/widgets/utility/visual/colormap_widget/colormap_widget.py +4 -6
- bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py +4 -8
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/METADATA +3 -3
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/RECORD +169 -154
- pyproject.toml +3 -3
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +0 -198
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +0 -615
- bec_widgets/applications/bec_app.py +0 -84
- bec_widgets/cli/auto_updates.py +0 -168
- bec_widgets/widgets/containers/figure/__init__.py +0 -1
- bec_widgets/widgets/containers/figure/figure.py +0 -796
- bec_widgets/widgets/containers/figure/plots/axis_settings.py +0 -91
- bec_widgets/widgets/containers/figure/plots/axis_settings.ui +0 -256
- bec_widgets/widgets/containers/figure/plots/image/image.py +0 -772
- bec_widgets/widgets/containers/figure/plots/image/image_item.py +0 -337
- bec_widgets/widgets/containers/figure/plots/motor_map/motor_map.py +0 -525
- bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py +0 -340
- bec_widgets/widgets/containers/figure/plots/plot_base.py +0 -505
- bec_widgets/widgets/containers/figure/plots/waveform/waveform.py +0 -1563
- bec_widgets/widgets/plots/image/bec_image_widget.pyproject +0 -1
- bec_widgets/widgets/plots/image/image_widget.py +0 -515
- bec_widgets/widgets/plots/motor_map/bec_motor_map_widget.pyproject +0 -1
- bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.py +0 -56
- bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.ui +0 -108
- bec_widgets/widgets/plots/motor_map/motor_map_widget.py +0 -234
- bec_widgets/widgets/plots/multi_waveform/bec_multi_waveform_widget.pyproject +0 -1
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_controls.ui +0 -99
- bec_widgets/widgets/plots/multi_waveform/multi_waveform_widget.py +0 -536
- bec_widgets/widgets/plots/waveform/bec_waveform_widget.pyproject +0 -1
- bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py +0 -336
- bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui +0 -372
- bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +0 -25
- bec_widgets/widgets/plots/waveform/waveform_widget.py +0 -751
- /bec_widgets/{qt_utils → utils}/collapsible_panel_manager.py +0 -0
- /bec_widgets/{applications/alignment → utils/forms_from_types}/__init__.py +0 -0
- /bec_widgets/{qt_utils → utils}/redis_message_waiter.py +0 -0
- /bec_widgets/{applications/alignment/alignment_1d → widgets/containers/auto_update}/__init__.py +0 -0
- /bec_widgets/{qt_utils → widgets/containers/main_window/addons}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots → plots/image/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/image → plots/motor_map/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/motor_map → plots/motor_map/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/multi_waveform → plots/multi_waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{containers/figure/plots/waveform → plots/multi_waveform/toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{motor_map/motor_map_dialog → scatter_waveform}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups → scatter_waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups/curve_dialog → setting_menus}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_horizontal.ui +0 -0
- /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_vertical.ui +0 -0
- /bec_widgets/widgets/plots/{waveform/waveform_popups/dap_summary_dialog → toolbar_bundles}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen/setting_menus → plots/waveform/settings}/__init__.py +0 -0
- /bec_widgets/widgets/{plots_next_gen/toolbar_bundles → plots/waveform/settings/curve_settings}/__init__.py +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,772 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from collections import defaultdict
|
4
|
-
from typing import Any, Literal, Optional
|
5
|
-
|
6
|
-
import numpy as np
|
7
|
-
from bec_lib.endpoints import MessageEndpoints
|
8
|
-
from bec_lib.logger import bec_logger
|
9
|
-
from pydantic import Field, ValidationError
|
10
|
-
from qtpy.QtCore import QThread, Slot
|
11
|
-
from qtpy.QtWidgets import QWidget
|
12
|
-
|
13
|
-
# from bec_widgets.qt_utils.error_popups import SafeSlot as Slot
|
14
|
-
from bec_widgets.utils import EntryValidator
|
15
|
-
from bec_widgets.widgets.containers.figure.plots.image.image_item import (
|
16
|
-
BECImageItem,
|
17
|
-
ImageItemConfig,
|
18
|
-
)
|
19
|
-
from bec_widgets.widgets.containers.figure.plots.image.image_processor import (
|
20
|
-
ImageProcessor,
|
21
|
-
ImageStats,
|
22
|
-
ProcessorWorker,
|
23
|
-
)
|
24
|
-
from bec_widgets.widgets.containers.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
25
|
-
|
26
|
-
logger = bec_logger.logger
|
27
|
-
|
28
|
-
|
29
|
-
class ImageConfig(SubplotConfig):
|
30
|
-
images: dict[str, ImageItemConfig] = Field(
|
31
|
-
{},
|
32
|
-
description="The configuration of the images. The key is the name of the image (source).",
|
33
|
-
)
|
34
|
-
|
35
|
-
|
36
|
-
class BECImageShow(BECPlotBase):
|
37
|
-
USER_ACCESS = [
|
38
|
-
"_rpc_id",
|
39
|
-
"_config_dict",
|
40
|
-
"add_image_by_config",
|
41
|
-
"image",
|
42
|
-
"add_custom_image",
|
43
|
-
"set_vrange",
|
44
|
-
"set_color_map",
|
45
|
-
"set_autorange",
|
46
|
-
"set_autorange_mode",
|
47
|
-
"set_monitor",
|
48
|
-
"set_processing",
|
49
|
-
"set_image_properties",
|
50
|
-
"set_fft",
|
51
|
-
"set_log",
|
52
|
-
"set_rotation",
|
53
|
-
"set_transpose",
|
54
|
-
"set",
|
55
|
-
"set_title",
|
56
|
-
"set_x_label",
|
57
|
-
"set_y_label",
|
58
|
-
"set_x_scale",
|
59
|
-
"set_y_scale",
|
60
|
-
"set_x_lim",
|
61
|
-
"set_y_lim",
|
62
|
-
"set_grid",
|
63
|
-
"enable_fps_monitor",
|
64
|
-
"lock_aspect_ratio",
|
65
|
-
"export",
|
66
|
-
"remove",
|
67
|
-
"images",
|
68
|
-
]
|
69
|
-
|
70
|
-
def __init__(
|
71
|
-
self,
|
72
|
-
parent: Optional[QWidget] = None,
|
73
|
-
parent_figure=None,
|
74
|
-
config: Optional[ImageConfig] = None,
|
75
|
-
client=None,
|
76
|
-
gui_id: Optional[str] = None,
|
77
|
-
single_image: bool = True,
|
78
|
-
):
|
79
|
-
if config is None:
|
80
|
-
config = ImageConfig(widget_class=self.__class__.__name__)
|
81
|
-
super().__init__(
|
82
|
-
parent=parent, parent_figure=parent_figure, config=config, client=client, gui_id=gui_id
|
83
|
-
)
|
84
|
-
# Get bec shortcuts dev, scans, queue, scan_storage, dap
|
85
|
-
self.single_image = single_image
|
86
|
-
self.image_type = "device_monitor_2d"
|
87
|
-
self.scan_id = None
|
88
|
-
self.get_bec_shortcuts()
|
89
|
-
self.entry_validator = EntryValidator(self.dev)
|
90
|
-
self._images = defaultdict(dict)
|
91
|
-
self.apply_config(self.config)
|
92
|
-
self.processor = ImageProcessor()
|
93
|
-
self.use_threading = False # TODO WILL be moved to the init method and to figure method
|
94
|
-
|
95
|
-
def _create_thread_worker(self, device: str, image: np.ndarray):
|
96
|
-
thread = QThread()
|
97
|
-
worker = ProcessorWorker(self.processor)
|
98
|
-
worker.moveToThread(thread)
|
99
|
-
|
100
|
-
# Connect signals and slots
|
101
|
-
thread.started.connect(lambda: worker.process_image(device, image))
|
102
|
-
worker.processed.connect(self.update_image)
|
103
|
-
worker.stats.connect(self.update_vrange)
|
104
|
-
worker.finished.connect(thread.quit)
|
105
|
-
worker.finished.connect(thread.wait)
|
106
|
-
worker.finished.connect(worker.deleteLater)
|
107
|
-
thread.finished.connect(thread.deleteLater)
|
108
|
-
|
109
|
-
thread.start()
|
110
|
-
|
111
|
-
def find_image_by_monitor(self, item_id: str) -> BECImageItem:
|
112
|
-
"""
|
113
|
-
Find the image item by its gui_id.
|
114
|
-
|
115
|
-
Args:
|
116
|
-
item_id(str): The gui_id of the widget.
|
117
|
-
|
118
|
-
Returns:
|
119
|
-
BECImageItem: The widget with the given gui_id.
|
120
|
-
"""
|
121
|
-
for source, images in self._images.items():
|
122
|
-
for key, value in images.items():
|
123
|
-
if key == item_id and isinstance(value, BECImageItem):
|
124
|
-
return value
|
125
|
-
elif isinstance(value, dict):
|
126
|
-
result = self.find_image_by_monitor(item_id)
|
127
|
-
if result is not None:
|
128
|
-
return result
|
129
|
-
|
130
|
-
def apply_config(self, config: dict | SubplotConfig):
|
131
|
-
"""
|
132
|
-
Apply the configuration to the 1D waveform widget.
|
133
|
-
|
134
|
-
Args:
|
135
|
-
config(dict|SubplotConfig): Configuration settings.
|
136
|
-
replot_last_scan(bool, optional): If True, replot the last scan. Defaults to False.
|
137
|
-
"""
|
138
|
-
if isinstance(config, dict):
|
139
|
-
try:
|
140
|
-
config = ImageConfig(**config)
|
141
|
-
except ValidationError as e:
|
142
|
-
logger.error(f"Validation error when applying config to BECImageShow: {e}")
|
143
|
-
return
|
144
|
-
self.config = config
|
145
|
-
self.plot_item.clear()
|
146
|
-
|
147
|
-
self.apply_axis_config()
|
148
|
-
self._images = defaultdict(dict)
|
149
|
-
|
150
|
-
for image_id, image_config in config.images.items():
|
151
|
-
self.add_image_by_config(image_config)
|
152
|
-
|
153
|
-
def change_gui_id(self, new_gui_id: str):
|
154
|
-
"""
|
155
|
-
Change the GUI ID of the image widget and update the parent_id in all associated curves.
|
156
|
-
|
157
|
-
Args:
|
158
|
-
new_gui_id (str): The new GUI ID to be set for the image widget.
|
159
|
-
"""
|
160
|
-
self.gui_id = new_gui_id
|
161
|
-
self.config.gui_id = new_gui_id
|
162
|
-
|
163
|
-
for source, images in self._images.items():
|
164
|
-
for id, image_item in images.items():
|
165
|
-
image_item.config.parent_id = new_gui_id
|
166
|
-
|
167
|
-
def add_image_by_config(self, config: ImageItemConfig | dict) -> BECImageItem:
|
168
|
-
"""
|
169
|
-
Add an image to the widget by configuration.
|
170
|
-
|
171
|
-
Args:
|
172
|
-
config(ImageItemConfig|dict): The configuration of the image.
|
173
|
-
|
174
|
-
Returns:
|
175
|
-
BECImageItem: The image object.
|
176
|
-
"""
|
177
|
-
if isinstance(config, dict):
|
178
|
-
config = ImageItemConfig(**config)
|
179
|
-
config.parent_id = self.gui_id
|
180
|
-
name = config.monitor if config.monitor is not None else config.gui_id
|
181
|
-
image = self._add_image_object(source=config.source, name=name, config=config)
|
182
|
-
return image
|
183
|
-
|
184
|
-
def get_image_config(self, image_id, dict_output: bool = True) -> ImageItemConfig | dict:
|
185
|
-
"""
|
186
|
-
Get the configuration of the image.
|
187
|
-
|
188
|
-
Args:
|
189
|
-
image_id(str): The ID of the image.
|
190
|
-
dict_output(bool): Whether to return the configuration as a dictionary. Defaults to True.
|
191
|
-
|
192
|
-
Returns:
|
193
|
-
ImageItemConfig|dict: The configuration of the image.
|
194
|
-
"""
|
195
|
-
for source, images in self._images.items():
|
196
|
-
for id, image in images.items():
|
197
|
-
if id == image_id:
|
198
|
-
if dict_output:
|
199
|
-
return image.config.dict()
|
200
|
-
else:
|
201
|
-
return image.config # TODO check if this works
|
202
|
-
|
203
|
-
@property
|
204
|
-
def images(self) -> list[BECImageItem]:
|
205
|
-
"""
|
206
|
-
Get the list of images.
|
207
|
-
Returns:
|
208
|
-
list[BECImageItem]: The list of images.
|
209
|
-
"""
|
210
|
-
images = []
|
211
|
-
for source, images_dict in self._images.items():
|
212
|
-
for id, image in images_dict.items():
|
213
|
-
images.append(image)
|
214
|
-
return images
|
215
|
-
|
216
|
-
@images.setter
|
217
|
-
def images(self, value: dict[str, dict[str, BECImageItem]]):
|
218
|
-
"""
|
219
|
-
Set the images from a dictionary.
|
220
|
-
|
221
|
-
Args:
|
222
|
-
value (dict[str, dict[str, BECImageItem]]): The images to set, organized by source and id.
|
223
|
-
"""
|
224
|
-
self._images = value
|
225
|
-
|
226
|
-
def get_image_dict(self) -> dict[str, dict[str, BECImageItem]]:
|
227
|
-
"""
|
228
|
-
Get all images.
|
229
|
-
|
230
|
-
Returns:
|
231
|
-
dict[str, dict[str, BECImageItem]]: The dictionary of images.
|
232
|
-
"""
|
233
|
-
return self._images
|
234
|
-
|
235
|
-
def image(
|
236
|
-
self,
|
237
|
-
monitor: str,
|
238
|
-
monitor_type: Literal["1d", "2d"] = "2d",
|
239
|
-
color_map: Optional[str] = "magma",
|
240
|
-
color_bar: Optional[Literal["simple", "full"]] = "full",
|
241
|
-
downsample: Optional[bool] = True,
|
242
|
-
opacity: Optional[float] = 1.0,
|
243
|
-
vrange: Optional[tuple[int, int]] = None,
|
244
|
-
# post_processing: Optional[PostProcessingConfig] = None,
|
245
|
-
**kwargs,
|
246
|
-
) -> BECImageItem:
|
247
|
-
"""
|
248
|
-
Add an image to the figure. Always access the first image widget in the figure.
|
249
|
-
|
250
|
-
Args:
|
251
|
-
monitor(str): The name of the monitor to display.
|
252
|
-
monitor_type(Literal["1d","2d"]): The type of monitor to display.
|
253
|
-
color_bar(Literal["simple","full"]): The type of color bar to display.
|
254
|
-
color_map(str): The color map to use for the image.
|
255
|
-
data(np.ndarray): Custom data to display.
|
256
|
-
vrange(tuple[float, float]): The range of values to display.
|
257
|
-
|
258
|
-
Returns:
|
259
|
-
BECImageItem: The image item.
|
260
|
-
"""
|
261
|
-
if monitor_type == "1d":
|
262
|
-
image_source = "device_monitor_1d"
|
263
|
-
self.image_type = "device_monitor_1d"
|
264
|
-
elif monitor_type == "2d":
|
265
|
-
image_source = "device_monitor_2d"
|
266
|
-
self.image_type = "device_monitor_2d"
|
267
|
-
|
268
|
-
image_exits = self._check_image_id(monitor, self._images)
|
269
|
-
if image_exits:
|
270
|
-
# raise ValueError(
|
271
|
-
# f"Monitor with ID '{monitor}' already exists in widget '{self.gui_id}'."
|
272
|
-
# )
|
273
|
-
return
|
274
|
-
|
275
|
-
# monitor = self.entry_validator.validate_monitor(monitor)
|
276
|
-
|
277
|
-
image_config = ImageItemConfig(
|
278
|
-
widget_class="BECImageItem",
|
279
|
-
parent_id=self.gui_id,
|
280
|
-
color_map=color_map,
|
281
|
-
color_bar=color_bar,
|
282
|
-
downsample=downsample,
|
283
|
-
opacity=opacity,
|
284
|
-
vrange=vrange,
|
285
|
-
source=image_source,
|
286
|
-
monitor=monitor,
|
287
|
-
# post_processing=post_processing,
|
288
|
-
**kwargs,
|
289
|
-
)
|
290
|
-
|
291
|
-
image = self._add_image_object(source=image_source, name=monitor, config=image_config)
|
292
|
-
return image
|
293
|
-
|
294
|
-
def add_custom_image(
|
295
|
-
self,
|
296
|
-
name: str,
|
297
|
-
data: Optional[np.ndarray] = None,
|
298
|
-
color_map: Optional[str] = "magma",
|
299
|
-
color_bar: Optional[Literal["simple", "full"]] = "full",
|
300
|
-
downsample: Optional[bool] = True,
|
301
|
-
opacity: Optional[float] = 1.0,
|
302
|
-
vrange: Optional[tuple[int, int]] = None,
|
303
|
-
# post_processing: Optional[PostProcessingConfig] = None,
|
304
|
-
**kwargs,
|
305
|
-
):
|
306
|
-
image_source = "custom"
|
307
|
-
|
308
|
-
image_exits = self._check_image_id(name, self._images)
|
309
|
-
if image_exits:
|
310
|
-
raise ValueError(f"Monitor with ID '{name}' already exists in widget '{self.gui_id}'.")
|
311
|
-
|
312
|
-
image_config = ImageItemConfig(
|
313
|
-
widget_class="BECImageItem",
|
314
|
-
parent_id=self.gui_id,
|
315
|
-
monitor=name,
|
316
|
-
color_map=color_map,
|
317
|
-
color_bar=color_bar,
|
318
|
-
downsample=downsample,
|
319
|
-
opacity=opacity,
|
320
|
-
vrange=vrange,
|
321
|
-
# post_processing=post_processing,
|
322
|
-
**kwargs,
|
323
|
-
)
|
324
|
-
|
325
|
-
image = self._add_image_object(
|
326
|
-
source=image_source, name=name, config=image_config, data=data
|
327
|
-
)
|
328
|
-
return image
|
329
|
-
|
330
|
-
def apply_setting_to_images(
|
331
|
-
self, setting_method_name: str, args: list, kwargs: dict, image_id: str = None
|
332
|
-
):
|
333
|
-
"""
|
334
|
-
Apply a setting to all images or a specific image by its ID.
|
335
|
-
|
336
|
-
Args:
|
337
|
-
setting_method_name (str): The name of the method to apply (e.g., 'set_color_map').
|
338
|
-
args (list): Positional arguments for the setting method.
|
339
|
-
kwargs (dict): Keyword arguments for the setting method.
|
340
|
-
image_id (str, optional): The ID of the specific image to apply the setting to. If None, applies to all images.
|
341
|
-
"""
|
342
|
-
if image_id:
|
343
|
-
image = self.find_image_by_monitor(image_id)
|
344
|
-
if image:
|
345
|
-
getattr(image, setting_method_name)(*args, **kwargs)
|
346
|
-
else:
|
347
|
-
for source, images in self._images.items():
|
348
|
-
for _, image in images.items():
|
349
|
-
getattr(image, setting_method_name)(*args, **kwargs)
|
350
|
-
self.refresh_image()
|
351
|
-
|
352
|
-
def set_vrange(self, vmin: float, vmax: float, name: str = None):
|
353
|
-
"""
|
354
|
-
Set the range of the color bar.
|
355
|
-
If name is not specified, then set vrange for all images.
|
356
|
-
|
357
|
-
Args:
|
358
|
-
vmin(float): Minimum value of the color bar.
|
359
|
-
vmax(float): Maximum value of the color bar.
|
360
|
-
name(str): The name of the image. If None, apply to all images.
|
361
|
-
"""
|
362
|
-
self.apply_setting_to_images("set_vrange", args=[vmin, vmax], kwargs={}, image_id=name)
|
363
|
-
|
364
|
-
def set_color_map(self, cmap: str, name: str = None):
|
365
|
-
"""
|
366
|
-
Set the color map of the image.
|
367
|
-
If name is not specified, then set color map for all images.
|
368
|
-
|
369
|
-
Args:
|
370
|
-
cmap(str): The color map of the image.
|
371
|
-
name(str): The name of the image. If None, apply to all images.
|
372
|
-
"""
|
373
|
-
self.apply_setting_to_images("set_color_map", args=[cmap], kwargs={}, image_id=name)
|
374
|
-
|
375
|
-
def set_autorange(self, enable: bool = False, name: str = None):
|
376
|
-
"""
|
377
|
-
Set the autoscale of the image.
|
378
|
-
|
379
|
-
Args:
|
380
|
-
enable(bool): Whether to autoscale the color bar.
|
381
|
-
name(str): The name of the image. If None, apply to all images.
|
382
|
-
"""
|
383
|
-
self.apply_setting_to_images("set_autorange", args=[enable], kwargs={}, image_id=name)
|
384
|
-
|
385
|
-
def set_autorange_mode(self, mode: Literal["max", "mean"], name: str = None):
|
386
|
-
"""
|
387
|
-
Set the autoscale mode of the image, that decides how the vrange of the color bar is scaled.
|
388
|
-
Choose betwen 'max' -> min/max of the data, 'mean' -> mean +/- fudge_factor*std of the data (fudge_factor~2).
|
389
|
-
|
390
|
-
Args:
|
391
|
-
mode(str): The autoscale mode of the image.
|
392
|
-
name(str): The name of the image. If None, apply to all images.
|
393
|
-
"""
|
394
|
-
self.apply_setting_to_images("set_autorange_mode", args=[mode], kwargs={}, image_id=name)
|
395
|
-
|
396
|
-
def set_monitor(self, monitor: str, name: str = None):
|
397
|
-
"""
|
398
|
-
Set the monitor of the image.
|
399
|
-
If name is not specified, then set monitor for all images.
|
400
|
-
|
401
|
-
Args:
|
402
|
-
monitor(str): The name of the monitor.
|
403
|
-
name(str): The name of the image. If None, apply to all images.
|
404
|
-
"""
|
405
|
-
self.apply_setting_to_images("set_monitor", args=[monitor], kwargs={}, image_id=name)
|
406
|
-
|
407
|
-
def set_processing(self, name: str = None, **kwargs):
|
408
|
-
"""
|
409
|
-
Set the post processing of the image.
|
410
|
-
If name is not specified, then set post processing for all images.
|
411
|
-
|
412
|
-
Args:
|
413
|
-
name(str): The name of the image. If None, apply to all images.
|
414
|
-
**kwargs: Keyword arguments for the properties to be set.
|
415
|
-
Possible properties:
|
416
|
-
- fft: bool
|
417
|
-
- log: bool
|
418
|
-
- rot: int
|
419
|
-
- transpose: bool
|
420
|
-
"""
|
421
|
-
self.apply_setting_to_images("set", args=[], kwargs=kwargs, image_id=name)
|
422
|
-
|
423
|
-
def set_image_properties(self, name: str = None, **kwargs):
|
424
|
-
"""
|
425
|
-
Set the properties of the image.
|
426
|
-
|
427
|
-
Args:
|
428
|
-
name(str): The name of the image. If None, apply to all images.
|
429
|
-
**kwargs: Keyword arguments for the properties to be set.
|
430
|
-
Possible properties:
|
431
|
-
- downsample: bool
|
432
|
-
- color_map: str
|
433
|
-
- monitor: str
|
434
|
-
- opacity: float
|
435
|
-
- vrange: tuple[int,int]
|
436
|
-
- fft: bool
|
437
|
-
- log: bool
|
438
|
-
- rot: int
|
439
|
-
- transpose: bool
|
440
|
-
"""
|
441
|
-
self.apply_setting_to_images("set", args=[], kwargs=kwargs, image_id=name)
|
442
|
-
|
443
|
-
def set_fft(self, enable: bool = False, name: str = None):
|
444
|
-
"""
|
445
|
-
Set the FFT of the image.
|
446
|
-
If name is not specified, then set FFT for all images.
|
447
|
-
|
448
|
-
Args:
|
449
|
-
enable(bool): Whether to perform FFT on the monitor data.
|
450
|
-
name(str): The name of the image. If None, apply to all images.
|
451
|
-
"""
|
452
|
-
self.apply_setting_to_images("set_fft", args=[enable], kwargs={}, image_id=name)
|
453
|
-
|
454
|
-
def set_log(self, enable: bool = False, name: str = None):
|
455
|
-
"""
|
456
|
-
Set the log of the image.
|
457
|
-
If name is not specified, then set log for all images.
|
458
|
-
|
459
|
-
Args:
|
460
|
-
enable(bool): Whether to perform log on the monitor data.
|
461
|
-
name(str): The name of the image. If None, apply to all images.
|
462
|
-
"""
|
463
|
-
self.apply_setting_to_images("set_log", args=[enable], kwargs={}, image_id=name)
|
464
|
-
|
465
|
-
def set_rotation(self, deg_90: int = 0, name: str = None):
|
466
|
-
"""
|
467
|
-
Set the rotation of the image.
|
468
|
-
If name is not specified, then set rotation for all images.
|
469
|
-
|
470
|
-
Args:
|
471
|
-
deg_90(int): The rotation angle of the monitor data before displaying.
|
472
|
-
name(str): The name of the image. If None, apply to all images.
|
473
|
-
"""
|
474
|
-
self.apply_setting_to_images("set_rotation", args=[deg_90], kwargs={}, image_id=name)
|
475
|
-
|
476
|
-
def set_transpose(self, enable: bool = False, name: str = None):
|
477
|
-
"""
|
478
|
-
Set the transpose of the image.
|
479
|
-
If name is not specified, then set transpose for all images.
|
480
|
-
|
481
|
-
Args:
|
482
|
-
enable(bool): Whether to transpose the monitor data before displaying.
|
483
|
-
name(str): The name of the image. If None, apply to all images.
|
484
|
-
"""
|
485
|
-
self.apply_setting_to_images("set_transpose", args=[enable], kwargs={}, image_id=name)
|
486
|
-
|
487
|
-
def toggle_threading(self, use_threading: bool):
|
488
|
-
"""
|
489
|
-
Toggle threading for the widgets postprocessing and updating.
|
490
|
-
|
491
|
-
Args:
|
492
|
-
use_threading(bool): Whether to use threading.
|
493
|
-
"""
|
494
|
-
self.use_threading = use_threading
|
495
|
-
if self.use_threading is False and self.thread.isRunning():
|
496
|
-
self.cleanup()
|
497
|
-
|
498
|
-
def process_image(self, device: str, image: BECImageItem, data: np.ndarray):
|
499
|
-
"""
|
500
|
-
Process the image data.
|
501
|
-
|
502
|
-
Args:
|
503
|
-
device(str): The name of the device - image_id of image.
|
504
|
-
image(np.ndarray): The image data to be processed.
|
505
|
-
data(np.ndarray): The image data to be processed.
|
506
|
-
|
507
|
-
Returns:
|
508
|
-
np.ndarray: The processed image data.
|
509
|
-
"""
|
510
|
-
processing_config = image.config.processing
|
511
|
-
self.processor.set_config(processing_config)
|
512
|
-
if self.use_threading:
|
513
|
-
self._create_thread_worker(device, data)
|
514
|
-
else:
|
515
|
-
data = self.processor.process_image(data)
|
516
|
-
self.update_image(device, data)
|
517
|
-
self.update_vrange(device, self.processor.config.stats)
|
518
|
-
|
519
|
-
@Slot(dict, dict)
|
520
|
-
def on_image_update(self, msg: dict, metadata: dict):
|
521
|
-
"""
|
522
|
-
Update the image of the device monitor from bec.
|
523
|
-
|
524
|
-
Args:
|
525
|
-
msg(dict): The message from bec.
|
526
|
-
metadata(dict): The metadata of the message.
|
527
|
-
"""
|
528
|
-
data = msg["data"]
|
529
|
-
device = msg["device"]
|
530
|
-
if self.image_type == "device_monitor_1d":
|
531
|
-
image = self._images["device_monitor_1d"][device]
|
532
|
-
current_scan_id = metadata.get("scan_id", None)
|
533
|
-
if current_scan_id is None:
|
534
|
-
return
|
535
|
-
if current_scan_id != self.scan_id:
|
536
|
-
self.reset()
|
537
|
-
self.scan_id = current_scan_id
|
538
|
-
image.image_buffer_list = []
|
539
|
-
image.max_len = 0
|
540
|
-
image_buffer = self.adjust_image_buffer(image, data)
|
541
|
-
image.raw_data = image_buffer
|
542
|
-
self.process_image(device, image, image_buffer)
|
543
|
-
elif self.image_type == "device_monitor_2d":
|
544
|
-
image = self._images["device_monitor_2d"][device]
|
545
|
-
image.raw_data = data
|
546
|
-
self.process_image(device, image, data)
|
547
|
-
|
548
|
-
def adjust_image_buffer(self, image: BECImageItem, new_data: np.ndarray) -> np.ndarray:
|
549
|
-
"""
|
550
|
-
Adjusts the image buffer to accommodate the new data, ensuring that all rows have the same length.
|
551
|
-
|
552
|
-
Args:
|
553
|
-
image: The image object (used to store buffer list and max_len).
|
554
|
-
new_data (np.ndarray): The new incoming 1D waveform data.
|
555
|
-
|
556
|
-
Returns:
|
557
|
-
np.ndarray: The updated image buffer with adjusted shapes.
|
558
|
-
"""
|
559
|
-
new_len = new_data.shape[0]
|
560
|
-
if not hasattr(image, "image_buffer_list"):
|
561
|
-
image.image_buffer_list = []
|
562
|
-
image.max_len = 0
|
563
|
-
|
564
|
-
if new_len > image.max_len:
|
565
|
-
image.max_len = new_len
|
566
|
-
for i in range(len(image.image_buffer_list)):
|
567
|
-
wf = image.image_buffer_list[i]
|
568
|
-
pad_width = image.max_len - wf.shape[0]
|
569
|
-
if pad_width > 0:
|
570
|
-
image.image_buffer_list[i] = np.pad(
|
571
|
-
wf, (0, pad_width), mode="constant", constant_values=0
|
572
|
-
)
|
573
|
-
image.image_buffer_list.append(new_data)
|
574
|
-
else:
|
575
|
-
pad_width = image.max_len - new_len
|
576
|
-
if pad_width > 0:
|
577
|
-
new_data = np.pad(new_data, (0, pad_width), mode="constant", constant_values=0)
|
578
|
-
image.image_buffer_list.append(new_data)
|
579
|
-
|
580
|
-
image_buffer = np.array(image.image_buffer_list)
|
581
|
-
return image_buffer
|
582
|
-
|
583
|
-
@Slot(str, np.ndarray)
|
584
|
-
def update_image(self, device: str, data: np.ndarray):
|
585
|
-
"""
|
586
|
-
Update the image of the device monitor.
|
587
|
-
|
588
|
-
Args:
|
589
|
-
device(str): The name of the device.
|
590
|
-
data(np.ndarray): The data to be updated.
|
591
|
-
"""
|
592
|
-
image_to_update = self._images[self.image_type][device]
|
593
|
-
image_to_update.updateImage(data, autoLevels=image_to_update.config.autorange)
|
594
|
-
|
595
|
-
@Slot(str, ImageStats)
|
596
|
-
def update_vrange(self, device: str, stats: ImageStats):
|
597
|
-
"""
|
598
|
-
Update the scaling of the image.
|
599
|
-
|
600
|
-
Args:
|
601
|
-
stats(ImageStats): The statistics of the image.
|
602
|
-
"""
|
603
|
-
image_to_update = self._images[self.image_type][device]
|
604
|
-
if image_to_update.config.autorange:
|
605
|
-
image_to_update.auto_update_vrange(stats)
|
606
|
-
|
607
|
-
def refresh_image(self):
|
608
|
-
"""
|
609
|
-
Refresh the image.
|
610
|
-
"""
|
611
|
-
for source, images in self._images.items():
|
612
|
-
for image_id, image in images.items():
|
613
|
-
data = image.raw_data
|
614
|
-
self.process_image(image_id, image, data)
|
615
|
-
|
616
|
-
def _connect_device_monitor(self, monitor: str):
|
617
|
-
"""
|
618
|
-
Connect to the device monitor.
|
619
|
-
|
620
|
-
Args:
|
621
|
-
monitor(str): The name of the monitor.
|
622
|
-
"""
|
623
|
-
image_item = self.find_image_by_monitor(monitor)
|
624
|
-
try:
|
625
|
-
previous_monitor = image_item.config.monitor
|
626
|
-
except AttributeError:
|
627
|
-
previous_monitor = None
|
628
|
-
if previous_monitor and image_item.connected is True:
|
629
|
-
self.bec_dispatcher.disconnect_slot(
|
630
|
-
self.on_image_update, MessageEndpoints.device_monitor_1d(previous_monitor)
|
631
|
-
)
|
632
|
-
self.bec_dispatcher.disconnect_slot(
|
633
|
-
self.on_image_update, MessageEndpoints.device_monitor_2d(previous_monitor)
|
634
|
-
)
|
635
|
-
image_item.connected = False
|
636
|
-
if monitor and image_item.connected is False:
|
637
|
-
self.entry_validator.validate_monitor(monitor)
|
638
|
-
if self.image_type == "device_monitor_1d":
|
639
|
-
self.bec_dispatcher.connect_slot(
|
640
|
-
self.on_image_update, MessageEndpoints.device_monitor_1d(monitor)
|
641
|
-
)
|
642
|
-
elif self.image_type == "device_monitor_2d":
|
643
|
-
self.bec_dispatcher.connect_slot(
|
644
|
-
self.on_image_update, MessageEndpoints.device_monitor_2d(monitor)
|
645
|
-
)
|
646
|
-
image_item.set_monitor(monitor)
|
647
|
-
image_item.connected = True
|
648
|
-
|
649
|
-
def _add_image_object(
|
650
|
-
self, source: str, name: str, config: ImageItemConfig, data=None
|
651
|
-
) -> BECImageItem:
|
652
|
-
config.parent_id = self.gui_id
|
653
|
-
if self.single_image is True and len(self.images) > 0:
|
654
|
-
self.remove_image(0)
|
655
|
-
image = BECImageItem(config=config, parent_image=self)
|
656
|
-
self.plot_item.addItem(image)
|
657
|
-
self._images[source][name] = image
|
658
|
-
self._connect_device_monitor(config.monitor)
|
659
|
-
self.config.images[name] = config
|
660
|
-
if data is not None:
|
661
|
-
image.setImage(data)
|
662
|
-
return image
|
663
|
-
|
664
|
-
def _check_image_id(self, val: Any, dict_to_check: dict) -> bool:
|
665
|
-
"""
|
666
|
-
Check if val is in the values of the dict_to_check or in the values of the nested dictionaries.
|
667
|
-
|
668
|
-
Args:
|
669
|
-
val(Any): Value to check.
|
670
|
-
dict_to_check(dict): Dictionary to check.
|
671
|
-
|
672
|
-
Returns:
|
673
|
-
bool: True if val is in the values of the dict_to_check or in the values of the nested dictionaries, False otherwise.
|
674
|
-
"""
|
675
|
-
if val in dict_to_check.keys():
|
676
|
-
return True
|
677
|
-
for key in dict_to_check:
|
678
|
-
if isinstance(dict_to_check[key], dict):
|
679
|
-
if self._check_image_id(val, dict_to_check[key]):
|
680
|
-
return True
|
681
|
-
return False
|
682
|
-
|
683
|
-
def remove_image(self, *identifiers):
|
684
|
-
"""
|
685
|
-
Remove an image from the plot widget.
|
686
|
-
|
687
|
-
Args:
|
688
|
-
*identifiers: Identifier of the image to be removed. Can be either an integer (index) or a string (image_id).
|
689
|
-
"""
|
690
|
-
for identifier in identifiers:
|
691
|
-
if isinstance(identifier, int):
|
692
|
-
self._remove_image_by_order(identifier)
|
693
|
-
elif isinstance(identifier, str):
|
694
|
-
self._remove_image_by_id(identifier)
|
695
|
-
else:
|
696
|
-
raise ValueError(
|
697
|
-
"Each identifier must be either an integer (index) or a string (image_id)."
|
698
|
-
)
|
699
|
-
|
700
|
-
def _remove_image_by_id(self, image_id):
|
701
|
-
for source, images in self._images.items():
|
702
|
-
if image_id in images:
|
703
|
-
self._disconnect_monitor(image_id)
|
704
|
-
image = images.pop(image_id)
|
705
|
-
self.removeItem(image.color_bar)
|
706
|
-
self.plot_item.removeItem(image)
|
707
|
-
del self.config.images[image_id]
|
708
|
-
if image in self.images:
|
709
|
-
self.images.remove(image)
|
710
|
-
return
|
711
|
-
raise KeyError(f"Image with ID '{image_id}' not found.")
|
712
|
-
|
713
|
-
def _remove_image_by_order(self, N):
|
714
|
-
"""
|
715
|
-
Remove an image by its order from the plot widget.
|
716
|
-
|
717
|
-
Args:
|
718
|
-
N(int): Order of the image to be removed.
|
719
|
-
"""
|
720
|
-
if N < len(self.images):
|
721
|
-
image = self.images[N]
|
722
|
-
image_id = image.config.monitor
|
723
|
-
self._disconnect_monitor(image_id)
|
724
|
-
self.removeItem(image.color_bar)
|
725
|
-
self.plot_item.removeItem(image)
|
726
|
-
del self.config.images[image_id]
|
727
|
-
for source, images in self._images.items():
|
728
|
-
if image_id in images:
|
729
|
-
del images[image_id]
|
730
|
-
break
|
731
|
-
else:
|
732
|
-
raise IndexError(f"Image order {N} out of range.")
|
733
|
-
|
734
|
-
def _disconnect_monitor(self, image_id):
|
735
|
-
"""
|
736
|
-
Disconnect the monitor from the device.
|
737
|
-
|
738
|
-
Args:
|
739
|
-
image_id(str): The ID of the monitor.
|
740
|
-
"""
|
741
|
-
image = self.find_image_by_monitor(image_id)
|
742
|
-
if image:
|
743
|
-
self.bec_dispatcher.disconnect_slot(
|
744
|
-
self.on_image_update, MessageEndpoints.device_monitor_1d(image.config.monitor)
|
745
|
-
)
|
746
|
-
self.bec_dispatcher.disconnect_slot(
|
747
|
-
self.on_image_update, MessageEndpoints.device_monitor_2d(image.config.monitor)
|
748
|
-
)
|
749
|
-
|
750
|
-
def cleanup(self):
|
751
|
-
"""
|
752
|
-
Clean up the widget.
|
753
|
-
"""
|
754
|
-
for monitor in self._images[self.image_type]:
|
755
|
-
self.bec_dispatcher.disconnect_slot(
|
756
|
-
self.on_image_update, MessageEndpoints.device_monitor_1d(monitor)
|
757
|
-
)
|
758
|
-
self.images.clear()
|
759
|
-
|
760
|
-
def cleanup_pyqtgraph(self):
|
761
|
-
"""Cleanup pyqtgraph items."""
|
762
|
-
super().cleanup_pyqtgraph()
|
763
|
-
item = self.plot_item
|
764
|
-
if not item.items:
|
765
|
-
return
|
766
|
-
cbar = item.items[0].color_bar
|
767
|
-
cbar.vb.menu.close()
|
768
|
-
cbar.vb.menu.deleteLater()
|
769
|
-
cbar.gradient.menu.close()
|
770
|
-
cbar.gradient.menu.deleteLater()
|
771
|
-
cbar.gradient.colorDialog.close()
|
772
|
-
cbar.gradient.colorDialog.deleteLater()
|