bec-widgets 0.87.1__py3-none-any.whl → 0.88.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 (65) hide show
  1. CHANGELOG.md +72 -70
  2. PKG-INFO +1 -1
  3. bec_widgets/assets/designer_icons/BECWaveformWidget.png +0 -0
  4. bec_widgets/assets/designer_icons/colormap_selector.png +0 -0
  5. bec_widgets/assets/designer_icons/motor_map.png +0 -0
  6. bec_widgets/assets/toolbar_icons/add.svg +3 -0
  7. bec_widgets/assets/toolbar_icons/auto_range.svg +3 -0
  8. bec_widgets/assets/toolbar_icons/drag_pan_mode.svg +3 -0
  9. bec_widgets/assets/toolbar_icons/export.svg +9 -0
  10. bec_widgets/assets/toolbar_icons/fitting_parameters.svg +3 -0
  11. bec_widgets/assets/toolbar_icons/import.svg +9 -0
  12. bec_widgets/assets/toolbar_icons/line_axis.svg +3 -0
  13. bec_widgets/assets/toolbar_icons/photo_library.svg +3 -0
  14. bec_widgets/assets/toolbar_icons/rectangle_mode.svg +3 -0
  15. bec_widgets/assets/toolbar_icons/remove.svg +5 -0
  16. bec_widgets/assets/toolbar_icons/save.svg +3 -0
  17. bec_widgets/cli/client.py +320 -5
  18. bec_widgets/cli/server.py +2 -1
  19. bec_widgets/examples/jupyter_console/jupyter_console_window.py +12 -5
  20. bec_widgets/examples/plugin_example_pyside/tictactoe.py +1 -0
  21. bec_widgets/qt_utils/toolbar.py +74 -2
  22. bec_widgets/widgets/{color_map_selector/color_map_selector.py → colormap_selector/colormap_selector.py} +3 -1
  23. bec_widgets/widgets/colormap_selector/colormap_selector.pyproject +1 -0
  24. bec_widgets/widgets/{color_map_selector/color_map_selector_plugin.py → colormap_selector/colormap_selector_plugin.py} +8 -6
  25. bec_widgets/widgets/{color_map_selector/register_color_map_selector.py → colormap_selector/register_colormap_selector.py} +1 -1
  26. bec_widgets/widgets/figure/figure.py +12 -0
  27. bec_widgets/widgets/figure/plots/axis_settings.py +38 -11
  28. bec_widgets/widgets/figure/plots/image/image.py +1 -0
  29. bec_widgets/widgets/figure/plots/motor_map/motor_map.py +1 -0
  30. bec_widgets/widgets/figure/plots/plot_base.py +20 -3
  31. bec_widgets/widgets/figure/plots/waveform/waveform.py +48 -50
  32. bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +9 -0
  33. bec_widgets/widgets/motor_map/bec_motor_map_widget_plugin.py +5 -3
  34. bec_widgets/widgets/motor_map/motor_map_widget.py +18 -12
  35. bec_widgets/widgets/waveform/__init__.py +0 -0
  36. bec_widgets/widgets/waveform/bec_waveform_widget.pyproject +1 -0
  37. bec_widgets/widgets/waveform/bec_waveform_widget_plugin.py +59 -0
  38. bec_widgets/widgets/waveform/register_bec_waveform_widget.py +15 -0
  39. bec_widgets/widgets/waveform/waveform_popups/__init__.py +0 -0
  40. bec_widgets/widgets/waveform/waveform_popups/curve_dialog/__init__.py +0 -0
  41. bec_widgets/widgets/waveform/waveform_popups/curve_dialog/curve_dialog.py +341 -0
  42. bec_widgets/widgets/waveform/waveform_popups/curve_dialog/curve_dialog.ui +358 -0
  43. bec_widgets/widgets/waveform/waveform_popups/dap_summary_dialog/__init__.py +0 -0
  44. bec_widgets/widgets/waveform/waveform_popups/dap_summary_dialog/dap_summary.ui +127 -0
  45. bec_widgets/widgets/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py +69 -0
  46. bec_widgets/widgets/waveform/waveform_widget.py +579 -0
  47. {bec_widgets-0.87.1.dist-info → bec_widgets-0.88.1.dist-info}/METADATA +1 -1
  48. {bec_widgets-0.87.1.dist-info → bec_widgets-0.88.1.dist-info}/RECORD +61 -37
  49. docs/conf.py +1 -1
  50. pyproject.toml +1 -1
  51. tests/unit_tests/test_color_map_selector.py +1 -1
  52. tests/unit_tests/test_waveform_widget.py +264 -0
  53. bec_widgets/widgets/color_map_selector/assets/color_map_selector_icon.png +0 -0
  54. bec_widgets/widgets/color_map_selector/color_map_selector.pyproject +0 -1
  55. bec_widgets/widgets/motor_map/assets/motor_map.png +0 -0
  56. bec_widgets/widgets/motor_map/motor_map_dialog/motor_map_toolbar.py +0 -59
  57. /bec_widgets/assets/{bec_widgets_icon.png → app_icons/bec_widgets_icon.png} +0 -0
  58. /bec_widgets/assets/{terminal_icon.png → app_icons/terminal_icon.png} +0 -0
  59. /bec_widgets/{widgets/motor_map/assets → assets/toolbar_icons}/connection.svg +0 -0
  60. /bec_widgets/{widgets/motor_map/assets → assets/toolbar_icons}/history.svg +0 -0
  61. /bec_widgets/{widgets/motor_map/assets → assets/toolbar_icons}/settings.svg +0 -0
  62. /bec_widgets/widgets/{color_map_selector → colormap_selector}/__init__.py +0 -0
  63. {bec_widgets-0.87.1.dist-info → bec_widgets-0.88.1.dist-info}/WHEEL +0 -0
  64. {bec_widgets-0.87.1.dist-info → bec_widgets-0.88.1.dist-info}/entry_points.txt +0 -0
  65. {bec_widgets-0.87.1.dist-info → bec_widgets-0.88.1.dist-info}/licenses/LICENSE +0 -0
