bec-widgets 0.83.1__py3-none-any.whl → 0.85.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 (25) hide show
  1. CHANGELOG.md +40 -42
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +61 -8
  4. bec_widgets/examples/jupyter_console/jupyter_console_window.py +105 -57
  5. bec_widgets/utils/bec_dispatcher.py +5 -2
  6. bec_widgets/widgets/color_map_selector/__init__.py +0 -0
  7. bec_widgets/widgets/color_map_selector/assets/color_map_selector_icon.png +0 -0
  8. bec_widgets/widgets/color_map_selector/color_map_selector.py +111 -0
  9. bec_widgets/widgets/color_map_selector/color_map_selector.pyproject +1 -0
  10. bec_widgets/widgets/color_map_selector/color_map_selector_plugin.py +57 -0
  11. bec_widgets/widgets/color_map_selector/register_color_map_selector.py +17 -0
  12. bec_widgets/widgets/figure/figure.py +21 -114
  13. bec_widgets/widgets/figure/plots/waveform/waveform.py +651 -94
  14. bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +9 -2
  15. {bec_widgets-0.83.1.dist-info → bec_widgets-0.85.0.dist-info}/METADATA +1 -1
  16. {bec_widgets-0.83.1.dist-info → bec_widgets-0.85.0.dist-info}/RECORD +24 -18
  17. pyproject.toml +1 -1
  18. tests/unit_tests/client_mocks.py +13 -4
  19. tests/unit_tests/test_color_map_selector.py +43 -0
  20. tests/unit_tests/test_device_input_widgets.py +2 -0
  21. tests/unit_tests/test_waveform1d.py +202 -23
  22. bec_widgets/examples/jupyter_console/jupyter_console_window.ui +0 -54
  23. {bec_widgets-0.83.1.dist-info → bec_widgets-0.85.0.dist-info}/WHEEL +0 -0
  24. {bec_widgets-0.83.1.dist-info → bec_widgets-0.85.0.dist-info}/entry_points.txt +0 -0
  25. {bec_widgets-0.83.1.dist-info → bec_widgets-0.85.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.85.0 (2024-07-16)
4
+
5
+ ### Feature
6
+
7
+ * feat(color_map_selector): added colormap selector with plugin ([`b98fd00`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b98fd00adef97adf57f49b60ade99972b9f5a6bc))
8
+
9
+ ## v0.84.0 (2024-07-15)
10
+
11
+ ### Feature
12
+
13
+ * feat(waveform): async readback update implemented for async devices ([`0c6a9f2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0c6a9f2310df31ddcd68050a17cfbf52c3e2e226))
14
+
15
+ * feat(waveform): data are taken directly from ScanItem which is defined from scan_status endpoint; scan update is triggered from scan_segment; plots can be added just specifying y_name -> best effort for setting x reported device ([`b8717f1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b8717f13276734dd655ab03cd6005985ad5af9fb))
16
+
17
+ ### Fix
18
+
19
+ * fix(waveform): timestamp are not converted to human readable format ([`e495fd3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e495fd30c4c16474689943c7263e3060cb09ffb4))
20
+
21
+ * fix(waveform): set_x method various bugs fixed ([`8516a1d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8516a1d639925a877f174fa13f427a71131cc918))
22
+
23
+ * fix(waveform): x axis switching logic fixed when axis are not compatible ([`e4e1a90`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e4e1a905d19def22f970b364c18c953f00e10389))
24
+
25
+ * fix(waveform): dap leaked RID for all daps in current process; dap RID is now f"{scan_id}-{gui_id}" to distinguish for each plot instance ([`d23fd8b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d23fd8bd074ede6e14eb8e85e025cbced4bd45ef))
26
+
27
+ * fix(waveform): only one type of x axis allowed; x mode validated ([`9d6ae87`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9d6ae87d0f03ca227570fcca8af2d8190828d271))
28
+
29
+ * fix(waveform): data for axis are taken by separate method; validation consolidated ([`fc5a8bd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fc5a8bdd8b260f5e9b59ec71a4610c57442e43fe))
30
+
31
+ * fix(bec_dispatcher): connect_slot can accept kwargs ([`0aa317a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0aa317aae58d3612d46f05b85f8b0db3d12bbe14))
32
+
33
+ ### Refactor
34
+
35
+ * refactor(waveform): plot can be prompted without specifying kwargs ([`48911e9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/48911e934815923c94edb5ced6042058a11a97f5))
36
+
37
+ * refactor(jupyter_console_window): added more examples of waveforms ([`fc935d9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fc935d9fc81067c3a67389ff88ea97da2e0c903e))
38
+
39
+ ### Test
40
+
41
+ * test(waveform): tests extended ([`006992e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/006992e43cc56d56261bc4fd3e9cae9abcab2153))
42
+
3
43
  ## v0.83.1 (2024-07-14)
4
44
 
5
45
  ### Fix
@@ -99,45 +139,3 @@
99
139
  * fix(motor_control): temporary remove of motor control widgets ([`99114f1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/99114f14f62202e1fd8bf145616fa8c69937ada4))
100
140
 
101
141
  ## v0.81.0 (2024-07-06)
102
-
103
- ### Feature
104
-
105
- * feat(color_button): can get colors in RGBA or HEX ([`9594be2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9594be260680d11c8550ff74ffb8d679e5a5b8f6))
106
-
107
- ## v0.80.1 (2024-07-06)
108
-
109
- ### Fix
110
-
111
- * fix(entry_validator): check for entry == "" ([`61de7e9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/61de7e9e221c766b9fb3ec23246da6a11c96a986))
112
-
113
- ## v0.80.0 (2024-07-06)
114
-
115
- ### Feature
116
-
117
- * feat(qt5): dropped support for qt5; pyside2 and pyqt5 ([`fadbf77`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fadbf77866903beff6580802bc203d53367fc7e7))
118
-
119
- * feat(plugins): moved plugin dict to dataclass and container ([`03819a3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/03819a3d902b4a51f3e882d52aedd971b2a8e127))
120
-
121
- * feat(plugins): added support for pyqt6 ui files ([`d6d0777`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d6d07771135335cb78dc648508ce573b8970261a))
122
-
123
- * feat(plugins): added bec widgets base class ([`1aa83e0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1aa83e0ef1ffe45b01677b0b4590535cb0ca1cff))
124
-
125
- ## v0.79.3 (2024-07-05)
126
-
127
- ### Fix
128
-
129
- * fix: changed inheritance to adress qt designer bug in rendering ([`e403870`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e403870874bd5e45840a034d6f1b3dd576d9c846))
130
-
131
- * fix: add designer plugin classes ([`1586ce2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1586ce2d6cba2bb086b2ef596e724bb9e40ab4f2))
132
-
133
- ### Refactor
134
-
135
- * refactor: simplify logic in bec_status_box ([`576353c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/576353cfe8c6fd64db561f0b6e2bc951300643d3))
136
-
137
- ## v0.79.2 (2024-07-04)
138
-
139
- ### Fix
140
-
141
- * fix: overwrite closeEvent and call super class ([`bc0ef78`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bc0ef7893ef100b71b62101c459655509b534a56))
142
-
143
- ## v0.79.1 (2024-07-03)
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.83.1
3
+ Version: 0.85.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
bec_widgets/cli/client.py CHANGED
@@ -19,6 +19,9 @@ class Widgets(str, enum.Enum):
19
19
  BECMotorMapWidget = "BECMotorMapWidget"
20
20
  BECQueue = "BECQueue"
21
21
  BECStatusBox = "BECStatusBox"
22
+ DeviceBox = "DeviceBox"
23
+ DeviceComboBox = "DeviceComboBox"
24
+ DeviceLineEdit = "DeviceLineEdit"
22
25
  RingProgressBar = "RingProgressBar"
23
26
  ScanControl = "ScanControl"
24
27
  StopButton = "StopButton"
@@ -465,8 +468,9 @@ class BECFigure(RPCBase):
465
468
  @rpc_call
466
469
  def plot(
467
470
  self,
468
- x: "list | np.ndarray | None" = None,
471
+ arg1: "list | np.ndarray | str | None" = None,
469
472
  y: "list | np.ndarray | None" = None,
473
+ x: "list | np.ndarray | None" = None,
470
474
  x_name: "str | None" = None,
471
475
  y_name: "str | None" = None,
472
476
  z_name: "str | None" = None,
@@ -488,8 +492,9 @@ class BECFigure(RPCBase):
488
492
  Add a 1D waveform plot to the figure. Always access the first waveform widget in the figure.
489
493
 
490
494
  Args:
491
- x(list | np.ndarray): Custom x data to plot.
495
+ arg1(list | np.ndarray | str | None): First argument which can be x data, y data, or y_name.
492
496
  y(list | np.ndarray): Custom y data to plot.
497
+ x(list | np.ndarray): Custom x data to plot.
493
498
  x_name(str): The name of the device for the x-axis.
494
499
  y_name(str): The name of the device for the y-axis.
495
500
  z_name(str): The name of the device for the z-axis.
@@ -1494,8 +1499,9 @@ class BECWaveform(RPCBase):
1494
1499
  @rpc_call
1495
1500
  def plot(
1496
1501
  self,
1497
- x: "list | np.ndarray | None" = None,
1502
+ arg1: "list | np.ndarray | str | None" = None,
1498
1503
  y: "list | np.ndarray | None" = None,
1504
+ x: "list | np.ndarray | None" = None,
1499
1505
  x_name: "str | None" = None,
1500
1506
  y_name: "str | None" = None,
1501
1507
  z_name: "str | None" = None,
@@ -1507,13 +1513,20 @@ class BECWaveform(RPCBase):
1507
1513
  label: "str | None" = None,
1508
1514
  validate: "bool" = True,
1509
1515
  dap: "str | None" = None,
1516
+ **kwargs,
1510
1517
  ) -> "BECCurve":
1511
1518
  """
1512
1519
  Plot a curve to the plot widget.
1520
+
1513
1521
  Args:
1514
- x(list | np.ndarray): Custom x data to plot.
1522
+ arg1(list | np.ndarray | str | None): First argument which can be x data, y data, or y_name.
1515
1523
  y(list | np.ndarray): Custom y data to plot.
1516
- x_name(str): The name of the device for the x-axis.
1524
+ x(list | np.ndarray): Custom y data to plot.
1525
+ x_name(str): Name of the x signal.
1526
+ - "best_effort": Use the best effort signal.
1527
+ - "timestamp": Use the timestamp signal.
1528
+ - "index": Use the index signal.
1529
+ - Custom signal name of device from BEC.
1517
1530
  y_name(str): The name of the device for the y-axis.
1518
1531
  z_name(str): The name of the device for the z-axis.
1519
1532
  x_entry(str): The name of the entry for the x-axis.
@@ -1523,7 +1536,7 @@ class BECWaveform(RPCBase):
1523
1536
  color_map_z(str): The color map to use for the z-axis.
1524
1537
  label(str): The label of the curve.
1525
1538
  validate(bool): If True, validate the device names and entries.
1526
- dap(str): The dap model to use for the curve. If not specified, none will be added.
1539
+ dap(str): The dap model to use for the curve, only available for sync devices. If not specified, none will be added.
1527
1540
 
1528
1541
  Returns:
1529
1542
  BECCurve: The curve object.
@@ -1532,12 +1545,13 @@ class BECWaveform(RPCBase):
1532
1545
  @rpc_call
1533
1546
  def add_dap(
1534
1547
  self,
1535
- x_name: "str",
1536
- y_name: "str",
1548
+ x_name: "str | None" = None,
1549
+ y_name: "str | None" = None,
1537
1550
  x_entry: "Optional[str]" = None,
1538
1551
  y_entry: "Optional[str]" = None,
1539
1552
  color: "Optional[str]" = None,
1540
1553
  dap: "str" = "GaussianModel",
1554
+ validate_bec: "bool" = True,
1541
1555
  **kwargs,
1542
1556
  ) -> "BECCurve":
1543
1557
  """
@@ -1552,12 +1566,27 @@ class BECWaveform(RPCBase):
1552
1566
  color_map_z(str): The color map to use for the z-axis.
1553
1567
  label(str, optional): Label of the curve. Defaults to None.
1554
1568
  dap(str): The dap model to use for the curve.
1569
+ validate_bec(bool, optional): If True, validate the signal with BEC. Defaults to True.
1555
1570
  **kwargs: Additional keyword arguments for the curve configuration.
1556
1571
 
1557
1572
  Returns:
1558
1573
  BECCurve: The curve object.
1559
1574
  """
1560
1575
 
1576
+ @rpc_call
1577
+ def set_x(self, x_name: "str", x_entry: "str | None" = None):
1578
+ """
1579
+ Change the x axis of the plot widget.
1580
+
1581
+ Args:
1582
+ x_name(str): Name of the x signal.
1583
+ - "best_effort": Use the best effort signal.
1584
+ - "timestamp": Use the timestamp signal.
1585
+ - "index": Use the index signal.
1586
+ - Custom signal name of device from BEC.
1587
+ x_entry(str): Entry of the x signal.
1588
+ """
1589
+
1561
1590
  @rpc_call
1562
1591
  def get_dap_params(self) -> "dict":
1563
1592
  """
@@ -1742,6 +1771,12 @@ class BECWaveform(RPCBase):
1742
1771
  Remove the plot widget from the figure.
1743
1772
  """
1744
1773
 
1774
+ @rpc_call
1775
+ def clear_all(self):
1776
+ """
1777
+ None
1778
+ """
1779
+
1745
1780
  @rpc_call
1746
1781
  def set_legend_label_size(self, size: "int" = None):
1747
1782
  """
@@ -1752,6 +1787,24 @@ class BECWaveform(RPCBase):
1752
1787
  """
1753
1788
 
1754
1789
 
1790
+ class DeviceBox(RPCBase):
1791
+ @property
1792
+ @rpc_call
1793
+ def _config_dict(self) -> "dict":
1794
+ """
1795
+ Get the configuration of the widget.
1796
+
1797
+ Returns:
1798
+ dict: The configuration of the widget.
1799
+ """
1800
+
1801
+ @rpc_call
1802
+ def _get_all_rpc(self) -> "dict":
1803
+ """
1804
+ Get all registered RPC objects.
1805
+ """
1806
+
1807
+
1755
1808
  class DeviceComboBox(RPCBase):
1756
1809
  @property
1757
1810
  @rpc_call
@@ -2,13 +2,19 @@ import os
2
2
 
3
3
  import numpy as np
4
4
  import pyqtgraph as pg
5
- from qtconsole.inprocess import QtInProcessKernelManager
6
- from qtconsole.rich_jupyter_widget import RichJupyterWidget
7
5
  from qtpy.QtCore import QSize
8
6
  from qtpy.QtGui import QIcon
9
- from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
10
-
11
- from bec_widgets.utils import BECDispatcher, UILoader
7
+ from qtpy.QtWidgets import (
8
+ QApplication,
9
+ QGroupBox,
10
+ QHBoxLayout,
11
+ QSplitter,
12
+ QTabWidget,
13
+ QVBoxLayout,
14
+ QWidget,
15
+ )
16
+
17
+ from bec_widgets.utils import BECDispatcher
12
18
  from bec_widgets.utils.colors import apply_theme
13
19
  from bec_widgets.widgets.dock.dock_area import BECDockArea
14
20
  from bec_widgets.widgets.figure import BECFigure
@@ -21,14 +27,8 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
21
27
  def __init__(self, parent=None):
22
28
  super().__init__(parent)
23
29
 
24
- current_path = os.path.dirname(__file__)
25
- self.ui = UILoader().load_ui(os.path.join(current_path, "jupyter_console_window.ui"), self)
26
-
27
30
  self._init_ui()
28
31
 
29
- self.ui.splitter.setSizes([200, 100])
30
- self.safe_close = False
31
-
32
32
  # console push
33
33
  if self.console.inprocess is True:
34
34
  self.console.kernel_manager.kernel.shell.push(
@@ -40,10 +40,12 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
40
40
  "w1": self.w1,
41
41
  "w2": self.w2,
42
42
  "w3": self.w3,
43
- "w1_c": self.w1_c,
44
- "w2_c": self.w2_c,
45
- "w3_c": self.w3_c,
46
43
  "w4": self.w4,
44
+ "w5": self.w5,
45
+ "w6": self.w6,
46
+ "w7": self.w7,
47
+ "w8": self.w8,
48
+ "w9": self.w9,
47
49
  "d0": self.d0,
48
50
  "d1": self.d1,
49
51
  "d2": self.d2,
@@ -53,14 +55,30 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
53
55
  )
54
56
 
55
57
  def _init_ui(self):
56
- # Plotting window
57
- self.glw_1_layout = QVBoxLayout(self.ui.glw) # Create a new QVBoxLayout
58
- self.figure = BECFigure(parent=self, gui_id="remote") # Create a new BECDeviceMonitor
59
- self.glw_1_layout.addWidget(self.figure) # Add BECDeviceMonitor to the layout
58
+ self.layout = QHBoxLayout(self)
59
+
60
+ # Horizontal splitter
61
+ splitter = QSplitter(self)
62
+ self.layout.addWidget(splitter)
60
63
 
61
- self.dock_layout = QVBoxLayout(self.ui.dock_placeholder)
62
- self.dock = BECDockArea(gui_id="remote")
63
- self.dock_layout.addWidget(self.dock)
64
+ tab_widget = QTabWidget(splitter)
65
+
66
+ first_tab = QWidget()
67
+ first_tab_layout = QVBoxLayout(first_tab)
68
+ self.dock = BECDockArea(gui_id="dock")
69
+ first_tab_layout.addWidget(self.dock)
70
+ tab_widget.addTab(first_tab, "Dock Area")
71
+
72
+ second_tab = QWidget()
73
+ second_tab_layout = QVBoxLayout(second_tab)
74
+ self.figure = BECFigure(parent=self, gui_id="figure")
75
+ second_tab_layout.addWidget(self.figure)
76
+ tab_widget.addTab(second_tab, "BEC Figure")
77
+
78
+ group_box = QGroupBox("Jupyter Console", splitter)
79
+ group_box_layout = QVBoxLayout(group_box)
80
+ self.console = BECJupyterConsole(inprocess=True)
81
+ group_box_layout.addWidget(self.console)
64
82
 
65
83
  # add stuff to figure
66
84
  self._init_figure()
@@ -68,44 +86,70 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
68
86
  # init dock for testing
69
87
  self._init_dock()
70
88
 
71
- self.console_layout = QVBoxLayout(self.ui.widget_console)
72
- self.console = BECJupyterConsole(inprocess=True)
73
- self.console_layout.addWidget(self.console)
89
+ self.setWindowTitle("Jupyter Console Window")
74
90
 
75
91
  def _init_figure(self):
76
- self.figure.plot(x_name="samx", y_name="samy", z_name="bpm4i", color_map_z="cividis")
77
- self.figure.motor_map("samx", "samy")
78
- self.figure.image("eiger", color_map="viridis", vrange=(0, 100))
79
- self.figure.plot(
80
- x_name="samx", y_name="samy", z_name="bpm4i", color_map_z="magma", new=True
92
+ self.w1 = self.figure.plot(
93
+ x_name="samx",
94
+ y_name="bpm4i",
95
+ # title="Standard Plot with sync device, custom labels - w1",
96
+ # x_label="Motor Position",
97
+ # y_label="Intensity (A.U.)",
98
+ row=0,
99
+ col=0,
100
+ )
101
+ self.w1.set(
102
+ title="Standard Plot with sync device, custom labels - w1",
103
+ x_label="Motor Position",
104
+ y_label="Intensity (A.U.)",
105
+ )
106
+ self.w2 = self.figure.motor_map("samx", "samy", row=0, col=1)
107
+ self.w3 = self.figure.image(
108
+ "eiger", color_map="viridis", vrange=(0, 100), title="Eiger Image - w3", row=0, col=2
109
+ )
110
+ self.w4 = self.figure.plot(
111
+ x_name="samx",
112
+ y_name="samy",
113
+ z_name="bpm4i",
114
+ color_map_z="magma",
115
+ new=True,
116
+ title="2D scatter plot - w4",
117
+ row=0,
118
+ col=3,
119
+ )
120
+ self.w5 = self.figure.plot(
121
+ y_name="bpm4i",
122
+ new=True,
123
+ title="Best Effort Plot - w5",
124
+ dap="GaussianModel",
125
+ row=1,
126
+ col=0,
127
+ )
128
+ self.w6 = self.figure.plot(
129
+ x_name="timestamp", y_name="bpm4i", new=True, title="Timestamp Plot - w6", row=1, col=1
130
+ )
131
+ self.w7 = self.figure.plot(
132
+ x_name="index", y_name="bpm4i", new=True, title="Index Plot - w7", row=1, col=2
133
+ )
134
+ self.w8 = self.figure.plot(
135
+ y_name="monitor_async", new=True, title="Async Plot - Best Effort - w8", row=2, col=0
136
+ )
137
+ self.w9 = self.figure.plot(
138
+ x_name="timestamp",
139
+ y_name="monitor_async",
140
+ new=True,
141
+ title="Async Plot - timestamp - w9",
142
+ row=2,
143
+ col=1,
144
+ )
145
+ self.w10 = self.figure.plot(
146
+ x_name="index",
147
+ y_name="monitor_async",
148
+ new=True,
149
+ title="Async Plot - index - w10",
150
+ row=2,
151
+ col=2,
81
152
  )
82
-
83
- self.figure.change_layout(2, 2)
84
-
85
- self.w1 = self.figure[0, 0]
86
- self.w2 = self.figure[0, 1]
87
- self.w3 = self.figure[1, 0]
88
- self.w4 = self.figure[1, 1]
89
-
90
- # Plot Customisation
91
- self.w1.set_title("Waveform 1")
92
- self.w1.set_x_label("Motor Position (samx)")
93
- self.w1.set_y_label("Intensity A.U.")
94
-
95
- # Image Customisation
96
- self.w3.set_title("Eiger Image")
97
- self.w3.set_x_label("X")
98
- self.w3.set_y_label("Y")
99
-
100
- # Configs to try to pass
101
- self.w1_c = self.w1._config_dict
102
- self.w2_c = self.w2._config_dict
103
- self.w3_c = self.w3._config_dict
104
-
105
- # curves for w1
106
- self.c1 = self.w1.get_config()
107
-
108
- self.fig_c = self.figure._config_dict
109
153
 
110
154
  def _init_dock(self):
111
155
 
@@ -131,9 +175,13 @@ class JupyterConsoleWindow(QWidget): # pragma: no cover:
131
175
 
132
176
  def closeEvent(self, event):
133
177
  """Override to handle things when main window is closed."""
178
+ self.dock.clear_all()
134
179
  self.dock.cleanup()
180
+ self.dock.close()
135
181
  self.figure.clear_all()
136
- self.figure.client.shutdown()
182
+ self.figure.cleanup()
183
+ self.figure.close()
184
+
137
185
  super().closeEvent(event)
138
186
 
139
187
 
@@ -134,7 +134,10 @@ class BECDispatcher:
134
134
  cls.qapp = None
135
135
 
136
136
  def connect_slot(
137
- self, slot: Callable, topics: Union[EndpointInfo, str, list[Union[EndpointInfo, str]]]
137
+ self,
138
+ slot: Callable,
139
+ topics: Union[EndpointInfo, str, list[Union[EndpointInfo, str]]],
140
+ **kwargs,
138
141
  ) -> None:
139
142
  """Connect widget's pyqt slot, so that it is called on new pub/sub topic message.
140
143
 
@@ -144,7 +147,7 @@ class BECDispatcher:
144
147
  topics (EndpointInfo | str | list): A topic or list of topics that can typically be acquired via bec_lib.MessageEndpoints
145
148
  """
146
149
  slot = QtThreadSafeCallback(slot)
147
- self.client.connector.register(topics, cb=slot)
150
+ self.client.connector.register(topics, cb=slot, **kwargs)
148
151
  topics_str, _ = self.client.connector._convert_endpointinfo(topics)
149
152
  self._slots[slot].update(set(topics_str))
150
153
 
File without changes
@@ -0,0 +1,111 @@
1
+ import pyqtgraph as pg
2
+ from qtpy.QtCore import Property, Signal, Slot
3
+ from qtpy.QtGui import QColor, QFontMetrics, QImage
4
+ from qtpy.QtWidgets import QApplication, QComboBox, QStyledItemDelegate, QVBoxLayout, QWidget
5
+
6
+
7
+ class ColormapDelegate(QStyledItemDelegate):
8
+ def __init__(self, parent=None):
9
+ super(ColormapDelegate, self).__init__(parent)
10
+ self.image_width = 25
11
+ self.image_height = 10
12
+ self.gap = 10
13
+
14
+ def paint(self, painter, option, index):
15
+ text = index.data()
16
+ colormap = pg.colormap.get(text)
17
+ colors = colormap.getLookupTable(start=0.0, stop=1.0, alpha=False)
18
+
19
+ font_metrics = QFontMetrics(painter.font())
20
+ text_width = font_metrics.width(text)
21
+ text_height = font_metrics.height()
22
+
23
+ total_height = max(text_height, self.image_height)
24
+
25
+ image = QImage(self.image_width, self.image_height, QImage.Format_RGB32)
26
+ for i in range(self.image_width):
27
+ color = QColor(*colors[int(i * (len(colors) - 1) / (self.image_width - 1))])
28
+ for j in range(self.image_height):
29
+ image.setPixel(i, j, color.rgb())
30
+
31
+ painter.drawImage(
32
+ option.rect.x(), option.rect.y() + (total_height - self.image_height) // 2, image
33
+ )
34
+ painter.drawText(
35
+ option.rect.x() + self.image_width + self.gap,
36
+ option.rect.y() + (total_height - text_height) // 2 + font_metrics.ascent(),
37
+ text,
38
+ )
39
+
40
+
41
+ class ColormapSelector(QWidget):
42
+ """
43
+ Simple colormap combobox widget. By default it loads all the available colormaps in pyqtgraph.
44
+ """
45
+
46
+ colormap_changed_signal = Signal(str)
47
+
48
+ def __init__(self, parent=None, default_colormaps=None):
49
+ super().__init__(parent)
50
+ self._colormaps = []
51
+ self.initUI(default_colormaps)
52
+
53
+ def initUI(self, default_colormaps=None):
54
+ self.layout = QVBoxLayout(self)
55
+ self.combo = QComboBox()
56
+ self.combo.setItemDelegate(ColormapDelegate())
57
+ self.combo.currentTextChanged.connect(self.colormap_changed)
58
+ self.available_colormaps = pg.colormap.listMaps()
59
+ if default_colormaps is None:
60
+ default_colormaps = self.available_colormaps
61
+ self.add_color_maps(default_colormaps)
62
+ self.layout.addWidget(self.combo)
63
+
64
+ @Slot()
65
+ def colormap_changed(self):
66
+ """
67
+ Emit the colormap changed signal with the current colormap selected in the combobox.
68
+ """
69
+ self.colormap_changed_signal.emit(self.combo.currentText())
70
+
71
+ def add_color_maps(self, colormaps=None):
72
+ """
73
+ Add colormaps to the combobox.
74
+
75
+ Args:
76
+ colormaps(list): List of colormaps to add to the combobox. If None, all available colormaps are added.
77
+ """
78
+ self.combo.clear()
79
+ if colormaps is not None:
80
+ for name in colormaps:
81
+ if name in self.available_colormaps:
82
+ self.combo.addItem(name)
83
+ else:
84
+ for name in self.available_colormaps:
85
+ self.combo.addItem(name)
86
+ self._colormaps = colormaps if colormaps is not None else self.available_colormaps
87
+
88
+ @Property("QStringList")
89
+ def colormaps(self):
90
+ """
91
+ Property to get and set the colormaps in the combobox.
92
+ """
93
+ return self._colormaps
94
+
95
+ @colormaps.setter
96
+ def colormaps(self, value):
97
+ """
98
+ Set the colormaps in the combobox.
99
+ """
100
+ if self._colormaps != value:
101
+ self._colormaps = value
102
+ self.add_color_maps(value)
103
+
104
+
105
+ if __name__ == "__main__": # pragma: no cover
106
+ import sys
107
+
108
+ app = QApplication(sys.argv)
109
+ ex = ColormapSelector()
110
+ ex.show()
111
+ sys.exit(app.exec_())
@@ -0,0 +1 @@
1
+ {'files': ['color_map_selector.py']}
@@ -0,0 +1,57 @@
1
+ # Copyright (C) 2022 The Qt Company Ltd.
2
+ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
+ import os
4
+
5
+ from qtpy.QtDesigner import QDesignerCustomWidgetInterface
6
+ from qtpy.QtGui import QIcon
7
+
8
+ from bec_widgets.widgets.color_map_selector.color_map_selector import ColormapSelector
9
+
10
+ DOM_XML = """
11
+ <ui language='c++'>
12
+ <widget class='ColormapSelector' name='color_map_selector'>
13
+ </widget>
14
+ </ui>
15
+ """
16
+
17
+
18
+ class ColormapSelectorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
19
+ def __init__(self):
20
+ super().__init__()
21
+ self._form_editor = None
22
+
23
+ def createWidget(self, parent):
24
+ t = ColormapSelector(parent)
25
+ return t
26
+
27
+ def domXml(self):
28
+ return DOM_XML
29
+
30
+ def group(self):
31
+ return "BEC Buttons"
32
+
33
+ def icon(self):
34
+ current_path = os.path.dirname(__file__)
35
+ icon_path = os.path.join(current_path, "assets", "color_map_selector_icon.png")
36
+ return QIcon(icon_path)
37
+
38
+ def includeFile(self):
39
+ return "color_map_selector"
40
+
41
+ def initialize(self, form_editor):
42
+ self._form_editor = form_editor
43
+
44
+ def isContainer(self):
45
+ return False
46
+
47
+ def isInitialized(self):
48
+ return self._form_editor is not None
49
+
50
+ def name(self):
51
+ return "ColormapSelector"
52
+
53
+ def toolTip(self):
54
+ return "A custom QComboBox widget for selecting colormaps."
55
+
56
+ def whatsThis(self):
57
+ return self.toolTip()
@@ -0,0 +1,17 @@
1
+ def main(): # pragma: no cover
2
+ from qtpy import PYSIDE6
3
+
4
+ if not PYSIDE6:
5
+ print("PYSIDE6 is not available in the environment. Cannot patch designer.")
6
+ return
7
+ from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
8
+
9
+ from bec_widgets.widgets.color_map_selector.color_map_selector_plugin import (
10
+ ColormapSelectorPlugin,
11
+ )
12
+
13
+ QPyDesignerCustomWidgetCollection.addCustomWidget(ColormapSelectorPlugin())
14
+
15
+
16
+ if __name__ == "__main__": # pragma: no cover
17
+ main()