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
@@ -1,505 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import Literal, Optional
|
4
|
-
|
5
|
-
import bec_qthemes
|
6
|
-
import pyqtgraph as pg
|
7
|
-
from bec_lib.logger import bec_logger
|
8
|
-
from pydantic import BaseModel, Field
|
9
|
-
from qtpy.QtCore import Signal, Slot
|
10
|
-
from qtpy.QtWidgets import QApplication, QWidget
|
11
|
-
|
12
|
-
from bec_widgets.utils import BECConnector, ConnectionConfig
|
13
|
-
from bec_widgets.utils.crosshair import Crosshair
|
14
|
-
from bec_widgets.utils.fps_counter import FPSCounter
|
15
|
-
from bec_widgets.utils.plot_indicator_items import BECArrowItem, BECTickItem
|
16
|
-
|
17
|
-
logger = bec_logger.logger
|
18
|
-
|
19
|
-
|
20
|
-
class AxisConfig(BaseModel):
|
21
|
-
title: Optional[str] = Field(None, description="The title of the axes.")
|
22
|
-
title_size: Optional[int] = Field(None, description="The font size of the title.")
|
23
|
-
x_label: Optional[str] = Field(None, description="The label for the x-axis.")
|
24
|
-
x_label_size: Optional[int] = Field(None, description="The font size of the x-axis label.")
|
25
|
-
y_label: Optional[str] = Field(None, description="The label for the y-axis.")
|
26
|
-
y_label_size: Optional[int] = Field(None, description="The font size of the y-axis label.")
|
27
|
-
legend_label_size: Optional[int] = Field(
|
28
|
-
None, description="The font size of the legend labels."
|
29
|
-
)
|
30
|
-
x_scale: Literal["linear", "log"] = Field("linear", description="The scale of the x-axis.")
|
31
|
-
y_scale: Literal["linear", "log"] = Field("linear", description="The scale of the y-axis.")
|
32
|
-
x_lim: Optional[tuple] = Field(None, description="The limits of the x-axis.")
|
33
|
-
y_lim: Optional[tuple] = Field(None, description="The limits of the y-axis.")
|
34
|
-
x_grid: bool = Field(False, description="Show grid on the x-axis.")
|
35
|
-
y_grid: bool = Field(False, description="Show grid on the y-axis.")
|
36
|
-
outer_axes: bool = Field(False, description="Show the outer axes of the plot widget.")
|
37
|
-
model_config: dict = {"validate_assignment": True}
|
38
|
-
|
39
|
-
|
40
|
-
class SubplotConfig(ConnectionConfig):
|
41
|
-
parent_id: Optional[str] = Field(None, description="The parent figure of the plot.")
|
42
|
-
|
43
|
-
# Coordinates in the figure
|
44
|
-
row: int = Field(0, description="The row coordinate in the figure.")
|
45
|
-
col: int = Field(0, description="The column coordinate in the figure.")
|
46
|
-
|
47
|
-
# Appearance settings
|
48
|
-
axis: AxisConfig = Field(
|
49
|
-
default_factory=AxisConfig, description="The axis configuration of the plot."
|
50
|
-
)
|
51
|
-
|
52
|
-
|
53
|
-
class BECViewBox(pg.ViewBox):
|
54
|
-
sigPaint = Signal()
|
55
|
-
|
56
|
-
def paint(self, painter, opt, widget):
|
57
|
-
super().paint(painter, opt, widget)
|
58
|
-
self.sigPaint.emit()
|
59
|
-
|
60
|
-
def itemBoundsChanged(self, item):
|
61
|
-
self._itemBoundsCache.pop(item, None)
|
62
|
-
if (self.state["autoRange"][0] is not False) or (self.state["autoRange"][1] is not False):
|
63
|
-
# check if the call is coming from a mouse-move event
|
64
|
-
if hasattr(item, "skip_auto_range") and item.skip_auto_range:
|
65
|
-
return
|
66
|
-
self._autoRangeNeedsUpdate = True
|
67
|
-
self.update()
|
68
|
-
|
69
|
-
|
70
|
-
class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
71
|
-
crosshair_position_changed = Signal(tuple)
|
72
|
-
crosshair_position_clicked = Signal(tuple)
|
73
|
-
crosshair_coordinates_changed = Signal(tuple)
|
74
|
-
crosshair_coordinates_clicked = Signal(tuple)
|
75
|
-
USER_ACCESS = [
|
76
|
-
"_config_dict",
|
77
|
-
"set",
|
78
|
-
"set_title",
|
79
|
-
"set_x_label",
|
80
|
-
"set_y_label",
|
81
|
-
"set_x_scale",
|
82
|
-
"set_y_scale",
|
83
|
-
"set_x_lim",
|
84
|
-
"set_y_lim",
|
85
|
-
"set_grid",
|
86
|
-
"set_outer_axes",
|
87
|
-
"enable_fps_monitor",
|
88
|
-
"lock_aspect_ratio",
|
89
|
-
"export",
|
90
|
-
"remove",
|
91
|
-
"set_legend_label_size",
|
92
|
-
]
|
93
|
-
|
94
|
-
def __init__(
|
95
|
-
self,
|
96
|
-
parent: Optional[QWidget] = None, # TODO decide if needed for this class
|
97
|
-
parent_figure=None,
|
98
|
-
config: Optional[SubplotConfig] = None,
|
99
|
-
client=None,
|
100
|
-
gui_id: Optional[str] = None,
|
101
|
-
**kwargs,
|
102
|
-
):
|
103
|
-
if config is None:
|
104
|
-
config = SubplotConfig(widget_class=self.__class__.__name__)
|
105
|
-
super().__init__(client=client, config=config, gui_id=gui_id, **kwargs)
|
106
|
-
pg.GraphicsLayout.__init__(self, parent)
|
107
|
-
|
108
|
-
self.figure = parent_figure
|
109
|
-
|
110
|
-
self.plot_item = pg.PlotItem(viewBox=BECViewBox(parent=self, enableMenu=True), parent=self)
|
111
|
-
self.addItem(self.plot_item, row=1, col=0)
|
112
|
-
|
113
|
-
self.add_legend()
|
114
|
-
self.crosshair = None
|
115
|
-
self.fps_monitor = None
|
116
|
-
self.fps_label = None
|
117
|
-
self.tick_item = BECTickItem(parent=self, plot_item=self.plot_item)
|
118
|
-
self.arrow_item = BECArrowItem(parent=self, plot_item=self.plot_item)
|
119
|
-
self._connect_to_theme_change()
|
120
|
-
|
121
|
-
def _connect_to_theme_change(self):
|
122
|
-
"""Connect to the theme change signal."""
|
123
|
-
qapp = QApplication.instance()
|
124
|
-
if hasattr(qapp, "theme_signal"):
|
125
|
-
qapp.theme_signal.theme_updated.connect(self._update_theme)
|
126
|
-
|
127
|
-
@Slot(str)
|
128
|
-
def _update_theme(self, theme: str):
|
129
|
-
"""Update the theme."""
|
130
|
-
if theme is None:
|
131
|
-
qapp = QApplication.instance()
|
132
|
-
if hasattr(qapp, "theme"):
|
133
|
-
theme = qapp.theme.theme
|
134
|
-
else:
|
135
|
-
theme = "dark"
|
136
|
-
self.apply_theme(theme)
|
137
|
-
|
138
|
-
def apply_theme(self, theme: str):
|
139
|
-
"""
|
140
|
-
Apply the theme to the plot widget.
|
141
|
-
|
142
|
-
Args:
|
143
|
-
theme(str, optional): The theme to be applied.
|
144
|
-
"""
|
145
|
-
palette = bec_qthemes.load_palette(theme)
|
146
|
-
text_pen = pg.mkPen(color=palette.text().color())
|
147
|
-
|
148
|
-
for axis in ["left", "bottom", "right", "top"]:
|
149
|
-
self.plot_item.getAxis(axis).setPen(text_pen)
|
150
|
-
self.plot_item.getAxis(axis).setTextPen(text_pen)
|
151
|
-
if self.plot_item.legend is not None:
|
152
|
-
for sample, label in self.plot_item.legend.items:
|
153
|
-
label.setText(label.text, color=palette.text().color())
|
154
|
-
|
155
|
-
def set(self, **kwargs) -> None:
|
156
|
-
"""
|
157
|
-
Set the properties of the plot widget.
|
158
|
-
|
159
|
-
Args:
|
160
|
-
**kwargs: Keyword arguments for the properties to be set.
|
161
|
-
|
162
|
-
Possible properties:
|
163
|
-
- title: str
|
164
|
-
- x_label: str
|
165
|
-
- y_label: str
|
166
|
-
- x_scale: Literal["linear", "log"]
|
167
|
-
- y_scale: Literal["linear", "log"]
|
168
|
-
- x_lim: tuple
|
169
|
-
- y_lim: tuple
|
170
|
-
- legend_label_size: int
|
171
|
-
"""
|
172
|
-
# Mapping of keywords to setter methods
|
173
|
-
method_map = {
|
174
|
-
"title": self.set_title,
|
175
|
-
"x_label": self.set_x_label,
|
176
|
-
"y_label": self.set_y_label,
|
177
|
-
"x_scale": self.set_x_scale,
|
178
|
-
"y_scale": self.set_y_scale,
|
179
|
-
"x_lim": self.set_x_lim,
|
180
|
-
"y_lim": self.set_y_lim,
|
181
|
-
"legend_label_size": self.set_legend_label_size,
|
182
|
-
}
|
183
|
-
for key, value in kwargs.items():
|
184
|
-
if key in method_map:
|
185
|
-
method_map[key](value)
|
186
|
-
else:
|
187
|
-
logger.warning(f"Warning: '{key}' is not a recognized property.")
|
188
|
-
|
189
|
-
def apply_axis_config(self):
|
190
|
-
"""Apply the axis configuration to the plot widget."""
|
191
|
-
config_mappings = {
|
192
|
-
"title": self.config.axis.title,
|
193
|
-
"x_label": self.config.axis.x_label,
|
194
|
-
"y_label": self.config.axis.y_label,
|
195
|
-
"x_scale": self.config.axis.x_scale,
|
196
|
-
"y_scale": self.config.axis.y_scale,
|
197
|
-
"x_lim": self.config.axis.x_lim,
|
198
|
-
"y_lim": self.config.axis.y_lim,
|
199
|
-
}
|
200
|
-
|
201
|
-
self.set(**{k: v for k, v in config_mappings.items() if v is not None})
|
202
|
-
|
203
|
-
def set_legend_label_size(self, size: int = None):
|
204
|
-
"""
|
205
|
-
Set the font size of the legend.
|
206
|
-
|
207
|
-
Args:
|
208
|
-
size(int): Font size of the legend.
|
209
|
-
"""
|
210
|
-
if not self.plot_item.legend:
|
211
|
-
return
|
212
|
-
if self.config.axis.legend_label_size or size:
|
213
|
-
if size:
|
214
|
-
self.config.axis.legend_label_size = size
|
215
|
-
scale = (
|
216
|
-
size / 9
|
217
|
-
) # 9 is the default font size of the legend, so we always scale it against 9
|
218
|
-
self.plot_item.legend.setScale(scale)
|
219
|
-
|
220
|
-
def get_text_color(self):
|
221
|
-
return "#FFF" if self.figure.config.theme == "dark" else "#000"
|
222
|
-
|
223
|
-
def set_title(self, title: str, size: int = None):
|
224
|
-
"""
|
225
|
-
Set the title of the plot widget.
|
226
|
-
|
227
|
-
Args:
|
228
|
-
title(str): Title of the plot widget.
|
229
|
-
size(int): Font size of the title.
|
230
|
-
"""
|
231
|
-
if self.config.axis.title_size or size:
|
232
|
-
if size:
|
233
|
-
self.config.axis.title_size = size
|
234
|
-
style = {"color": self.get_text_color(), "size": f"{self.config.axis.title_size}pt"}
|
235
|
-
else:
|
236
|
-
style = {}
|
237
|
-
self.plot_item.setTitle(title, **style)
|
238
|
-
self.config.axis.title = title
|
239
|
-
|
240
|
-
def set_x_label(self, label: str, size: int = None):
|
241
|
-
"""
|
242
|
-
Set the label of the x-axis.
|
243
|
-
|
244
|
-
Args:
|
245
|
-
label(str): Label of the x-axis.
|
246
|
-
size(int): Font size of the label.
|
247
|
-
"""
|
248
|
-
if self.config.axis.x_label_size or size:
|
249
|
-
if size:
|
250
|
-
self.config.axis.x_label_size = size
|
251
|
-
style = {
|
252
|
-
"color": self.get_text_color(),
|
253
|
-
"font-size": f"{self.config.axis.x_label_size}pt",
|
254
|
-
}
|
255
|
-
else:
|
256
|
-
style = {}
|
257
|
-
self.plot_item.setLabel("bottom", label, **style)
|
258
|
-
self.config.axis.x_label = label
|
259
|
-
|
260
|
-
def set_y_label(self, label: str, size: int = None):
|
261
|
-
"""
|
262
|
-
Set the label of the y-axis.
|
263
|
-
|
264
|
-
Args:
|
265
|
-
label(str): Label of the y-axis.
|
266
|
-
size(int): Font size of the label.
|
267
|
-
"""
|
268
|
-
if self.config.axis.y_label_size or size:
|
269
|
-
if size:
|
270
|
-
self.config.axis.y_label_size = size
|
271
|
-
color = self.get_text_color()
|
272
|
-
style = {"color": color, "font-size": f"{self.config.axis.y_label_size}pt"}
|
273
|
-
else:
|
274
|
-
style = {}
|
275
|
-
self.plot_item.setLabel("left", label, **style)
|
276
|
-
self.config.axis.y_label = label
|
277
|
-
|
278
|
-
def set_x_scale(self, scale: Literal["linear", "log"] = "linear"):
|
279
|
-
"""
|
280
|
-
Set the scale of the x-axis.
|
281
|
-
|
282
|
-
Args:
|
283
|
-
scale(Literal["linear", "log"]): Scale of the x-axis.
|
284
|
-
"""
|
285
|
-
self.plot_item.setLogMode(x=(scale == "log"))
|
286
|
-
self.config.axis.x_scale = scale
|
287
|
-
|
288
|
-
def set_y_scale(self, scale: Literal["linear", "log"] = "linear"):
|
289
|
-
"""
|
290
|
-
Set the scale of the y-axis.
|
291
|
-
|
292
|
-
Args:
|
293
|
-
scale(Literal["linear", "log"]): Scale of the y-axis.
|
294
|
-
"""
|
295
|
-
self.plot_item.setLogMode(y=(scale == "log"))
|
296
|
-
self.config.axis.y_scale = scale
|
297
|
-
|
298
|
-
def set_x_lim(self, *args) -> None:
|
299
|
-
"""
|
300
|
-
Set the limits of the x-axis. This method can accept either two separate arguments
|
301
|
-
for the minimum and maximum x-axis values, or a single tuple containing both limits.
|
302
|
-
|
303
|
-
Usage:
|
304
|
-
set_x_lim(x_min, x_max)
|
305
|
-
set_x_lim((x_min, x_max))
|
306
|
-
|
307
|
-
Args:
|
308
|
-
*args: A variable number of arguments. Can be two integers (x_min and x_max)
|
309
|
-
or a single tuple with two integers.
|
310
|
-
"""
|
311
|
-
if len(args) == 1 and isinstance(args[0], tuple):
|
312
|
-
x_min, x_max = args[0]
|
313
|
-
elif len(args) == 2:
|
314
|
-
x_min, x_max = args
|
315
|
-
else:
|
316
|
-
raise ValueError("set_x_lim expects either two separate arguments or a single tuple")
|
317
|
-
|
318
|
-
self.plot_item.setXRange(x_min, x_max)
|
319
|
-
self.config.axis.x_lim = (x_min, x_max)
|
320
|
-
|
321
|
-
def set_y_lim(self, *args) -> None:
|
322
|
-
"""
|
323
|
-
Set the limits of the y-axis. This method can accept either two separate arguments
|
324
|
-
for the minimum and maximum y-axis values, or a single tuple containing both limits.
|
325
|
-
|
326
|
-
Usage:
|
327
|
-
set_y_lim(y_min, y_max)
|
328
|
-
set_y_lim((y_min, y_max))
|
329
|
-
|
330
|
-
Args:
|
331
|
-
*args: A variable number of arguments. Can be two integers (y_min and y_max)
|
332
|
-
or a single tuple with two integers.
|
333
|
-
"""
|
334
|
-
if len(args) == 1 and isinstance(args[0], tuple):
|
335
|
-
y_min, y_max = args[0]
|
336
|
-
elif len(args) == 2:
|
337
|
-
y_min, y_max = args
|
338
|
-
else:
|
339
|
-
raise ValueError("set_y_lim expects either two separate arguments or a single tuple")
|
340
|
-
|
341
|
-
self.plot_item.setYRange(y_min, y_max)
|
342
|
-
self.config.axis.y_lim = (y_min, y_max)
|
343
|
-
|
344
|
-
def set_grid(self, x: bool = False, y: bool = False):
|
345
|
-
"""
|
346
|
-
Set the grid of the plot widget.
|
347
|
-
|
348
|
-
Args:
|
349
|
-
x(bool): Show grid on the x-axis.
|
350
|
-
y(bool): Show grid on the y-axis.
|
351
|
-
"""
|
352
|
-
self.plot_item.showGrid(x, y)
|
353
|
-
self.config.axis.x_grid = x
|
354
|
-
self.config.axis.y_grid = y
|
355
|
-
|
356
|
-
def set_outer_axes(self, show: bool = True):
|
357
|
-
"""
|
358
|
-
Set the outer axes of the plot widget.
|
359
|
-
|
360
|
-
Args:
|
361
|
-
show(bool): Show the outer axes.
|
362
|
-
"""
|
363
|
-
self.plot_item.showAxis("top", show)
|
364
|
-
self.plot_item.showAxis("right", show)
|
365
|
-
self.config.axis.outer_axes = show
|
366
|
-
|
367
|
-
def add_legend(self):
|
368
|
-
"""Add legend to the plot"""
|
369
|
-
self.plot_item.addLegend()
|
370
|
-
|
371
|
-
def lock_aspect_ratio(self, lock):
|
372
|
-
"""
|
373
|
-
Lock aspect ratio.
|
374
|
-
|
375
|
-
Args:
|
376
|
-
lock(bool): True to lock, False to unlock.
|
377
|
-
"""
|
378
|
-
self.plot_item.setAspectLocked(lock)
|
379
|
-
|
380
|
-
def set_auto_range(self, enabled: bool, axis: str = "xy"):
|
381
|
-
"""
|
382
|
-
Set the auto range of the plot widget.
|
383
|
-
|
384
|
-
Args:
|
385
|
-
enabled(bool): If True, enable the auto range.
|
386
|
-
axis(str, optional): The axis to enable the auto range.
|
387
|
-
- "xy": Enable auto range for both x and y axis.
|
388
|
-
- "x": Enable auto range for x axis.
|
389
|
-
- "y": Enable auto range for y axis.
|
390
|
-
"""
|
391
|
-
self.plot_item.enableAutoRange(axis, enabled)
|
392
|
-
|
393
|
-
############################################################
|
394
|
-
###################### Crosshair ###########################
|
395
|
-
############################################################
|
396
|
-
|
397
|
-
def hook_crosshair(self) -> None:
|
398
|
-
"""Hook the crosshair to all plots."""
|
399
|
-
if self.crosshair is None:
|
400
|
-
self.crosshair = Crosshair(self.plot_item, precision=3)
|
401
|
-
self.crosshair.crosshairChanged.connect(self.crosshair_position_changed)
|
402
|
-
self.crosshair.crosshairClicked.connect(self.crosshair_position_clicked)
|
403
|
-
self.crosshair.coordinatesChanged1D.connect(self.crosshair_coordinates_changed)
|
404
|
-
self.crosshair.coordinatesClicked1D.connect(self.crosshair_coordinates_clicked)
|
405
|
-
self.crosshair.coordinatesChanged2D.connect(self.crosshair_coordinates_changed)
|
406
|
-
self.crosshair.coordinatesClicked2D.connect(self.crosshair_coordinates_clicked)
|
407
|
-
|
408
|
-
def unhook_crosshair(self) -> None:
|
409
|
-
"""Unhook the crosshair from all plots."""
|
410
|
-
if self.crosshair is not None:
|
411
|
-
self.crosshair.crosshairChanged.disconnect(self.crosshair_position_changed)
|
412
|
-
self.crosshair.crosshairClicked.disconnect(self.crosshair_position_clicked)
|
413
|
-
self.crosshair.coordinatesChanged1D.disconnect(self.crosshair_coordinates_changed)
|
414
|
-
self.crosshair.coordinatesClicked1D.disconnect(self.crosshair_coordinates_clicked)
|
415
|
-
self.crosshair.coordinatesChanged2D.disconnect(self.crosshair_coordinates_changed)
|
416
|
-
self.crosshair.coordinatesClicked2D.disconnect(self.crosshair_coordinates_clicked)
|
417
|
-
self.crosshair.cleanup()
|
418
|
-
self.crosshair.deleteLater()
|
419
|
-
self.crosshair = None
|
420
|
-
|
421
|
-
def toggle_crosshair(self) -> None:
|
422
|
-
"""Toggle the crosshair on all plots."""
|
423
|
-
if self.crosshair is None:
|
424
|
-
return self.hook_crosshair()
|
425
|
-
|
426
|
-
self.unhook_crosshair()
|
427
|
-
|
428
|
-
@Slot()
|
429
|
-
def reset(self) -> None:
|
430
|
-
"""Reset the plot widget."""
|
431
|
-
if self.crosshair is not None:
|
432
|
-
self.crosshair.clear_markers()
|
433
|
-
self.crosshair.update_markers()
|
434
|
-
|
435
|
-
############################################################
|
436
|
-
##################### FPS Counter ##########################
|
437
|
-
############################################################
|
438
|
-
|
439
|
-
def update_fps_label(self, fps: float) -> None:
|
440
|
-
"""
|
441
|
-
Update the FPS label.
|
442
|
-
|
443
|
-
Args:
|
444
|
-
fps(float): The frames per second.
|
445
|
-
"""
|
446
|
-
if self.fps_label:
|
447
|
-
self.fps_label.setText(f"FPS: {fps:.2f}")
|
448
|
-
|
449
|
-
def hook_fps_monitor(self):
|
450
|
-
"""Hook the FPS monitor to the plot."""
|
451
|
-
if self.fps_monitor is None:
|
452
|
-
# text_color = self.get_text_color()#TODO later
|
453
|
-
self.fps_monitor = FPSCounter(self.plot_item.vb) # text_color=text_color)
|
454
|
-
self.fps_label = pg.LabelItem(justify="right")
|
455
|
-
self.addItem(self.fps_label, row=0, col=0)
|
456
|
-
|
457
|
-
self.fps_monitor.sigFpsUpdate.connect(self.update_fps_label)
|
458
|
-
|
459
|
-
def unhook_fps_monitor(self, delete_label=True):
|
460
|
-
"""Unhook the FPS monitor from the plot."""
|
461
|
-
if self.fps_monitor is not None:
|
462
|
-
# Remove Monitor
|
463
|
-
self.fps_monitor.cleanup()
|
464
|
-
self.fps_monitor.deleteLater()
|
465
|
-
self.fps_monitor = None
|
466
|
-
if self.fps_label is not None and delete_label:
|
467
|
-
# Remove Label
|
468
|
-
self.removeItem(self.fps_label)
|
469
|
-
self.fps_label.deleteLater()
|
470
|
-
self.fps_label = None
|
471
|
-
|
472
|
-
def enable_fps_monitor(self, enable: bool = True):
|
473
|
-
"""
|
474
|
-
Enable the FPS monitor.
|
475
|
-
|
476
|
-
Args:
|
477
|
-
enable(bool): True to enable, False to disable.
|
478
|
-
"""
|
479
|
-
if enable and self.fps_monitor is None:
|
480
|
-
self.hook_fps_monitor()
|
481
|
-
elif not enable and self.fps_monitor is not None:
|
482
|
-
self.unhook_fps_monitor()
|
483
|
-
|
484
|
-
def export(self):
|
485
|
-
"""Show the Export Dialog of the plot widget."""
|
486
|
-
scene = self.plot_item.scene()
|
487
|
-
scene.contextMenuItem = self.plot_item
|
488
|
-
scene.showExportDialog()
|
489
|
-
|
490
|
-
def remove(self):
|
491
|
-
"""Remove the plot widget from the figure."""
|
492
|
-
if self.figure is not None:
|
493
|
-
self.figure.remove(widget_id=self.gui_id)
|
494
|
-
|
495
|
-
def cleanup_pyqtgraph(self):
|
496
|
-
"""Cleanup pyqtgraph items."""
|
497
|
-
self.unhook_crosshair()
|
498
|
-
self.unhook_fps_monitor(delete_label=False)
|
499
|
-
self.tick_item.cleanup()
|
500
|
-
self.arrow_item.cleanup()
|
501
|
-
item = self.plot_item
|
502
|
-
item.vb.menu.close()
|
503
|
-
item.vb.menu.deleteLater()
|
504
|
-
item.ctrlMenu.close()
|
505
|
-
item.ctrlMenu.deleteLater()
|