bec_widgets/cli/client.py CHANGED
@@ -19,6 +19,7 @@ class Widgets(str, enum.Enum):
19
19
  BECMotorMapWidget = "BECMotorMapWidget"
20
20
  BECQueue = "BECQueue"
21
21
  BECStatusBox = "BECStatusBox"
22
+ BECWaveformWidget = "BECWaveformWidget"
22
23
  DeviceBox = "DeviceBox"
23
24
  DeviceComboBox = "DeviceComboBox"
24
25
  DeviceLineEdit = "DeviceLineEdit"
@@ -184,7 +185,7 @@ class BECDock(RPCBase):
184
185
 
185
186
  @property
186
187
  @rpc_call
187
- def widget_list(self) -> "list[BECConnector]":
188
+ def widget_list(self) -> "list[BECWidget]":
188
189
  """
189
190
  Get the widgets in the dock.
190
191
 
@@ -225,13 +226,13 @@ class BECDock(RPCBase):
225
226
  @rpc_call
226
227
  def add_widget(
227
228
  self,
228
- widget: "BECConnector | str",
229
+ widget: "BECWidget | str",
229
230
  row=None,
230
231
  col=0,
231
232
  rowspan=1,
232
233
  colspan=1,
233
234
  shift: "Literal['down', 'up', 'left', 'right']" = "down",
234
- ) -> "BECConnector":
235
+ ) -> "BECWidget":
235
236
  """
236
237
  Add a widget to the dock.
237
238
 
@@ -614,6 +615,12 @@ class BECFigure(RPCBase):
614
615
  theme(Literal["dark","light"]): The theme to set for the figure widget.
615
616
  """
616
617
 
618
+ @rpc_call
619
+ def export(self):
620
+ """
621
+ Export the plot widget.
622
+ """
623
+
617
624
  @rpc_call
618
625
  def clear_all(self):
619
626
  """
@@ -1096,6 +1103,12 @@ class BECImageShow(RPCBase):
1096
1103
  lock(bool): True to lock, False to unlock.
