bec-widgets 1.25.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. .gitlab-ci.yml +11 -6
  2. CHANGELOG.md +650 -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 +186 -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 +37 -18
  60. bec_widgets/widgets/control/device_control/positioner_box/positioner_box/positioner_box.py +28 -4
  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/spinner/spinner.py +2 -2
  143. bec_widgets/widgets/utility/visual/color_button/color_button.py +1 -1
  144. bec_widgets/widgets/utility/visual/colormap_widget/colormap_widget.py +4 -6
  145. bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py +4 -8
  146. {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/METADATA +3 -3
  147. {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/RECORD +169 -154
  148. pyproject.toml +3 -3
  149. bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +0 -198
  150. bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +0 -615
  151. bec_widgets/applications/bec_app.py +0 -84
  152. bec_widgets/cli/auto_updates.py +0 -168
  153. bec_widgets/widgets/containers/figure/__init__.py +0 -1
  154. bec_widgets/widgets/containers/figure/figure.py +0 -796
  155. bec_widgets/widgets/containers/figure/plots/axis_settings.py +0 -91
  156. bec_widgets/widgets/containers/figure/plots/axis_settings.ui +0 -256
  157. bec_widgets/widgets/containers/figure/plots/image/image.py +0 -772
  158. bec_widgets/widgets/containers/figure/plots/image/image_item.py +0 -337
  159. bec_widgets/widgets/containers/figure/plots/motor_map/motor_map.py +0 -525
  160. bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py +0 -340
  161. bec_widgets/widgets/containers/figure/plots/plot_base.py +0 -505
  162. bec_widgets/widgets/containers/figure/plots/waveform/waveform.py +0 -1563
  163. bec_widgets/widgets/plots/image/bec_image_widget.pyproject +0 -1
  164. bec_widgets/widgets/plots/image/image_widget.py +0 -515
  165. bec_widgets/widgets/plots/motor_map/bec_motor_map_widget.pyproject +0 -1
  166. bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.py +0 -56
  167. bec_widgets/widgets/plots/motor_map/motor_map_dialog/motor_map_settings.ui +0 -108
  168. bec_widgets/widgets/plots/motor_map/motor_map_widget.py +0 -234
  169. bec_widgets/widgets/plots/multi_waveform/bec_multi_waveform_widget.pyproject +0 -1
  170. bec_widgets/widgets/plots/multi_waveform/multi_waveform_controls.ui +0 -99
  171. bec_widgets/widgets/plots/multi_waveform/multi_waveform_widget.py +0 -536
  172. bec_widgets/widgets/plots/waveform/bec_waveform_widget.pyproject +0 -1
  173. bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.py +0 -336
  174. bec_widgets/widgets/plots/waveform/waveform_popups/curve_dialog/curve_dialog.ui +0 -372
  175. bec_widgets/widgets/plots/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +0 -25
  176. bec_widgets/widgets/plots/waveform/waveform_widget.py +0 -751
  177. /bec_widgets/{qt_utils → utils}/collapsible_panel_manager.py +0 -0
  178. /bec_widgets/{applications/alignment → utils/forms_from_types}/__init__.py +0 -0
  179. /bec_widgets/{qt_utils → utils}/redis_message_waiter.py +0 -0
  180. /bec_widgets/{applications/alignment/alignment_1d → widgets/containers/auto_update}/__init__.py +0 -0
  181. /bec_widgets/{qt_utils → widgets/containers/main_window/addons}/__init__.py +0 -0
  182. /bec_widgets/widgets/{containers/figure/plots → plots/image/toolbar_bundles}/__init__.py +0 -0
  183. /bec_widgets/widgets/{containers/figure/plots/image → plots/motor_map/settings}/__init__.py +0 -0
  184. /bec_widgets/widgets/{containers/figure/plots/motor_map → plots/motor_map/toolbar_bundles}/__init__.py +0 -0
  185. /bec_widgets/widgets/{containers/figure/plots/multi_waveform → plots/multi_waveform/settings}/__init__.py +0 -0
  186. /bec_widgets/widgets/{containers/figure/plots/waveform → plots/multi_waveform/toolbar_bundles}/__init__.py +0 -0
  187. /bec_widgets/widgets/plots/{motor_map/motor_map_dialog → scatter_waveform}/__init__.py +0 -0
  188. /bec_widgets/widgets/plots/{waveform/waveform_popups → scatter_waveform/settings}/__init__.py +0 -0
  189. /bec_widgets/widgets/plots/{waveform/waveform_popups/curve_dialog → setting_menus}/__init__.py +0 -0
  190. /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_horizontal.ui +0 -0
  191. /bec_widgets/widgets/{plots_next_gen → plots}/setting_menus/axis_settings_vertical.ui +0 -0
  192. /bec_widgets/widgets/plots/{waveform/waveform_popups/dap_summary_dialog → toolbar_bundles}/__init__.py +0 -0
  193. /bec_widgets/widgets/{plots_next_gen/setting_menus → plots/waveform/settings}/__init__.py +0 -0
  194. /bec_widgets/widgets/{plots_next_gen/toolbar_bundles → plots/waveform/settings/curve_settings}/__init__.py +0 -0
  195. {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/WHEEL +0 -0
  196. {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/entry_points.txt +0 -0
  197. {bec_widgets-1.25.0.dist-info → bec_widgets-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -8,22 +8,23 @@ from bec_lib import bec_logger
8
8
  from qtpy.QtCore import QPoint, QPointF, Qt, Signal
9
9
  from qtpy.QtWidgets import QHBoxLayout, QLabel, QMainWindow, QVBoxLayout, QWidget
10
10
 
11
- from bec_widgets.qt_utils.error_popups import SafeProperty, SafeSlot
12
- from bec_widgets.qt_utils.round_frame import RoundedFrame
13
- from bec_widgets.qt_utils.settings_dialog import SettingsDialog
14
- from bec_widgets.qt_utils.side_panel import SidePanel
15
- from bec_widgets.qt_utils.toolbar import MaterialIconAction, ModularToolBar, ToolbarBundle
16
11
  from bec_widgets.utils import ConnectionConfig, Crosshair, EntryValidator
17
12
  from bec_widgets.utils.bec_widget import BECWidget
13
+ from bec_widgets.utils.error_popups import SafeProperty, SafeSlot
18
14
  from bec_widgets.utils.fps_counter import FPSCounter
15
+ from bec_widgets.utils.plot_indicator_items import BECArrowItem, BECTickItem
16
+ from bec_widgets.utils.round_frame import RoundedFrame
17
+ from bec_widgets.utils.settings_dialog import SettingsDialog
18
+ from bec_widgets.utils.side_panel import SidePanel
19
+ from bec_widgets.utils.toolbar import MaterialIconAction, ModularToolBar, ToolbarBundle
19
20
  from bec_widgets.utils.widget_state_manager import WidgetStateManager
20
21
  from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget
21
- from bec_widgets.widgets.plots_next_gen.setting_menus.axis_settings import AxisSettings
22
- from bec_widgets.widgets.plots_next_gen.toolbar_bundles.mouse_interactions import (
22
+ from bec_widgets.widgets.plots.setting_menus.axis_settings import AxisSettings
23
+ from bec_widgets.widgets.plots.toolbar_bundles.mouse_interactions import (
23
24
  MouseInteractionToolbarBundle,
24
25
  )
25
- from bec_widgets.widgets.plots_next_gen.toolbar_bundles.plot_export import PlotExportBundle
26
- from bec_widgets.widgets.plots_next_gen.toolbar_bundles.roi_bundle import ROIBundle
26
+ from bec_widgets.widgets.plots.toolbar_bundles.plot_export import PlotExportBundle
27
+ from bec_widgets.widgets.plots.toolbar_bundles.roi_bundle import ROIBundle
27
28
 
28
29
  logger = bec_logger.logger
29
30
 
@@ -68,16 +69,14 @@ class PlotBase(BECWidget, QWidget):
68
69
  config: ConnectionConfig | None = None,
69
70
  client=None,
70
71
  gui_id: str | None = None,
71
- popups: bool = False,
72
+ popups: bool = True,
72
73
  **kwargs,
73
74
  ) -> None:
74
75
  if config is None:
75
76
  config = ConnectionConfig(widget_class=self.__class__.__name__)
76
- super().__init__(client=client, gui_id=gui_id, config=config, **kwargs)
77
- QWidget.__init__(self, parent=parent)
77
+ super().__init__(parent=parent, client=client, gui_id=gui_id, config=config, **kwargs)
78
78
 
79
79
  # For PropertyManager identification
80
- self.setObjectName("PlotBase")
81
80
  self.get_bec_shortcuts()
82
81
 
83
82
  # Layout Management
@@ -95,13 +94,14 @@ class PlotBase(BECWidget, QWidget):
95
94
  self.entry_validator = EntryValidator(self.dev)
96
95
 
97
96
  # Base widgets elements
97
+ self._popups = popups
98
98
  self._ui_mode = UIMode.POPUP if popups else UIMode.SIDE
99
99
  self.axis_settings_dialog = None
100
100
  self.plot_widget = pg.GraphicsLayoutWidget(parent=self)
101
101
  self.plot_item = pg.PlotItem(viewBox=BECViewBox(enableMenu=True))
102
102
  self.plot_widget.addItem(self.plot_item)
103
103
  self.side_panel = SidePanel(self, orientation="left", panel_max_width=280)
104
- self.toolbar = ModularToolBar(target_widget=self, orientation="horizontal")
104
+ self.toolbar = ModularToolBar(parent=self, target_widget=self, orientation="horizontal")
105
105
  self._init_toolbar()
106
106
 
107
107
  # PlotItem Addons
@@ -111,6 +111,12 @@ class PlotBase(BECWidget, QWidget):
111
111
  self.fps_label = QLabel(alignment=Qt.AlignmentFlag.AlignRight)
112
112
  self._user_x_label = ""
113
113
  self._x_label_suffix = ""
114
+ self._user_y_label = ""
115
+ self._y_label_suffix = ""
116
+
117
+ # Plot Indicator Items
118
+ self.tick_item = BECTickItem(parent=self, plot_item=self.plot_item)
119
+ self.arrow_item = BECArrowItem(parent=self, plot_item=self.plot_item)
114
120
 
115
121
  self._init_ui()
116
122
 
@@ -122,7 +128,7 @@ class PlotBase(BECWidget, QWidget):
122
128
 
123
129
  def _init_ui(self):
124
130
  self.layout.addWidget(self.layout_manager)
125
- self.round_plot_widget = RoundedFrame(content_widget=self.plot_widget, theme_update=True)
131
+ self.round_plot_widget = RoundedFrame(parent=self, content_widget=self.plot_widget)
126
132
 
127
133
  self.layout_manager.add_widget(self.round_plot_widget)
128
134
  self.layout_manager.add_widget_relative(self.fps_label, self.round_plot_widget, "top")
@@ -164,11 +170,14 @@ class PlotBase(BECWidget, QWidget):
164
170
  # hide some options by default
165
171
  self.toolbar.toggle_action_visibility("fps_monitor", False)
166
172
 
173
+ # Get default viewbox state
174
+ self.mouse_bundle.get_viewbox_mode()
175
+
167
176
  def add_side_menus(self):
168
177
  """Adds multiple menus to the side panel."""
169
178
  # Setting Axis Widget
170
179
  try:
171
- axis_setting = AxisSettings(target_widget=self)
180
+ axis_setting = AxisSettings(parent=self, target_widget=self)
172
181
  self.side_panel.add_menu(
173
182
  action_id="axis",
174
183
  icon_name="settings",
@@ -197,7 +206,7 @@ class PlotBase(BECWidget, QWidget):
197
206
  """
198
207
  settings_action = self.toolbar.widgets["axis"].action
199
208
  if self.axis_settings_dialog is None or not self.axis_settings_dialog.isVisible():
200
- axis_setting = AxisSettings(target_widget=self, popup=True)
209
+ axis_setting = AxisSettings(parent=self, target_widget=self, popup=True)
201
210
  self.axis_settings_dialog = SettingsDialog(
202
211
  self, settings_widget=axis_setting, window_title="Axis Settings", modal=False
203
212
  )
@@ -218,15 +227,28 @@ class PlotBase(BECWidget, QWidget):
218
227
  self.axis_settings_dialog = None
219
228
  self.toolbar.widgets["axis"].action.setChecked(False)
220
229
 
230
+ def reset_legend(self):
231
+ """In the case that the legend is not visible, reset it to be visible to top left corner"""
232
+ self.plot_item.legend.autoAnchor(50)
233
+
221
234
  ################################################################################
222
235
  # Toggle UI Elements
223
236
  ################################################################################
224
237
  @property
225
238
  def ui_mode(self) -> UIMode:
239
+ """
240
+ Get the UI mode.
241
+ """
226
242
  return self._ui_mode
227
243
 
228
244
  @ui_mode.setter
229
245
  def ui_mode(self, mode: UIMode):
246
+ """
247
+ Set the UI mode.
248
+
249
+ Args:
250
+ mode(UIMode): The UI mode to set.
251
+ """
230
252
  if not isinstance(mode, UIMode):
231
253
  raise ValueError("ui_mode must be an instance of UIMode")
232
254
  self._ui_mode = mode
@@ -252,10 +274,19 @@ class PlotBase(BECWidget, QWidget):
252
274
 
253
275
  @SafeProperty(bool, doc="Enable popups setting dialogs for the plot widget.")
254
276
  def enable_popups(self):
277
+ """
278
+ Enable popups setting dialogs for the plot widget.
279
+ """
255
280
  return self.ui_mode == UIMode.POPUP
256
281
 
257
282
  @enable_popups.setter
258
283
  def enable_popups(self, value: bool):
284
+ """
285
+ Set the popups setting dialogs for the plot widget.
286
+
287
+ Args:
288
+ value(bool): The value to set.
289
+ """
259
290
  if value:
260
291
  self.ui_mode = UIMode.POPUP
261
292
  else:
@@ -264,10 +295,19 @@ class PlotBase(BECWidget, QWidget):
264
295
 
265
296
  @SafeProperty(bool, doc="Show Side Panel")
266
297
  def enable_side_panel(self) -> bool:
298
+ """
299
+ Show Side Panel
300
+ """
267
301
  return self.ui_mode == UIMode.SIDE
268
302
 
269
303
  @enable_side_panel.setter
270
304
  def enable_side_panel(self, value: bool):
305
+ """
306
+ Show Side Panel
307
+
308
+ Args:
309
+ value(bool): The value to set.
310
+ """
271
311
  if value:
272
312
  self.ui_mode = UIMode.SIDE
273
313
  else:
@@ -276,33 +316,36 @@ class PlotBase(BECWidget, QWidget):
276
316
 
277
317
  @SafeProperty(bool, doc="Show Toolbar")
278
318
  def enable_toolbar(self) -> bool:
319
+ """
320
+ Show Toolbar.
321
+ """
279
322
  return self.toolbar.isVisible()
280
323
 
281
324
  @enable_toolbar.setter
282
325
  def enable_toolbar(self, value: bool):
283
- if value:
284
- # Disable popup mode
285
- if self._popups:
286
- # Directly update the internal flag to avoid recursion
287
- self._popups = False
288
- # Hide the popup bundle if it exists and close any open dialogs
289
- if self.popup_bundle is not None:
290
- for action in self.toolbar.bundles["popup_bundle"].actions:
291
- action.setVisible(False)
292
- if self.axis_settings_dialog is not None and self.axis_settings_dialog.isVisible():
293
- self.axis_settings_dialog.close()
294
- self.side_panel.show()
295
- # Add side menus if not already added
296
- self.add_side_menus()
297
- else:
298
- self.side_panel.hide()
326
+ """
327
+ Show Toolbar.
328
+
329
+ Args:
330
+ value(bool): The value to set.
331
+ """
332
+ self.toolbar.setVisible(value)
299
333
 
300
334
  @SafeProperty(bool, doc="Enable the FPS monitor.")
301
335
  def enable_fps_monitor(self) -> bool:
336
+ """
337
+ Enable the FPS monitor.
338
+ """
302
339
  return self.fps_label.isVisible()
303
340
 
304
341
  @enable_fps_monitor.setter
305
342
  def enable_fps_monitor(self, value: bool):
343
+ """
344
+ Enable the FPS monitor.
345
+
346
+ Args:
347
+ value(bool): The value to set.
348
+ """
306
349
  if value and self.fps_monitor is None:
307
350
  self.hook_fps_monitor()
308
351
  elif not value and self.fps_monitor is not None:
@@ -341,6 +384,14 @@ class PlotBase(BECWidget, QWidget):
341
384
  **kwargs: Keyword arguments for the properties to be set.
342
385
 
343
386
  Possible properties:
387
+ - title: str
388
+ - x_label: str
389
+ - y_label: str
390
+ - x_scale: Literal["linear", "log"]
391
+ - y_scale: Literal["linear", "log"]
392
+ - x_lim: tuple
393
+ - y_lim: tuple
394
+ - legend_label_size: int
344
395
 
345
396
  """
346
397
  property_map = {
@@ -369,19 +420,37 @@ class PlotBase(BECWidget, QWidget):
369
420
 
370
421
  @SafeProperty(str, doc="The title of the axes.")
371
422
  def title(self) -> str:
423
+ """
424
+ Set title of the plot.
425
+ """
372
426
  return self.plot_item.titleLabel.text
373
427
 
374
428
  @title.setter
375
429
  def title(self, value: str):
430
+ """
431
+ Set title of the plot.
432
+
433
+ Args:
434
+ value(str): The title to set.
435
+ """
376
436
  self.plot_item.setTitle(value)
377
437
  self.property_changed.emit("title", value)
378
438
 
379
439
  @SafeProperty(str, doc="The text of the x label")
380
440
  def x_label(self) -> str:
441
+ """
442
+ The set label for the x-axis.
443
+ """
381
444
  return self._user_x_label
382
445
 
383
446
  @x_label.setter
384
447
  def x_label(self, value: str):
448
+ """
449
+ The set label for the x-axis.
450
+
451
+ Args:
452
+ value(str): The label to set.
453
+ """
385
454
  self._user_x_label = value
386
455
  self._apply_x_label()
387
456
  self.property_changed.emit("x_label", self._user_x_label)
@@ -416,17 +485,57 @@ class PlotBase(BECWidget, QWidget):
416
485
  the combined label. Called whenever user label or suffix changes.
417
486
  """
418
487
  final_label = self.x_label_combined
419
- self.plot_item.setLabel("bottom", text=final_label)
488
+ if self.plot_item.getAxis("bottom").isVisible():
489
+ self.plot_item.setLabel("bottom", text=final_label)
420
490
 
421
491
  @SafeProperty(str, doc="The text of the y label")
422
492
  def y_label(self) -> str:
423
- return self.plot_item.getAxis("left").labelText
493
+ """
494
+ The set label for the y-axis.
495
+ """
496
+ return self._user_y_label
424
497
 
425
498
  @y_label.setter
426
499
  def y_label(self, value: str):
427
- self.plot_item.setLabel("left", text=value)
500
+ """
501
+ The set label for the y-axis.
502
+ Args:
503
+ value(str): The label to set.
504
+ """
505
+ self._user_y_label = value
506
+ self._apply_y_label()
428
507
  self.property_changed.emit("y_label", value)
429
508
 
509
+ @property
510
+ def y_label_suffix(self) -> str:
511
+ """
512
+ A read-only suffix automatically appended to the y label.
513
+ """
514
+ return self._y_label_suffix
515
+
516
+ def set_y_label_suffix(self, suffix: str):
517
+ """
518
+ Public method to update the y label suffix.
519
+ """
520
+ self._y_label_suffix = suffix
521
+ self._apply_y_label()
522
+
523
+ @property
524
+ def y_label_combined(self) -> str:
525
+ """
526
+ The final y label shown on the axis = user portion + suffix.
527
+ """
528
+ return self._user_y_label + self._y_label_suffix
529
+
530
+ def _apply_y_label(self):
531
+ """
532
+ Actually updates the pyqtgraph y axis label text to
533
+ the combined y label. Called whenever y label or suffix changes.
534
+ """
535
+ final_label = self.y_label_combined
536
+ if self.plot_item.getAxis("bottom").isVisible():
537
+ self.plot_item.setLabel("left", text=final_label)
538
+
430
539
  def _tuple_to_qpointf(self, tuple: tuple | list):
431
540
  """
432
541
  Helper function to convert a tuple to a QPointF.
@@ -452,37 +561,74 @@ class PlotBase(BECWidget, QWidget):
452
561
 
453
562
  @SafeProperty("QPointF")
454
563
  def x_limits(self) -> QPointF:
564
+ """
565
+ Get the x limits of the plot.
566
+ """
455
567
  current_lim = self.plot_item.vb.viewRange()[0]
456
568
  return QPointF(current_lim[0], current_lim[1])
457
569
 
458
570
  @x_limits.setter
459
571
  def x_limits(self, value):
572
+ """
573
+ Set the x limits of the plot.
574
+
575
+ Args:
576
+ value(QPointF|tuple|list): The x limits to set.
577
+ """
460
578
  if isinstance(value, (tuple, list)):
461
579
  value = self._tuple_to_qpointf(value)
462
580
  self.plot_item.vb.setXRange(value.x(), value.y(), padding=0)
463
581
 
464
582
  @property
465
583
  def x_lim(self) -> tuple:
584
+ """
585
+ Get the x limits of the plot.
586
+ """
466
587
  return (self.x_limits.x(), self.x_limits.y())
467
588
 
468
589
  @x_lim.setter
469
590
  def x_lim(self, value):
591
+ """
592
+ Set the x limits of the plot.
593
+
594
+ Args:
595
+ value(tuple): The x limits to set.
596
+ """
470
597
  self.x_limits = value
471
598
 
472
599
  @property
473
600
  def x_min(self) -> float:
601
+ """
602
+ Get the minimum x limit of the plot.
603
+
604
+ """
474
605
  return self.x_limits.x()
475
606
 
476
607
  @x_min.setter
477
608
  def x_min(self, value: float):
609
+ """
610
+ Set the minimum x limit of the plot.
611
+
612
+ Args:
613
+ value(float): The minimum x limit to set.
614
+ """
478
615
  self.x_limits = (value, self.x_lim[1])
479
616
 
480
617
  @property
481
618
  def x_max(self) -> float:
619
+ """
620
+ Get the maximum x limit of the plot.
621
+ """
482
622
  return self.x_limits.y()
483
623
 
484
624
  @x_max.setter
485
625
  def x_max(self, value: float):
626
+ """
627
+ Set the maximum x limit of the plot.
628
+
629
+ Args:
630
+ value(float): The maximum x limit to set.
631
+ """
486
632
  self.x_limits = (self.x_lim[0], value)
487
633
 
488
634
  ################################################################################
@@ -491,121 +637,243 @@ class PlotBase(BECWidget, QWidget):
491
637
 
492
638
  @SafeProperty("QPointF")
493
639
  def y_limits(self) -> QPointF:
640
+ """
641
+ Get the y limits of the plot.
642
+ """
494
643
  current_lim = self.plot_item.vb.viewRange()[1]
495
644
  return QPointF(current_lim[0], current_lim[1])
496
645
 
497
646
  @y_limits.setter
498
647
  def y_limits(self, value):
648
+ """
649
+ Set the y limits of the plot.
650
+
651
+ Args:
652
+ value(QPointF|tuple|list): The y limits to set.
653
+ """
499
654
  if isinstance(value, (tuple, list)):
500
655
  value = self._tuple_to_qpointf(value)
501
656
  self.plot_item.vb.setYRange(value.x(), value.y(), padding=0)
502
657
 
503
658
  @property
504
659
  def y_lim(self) -> tuple:
660
+ """
661
+ Get the y limits of the plot.
662
+ """
505
663
  return (self.y_limits.x(), self.y_limits.y())
506
664
 
507
665
  @y_lim.setter
508
666
  def y_lim(self, value):
667
+ """
668
+ Set the y limits of the plot.
669
+
670
+ Args:
671
+ value(tuple): The y limits to set.
672
+ """
509
673
  self.y_limits = value
510
674
 
511
675
  @property
512
676
  def y_min(self) -> float:
677
+ """
678
+ Get the minimum y limit of the plot.
679
+ """
513
680
  return self.y_limits.x()
514
681
 
515
682
  @y_min.setter
516
683
  def y_min(self, value: float):
684
+ """
685
+ Set the minimum y limit of the plot.
686
+
687
+ Args:
688
+ value(float): The minimum y limit to set.
689
+ """
517
690
  self.y_limits = (value, self.y_lim[1])
518
691
 
519
692
  @property
520
693
  def y_max(self) -> float:
694
+ """
695
+ Get the maximum y limit of the plot.
696
+ """
521
697
  return self.y_limits.y()
522
698
 
523
699
  @y_max.setter
524
700
  def y_max(self, value: float):
701
+ """
702
+ Set the maximum y limit of the plot.
703
+
704
+ Args:
705
+ value(float): The maximum y limit to set.
706
+ """
525
707
  self.y_limits = (self.y_lim[0], value)
526
708
 
527
709
  @SafeProperty(bool, doc="Show grid on the x-axis.")
528
710
  def x_grid(self) -> bool:
711
+ """
712
+ Show grid on the x-axis.
713
+ """
529
714
  return self.plot_item.ctrl.xGridCheck.isChecked()
530
715
 
531
716
  @x_grid.setter
532
717
  def x_grid(self, value: bool):
718
+ """
719
+ Show grid on the x-axis.
720
+
721
+ Args:
722
+ value(bool): The value to set.
723
+ """
533
724
  self.plot_item.showGrid(x=value)
534
725
  self.property_changed.emit("x_grid", value)
535
726
 
536
727
  @SafeProperty(bool, doc="Show grid on the y-axis.")
537
728
  def y_grid(self) -> bool:
729
+ """
730
+ Show grid on the y-axis.
731
+ """
538
732
  return self.plot_item.ctrl.yGridCheck.isChecked()
539
733
 
540
734
  @y_grid.setter
541
735
  def y_grid(self, value: bool):
736
+ """
737
+ Show grid on the y-axis.
738
+
739
+ Args:
740
+ value(bool): The value to set.
741
+ """
542
742
  self.plot_item.showGrid(y=value)
543
743
  self.property_changed.emit("y_grid", value)
544
744
 
545
745
  @SafeProperty(bool, doc="Set X-axis to log scale if True, linear if False.")
546
746
  def x_log(self) -> bool:
747
+ """
748
+ Set X-axis to log scale if True, linear if False.
749
+ """
547
750
  return bool(self.plot_item.vb.state.get("logMode", [False, False])[0])
548
751
 
549
752
  @x_log.setter
550
753
  def x_log(self, value: bool):
754
+ """
755
+ Set X-axis to log scale if True, linear if False.
756
+
757
+ Args:
758
+ value(bool): The value to set.
759
+ """
551
760
  self.plot_item.setLogMode(x=value)
552
761
  self.property_changed.emit("x_log", value)
553
762
 
554
763
  @SafeProperty(bool, doc="Set Y-axis to log scale if True, linear if False.")
555
764
  def y_log(self) -> bool:
765
+ """
766
+ Set Y-axis to log scale if True, linear if False.
767
+ """
556
768
  return bool(self.plot_item.vb.state.get("logMode", [False, False])[1])
557
769
 
558
770
  @y_log.setter
559
771
  def y_log(self, value: bool):
772
+ """
773
+ Set Y-axis to log scale if True, linear if False.
774
+
775
+ Args:
776
+ value(bool): The value to set.
777
+ """
560
778
  self.plot_item.setLogMode(y=value)
561
779
  self.property_changed.emit("y_log", value)
562
780
 
563
781
  @SafeProperty(bool, doc="Show the outer axes of the plot widget.")
564
782
  def outer_axes(self) -> bool:
783
+ """
784
+ Show the outer axes of the plot widget.
785
+ """
565
786
  return self.plot_item.getAxis("top").isVisible()
566
787
 
567
788
  @outer_axes.setter
568
789
  def outer_axes(self, value: bool):
790
+ """
791
+ Show the outer axes of the plot widget.
792
+
793
+ Args:
794
+ value(bool): The value to set.
795
+ """
569
796
  self.plot_item.showAxis("top", value)
570
797
  self.plot_item.showAxis("right", value)
571
798
  self.property_changed.emit("outer_axes", value)
572
799
 
573
800
  @SafeProperty(bool, doc="Show inner axes of the plot widget.")
574
801
  def inner_axes(self) -> bool:
802
+ """
803
+ Show inner axes of the plot widget.
804
+ """
575
805
  return self.plot_item.getAxis("bottom").isVisible()
576
806
 
577
807
  @inner_axes.setter
578
808
  def inner_axes(self, value: bool):
809
+ """
810
+ Show inner axes of the plot widget.
811
+
812
+ Args:
813
+ value(bool): The value to set.
814
+ """
579
815
  self.plot_item.showAxis("bottom", value)
580
816
  self.plot_item.showAxis("left", value)
817
+ self._apply_x_label()
818
+ self._apply_y_label()
581
819
  self.property_changed.emit("inner_axes", value)
582
820
 
583
821
  @SafeProperty(bool, doc="Lock aspect ratio of the plot widget.")
584
822
  def lock_aspect_ratio(self) -> bool:
823
+ """
824
+ Lock aspect ratio of the plot widget.
825
+ """
585
826
  return bool(self.plot_item.vb.getState()["aspectLocked"])
586
827
 
587
828
  @lock_aspect_ratio.setter
588
829
  def lock_aspect_ratio(self, value: bool):
830
+ """
831
+ Lock aspect ratio of the plot widget.
832
+
833
+ Args:
834
+ value(bool): The value to set.
835
+ """
589
836
  self.plot_item.setAspectLocked(value)
590
837
 
591
838
  @SafeProperty(bool, doc="Set auto range for the x-axis.")
592
839
  def auto_range_x(self) -> bool:
840
+ """
841
+ Set auto range for the x-axis.
842
+ """
593
843
  return bool(self.plot_item.vb.getState()["autoRange"][0])
594
844
 
595
845
  @auto_range_x.setter
596
846
  def auto_range_x(self, value: bool):
847
+ """
848
+ Set auto range for the x-axis.
849
+
850
+ Args:
851
+ value(bool): The value to set.
852
+ """
597
853
  self.plot_item.enableAutoRange(x=value)
598
854
 
599
855
  @SafeProperty(bool, doc="Set auto range for the y-axis.")
600
856
  def auto_range_y(self) -> bool:
857
+ """
858
+ Set auto range for the y-axis.
859
+ """
601
860
  return bool(self.plot_item.vb.getState()["autoRange"][1])
602
861
 
603
862
  @auto_range_y.setter
604
863
  def auto_range_y(self, value: bool):
864
+ """
865
+ Set auto range for the y-axis.
866
+
867
+ Args:
868
+ value(bool): The value to set.
869
+ """
605
870
  self.plot_item.enableAutoRange(y=value)
606
871
 
607
872
  @SafeProperty(int, doc="The font size of the legend font.")
608
873
  def legend_label_size(self) -> int:
874
+ """
875
+ The font size of the legend font.
876
+ """
609
877
  if not self.plot_item.legend:
610
878
  return
611
879
  scale = self.plot_item.legend.scale() * 9
@@ -613,6 +881,12 @@ class PlotBase(BECWidget, QWidget):
613
881
 
614
882
  @legend_label_size.setter
615
883
  def legend_label_size(self, value: int):
884
+ """
885
+ The font size of the legend font.
886
+
887
+ Args:
888
+ value(int): The font size to set.
889
+ """
616
890
  if not self.plot_item.legend:
617
891
  return
618
892
  scale = (
@@ -699,12 +973,19 @@ class PlotBase(BECWidget, QWidget):
699
973
  def cleanup(self):
700
974
  self.unhook_crosshair()
701
975
  self.unhook_fps_monitor(delete_label=True)
976
+ self.tick_item.cleanup()
977
+ self.arrow_item.cleanup()
978
+ if self.axis_settings_dialog is not None:
979
+ self.axis_settings_dialog.close()
980
+ self.axis_settings_dialog = None
702
981
  self.cleanup_pyqtgraph()
703
- self.rpc_register.remove_rpc(self)
982
+ self.round_plot_widget.close()
983
+ super().cleanup()
704
984
 
705
- def cleanup_pyqtgraph(self):
985
+ def cleanup_pyqtgraph(self, item: pg.PlotItem | None = None):
706
986
  """Cleanup pyqtgraph items."""
707
- item = self.plot_item
987
+ if item is None:
988
+ item = self.plot_item
708
989
  item.vb.menu.close()
709
990
  item.vb.menu.deleteLater()
710
991
  item.ctrlMenu.close()
@@ -738,7 +1019,7 @@ if __name__ == "__main__": # pragma: no cover:
738
1019
  from qtpy.QtWidgets import QApplication
739
1020
 
740
1021
  app = QApplication(sys.argv)
741
- window = DemoPlotBase()
1022
+ window = PlotBase()
742
1023
  window.show()
743
1024
 
744
1025
  sys.exit(app.exec_())