bec-widgets 0.87.0__py3-none-any.whl → 0.88.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.
- CHANGELOG.md +66 -70
- PKG-INFO +1 -1
- bec_widgets/assets/designer_icons/BECWaveformWidget.png +0 -0
- bec_widgets/assets/designer_icons/colormap_selector.png +0 -0
- bec_widgets/assets/toolbar_icons/add.svg +3 -0
- bec_widgets/assets/toolbar_icons/auto_range.svg +3 -0
- bec_widgets/assets/toolbar_icons/drag_pan_mode.svg +3 -0
- bec_widgets/assets/toolbar_icons/export.svg +9 -0
- bec_widgets/assets/toolbar_icons/fitting_parameters.svg +3 -0
- bec_widgets/assets/toolbar_icons/import.svg +9 -0
- bec_widgets/assets/toolbar_icons/line_axis.svg +3 -0
- bec_widgets/assets/toolbar_icons/photo_library.svg +3 -0
- bec_widgets/assets/toolbar_icons/rectangle_mode.svg +3 -0
- bec_widgets/assets/toolbar_icons/remove.svg +5 -0
- bec_widgets/assets/toolbar_icons/save.svg +3 -0
- bec_widgets/assets/toolbar_icons/settings.svg +4 -0
- bec_widgets/cli/client.py +307 -5
- bec_widgets/cli/server.py +2 -1
- bec_widgets/examples/jupyter_console/jupyter_console_window.py +13 -7
- bec_widgets/examples/plugin_example_pyside/tictactoe.py +1 -0
- bec_widgets/utils/bec_connector.py +19 -18
- bec_widgets/utils/bec_widget.py +19 -4
- bec_widgets/widgets/base_classes/device_input_base.py +3 -5
- bec_widgets/widgets/bec_queue/bec_queue.py +3 -2
- bec_widgets/widgets/bec_status_box/bec_status_box.py +2 -11
- bec_widgets/widgets/{color_map_selector/color_map_selector.py → colormap_selector/colormap_selector.py} +3 -1
- bec_widgets/widgets/colormap_selector/colormap_selector.pyproject +1 -0
- bec_widgets/widgets/{color_map_selector/color_map_selector_plugin.py → colormap_selector/colormap_selector_plugin.py} +8 -6
- bec_widgets/widgets/{color_map_selector/register_color_map_selector.py → colormap_selector/register_colormap_selector.py} +1 -1
- bec_widgets/widgets/device_box/device_box.py +2 -2
- bec_widgets/widgets/device_combobox/device_combobox.py +1 -8
- bec_widgets/widgets/device_line_edit/device_line_edit.py +2 -9
- bec_widgets/widgets/dock/dock.py +8 -6
- bec_widgets/widgets/dock/dock_area.py +4 -2
- bec_widgets/widgets/figure/figure.py +16 -8
- bec_widgets/widgets/figure/plots/axis_settings.py +38 -11
- bec_widgets/widgets/figure/plots/image/image.py +2 -4
- bec_widgets/widgets/figure/plots/motor_map/motor_map.py +1 -1
- bec_widgets/widgets/figure/plots/plot_base.py +7 -8
- bec_widgets/widgets/figure/plots/waveform/waveform.py +49 -53
- bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +10 -1
- bec_widgets/widgets/jupyter_console/jupyter_console.py +6 -0
- bec_widgets/widgets/motor_map/bec_motor_map_widget_plugin.py +1 -1
- bec_widgets/widgets/motor_map/motor_map_widget.py +9 -6
- bec_widgets/widgets/ring_progress_bar/ring.py +0 -4
- bec_widgets/widgets/ring_progress_bar/ring_progress_bar.py +8 -8
- bec_widgets/widgets/scan_control/scan_control.py +2 -6
- bec_widgets/widgets/stop_button/stop_button.py +2 -2
- bec_widgets/widgets/text_box/text_box.py +3 -2
- bec_widgets/widgets/waveform/__init__.py +0 -0
- bec_widgets/widgets/waveform/bec_waveform_widget.pyproject +1 -0
- bec_widgets/widgets/waveform/bec_waveform_widget_plugin.py +59 -0
- bec_widgets/widgets/waveform/register_bec_waveform_widget.py +15 -0
- bec_widgets/widgets/waveform/waveform_toolbar/__init__.py +0 -0
- bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/__init__.py +0 -0
- bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.py +341 -0
- bec_widgets/widgets/waveform/waveform_toolbar/curve_dialog/curve_dialog.ui +358 -0
- bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/__init__.py +0 -0
- bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary.ui +127 -0
- bec_widgets/widgets/waveform/waveform_toolbar/dap_summary_dialog/dap_summary_dialog.py +69 -0
- bec_widgets/widgets/waveform/waveform_toolbar/waveform_toolbar.py +117 -0
- bec_widgets/widgets/waveform/waveform_widget.py +571 -0
- bec_widgets/widgets/website/website.py +2 -2
- {bec_widgets-0.87.0.dist-info → bec_widgets-0.88.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.87.0.dist-info → bec_widgets-0.88.0.dist-info}/RECORD +75 -48
- pyproject.toml +1 -1
- tests/unit_tests/test_color_map_selector.py +1 -1
- tests/unit_tests/test_device_input_base.py +10 -4
- tests/unit_tests/test_waveform_widget.py +264 -0
- bec_widgets/widgets/color_map_selector/assets/color_map_selector_icon.png +0 -0
- bec_widgets/widgets/color_map_selector/color_map_selector.pyproject +0 -1
- /bec_widgets/assets/{bec_widgets_icon.png → app_icons/bec_widgets_icon.png} +0 -0
- /bec_widgets/assets/{terminal_icon.png → app_icons/terminal_icon.png} +0 -0
- /bec_widgets/widgets/{color_map_selector → colormap_selector}/__init__.py +0 -0
- {bec_widgets-0.87.0.dist-info → bec_widgets-0.88.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.87.0.dist-info → bec_widgets-0.88.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.87.0.dist-info → bec_widgets-0.88.0.dist-info}/licenses/LICENSE +0 -0
@@ -5,15 +5,18 @@ import os
|
|
5
5
|
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
6
6
|
from qtpy.QtGui import QIcon
|
7
7
|
|
8
|
-
|
8
|
+
import bec_widgets
|
9
|
+
from bec_widgets.widgets.colormap_selector.colormap_selector import ColormapSelector
|
9
10
|
|
10
11
|
DOM_XML = """
|
11
12
|
<ui language='c++'>
|
12
|
-
<widget class='ColormapSelector' name='
|
13
|
+
<widget class='ColormapSelector' name='colormap_selector'>
|
13
14
|
</widget>
|
14
15
|
</ui>
|
15
16
|
"""
|
16
17
|
|
18
|
+
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
19
|
+
|
17
20
|
|
18
21
|
class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
19
22
|
def __init__(self):
|
@@ -31,12 +34,11 @@ class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cove
|
|
31
34
|
return "BEC Buttons"
|
32
35
|
|
33
36
|
def icon(self):
|
34
|
-
|
35
|
-
icon_path = os.path.join(current_path, "assets", "color_map_selector_icon.png")
|
37
|
+
icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "colormap_selector.png")
|
36
38
|
return QIcon(icon_path)
|
37
39
|
|
38
40
|
def includeFile(self):
|
39
|
-
return "
|
41
|
+
return "colormap_selector"
|
40
42
|
|
41
43
|
def initialize(self, form_editor):
|
42
44
|
self._form_editor = form_editor
|
@@ -51,7 +53,7 @@ class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cove
|
|
51
53
|
return "ColormapSelector"
|
52
54
|
|
53
55
|
def toolTip(self):
|
54
|
-
return "
|
56
|
+
return ""
|
55
57
|
|
56
58
|
def whatsThis(self):
|
57
59
|
return self.toolTip()
|
@@ -6,7 +6,7 @@ def main(): # pragma: no cover
|
|
6
6
|
return
|
7
7
|
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
8
8
|
|
9
|
-
from bec_widgets.widgets.
|
9
|
+
from bec_widgets.widgets.colormap_selector.colormap_selector_plugin import (
|
10
10
|
ColormapSelectorPlugin,
|
11
11
|
)
|
12
12
|
|
@@ -8,11 +8,11 @@ from qtpy.QtGui import QDoubleValidator
|
|
8
8
|
from qtpy.QtWidgets import QDoubleSpinBox, QVBoxLayout, QWidget
|
9
9
|
|
10
10
|
from bec_widgets.utils import UILoader
|
11
|
-
from bec_widgets.utils.
|
11
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
12
12
|
from bec_widgets.utils.colors import apply_theme
|
13
13
|
|
14
14
|
|
15
|
-
class DeviceBox(
|
15
|
+
class DeviceBox(BECWidget, QWidget):
|
16
16
|
device_changed = Signal(str, str)
|
17
17
|
|
18
18
|
def __init__(self, parent=None, device=None, *args, **kwargs):
|
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|
2
2
|
|
3
3
|
from qtpy.QtWidgets import QComboBox
|
4
4
|
|
5
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
5
6
|
from bec_widgets.widgets.base_classes.device_input_base import DeviceInputBase, DeviceInputConfig
|
6
7
|
|
7
8
|
if TYPE_CHECKING:
|
@@ -82,11 +83,3 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
|
82
83
|
if device_obj is None:
|
83
84
|
raise ValueError(f"Device {device_name} is not found.")
|
84
85
|
return device_obj
|
85
|
-
|
86
|
-
def cleanup(self):
|
87
|
-
"""Cleanup the widget."""
|
88
|
-
super().cleanup()
|
89
|
-
|
90
|
-
def closeEvent(self, event):
|
91
|
-
super().cleanup()
|
92
|
-
return QComboBox.closeEvent(self, event)
|
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from qtpy.QtCore import QSize
|
4
4
|
from qtpy.QtWidgets import QCompleter, QLineEdit, QSizePolicy
|
5
5
|
|
6
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
6
7
|
from bec_widgets.widgets.base_classes.device_input_base import DeviceInputBase, DeviceInputConfig
|
7
8
|
|
8
9
|
if TYPE_CHECKING:
|
@@ -33,8 +34,8 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
|
33
34
|
default: str | None = None,
|
34
35
|
arg_name: str | None = None,
|
35
36
|
):
|
37
|
+
super().__init__(client=client, config=config, gui_id=gui_id)
|
36
38
|
QLineEdit.__init__(self, parent=parent)
|
37
|
-
DeviceInputBase.__init__(self, client=client, config=config, gui_id=gui_id)
|
38
39
|
|
39
40
|
self.completer = QCompleter(self)
|
40
41
|
self.setCompleter(self.completer)
|
@@ -94,11 +95,3 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
|
94
95
|
if device_obj is None:
|
95
96
|
raise ValueError(f"Device {device_name} is not found.")
|
96
97
|
return device_obj
|
97
|
-
|
98
|
-
def cleanup(self):
|
99
|
-
"""Cleanup the widget."""
|
100
|
-
super().cleanup()
|
101
|
-
|
102
|
-
def closeEvent(self, event):
|
103
|
-
super().cleanup()
|
104
|
-
return QLineEdit.closeEvent(self, event)
|
bec_widgets/widgets/dock/dock.py
CHANGED
@@ -6,7 +6,8 @@ from pydantic import Field
|
|
6
6
|
from pyqtgraph.dockarea import Dock
|
7
7
|
|
8
8
|
from bec_widgets.cli.rpc_wigdet_handler import widget_handler
|
9
|
-
from bec_widgets.utils import
|
9
|
+
from bec_widgets.utils import ConnectionConfig, GridLayoutManager
|
10
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
10
11
|
|
11
12
|
if TYPE_CHECKING:
|
12
13
|
from qtpy.QtWidgets import QWidget
|
@@ -24,7 +25,7 @@ class DockConfig(ConnectionConfig):
|
|
24
25
|
)
|
25
26
|
|
26
27
|
|
27
|
-
class BECDock(
|
28
|
+
class BECDock(BECWidget, Dock):
|
28
29
|
USER_ACCESS = [
|
29
30
|
"_config_dict",
|
30
31
|
"_rpc_id",
|
@@ -91,7 +92,7 @@ class BECDock(BECConnector, Dock):
|
|
91
92
|
super().float()
|
92
93
|
|
93
94
|
@property
|
94
|
-
def widget_list(self) -> list[
|
95
|
+
def widget_list(self) -> list[BECWidget]:
|
95
96
|
"""
|
96
97
|
Get the widgets in the dock.
|
97
98
|
|
@@ -101,7 +102,7 @@ class BECDock(BECConnector, Dock):
|
|
101
102
|
return self.widgets
|
102
103
|
|
103
104
|
@widget_list.setter
|
104
|
-
def widget_list(self, value: list[
|
105
|
+
def widget_list(self, value: list[BECWidget]):
|
105
106
|
self.widgets = value
|
106
107
|
|
107
108
|
def hide_title_bar(self):
|
@@ -153,13 +154,13 @@ class BECDock(BECConnector, Dock):
|
|
153
154
|
|
154
155
|
def add_widget(
|
155
156
|
self,
|
156
|
-
widget:
|
157
|
+
widget: BECWidget | str,
|
157
158
|
row=None,
|
158
159
|
col=0,
|
159
160
|
rowspan=1,
|
160
161
|
colspan=1,
|
161
162
|
shift: Literal["down", "up", "left", "right"] = "down",
|
162
|
-
) ->
|
163
|
+
) -> BECWidget:
|
163
164
|
"""
|
164
165
|
Add a widget to the dock.
|
165
166
|
|
@@ -238,6 +239,7 @@ class BECDock(BECConnector, Dock):
|
|
238
239
|
for widget in self.widgets:
|
239
240
|
if hasattr(widget, "cleanup"):
|
240
241
|
widget.cleanup()
|
242
|
+
self.widgets.clear()
|
241
243
|
super().cleanup()
|
242
244
|
|
243
245
|
def close(self):
|
@@ -9,7 +9,8 @@ from qtpy.QtCore import Qt
|
|
9
9
|
from qtpy.QtGui import QPainter, QPaintEvent
|
10
10
|
from qtpy.QtWidgets import QWidget
|
11
11
|
|
12
|
-
from bec_widgets.utils import
|
12
|
+
from bec_widgets.utils import ConnectionConfig, WidgetContainerUtils
|
13
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
13
14
|
|
14
15
|
from .dock import BECDock, DockConfig
|
15
16
|
|
@@ -21,7 +22,7 @@ class DockAreaConfig(ConnectionConfig):
|
|
21
22
|
)
|
22
23
|
|
23
24
|
|
24
|
-
class BECDockArea(
|
25
|
+
class BECDockArea(BECWidget, DockArea):
|
25
26
|
USER_ACCESS = [
|
26
27
|
"_config_dict",
|
27
28
|
"panels",
|
@@ -227,6 +228,7 @@ class BECDockArea(BECConnector, DockArea):
|
|
227
228
|
self.attach_all()
|
228
229
|
for dock in dict(self.docks).values():
|
229
230
|
dock.remove()
|
231
|
+
self.docks.clear()
|
230
232
|
|
231
233
|
def cleanup(self):
|
232
234
|
"""
|
@@ -12,7 +12,8 @@ from qtpy.QtCore import Signal as pyqtSignal
|
|
12
12
|
from qtpy.QtWidgets import QWidget
|
13
13
|
from typeguard import typechecked
|
14
14
|
|
15
|
-
from bec_widgets.utils import
|
15
|
+
from bec_widgets.utils import ConnectionConfig, WidgetContainerUtils
|
16
|
+
from bec_widgets.utils.bec_widget import BECWidget
|
16
17
|
from bec_widgets.utils.colors import apply_theme
|
17
18
|
from bec_widgets.widgets.figure.plots.image.image import BECImageShow, ImageConfig
|
18
19
|
from bec_widgets.widgets.figure.plots.motor_map.motor_map import BECMotorMap, MotorMapConfig
|
@@ -108,7 +109,7 @@ class WidgetHandler:
|
|
108
109
|
return widget
|
109
110
|
|
110
111
|
|
111
|
-
class BECFigure(
|
112
|
+
class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
112
113
|
USER_ACCESS = [
|
113
114
|
"_rpc_id",
|
114
115
|
"_config_dict",
|
@@ -121,6 +122,7 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
121
122
|
"remove",
|
122
123
|
"change_layout",
|
123
124
|
"change_theme",
|
125
|
+
"export",
|
124
126
|
"clear_all",
|
125
127
|
"widget_list",
|
126
128
|
]
|
@@ -227,6 +229,17 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
227
229
|
"""
|
228
230
|
self._widgets = value
|
229
231
|
|
232
|
+
def export(self):
|
233
|
+
"""Export the plot widget."""
|
234
|
+
try:
|
235
|
+
plot_item = self.widget_list[0]
|
236
|
+
except:
|
237
|
+
raise ValueError("No plot widget available to export.")
|
238
|
+
|
239
|
+
scene = plot_item.scene()
|
240
|
+
scene.contextMenuItem = plot_item
|
241
|
+
scene.showExportDialog()
|
242
|
+
|
230
243
|
@typechecked
|
231
244
|
def plot(
|
232
245
|
self,
|
@@ -728,14 +741,9 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
|
728
741
|
"""Clear all widgets from the figure and reset to default state"""
|
729
742
|
for widget in list(self._widgets.values()):
|
730
743
|
widget.remove()
|
731
|
-
|
732
|
-
self._widgets = defaultdict(dict)
|
744
|
+
self._widgets.clear()
|
733
745
|
self.grid = []
|
734
746
|
theme = self.config.theme
|
735
747
|
self.config = FigureConfig(
|
736
748
|
widget_class=self.__class__.__name__, gui_id=self.gui_id, theme=theme
|
737
749
|
)
|
738
|
-
|
739
|
-
# def cleanup(self):
|
740
|
-
# self.clear_all()
|
741
|
-
# super().cleanup()
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
from qtpy.QtCore import Slot
|
4
|
-
from qtpy.QtWidgets import QVBoxLayout
|
4
|
+
from qtpy.QtWidgets import QVBoxLayout
|
5
5
|
|
6
|
+
from bec_widgets.qt_utils.settings_dialog import SettingWidget
|
6
7
|
from bec_widgets.utils import UILoader
|
7
|
-
from bec_widgets.utils.colors import apply_theme
|
8
8
|
from bec_widgets.utils.widget_io import WidgetIO
|
9
9
|
|
10
10
|
|
11
|
-
class AxisSettings(
|
11
|
+
class AxisSettings(SettingWidget):
|
12
12
|
def __init__(self, parent=None, *args, **kwargs):
|
13
13
|
super().__init__(parent=parent, *args, **kwargs)
|
14
14
|
|
@@ -25,6 +25,10 @@ class AxisSettings(QWidget):
|
|
25
25
|
|
26
26
|
@Slot(dict)
|
27
27
|
def display_current_settings(self, axis_config: dict):
|
28
|
+
|
29
|
+
if axis_config == {}:
|
30
|
+
return
|
31
|
+
|
28
32
|
# Top Box
|
29
33
|
WidgetIO.set_value(self.ui.plot_title, axis_config["title"])
|
30
34
|
|
@@ -37,6 +41,10 @@ class AxisSettings(QWidget):
|
|
37
41
|
WidgetIO.check_and_adjust_limits(self.ui.x_max, axis_config["x_lim"][1])
|
38
42
|
WidgetIO.set_value(self.ui.x_min, axis_config["x_lim"][0])
|
39
43
|
WidgetIO.set_value(self.ui.x_max, axis_config["x_lim"][1])
|
44
|
+
if axis_config["x_lim"] is None:
|
45
|
+
x_range = self.target_widget.fig.widget_list[0].plot_item.viewRange()[0]
|
46
|
+
WidgetIO.set_value(self.ui.x_min, x_range[0])
|
47
|
+
WidgetIO.set_value(self.ui.x_max, x_range[1])
|
40
48
|
|
41
49
|
# Y Axis Box
|
42
50
|
WidgetIO.set_value(self.ui.y_label, axis_config["y_label"])
|
@@ -47,15 +55,34 @@ class AxisSettings(QWidget):
|
|
47
55
|
WidgetIO.check_and_adjust_limits(self.ui.y_max, axis_config["y_lim"][1])
|
48
56
|
WidgetIO.set_value(self.ui.y_min, axis_config["y_lim"][0])
|
49
57
|
WidgetIO.set_value(self.ui.y_max, axis_config["y_lim"][1])
|
58
|
+
if axis_config["y_lim"] is None:
|
59
|
+
y_range = self.target_widget.fig.widget_list[0].plot_item.viewRange()[1]
|
60
|
+
WidgetIO.set_value(self.ui.y_min, y_range[0])
|
61
|
+
WidgetIO.set_value(self.ui.y_max, y_range[1])
|
50
62
|
|
63
|
+
@Slot()
|
64
|
+
def accept_changes(self):
|
65
|
+
title = WidgetIO.get_value(self.ui.plot_title)
|
51
66
|
|
52
|
-
|
53
|
-
|
67
|
+
# X Axis
|
68
|
+
x_label = WidgetIO.get_value(self.ui.x_label)
|
69
|
+
x_scale = self.ui.x_scale.currentText()
|
70
|
+
x_grid = WidgetIO.get_value(self.ui.x_grid)
|
71
|
+
x_lim = (WidgetIO.get_value(self.ui.x_min), WidgetIO.get_value(self.ui.x_max))
|
54
72
|
|
55
|
-
|
73
|
+
# Y Axis
|
74
|
+
y_label = WidgetIO.get_value(self.ui.y_label)
|
75
|
+
y_scale = self.ui.y_scale.currentText()
|
76
|
+
y_grid = WidgetIO.get_value(self.ui.y_grid)
|
77
|
+
y_lim = (WidgetIO.get_value(self.ui.y_min), WidgetIO.get_value(self.ui.y_max))
|
56
78
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
79
|
+
self.target_widget.set(
|
80
|
+
title=title,
|
81
|
+
x_label=x_label,
|
82
|
+
x_scale=x_scale,
|
83
|
+
x_lim=x_lim,
|
84
|
+
y_label=y_label,
|
85
|
+
y_scale=y_scale,
|
86
|
+
y_lim=y_lim,
|
87
|
+
)
|
88
|
+
self.target_widget.set_grid(x_grid, y_grid)
|
@@ -55,6 +55,7 @@ class BECImageShow(BECPlotBase):
|
|
55
55
|
"set_y_lim",
|
56
56
|
"set_grid",
|
57
57
|
"lock_aspect_ratio",
|
58
|
+
"export",
|
58
59
|
"remove",
|
59
60
|
"images",
|
60
61
|
]
|
@@ -596,7 +597,4 @@ class BECImageShow(BECPlotBase):
|
|
596
597
|
self.bec_dispatcher.disconnect_slot(
|
597
598
|
self.on_image_update, MessageEndpoints.device_monitor(monitor)
|
598
599
|
)
|
599
|
-
|
600
|
-
image.cleanup()
|
601
|
-
|
602
|
-
super().cleanup()
|
600
|
+
self.images.clear()
|
@@ -58,6 +58,7 @@ class BECMotorMap(BECPlotBase):
|
|
58
58
|
"set_background_value",
|
59
59
|
"set_scatter_size",
|
60
60
|
"get_data",
|
61
|
+
"export",
|
61
62
|
"remove",
|
62
63
|
"reset_history",
|
63
64
|
]
|
@@ -518,4 +519,3 @@ class BECMotorMap(BECPlotBase):
|
|
518
519
|
def cleanup(self):
|
519
520
|
"""Cleanup the widget."""
|
520
521
|
self._disconnect_current_motors()
|
521
|
-
super().cleanup()
|
@@ -2,11 +2,8 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from typing import Literal, Optional
|
4
4
|
|
5
|
-
import numpy as np
|
6
5
|
import pyqtgraph as pg
|
7
6
|
from pydantic import BaseModel, Field
|
8
|
-
from qtpy import QT_VERSION
|
9
|
-
from qtpy.QtGui import QFont, QFontDatabase, QFontInfo
|
10
7
|
from qtpy.QtWidgets import QWidget
|
11
8
|
|
12
9
|
from bec_widgets.utils import BECConnector, ConnectionConfig
|
@@ -57,6 +54,7 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
|
57
54
|
"set_y_lim",
|
58
55
|
"set_grid",
|
59
56
|
"lock_aspect_ratio",
|
57
|
+
"export",
|
60
58
|
"remove",
|
61
59
|
"set_legend_label_size",
|
62
60
|
]
|
@@ -293,12 +291,13 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
|
293
291
|
"""
|
294
292
|
self.plot_item.setAspectLocked(lock)
|
295
293
|
|
294
|
+
def export(self):
|
295
|
+
"""Show the Export Dialog of the plot widget."""
|
296
|
+
scene = self.plot_item.scene()
|
297
|
+
scene.contextMenuItem = self.plot_item
|
298
|
+
scene.showExportDialog()
|
299
|
+
|
296
300
|
def remove(self):
|
297
301
|
"""Remove the plot widget from the figure."""
|
298
302
|
if self.figure is not None:
|
299
|
-
self.cleanup()
|
300
303
|
self.figure.remove(widget_id=self.gui_id)
|
301
|
-
|
302
|
-
def cleanup(self):
|
303
|
-
"""Cleanup the plot widget."""
|
304
|
-
super().cleanup()
|
@@ -1,7 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import datetime
|
4
|
-
import time
|
5
3
|
from collections import defaultdict
|
6
4
|
from typing import Any, Literal, Optional
|
7
5
|
|
@@ -10,7 +8,8 @@ import pyqtgraph as pg
|
|
10
8
|
from bec_lib import messages
|
11
9
|
from bec_lib.device import ReadoutPriority
|
12
10
|
from bec_lib.endpoints import MessageEndpoints
|
13
|
-
from pydantic import Field, ValidationError
|
11
|
+
from pydantic import Field, ValidationError, field_validator
|
12
|
+
from pyqtgraph.exporters import MatplotlibExporter
|
14
13
|
from qtpy.QtCore import Signal as pyqtSignal
|
15
14
|
from qtpy.QtCore import Slot as pyqtSlot
|
16
15
|
from qtpy.QtWidgets import QWidget
|
@@ -26,13 +25,16 @@ from bec_widgets.widgets.figure.plots.waveform.waveform_curve import (
|
|
26
25
|
|
27
26
|
|
28
27
|
class Waveform1DConfig(SubplotConfig):
|
29
|
-
color_palette:
|
30
|
-
"plasma", description="The color palette of the figure widget."
|
28
|
+
color_palette: Optional[str] = Field(
|
29
|
+
"plasma", description="The color palette of the figure widget.", validate_default=True
|
31
30
|
)
|
32
31
|
curves: dict[str, CurveConfig] = Field(
|
33
32
|
{}, description="The list of curves to be added to the 1D waveform widget."
|
34
33
|
)
|
35
34
|
|
35
|
+
model_config: dict = {"validate_assignment": True}
|
36
|
+
_validate_color_map_z = field_validator("color_palette")(Colors.validate_color_map)
|
37
|
+
|
36
38
|
|
37
39
|
class BECWaveform(BECPlotBase):
|
38
40
|
READOUT_PRIORITY_HANDLER = {
|
@@ -63,7 +65,9 @@ class BECWaveform(BECPlotBase):
|
|
63
65
|
"set_x_lim",
|
64
66
|
"set_y_lim",
|
65
67
|
"set_grid",
|
68
|
+
"set_colormap",
|
66
69
|
"lock_aspect_ratio",
|
70
|
+
"export",
|
67
71
|
"remove",
|
68
72
|
"clear_all",
|
69
73
|
"set_legend_label_size",
|
@@ -71,6 +75,7 @@ class BECWaveform(BECPlotBase):
|
|
71
75
|
scan_signal_update = pyqtSignal()
|
72
76
|
async_signal_update = pyqtSignal()
|
73
77
|
dap_params_update = pyqtSignal(dict)
|
78
|
+
dap_summary_update = pyqtSignal(dict)
|
74
79
|
autorange_signal = pyqtSignal()
|
75
80
|
|
76
81
|
def __init__(
|
@@ -385,7 +390,6 @@ class BECWaveform(BECPlotBase):
|
|
385
390
|
|
386
391
|
self.async_signal_update.emit()
|
387
392
|
self.scan_signal_update.emit()
|
388
|
-
# self.autorange_timer.start(200)
|
389
393
|
|
390
394
|
@pyqtSlot()
|
391
395
|
def auto_range(self):
|
@@ -651,6 +655,19 @@ class BECWaveform(BECPlotBase):
|
|
651
655
|
params[curve_id] = curve.dap_params
|
652
656
|
return params
|
653
657
|
|
658
|
+
@pyqtSlot()
|
659
|
+
def get_dap_summary(self) -> dict:
|
660
|
+
"""
|
661
|
+
Get the DAP summary of all DAP curves.
|
662
|
+
|
663
|
+
Returns:
|
664
|
+
dict: DAP summary of all DAP curves.
|
665
|
+
"""
|
666
|
+
summary = {}
|
667
|
+
for curve_id, curve in self._curves_data["DAP"].items():
|
668
|
+
summary[curve_id] = curve.dap_summary
|
669
|
+
return summary
|
670
|
+
|
654
671
|
def _add_curve_object(
|
655
672
|
self,
|
656
673
|
name: str,
|
@@ -954,6 +971,22 @@ class BECWaveform(BECPlotBase):
|
|
954
971
|
current_label = "" if self.config.axis.x_label is None else self.config.axis.x_label
|
955
972
|
self.plot_item.setLabel("bottom", f"{current_label}{self._x_axis_mode['label_suffix']}")
|
956
973
|
|
974
|
+
def set_colormap(self, colormap: str | None = None):
|
975
|
+
"""
|
976
|
+
Set the colormap of the plot widget.
|
977
|
+
|
978
|
+
Args:
|
979
|
+
colormap(str, optional): Scale the colors of curves to colormap. If None, use the default color palette.
|
980
|
+
"""
|
981
|
+
if colormap is not None:
|
982
|
+
self.config.color_palette = colormap
|
983
|
+
|
984
|
+
colors = Colors.golden_angle_color(
|
985
|
+
colormap=self.config.color_palette, num=len(self.plot_item.curves) + 1, format="HEX"
|
986
|
+
)
|
987
|
+
for curve, color in zip(self.curves, colors):
|
988
|
+
curve.set_color(color)
|
989
|
+
|
957
990
|
def setup_dap(self, old_scan_id: str | None, new_scan_id: str | None):
|
958
991
|
"""
|
959
992
|
Setup DAP for the new scan.
|
@@ -1051,7 +1084,9 @@ class BECWaveform(BECPlotBase):
|
|
1051
1084
|
y = msg["data"][0]["y"]
|
1052
1085
|
curve.setData(x, y)
|
1053
1086
|
curve.dap_params = msg["data"][1]["fit_parameters"]
|
1087
|
+
curve.dap_summary = msg["data"][1]["fit_summary"]
|
1054
1088
|
self.dap_params_update.emit(curve.dap_params)
|
1089
|
+
self.dap_summary_update.emit(curve.dap_summary)
|
1055
1090
|
break
|
1056
1091
|
|
1057
1092
|
@pyqtSlot(dict, dict)
|
@@ -1180,7 +1215,6 @@ class BECWaveform(BECPlotBase):
|
|
1180
1215
|
timestamps = self.scan_item.data[y_name][y_entry].timestamps
|
1181
1216
|
|
1182
1217
|
x_data = timestamps
|
1183
|
-
print(x_data)
|
1184
1218
|
return x_data
|
1185
1219
|
if self._x_axis_mode["name"] == "index":
|
1186
1220
|
x_data = None
|
@@ -1214,49 +1248,6 @@ class BECWaveform(BECPlotBase):
|
|
1214
1248
|
x_data = []
|
1215
1249
|
return x_data
|
1216
1250
|
|
1217
|
-
# def _get_x_data(self, curve: BECCurve, y_name: str, y_entry: str) -> list | np.ndarray | None:
|
1218
|
-
# """
|
1219
|
-
# Get the x data for the curve with the decision logic based on the curve configuration:
|
1220
|
-
# - If x is called 'timestamp', use the timestamp data from the scan item.
|
1221
|
-
# - If x is called 'index', use the rolling index.
|
1222
|
-
# - If x is a custom signal, use the data from the scan item.
|
1223
|
-
# - If x is not specified, use the first device from the scan report.
|
1224
|
-
#
|
1225
|
-
# Args:
|
1226
|
-
# curve(BECCurve): The curve object.
|
1227
|
-
#
|
1228
|
-
# Returns:
|
1229
|
-
# list|np.ndarray|None: X data for the curve.
|
1230
|
-
# """
|
1231
|
-
# x_data = None
|
1232
|
-
# if curve.config.signals.x is not None:
|
1233
|
-
# if curve.config.signals.x.name == "timestamp":
|
1234
|
-
# timestamps = self.scan_item.data[y_name][y_entry].timestamps
|
1235
|
-
# x_data = self.convert_timestamps(timestamps)
|
1236
|
-
# elif curve.config.signals.x.name == "index":
|
1237
|
-
# x_data = None
|
1238
|
-
# else:
|
1239
|
-
# x_name = curve.config.signals.x.name
|
1240
|
-
# x_entry = curve.config.signals.x.entry
|
1241
|
-
# try:
|
1242
|
-
# x_data = self.scan_item.data[x_name][x_entry].val
|
1243
|
-
# except TypeError:
|
1244
|
-
# x_data = []
|
1245
|
-
# else:
|
1246
|
-
# if len(self._curves_data["async"]) > 0:
|
1247
|
-
# x_data = None
|
1248
|
-
# else:
|
1249
|
-
# x_name = self.scan_item.status_message.info["scan_report_devices"][0]
|
1250
|
-
# x_entry = self.entry_validator.validate_signal(x_name, None)
|
1251
|
-
# x_data = self.scan_item.data[x_name][x_entry].val
|
1252
|
-
# self._x_axis_mode["label_suffix"] = f" [auto: {x_name}-{x_entry}]"
|
1253
|
-
# current_label = "" if self.config.axis.x_label is None else self.config.axis.x_label
|
1254
|
-
# self.plot_item.setLabel(
|
1255
|
-
# "bottom", f"{current_label}{self._x_axis_mode['label_suffix']}"
|
1256
|
-
# )
|
1257
|
-
#
|
1258
|
-
# return x_data
|
1259
|
-
|
1260
1251
|
def _make_z_gradient(self, data_z: list | np.ndarray, colormap: str) -> list | None:
|
1261
1252
|
"""
|
1262
1253
|
Make a gradient color for the z values.
|
@@ -1349,6 +1340,13 @@ class BECWaveform(BECPlotBase):
|
|
1349
1340
|
return combined_data
|
1350
1341
|
return data
|
1351
1342
|
|
1343
|
+
def export_to_matplotlib(self):
|
1344
|
+
"""
|
1345
|
+
Export current waveform to matplotlib gui. Available only if matplotlib is installed in the enviroment.
|
1346
|
+
|
1347
|
+
"""
|
1348
|
+
MatplotlibExporter(self.plot_item).export()
|
1349
|
+
|
1352
1350
|
def clear_all(self):
|
1353
1351
|
curves_data = self._curves_data
|
1354
1352
|
sources = list(curves_data.keys())
|
@@ -1368,6 +1366,4 @@ class BECWaveform(BECPlotBase):
|
|
1368
1366
|
self.on_async_readback,
|
1369
1367
|
MessageEndpoints.device_async_readback(self.scan_id, curve_id),
|
1370
1368
|
)
|
1371
|
-
|
1372
|
-
curve.cleanup()
|
1373
|
-
super().cleanup()
|
1369
|
+
self.curves.clear()
|
@@ -101,6 +101,7 @@ class BECCurve(BECConnector, pg.PlotDataItem):
|
|
101
101
|
self.parent_item = parent_item
|
102
102
|
self.apply_config()
|
103
103
|
self.dap_params = None
|
104
|
+
self.dap_summary = None
|
104
105
|
if kwargs:
|
105
106
|
self.set(**kwargs)
|
106
107
|
|
@@ -132,6 +133,14 @@ class BECCurve(BECConnector, pg.PlotDataItem):
|
|
132
133
|
def dap_params(self, value):
|
133
134
|
self._dap_params = value
|
134
135
|
|
136
|
+
@property
|
137
|
+
def dap_summary(self):
|
138
|
+
return self._dap_report
|
139
|
+
|
140
|
+
@dap_summary.setter
|
141
|
+
def dap_summary(self, value):
|
142
|
+
self._dap_report = value
|
143
|
+
|
135
144
|
def set_data(self, x, y):
|
136
145
|
if self.config.source == "custom":
|
137
146
|
self.setData(x, y)
|
@@ -262,4 +271,4 @@ class BECCurve(BECConnector, pg.PlotDataItem):
|
|
262
271
|
"""Remove the curve from the plot."""
|
263
272
|
# self.parent_item.removeItem(self)
|
264
273
|
self.parent_item.remove_curve(self.name())
|
265
|
-
self.
|
274
|
+
self.rpc_register.remove_rpc(self)
|
@@ -10,6 +10,7 @@ class BECJupyterConsole(RichJupyterWidget): # pragma: no cover:
|
|
10
10
|
super().__init__()
|
11
11
|
|
12
12
|
self.inprocess = None
|
13
|
+
self.client = None
|
13
14
|
|
14
15
|
self.kernel_manager, self.kernel_client = self._init_kernel(inprocess=inprocess)
|
15
16
|
self.set_default_style("linux")
|
@@ -60,6 +61,11 @@ class BECJupyterConsole(RichJupyterWidget): # pragma: no cover:
|
|
60
61
|
self.kernel_client.stop_channels()
|
61
62
|
self.kernel_manager.shutdown_kernel()
|
62
63
|
|
64
|
+
def closeEvent(self, event):
|
65
|
+
self.shutdown_kernel()
|
66
|
+
if self.client:
|
67
|
+
self.client.shutdown()
|
68
|
+
|
63
69
|
|
64
70
|
if __name__ == "__main__": # pragma: no cover
|
65
71
|
import sys
|