1097
1104
  """
1098
1105
 
1106
+ @rpc_call
1107
+ def export(self):
1108
+ """
1109
+ Show the Export Dialog of the plot widget.
1110
+ """
1111
+
1099
1112
  @rpc_call
1100
1113
  def remove(self):
1101
1114
  """
@@ -1204,6 +1217,12 @@ class BECMotorMap(RPCBase):
1204
1217
  dict: Data of the motor map.
1205
1218
  """
1206
1219
 
1220
+ @rpc_call
1221
+ def export(self):
1222
+ """
1223
+ Show the Export Dialog of the plot widget.
1224
+ """
1225
+
1207
1226
  @rpc_call
1208
1227
  def remove(self):
1209
1228
  """
@@ -1298,6 +1317,12 @@ class BECMotorMapWidget(RPCBase):
1298
1317
  Reset the history of the motor map.
1299
1318
  """
1300
1319
 
1320
+ @rpc_call
1321
+ def export(self):
1322
+ """
1323
+ Show the export dialog for the motor map.
1324
+ """
1325
+
1301
1326
 
1302
1327
  class BECPlotBase(RPCBase):
1303
1328
  @property
@@ -1426,6 +1451,12 @@ class BECPlotBase(RPCBase):
1426
1451
  lock(bool): True to lock, False to unlock.
1427
1452
  """
1428
1453
 
1454
+ @rpc_call
1455
+ def export(self):
1456
+ """
1457
+ Show the Export Dialog of the plot widget.
1458
+ """
1459
+
1429
1460
  @rpc_call
1430
1461
  def remove(self):
1431
1462
  """
@@ -1563,8 +1594,6 @@ class BECWaveform(RPCBase):
1563
1594
  y_name(str): Name of the y signal.
1564
1595
  y_entry(str): Entry of the y signal.
1565
1596
  color(str, optional): Color of the curve. Defaults to None.
1566
- color_map_z(str): The color map to use for the z-axis.
1567
- label(str, optional): Label of the curve. Defaults to None.
1568
1597
  dap(str): The dap model to use for the curve.
1569
1598
  validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
1570
1599
  **kwargs: Additional keyword arguments for the curve configuration.
@@ -1756,6 +1785,15 @@ class BECWaveform(RPCBase):
1756
1785
  y(bool): Show grid on the y-axis.
1757
1786
  """
1758
1787
 
1788
+ @rpc_call
1789
+ def set_colormap(self, colormap: "str | None" = None):
1790
+ """
1791
+ Set the colormap of the plot widget.
1792
+
1793
+ Args:
1794
+ colormap(str, optional): Scale the colors of curves to colormap. If None, use the default color palette.
1795
+ """
1796
+
1759
1797
  @rpc_call
1760
1798
  def lock_aspect_ratio(self, lock):
1761
1799
  """
@@ -1765,6 +1803,12 @@ class BECWaveform(RPCBase):
1765
1803
  lock(bool): True to lock, False to unlock.
1766
1804
  """
1767
1805
 
1806
+ @rpc_call
1807
+ def export(self):
1808
+ """
1809
+ Show the Export Dialog of the plot widget.
1810
+ """
1811
+
1768
1812
  @rpc_call
1769
1813
  def remove(self):
1770
1814
  """
@@ -1787,6 +1831,277 @@ class BECWaveform(RPCBase):
1787
1831
  """
1788
1832
 
1789
1833
 
