bec-widgets 1.25.1__py3-none-any.whl → 2.0.1__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.
Files changed (196) hide show
  1. .gitlab-ci.yml +3 -5
  2. CHANGELOG.md +639 -0
  3. PKG-INFO +3 -3
  4. bec_widgets/__init__.py +4 -0
  5. bec_widgets/applications/bw_launch.py +23 -0
  6. bec_widgets/applications/launch_window.py +430 -0
  7. bec_widgets/assets/app_icons/auto_update.png +0 -0
  8. bec_widgets/assets/app_icons/ui_loader_tile.png +0 -0
  9. bec_widgets/cli/__init__.py +0 -1
  10. bec_widgets/cli/client.py +1779 -2064
  11. bec_widgets/cli/client_utils.py +346 -174
  12. bec_widgets/cli/generate_cli.py +143 -37
  13. bec_widgets/cli/rpc/rpc_base.py +152 -21
  14. bec_widgets/cli/rpc/rpc_register.py +113 -6
  15. bec_widgets/cli/rpc/rpc_widget_handler.py +13 -11
  16. bec_widgets/cli/server.py +125 -239
  17. bec_widgets/examples/jupyter_console/jupyter_console_window.py +97 -145
  18. bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +1 -1
  19. bec_widgets/utils/bec_connector.py +190 -21
  20. bec_widgets/utils/bec_designer.py +7 -0
  21. bec_widgets/utils/bec_dispatcher.py +71 -4
  22. bec_widgets/utils/bec_plugin_helper.py +89 -0
  23. bec_widgets/utils/bec_signal_proxy.py +1 -1
  24. bec_widgets/utils/bec_widget.py +26 -10
  25. bec_widgets/utils/colors.py +1 -1
  26. bec_widgets/{qt_utils → utils}/compact_popup.py +2 -0
  27. bec_widgets/utils/container_utils.py +37 -12
  28. bec_widgets/utils/crosshair.py +25 -8
  29. bec_widgets/utils/entry_validator.py +3 -1
  30. bec_widgets/{qt_utils → utils}/error_popups.py +18 -0
  31. bec_widgets/{qt_utils → utils}/expandable_frame.py +2 -2
  32. bec_widgets/utils/forms_from_types/forms.py +182 -0
  33. bec_widgets/{widgets/editors/scan_metadata/_metadata_widgets.py → utils/forms_from_types/items.py} +41 -30
  34. bec_widgets/utils/generate_designer_plugin.py +40 -36
  35. bec_widgets/utils/linear_region_selector.py +2 -0
  36. bec_widgets/utils/name_utils.py +16 -0
  37. bec_widgets/{qt_utils → utils}/palette_viewer.py +2 -2
  38. bec_widgets/utils/plot_indicator_items.py +2 -5
  39. bec_widgets/utils/plugin_utils.py +47 -1
  40. bec_widgets/{qt_utils → utils}/round_frame.py +14 -14
  41. bec_widgets/utils/rpc_server.py +277 -0
  42. bec_widgets/utils/serialization.py +44 -0
  43. bec_widgets/{qt_utils → utils}/settings_dialog.py +26 -1
  44. bec_widgets/{qt_utils → utils}/side_panel.py +17 -10
  45. bec_widgets/{qt_utils → utils}/toolbar.py +69 -25
  46. bec_widgets/utils/ui_loader.py +8 -8
  47. bec_widgets/utils/widget_io.py +166 -25
  48. bec_widgets/widgets/containers/auto_update/auto_updates.py +364 -0
  49. bec_widgets/widgets/containers/dock/dock.py +157 -49
  50. bec_widgets/widgets/containers/dock/dock_area.py +188 -138
  51. bec_widgets/widgets/containers/layout_manager/layout_manager.py +2 -1
  52. bec_widgets/widgets/containers/main_window/addons/web_links.py +15 -0
  53. bec_widgets/widgets/containers/main_window/main_window.py +189 -41
  54. bec_widgets/widgets/control/buttons/button_abort/button_abort.py +3 -4
  55. bec_widgets/widgets/control/buttons/button_reset/button_reset.py +3 -4
  56. bec_widgets/widgets/control/buttons/button_resume/button_resume.py +3 -3
  57. bec_widgets/widgets/control/buttons/stop_button/stop_button.py +18 -7
  58. bec_widgets/widgets/control/device_control/position_indicator/position_indicator.py +22 -3
  59. bec_widgets/widgets/control/device_control/positioner_box/_base/positioner_box_base.py +31 -13
  60. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +3 -1
  61. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.ui +27 -4
  62. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.py +5 -2
  63. bec_widgets/widgets/control/device_control/positioner_box/positioner_box_2d/positioner_box_2d.ui +97 -31
  64. bec_widgets/widgets/control/device_control/positioner_box/positioner_control_line/positioner_control_line.ui +11 -4
  65. bec_widgets/widgets/control/device_control/positioner_group/positioner_group.py +2 -3
  66. bec_widgets/widgets/control/device_input/base_classes/device_input_base.py +29 -4
  67. bec_widgets/widgets/control/device_input/base_classes/device_signal_input_base.py +1 -0
  68. bec_widgets/widgets/control/device_input/device_combobox/device_combobox.py +2 -2
  69. bec_widgets/widgets/control/device_input/device_line_edit/device_line_edit.py +2 -2
  70. bec_widgets/widgets/control/device_input/signal_combobox/signal_combobox.py +1 -2
  71. bec_widgets/widgets/control/device_input/signal_line_edit/signal_line_edit.py +1 -2
  72. bec_widgets/widgets/control/scan_control/scan_control.py +7 -5
  73. bec_widgets/widgets/control/scan_control/scan_group_box.py +28 -5
  74. bec_widgets/widgets/dap/dap_combo_box/dap_combo_box.py +1 -2
  75. bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog.py +3 -4
  76. bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui +14 -8
  77. bec_widgets/widgets/editors/console/console.py +1 -1
  78. bec_widgets/widgets/editors/{scan_metadata/additional_metadata_table.py → dict_backed_table.py} +29 -6
  79. bec_widgets/widgets/editors/scan_metadata/__init__.py +0 -7
  80. bec_widgets/widgets/editors/scan_metadata/_util.py +1 -1
  81. bec_widgets/widgets/{plots/motor_map/register_bec_motor_map_widget.py → editors/scan_metadata/register_scan_metadata.py} +2 -4
  82. bec_widgets/widgets/editors/scan_metadata/scan_metadata.py +42 -136
  83. bec_widgets/widgets/editors/scan_metadata/scan_metadata.pyproject +1 -0
  84. bec_widgets/widgets/{plots/multi_waveform/bec_multi_waveform_widget_plugin.py → editors/scan_metadata/scan_metadata_plugin.py} +9 -9
  85. bec_widgets/widgets/editors/text_box/text_box.py +2 -3
  86. bec_widgets/widgets/editors/website/website.py +2 -2
  87. bec_widgets/widgets/games/minesweeper.py +3 -2
  88. bec_widgets/widgets/plots/image/image.py +960 -0
  89. bec_widgets/widgets/plots/image/image.pyproject +1 -0
  90. bec_widgets/widgets/plots/image/image_item.py +279 -0
  91. bec_widgets/widgets/plots/{motor_map/bec_motor_map_widget_plugin.py → image/image_plugin.py} +11 -13
  92. bec_widgets/widgets/{containers/figure/plots → plots}/image/image_processor.py +31 -64
  93. bec_widgets/widgets/plots/image/{register_bec_image_widget.py → register_image.py} +2 -2
  94. bec_widgets/widgets/plots/image/toolbar_bundles/image_selection.py +59 -0
  95. bec_widgets/widgets/plots/image/toolbar_bundles/processing.py +79 -0
  96. bec_widgets/widgets/plots/motor_map/motor_map.py +832 -0
  97. bec_widgets/widgets/plots/motor_map/motor_map.pyproject +1 -0
  98. bec_widgets/widgets/plots/motor_map/motor_map_plugin.py +54 -0
  99. bec_widgets/widgets/plots/{multi_waveform/register_bec_multi_waveform_widget.py → motor_map/register_motor_map.py} +2 -4
  100. bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.py +129 -0
  101. bec_widgets/widgets/plots/motor_map/settings/motor_map_settings.ui +120 -0
  102. bec_widgets/widgets/plots/motor_map/toolbar_bundles/motor_selection.py +70 -0
  103. bec_widgets/widgets/plots/multi_waveform/multi_waveform.py +508 -0
  104. bec_widgets/widgets/plots/multi_waveform/multi_waveform.pyproject +1 -0
  105. bec_widgets/widgets/plots/multi_waveform/multi_waveform_plugin.py +54 -0
  106. bec_widgets/widgets/plots/multi_waveform/register_multi_waveform.py +15 -0
  107. bec_widgets/widgets/plots/multi_waveform/settings/control_panel.py +144 -0
  108. bec_widgets/widgets/plots/multi_waveform/settings/multi_waveform_controls.ui +164 -0
  109. bec_widgets/widgets/plots/multi_waveform/toolbar_bundles/monitor_selection.py +65 -0
  110. bec_widgets/widgets/{plots_next_gen → plots}/plot_base.py +321 -40
  111. bec_widgets/widgets/plots/{waveform/register_bec_waveform_widget.py → scatter_waveform/register_scatter_waveform.py} +3 -3
  112. bec_widgets/widgets/plots/scatter_waveform/scatter_curve.py +197 -0
  113. bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.py +553 -0
  114. bec_widgets/widgets/plots/scatter_waveform/scatter_waveform.pyproject +1 -0
  115. bec_widgets/widgets/plots/{image/bec_image_widget_plugin.py → scatter_waveform/scatter_waveform_plugin.py} +9 -13
  116. bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_setting.py +138 -0
  117. bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_horizontal.ui +195 -0
  118. bec_widgets/widgets/plots/scatter_waveform/settings/scatter_curve_settings_vertical.ui +204 -0
  119. bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings.py +8 -8
  120. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/mouse_interactions.py +4 -18
  121. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/plot_export.py +14 -3
  122. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/roi_bundle.py +6 -1
  123. bec_widgets/widgets/{plots_next_gen → plots}/toolbar_bundles/save_state.py +2 -2
  124. bec_widgets/widgets/{containers/figure/plots/waveform/waveform_curve.py → plots/waveform/curve.py} +119 -49
  125. bec_widgets/widgets/plots/waveform/register_waveform.py +15 -0
  126. bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_setting.py +125 -0
  127. bec_widgets/widgets/plots/waveform/settings/curve_settings/curve_tree.py +576 -0
  128. bec_widgets/widgets/plots/waveform/utils/__init__.py +0 -0
  129. bec_widgets/widgets/plots/waveform/utils/roi_manager.py +84 -0
  130. bec_widgets/widgets/plots/waveform/waveform.py +1794 -0
  131. bec_widgets/widgets/plots/waveform/waveform.pyproject +1 -0
  132. bec_widgets/widgets/plots/waveform/{bec_waveform_widget_plugin.py → waveform_plugin.py} +9 -13
  133. bec_widgets/widgets/progress/bec_progressbar/bec_progressbar.py +1 -2
  134. bec_widgets/widgets/progress/ring_progress_bar/ring.py +11 -10
  135. bec_widgets/widgets/progress/ring_progress_bar/ring_progress_bar.py +24 -14
  136. bec_widgets/widgets/services/bec_queue/bec_queue.py +13 -11
  137. bec_widgets/widgets/services/bec_status_box/bec_status_box.py +3 -4
  138. bec_widgets/widgets/services/device_browser/device_browser.py +5 -2
  139. bec_widgets/widgets/services/device_browser/device_item/device_item.py +1 -1
  140. bec_widgets/widgets/utility/logpanel/logpanel.py +36 -17
  141. bec_widgets/widgets/utility/spinbox/decimal_spinbox.py +3 -3
  142. bec_widgets/widgets/utility/visual/color_button/color_button.py +1 -1
  143. bec_widgets/widgets/utility/visual/colormap_widget/colormap_widget.py +4 -6
  144. bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py +4 -8
  145. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.dist-info}/METADATA +3 -3
  146. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.dist-info}/RECORD +168 -153
  147. pyproject.toml +3 -3
  148. bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +0 -198
  149. bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +0 -615
  150. bec_widgets/applications/bec_app.py +0 -84
  151. bec_widgets/cli/auto_updates.py +0 -168
  152. bec_widgets/widgets/containers/figure/__init__.py +0 -1
  153. bec_widgets/widgets/containers/figure/figure.py +0 -796
  154. bec_widgets/widgets/containers/figure/plots/axis_settings.py +0 -91
  155. bec_widgets/widgets/containers/figure/plots/axis_settings.ui +0 -256
  156. bec_widgets/widgets/containers/figure/plots/image/image.py +0 -772
  157. bec_widgets/widgets/containers/figure/plots/image/image_item.py +0 -337
  158. bec_widgets/widgets/containers/figure/plots/motor_map/motor_map.py +0 -525
  159. bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py +0 -340
  160. bec_widgets/widgets/containers/figure/plots/plot_base.py +0 -505
  161. bec_widgets/widgets/containers/figure/plots/waveform/waveform.py +0 -1563
  162. bec_widgets/widgets/plots/image/bec_image_widget.pyproject +0 -1
  163. bec_widgets/widgets/plots/image/image_widget.py +0 -515
  164. bec_widgets/widgets/plots/motor_map/bec_motor_map_widget.pyproject +0 -1
  165. bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.py +0 -56
  166. bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.ui +0 -108
  167. bec_widgets/widgets/plots/motor_map/motor_map_widget.py +0 -234
  168. bec_widgets/widgets/plots/multi_waveform/bec_multi_waveform_widget.pyproject +0 -1
  169. bec_widgets/widgets/plots/multi_waveform/multi_waveform_controls.ui +0 -99
  170. bec_widgets/widgets/plots/multi_waveform/multi_waveform_widget.py +0 -536
  171. bec_widgets/widgets/plots/waveform/bec_waveform_widget.pyproject +0 -1
  172. bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py +0 -336
  173. bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui +0 -372
  174. bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +0 -25
  175. bec_widgets/widgets/plots/waveform/waveform_widget.py +0 -751
  176. /bec_widgets/{qt_utils → utils}/collapsible_panel_manager.py +0 -0
  177. /bec_widgets/{applications/alignment → utils/forms_from_types}/__init__.py +0 -0
  178. /bec_widgets/{qt_utils → utils}/redis_message_waiter.py +0 -0
  179. /bec_widgets/{applications/alignment/alignment_1d → widgets/containers/auto_update}/__init__.py +0 -0
  180. /bec_widgets/{qt_utils → widgets/containers/main_window/addons}/__init__.py +0 -0
  181. /bec_widgets/widgets/{containers/figure/plots → plots/image/toolbar_bundles}/__init__.py +0 -0
  182. /bec_widgets/widgets/{containers/figure/plots/image → plots/motor_map/settings}/__init__.py +0 -0
  183. /bec_widgets/widgets/{containers/figure/plots/motor_map → plots/motor_map/toolbar_bundles}/__init__.py +0 -0
  184. /bec_widgets/widgets/{containers/figure/plots/multi_waveform → plots/multi_waveform/settings}/__init__.py +0 -0
  185. /bec_widgets/widgets/{containers/figure/plots/waveform → plots/multi_waveform/toolbar_bundles}/__init__.py +0 -0
  186. /bec_widgets/widgets/plots/{motor_map/motor_map_dialog → scatter_waveform}/__init__.py +0 -0
  187. /bec_widgets/widgets/plots/{waveform/waveform_popups → scatter_waveform/settings}/__init__.py +0 -0
  188. /bec_widgets/widgets/plots/{waveform/waveform_popups/curve_dialog → setting_menus}/__init__.py +0 -0
  189. /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_horizontal.ui +0 -0
  190. /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_vertical.ui +0 -0
  191. /bec_widgets/widgets/plots/{waveform/waveform_popups/dap_summary_dialog → toolbar_bundles}/__init__.py +0 -0
  192. /bec_widgets/widgets/{plots_next_gen/setting_menus → plots/waveform/settings}/__init__.py +0 -0
  193. /bec_widgets/widgets/{plots_next_gen/toolbar_bundles → plots/waveform/settings/curve_settings}/__init__.py +0 -0
  194. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.dist-info}/WHEEL +0 -0
  195. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.dist-info}/entry_points.txt +0 -0
  196. {bec_widgets-1.25.1.dist-info → bec_widgets-2.0.1.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()