1834
+ class BECWaveformWidget(RPCBase):
1835
+ @property
1836
+ @rpc_call
1837
+ def curves(self) -> "list[BECCurve]":
1838
+ """
1839
+ Get the curves of the plot widget as a list
1840
+ Returns:
1841
+ list: List of curves.
1842
+ """
1843
+
1844
+ @rpc_call
1845
+ def plot(
1846
+ self,
1847
+ arg1: "list | np.ndarray | str | None" = None,
1848
+ x: "list | np.ndarray | None" = None,
1849
+ y: "list | np.ndarray | None" = None,
1850
+ x_name: "str | None" = None,
1851
+ y_name: "str | None" = None,
1852
+ z_name: "str | None" = None,
1853
+ x_entry: "str | None" = None,
1854
+ y_entry: "str | None" = None,
1855
+ z_entry: "str | None" = None,
1856
+ color: "str | None" = None,
1857
+ color_map_z: "str | None" = "plasma",
1858
+ label: "str | None" = None,
1859
+ validate: "bool" = True,
1860
+ dap: "str | None" = None,
1861
+ **kwargs,
1862
+ ) -> "BECCurve":
1863
+ """
1864
+ Plot a curve to the plot widget.
1865
+ Args:
1866
+ arg1(list | np.ndarray | str | None): First argument which can be x data(list | np.ndarray), y data(list | np.ndarray), or y_name(str).
1867
+ x(list | np.ndarray): Custom x data to plot.
1868
+ y(list | np.ndarray): Custom y data to plot.
1869
+ x_name(str): The name of the device for the x-axis.
1870
+ y_name(str): The name of the device for the y-axis.
1871
+ z_name(str): The name of the device for the z-axis.
1872
+ x_entry(str): The name of the entry for the x-axis.
1873
+ y_entry(str): The name of the entry for the y-axis.
1874
+ z_entry(str): The name of the entry for the z-axis.
1875
+ color(str): The color of the curve.
1876
+ color_map_z(str): The color map to use for the z-axis.
1877
+ label(str): The label of the curve.
1878
+ validate(bool): If True, validate the device names and entries.
1879
+ dap(str): The dap model to use for the curve. If not specified, none will be added.
1880
+
1881
+ Returns:
1882
+ BECCurve: The curve object.
1883
+ """
1884
+
1885
+ @rpc_call
1886
+ def add_dap(
1887
+ self,
1888
+ x_name: "str",
1889
+ y_name: "str",
1890
+ x_entry: "str | None" = None,
1891
+ y_entry: "str | None" = None,
1892
+ color: "str | None" = None,
1893
+ dap: "str" = "GaussianModel",
1894
+ validate_bec: "bool" = True,
1895
+ **kwargs,
1896
+ ) -> "BECCurve":
1897
+ """
1898
+ Add LMFIT dap model curve to the plot widget.
1899
+
1900
+ Args:
1901
+ x_name(str): Name of the x signal.
1902
+ x_entry(str): Entry of the x signal.
1903
+ y_name(str): Name of the y signal.
1904
+ y_entry(str): Entry of the y signal.
1905
+ color(str, optional): Color of the curve. Defaults to None.
1906
+ dap(str): The dap model to use for the curve.
1907
+ validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
1908
+ **kwargs: Additional keyword arguments for the curve configuration.
1909
+
1910
+ Returns:
1911
+ BECCurve: The curve object.
1912
+ """
1913
+
1914
+ @rpc_call
1915
+ def get_dap_params(self) -> "dict":
1916
+ """
1917
+ Get the DAP parameters of all DAP curves.
1918
+
1919
+ Returns:
1920
+ dict: DAP parameters of all DAP curves.
1921
+ """
1922
+
1923
+ @rpc_call
1924
+ def remove_curve(self, *identifiers):
1925
+ """
1926
+ Remove a curve from the plot widget.
1927
+
1928
+ Args:
1929
+ *identifiers: Identifier of the curve to be removed. Can be either an integer (index) or a string (curve_id).
1930
+ """
1931
+
1932
+ @rpc_call
1933
+ def scan_history(self, scan_index: "int" = None, scan_id: "str" = None):
1934
+ """
1935
+ Update the scan curves with the data from the scan storage.
1936
+ Provide only one of scan_id or scan_index.
1937
+
1938
+ Args:
1939
+ scan_id(str, optional): ScanID of the scan to be updated. Defaults to None.
1940
+ scan_index(int, optional): Index of the scan to be updated. Defaults to None.
1941
+ """
1942
+
1943
+ @rpc_call
1944
+ def get_all_data(self, output: "Literal['dict', 'pandas']" = "dict") -> "dict | pd.DataFrame":
1945
+ """
1946
+ Extract all curve data into a dictionary or a pandas DataFrame.
1947
+
1948
+ Args:
1949
+ output (Literal["dict", "pandas"]): Format of the output data.
1950
+
1951
+ Returns:
1952
+ dict | pd.DataFrame: Data of all curves in the specified format.
1953
+ """
1954
+
1955
+ @rpc_call
1956
+ def set(self, **kwargs):
1957
+ """
1958
+ Set the properties of the plot widget.
1959
+
1960
+ Args:
1961
+ **kwargs: Keyword arguments for the properties to be set.
1962
+
1963
+ Possible properties:
1964
+ - title: str
1965
+ - x_label: str
1966
+ - y_label: str
1967
+ - x_scale: Literal["linear", "log"]
1968
+ - y_scale: Literal["linear", "log"]
1969
+ - x_lim: tuple
1970
+ - y_lim: tuple
1971
+ - legend_label_size: int
1972
+ """
1973
+
1974
+ @rpc_call
1975
+ def set_x(self, x_name: "str", x_entry: "str | None" = None):
1976
+ """
1977
+ Change the x axis of the plot widget.
1978
+
1979
+ Args:
1980
+ x_name(str): Name of the x signal.
1981
+ - "best_effort": Use the best effort signal.
1982
+ - "timestamp": Use the timestamp signal.
1983
+ - "index": Use the index signal.
1984
+ - Custom signal name of device from BEC.
1985
+ x_entry(str): Entry of the x signal.
1986
+ """
1987
+
1988
+ @rpc_call
1989
+ def set_title(self, title: "str"):
1990
+ """
1991
+ Set the title of the plot widget.
1992
+
1993
+ Args:
1994
+ title(str): Title of the plot.
1995
+ """
1996
+
1997
+ @rpc_call
1998
+ def set_x_label(self, x_label: "str"):
1999
+ """
2000
+ Set the x-axis label of the plot widget.
2001
+
2002
+ Args:
2003
+ x_label(str): Label of the x-axis.
2004
+ """
2005
+
2006
+ @rpc_call
2007
+ def set_y_label(self, y_label: "str"):
2008
+ """
2009
+ Set the y-axis label of the plot widget.
2010
+
2011
+ Args:
2012
+ y_label(str): Label of the y-axis.
2013
+ """
2014
+
2015
+ @rpc_call
2016
+ def set_x_scale(self, x_scale: "Literal['linear', 'log']"):
2017
+ """
2018
+ Set the scale of the x-axis of the plot widget.
2019
+
2020
+ Args:
2021
+ x_scale(Literal["linear", "log"]): Scale of the x-axis.
2022
+ """
2023
+
2024
+ @rpc_call
2025
+ def set_y_scale(self, y_scale: "Literal['linear', 'log']"):
2026
+ """
2027
+ Set the scale of the y-axis of the plot widget.
2028
+
2029
+ Args:
2030
+ y_scale(Literal["linear", "log"]): Scale of the y-axis.
2031
+ """
2032
+
2033
+ @rpc_call
2034
+ def set_x_lim(self, x_lim: "tuple"):
2035
+ """
2036
+ Set the limits of the x-axis of the plot widget.
2037
+
2038
+ Args:
2039
+ x_lim(tuple): Limits of the x-axis.
2040
+ """
2041
+
2042
+ @rpc_call
2043
+ def set_y_lim(self, y_lim: "tuple"):
2044
+ """
2045
+ Set the limits of the y-axis of the plot widget.
2046
+
2047
+ Args:
2048
+ y_lim(tuple): Limits of the y-axis.
2049
+ """
2050
+
2051
+ @rpc_call
2052
+ def set_legend_label_size(self, legend_label_size: "int"):
2053
+ """
2054
+ Set the size of the legend labels of the plot widget.
2055
+
2056
+ Args:
2057
+ legend_label_size(int): Size of the legend labels.
2058
+ """
2059
+
2060
+ @rpc_call
2061
+ def set_auto_range(self, enabled: "bool", axis: "str" = "xy"):
2062
+ """
2063
+ Set the auto range of the plot widget.
2064
+
2065
+ Args:
2066
+ enabled(bool): If True, enable the auto range.
2067
+ axis(str, optional): The axis to enable the auto range.
2068
+ - "xy": Enable auto range for both x and y axis.
2069
+ - "x": Enable auto range for x axis.
2070
+ - "y": Enable auto range for y axis.
2071
+ """
2072
+
2073
+ @rpc_call
2074
+ def set_grid(self, x_grid: "bool", y_grid: "bool"):
2075
+ """
2076
+ Set the grid visibility of the plot widget.
2077
+
2078
+ Args:
2079
+ x_grid(bool): Visibility of the x-axis grid.
2080
+ y_grid(bool): Visibility of the y-axis grid.
2081
+ """
2082
+
2083
+ @rpc_call
2084
+ def lock_aspect_ratio(self, lock: "bool"):
2085
+ """
2086
+ Lock the aspect ratio of the plot widget.
2087
+
2088
+ Args:
2089
+ lock(bool): Lock the aspect ratio.
2090
+ """
2091
+
2092
+ @rpc_call
2093
+ def export(self):
2094
+ """
2095
+ Show the export dialog for the plot widget.
2096
+ """
2097
+
2098
+ @rpc_call
2099
+ def export_to_matplotlib(self):
2100
+ """
2101
+ Export the plot widget to Matplotlib.
2102
+ """
2103
+
2104
+
1790
2105
  class DeviceBox(RPCBase):
1791
2106
  @property
1792
2107
  @rpc_call
bec_widgets/cli/server.py CHANGED
@@ -202,7 +202,8 @@ def main():
202
202
  module_path = os.path.dirname(bec_widgets.__file__)
203
203
  icon = QIcon()
204
204
  icon.addFile(
205
- os.path.join(module_path, "assets", "bec_widgets_icon.png"), size=QSize(48, 48)
205
+ os.path.join(module_path, "assets", "app_icons", "bec_widgets_icon.png"),
206
+ size=QSize(48, 48),
206
207
  )
207
208
  app.setWindowIcon(icon)
208
209
 
@@ -49,8 +49,9 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
49
49
  "d0": self.d0,
50
50
  "d1": self.d1,
51
51
  "d2": self.d2,
52
- "plt": self.plt,
52
+ "wave": self.wave,
53
53
  "bar": self.bar,
54
+ "cm": self.colormap,
54
55
  }
55
56
  )
56
57
 
@@ -165,12 +166,16 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
165
166
  self.fig1.plot(x_name="samx", y_name="bpm3a")
166
167
 
167
168
  self.d2 = self.dock.add_dock(name="dock_2", position="bottom")
168
- self.fig2 = self.d2.add_widget("BECFigure", row=0, col=0)
169
- self.plt = self.fig2.plot(x_name="samx", y_name="bpm3a")
170
- self.plt.plot(x_name="samx", y_name="bpm4i", dap="GaussianModel")
169
+ self.wave = self.d2.add_widget("BECWaveformWidget", row=0, col=0)
170
+ # self.wave.plot(x_name="samx", y_name="bpm3a")
171
+ # self.wave.plot(x_name="samx", y_name="bpm4i", dap="GaussianModel")
171
172
  self.bar = self.d2.add_widget("RingProgressBar", row=0, col=1)
172
173
  self.bar.set_diameter(200)
173
174
 
175
+ self.d3 = self.dock.add_dock(name="dock_3", position="bottom")
176
+ self.colormap = pg.GradientWidget()
177
+ self.d3.add_widget(self.colormap, row=0, col=0)
178
+
174
179
  self.dock.save_state()
175
180
 
176
181
  def closeEvent(self, event):
@@ -196,7 +201,9 @@ if __name__ == "__main__": # pragma: no cover
196
201
  app.setApplicationDisplayName("Jupyter Console")
197
202
  apply_theme("dark")
198
203
  icon = QIcon()
199
- icon.addFile(os.path.join(module_path, "assets", "terminal_icon.png"), size=QSize(48, 48))
204
+ icon.addFile(
205
+ os.path.join(module_path, "assets", "app_icons", "terminal_icon.png"), size=QSize(48, 48)
206
+ )
200
207
  app.setWindowIcon(icon)
201
208
 
202
209
  bec_dispatcher = BECDispatcher()
@@ -16,6 +16,7 @@ class TicTacToe(QWidget): # pragma: no cover
16
16
  super().__init__(parent)
17
17
  self._state = DEFAULT_STATE
18
18
  self._turn_number = 0
19
+ print("TicTac HERE !!!!!!")
19
20
 
20
21
  def minimumSizeHint(self):
21
22
  return QSize(200, 200)
@@ -1,12 +1,35 @@
1
+ # pylint: disable=no-name-in-module
2
+ import os
1
3
  from abc import ABC, abstractmethod
2
4
  from collections import defaultdict
3
5
 
4
- # pylint: disable=no-name-in-module
5
6
  from qtpy.QtCore import QSize
6
- from qtpy.QtWidgets import QToolBar, QToolButton, QWidget
7
+ from qtpy.QtGui import QAction, QIcon
8
+ from qtpy.QtWidgets import QHBoxLayout, QLabel, QToolBar, QToolButton, QWidget
9
+
10
+ import bec_widgets
11
+
12
+ MODULE_PATH = os.path.dirname(bec_widgets.__file__)
7
13
 
8
14
 
9
15
  class ToolBarAction(ABC):
16
+ """
17
+ Abstract base class for toolbar actions.
18
+
19
+ Args:
20
+ icon_path (str, optional): The name of the icon file from `assets/toolbar_icons`. Defaults to None.
21
+ tooltip (bool, optional): The tooltip for the action. Defaults to None.
22
+ checkable (bool, optional): Whether the action is checkable. Defaults to False.
23
+ """
24
+
25
+ def __init__(self, icon_path: str = None, tooltip: str = None, checkable: bool = False):
26
+ self.icon_path = (
27
+ os.path.join(MODULE_PATH, "assets", "toolbar_icons", icon_path) if icon_path else None
28
+ )
29
+ self.tooltip = tooltip
30
+ self.checkable = checkable
31
+ self.action = None
32
+
10
33
  @abstractmethod
11
34
  def add_to_toolbar(self, toolbar: QToolBar, target: QWidget):
12
35
  """Adds an action or widget to a toolbar.
@@ -26,6 +49,55 @@ class SeparatorAction(ToolBarAction):
26
49
  toolbar.addWidget(self.separator)
27
50
 
28
51
 
52
+ class IconAction(ToolBarAction):
53
+ """
54
+ Action with an icon for the toolbar.
55
+
56
+ Args:
57
+ icon_path (str): The path to the icon file.
58
+ tooltip (str): The tooltip for the action.
59
+ checkable (bool, optional): Whether the action is checkable. Defaults to False.
60
+ """
61
+
62
+ def __init__(self, icon_path: str = None, tooltip: str = None, checkable: bool = False):
63
+ super().__init__(icon_path, tooltip, checkable)
64
+
65
+ def add_to_toolbar(self, toolbar: QToolBar, target: QWidget):
66
+ icon = QIcon()
67
+ icon.addFile(self.icon_path, size=QSize(20, 20))
68
+ self.action = QAction(icon, self.tooltip, target)
69
+ self.action.setCheckable(self.checkable)
70
+ toolbar.addAction(self.action)
71
+
72
+
73
+ class DeviceSelectionAction(ToolBarAction):
74
+ """
75
+ Action for selecting a device in a combobox.
76
+
77
+ Args:
78
+ label (str): The label for the combobox.
79
+ device_combobox (DeviceComboBox): The combobox for selecting the device.
80
+
81
+ """
82
+
83
+ def __init__(self, label: str, device_combobox):
84
+ super().__init__()
85
+ self.label = label
86
+ self.device_combobox = device_combobox
87
+ self.device_combobox.currentIndexChanged.connect(lambda: self.set_combobox_style("#ffa700"))
88
+
89
+ def add_to_toolbar(self, toolbar, target):
90
+ widget = QWidget()
91
+ layout = QHBoxLayout(widget)
92
+ label = QLabel(f"{self.label}")
93
+ layout.addWidget(label)
94
+ layout.addWidget(self.device_combobox)
95
+ toolbar.addWidget(widget)
96
+
97
+ def set_combobox_style(self, color: str):
98
+ self.device_combobox.setStyleSheet(f"QComboBox {{ background-color: {color}; }}")
99
+
100
+
29
101
  class ModularToolBar(QToolBar):
30
102
  """Modular toolbar with optional automatic initialization.
31
103
  Args:
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import pyqtgraph as pg
2
4
  from qtpy.QtCore import Property, Signal, Slot
3
5
  from qtpy.QtGui import QColor, QFontMetrics, QImage
@@ -46,7 +48,7 @@ class ColormapSelector(QWidget):
46
48
  colormap_changed_signal = Signal(str)
47
49
 
48
50
  def __init__(self, parent=None, default_colormaps=None):
49
- super().__init__(parent)
51
+ super().__init__(parent=parent)
50
52
  self._colormaps = []
51
53
  self.initUI(default_colormaps)
52
54
 
@@ -0,0 +1 @@
1
+ {'files': ['colormap_selector.py']}
@@ -5,15 +5,18 @@ import os
5
5
  from qtpy.QtDesigner import QDesignerCustomWidgetInterface
6
6
  from qtpy.QtGui import QIcon
7
7
 
8
- from bec_widgets.widgets.color_map_selector.color_map_selector import ColormapSelector
8
+ import bec_widgets
9
+ from bec_widgets.widgets.colormap_selector.colormap_selector import ColormapSelector
9
10
 
10
11
  DOM_XML = """
11
12
  <ui language='c++'>
12
- <widget class='ColormapSelector' name='color_map_selector'>
13
+ <widget class='ColormapSelector' name='colormap_selector'>
13
14
  </widget>
14
15
  </ui>
15
16
  """
16
17
 
18
+ MODULE_PATH = os.path.dirname(bec_widgets.__file__)
19
+
17
20
 
18
21
  class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
19
22
  def __init__(self):
@@ -31,12 +34,11 @@ class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cove
31
34
  return "BEC Buttons"
32
35
 
33
36
  def icon(self):
34
- current_path = os.path.dirname(__file__)
35
- icon_path = os.path.join(current_path, "assets", "color_map_selector_icon.png")
37
+ icon_path = os.path.join(MODULE_PATH, "assets", "designer_icons", "colormap_selector.png")
36
38
  return QIcon(icon_path)
37
39
 
38
40
  def includeFile(self):
39
- return "color_map_selector"
41
+ return "colormap_selector"
40
42
 
41
43
  def initialize(self, form_editor):
42
44
  self._form_editor = form_editor
@@ -51,7 +53,7 @@ class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cove
51
53
  return "ColormapSelector"
52
54
 
53
55
  def toolTip(self):
54
- return "A custom QComboBox widget for selecting colormaps."
56
+ return ""
55
57
 
56
58
  def whatsThis(self):
57
59
  return self.toolTip()
@@ -6,7 +6,7 @@ def main(): # pragma: no cover
6
6
  return
7
7
  from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
8
8
 
9
- from bec_widgets.widgets.color_map_selector.color_map_selector_plugin import (
9
+ from bec_widgets.widgets.colormap_selector.colormap_selector_plugin import (
10
10
  ColormapSelectorPlugin,
11
11
  )
12
12
 
@@ -122,6 +122,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
122
122
  "remove",
123
123
  "change_layout",
124
124
  "change_theme",
125
+ "export",
125
126
  "clear_all",
126
127
  "widget_list",
127
128
  ]
@@ -228,6 +229,17 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
228
229
  """
229
230
  self._widgets = value
230
231
 
232
+ def export(self):
233
+ """Export the plot widget."""
234
+ try:
235
+ plot_item = self.widget_list[0]
236
+ except:
237
+ raise ValueError("No plot widget available to export.")
238
+
239
+ scene = plot_item.scene()
240
+ scene.contextMenuItem = plot_item
241
+ scene.showExportDialog()
242
+
231
243
  @typechecked
232
244
  def plot(
233
245
  self,