bec-widgets 0.115.0__py3-none-any.whl → 0.117.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.
CHANGELOG.md CHANGED
@@ -1,6 +1,37 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v0.117.0 (2024-10-11)
5
+
6
+ ### Features
7
+
8
+ * feat(utils): FPS counter utility based on the viewBox updates, integrated to waveform and image widget ([`8c5ef26`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8c5ef268430d5243ac05fcbbdb6b76ad24ac5735))
9
+
10
+ ### Unknown
11
+
12
+ * tests(plot_base): tests extended ([`8dc892d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/8dc892df0a47ccbdd812555b7c5775a455a23ede))
13
+
14
+
15
+ ## v0.116.0 (2024-10-11)
16
+
17
+ ### Build System
18
+
19
+ * build: fix PySide6 to 6.7.2 ([`908dbc1`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/908dbc1760da5b323722207163f00850b84fb90b))
20
+
21
+ ### Features
22
+
23
+ * feat: UI changes to have top toolbar with compact popup widgets (fix issue #360) ([`499b6b9`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/499b6b9a12efd931b5728b519404c41a7e29e4d6))
24
+
25
+ * feat: adapt BECQueue and BECStatusBox widgets to use CompactPopupWidget ([`94ce92f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/94ce92f5b054d25ea3bb7976c1f75e14b78b9edc))
26
+
27
+ * feat: add 'CompactPopupWidget' container widget
28
+
29
+ Makes it easy to write widgets which can have a compact
30
+ representation with LED-like global state indicator,
31
+ with the possibility to display a popup dialog with more
32
+ complete UI ([`49268e3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/49268e3829406d70b09e4d88989812f5578e46f4))
33
+
34
+
4
35
  ## v0.115.0 (2024-10-08)
5
36
 
6
37
  ### Features
@@ -143,31 +174,3 @@ Fixes #361, do not try to change x axis when not permitted ([`efa2763`](https://
143
174
  ### Features
144
175
 
145
176
  * feat(progressbar): added bec progressbar ([`f6d1d0b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f6d1d0bbe3ba30a3b7291cd36a1f7f8e6bd5b895))
146
-
147
- * feat(generate_cli): added support for property and qproperty setter ([`a52182d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a52182dca978833bfc3fad755c596d3a2ef45c42))
148
-
149
-
150
- ## v0.107.0 (2024-09-06)
151
-
152
- ### Documentation
153
-
154
- * docs: extend waveform docs ([`e6976dc`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e6976dc15105209852090a00a97b7cda723142e9))
155
-
156
- ### Features
157
-
158
- * feat: add roi select for dap, allow automatic clear curves on plot request ([`7bdca84`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7bdca8431496fe6562d2c28f5a6af869d1a2e654))
159
-
160
- ### Refactoring
161
-
162
- * refactor: change style to bec_accent_colors ([`bd126dd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bd126dddbbec3e6c448cce263433d328d577c5c0))
163
-
164
- ### Testing
165
-
166
- * test: add tests, including extension to end-2-end test ([`b1aff6d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b1aff6d791ff847eb2f628e66ccaa4672fdeea08))
167
-
168
-
169
- ## v0.106.0 (2024-09-05)
170
-
171
- ### Features
172
-
173
- * feat(plot_base): toggle to switch outer axes for plotting widgets ([`06d7741`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/06d7741622aea8556208cd17cae521c37333f8b6))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.115.0
3
+ Version: 0.117.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
@@ -32,4 +32,4 @@ Provides-Extra: pyqt6
32
32
  Requires-Dist: pyqt6-webengine>=6.7; extra == 'pyqt6'
33
33
  Requires-Dist: pyqt6>=6.7; extra == 'pyqt6'
34
34
  Provides-Extra: pyside6
35
- Requires-Dist: pyside6>=6.7; extra == 'pyside6'
35
+ Requires-Dist: pyside6~=6.7.2; extra == 'pyside6'
@@ -15,47 +15,65 @@
15
15
  </property>
16
16
  <layout class="QVBoxLayout" name="verticalLayout">
17
17
  <item>
18
- <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0,0,0,0,1">
18
+ <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0,0,0,0,0,0,0,0,0,1">
19
19
  <item>
20
20
  <widget class="DarkModeButton" name="dark_mode_button"/>
21
21
  </item>
22
22
  <item>
23
- <widget class="QRadioButton" name="radioButton_2">
24
- <property name="enabled">
25
- <bool>false</bool>
26
- </property>
27
- <property name="text">
28
- <string>BEC Server State</string>
23
+ <spacer name="horizontalSpacer_6">
24
+ <property name="orientation">
25
+ <enum>Qt::Orientation::Horizontal</enum>
29
26
  </property>
30
- <property name="checkable">
31
- <bool>true</bool>
27
+ <property name="sizeHint" stdset="0">
28
+ <size>
29
+ <width>40</width>
30
+ <height>20</height>
31
+ </size>
32
32
  </property>
33
- <property name="checked">
33
+ </spacer>
34
+ </item>
35
+ <item>
36
+ <widget class="BECStatusBox" name="bec_status_box">
37
+ <property name="compact" stdset="0">
34
38
  <bool>true</bool>
35
39
  </property>
36
- <property name="autoExclusive">
37
- <bool>false</bool>
40
+ <property name="label" stdset="0">
41
+ <string>BEC Servers</string>
38
42
  </property>
39
43
  </widget>
40
44
  </item>
41
45
  <item>
42
- <widget class="QRadioButton" name="radioButton_4">
43
- <property name="enabled">
44
- <bool>false</bool>
46
+ <spacer name="horizontalSpacer_4">
47
+ <property name="orientation">
48
+ <enum>Qt::Orientation::Horizontal</enum>
45
49
  </property>
46
- <property name="text">
47
- <string>BEC Queue</string>
50
+ <property name="sizeHint" stdset="0">
51
+ <size>
52
+ <width>40</width>
53
+ <height>20</height>
54
+ </size>
48
55
  </property>
49
- <property name="checkable">
56
+ </spacer>
57
+ </item>
58
+ <item>
59
+ <widget class="BECQueue" name="bec_queue">
60
+ <property name="compact" stdset="0">
50
61
  <bool>true</bool>
51
62
  </property>
52
- <property name="checked">
53
- <bool>true</bool>
63
+ </widget>
64
+ </item>
65
+ <item>
66
+ <spacer name="horizontalSpacer_3">
67
+ <property name="orientation">
68
+ <enum>Qt::Orientation::Horizontal</enum>
54
69
  </property>
55
- <property name="autoExclusive">
56
- <bool>false</bool>
70
+ <property name="sizeHint" stdset="0">
71
+ <size>
72
+ <width>40</width>
73
+ <height>20</height>
74
+ </size>
57
75
  </property>
58
- </widget>
76
+ </spacer>
59
77
  </item>
60
78
  <item>
61
79
  <widget class="QRadioButton" name="radioButton">
@@ -76,6 +94,19 @@
76
94
  </property>
77
95
  </widget>
78
96
  </item>
97
+ <item>
98
+ <spacer name="horizontalSpacer_5">
99
+ <property name="orientation">
100
+ <enum>Qt::Orientation::Horizontal</enum>
101
+ </property>
102
+ <property name="sizeHint" stdset="0">
103
+ <size>
104
+ <width>40</width>
105
+ <height>20</height>
106
+ </size>
107
+ </property>
108
+ </spacer>
109
+ </item>
79
110
  <item>
80
111
  <widget class="QRadioButton" name="radioButton_3">
81
112
  <property name="enabled">
@@ -95,6 +126,19 @@
95
126
  </property>
96
127
  </widget>
97
128
  </item>
129
+ <item>
130
+ <spacer name="horizontalSpacer_7">
131
+ <property name="orientation">
132
+ <enum>Qt::Orientation::Horizontal</enum>
133
+ </property>
134
+ <property name="sizeHint" stdset="0">
135
+ <size>
136
+ <width>40</width>
137
+ <height>20</height>
138
+ </size>
139
+ </property>
140
+ </spacer>
141
+ </item>
98
142
  <item>
99
143
  <widget class="StopButton" name="stop_button">
100
144
  <property name="sizePolicy">
@@ -652,14 +696,19 @@
652
696
  </widget>
653
697
  <customwidgets>
654
698
  <customwidget>
655
- <class>DapComboBox</class>
699
+ <class>ToggleSwitch</class>
656
700
  <extends>QWidget</extends>
657
- <header>dap_combo_box</header>
701
+ <header>toggle_switch</header>
658
702
  </customwidget>
659
703
  <customwidget>
660
- <class>StopButton</class>
704
+ <class>BECWaveformWidget</class>
661
705
  <extends>QWidget</extends>
662
- <header>stop_button</header>
706
+ <header>bec_waveform_widget</header>
707
+ </customwidget>
708
+ <customwidget>
709
+ <class>BECStatusBox</class>
710
+ <extends>QWidget</extends>
711
+ <header>bec_status_box</header>
663
712
  </customwidget>
664
713
  <customwidget>
665
714
  <class>WebsiteWidget</class>
@@ -667,44 +716,49 @@
667
716
  <header>website_widget</header>
668
717
  </customwidget>
669
718
  <customwidget>
670
- <class>ScanControl</class>
719
+ <class>StopButton</class>
671
720
  <extends>QWidget</extends>
672
- <header>scan_control</header>
721
+ <header>stop_button</header>
673
722
  </customwidget>
674
723
  <customwidget>
675
- <class>ToggleSwitch</class>
724
+ <class>LMFitDialog</class>
676
725
  <extends>QWidget</extends>
677
- <header>toggle_switch</header>
726
+ <header>lm_fit_dialog</header>
678
727
  </customwidget>
679
728
  <customwidget>
680
- <class>PositionerBox</class>
729
+ <class>ScanControl</class>
681
730
  <extends>QWidget</extends>
682
- <header>positioner_box</header>
731
+ <header>scan_control</header>
732
+ </customwidget>
733
+ <customwidget>
734
+ <class>BECQueue</class>
735
+ <extends>QWidget</extends>
736
+ <header>bec_queue</header>
683
737
  </customwidget>
684
738
  <customwidget>
685
739
  <class>BECProgressBar</class>
686
740
  <extends>QWidget</extends>
687
741
  <header>bec_progress_bar</header>
688
742
  </customwidget>
743
+ <customwidget>
744
+ <class>DeviceComboBox</class>
745
+ <extends>QComboBox</extends>
746
+ <header>device_combobox</header>
747
+ </customwidget>
689
748
  <customwidget>
690
749
  <class>DarkModeButton</class>
691
750
  <extends>QWidget</extends>
692
751
  <header>dark_mode_button</header>
693
752
  </customwidget>
694
753
  <customwidget>
695
- <class>BECWaveformWidget</class>
754
+ <class>DapComboBox</class>
696
755
  <extends>QWidget</extends>
697
- <header>bec_waveform_widget</header>
698
- </customwidget>
699
- <customwidget>
700
- <class>DeviceComboBox</class>
701
- <extends>QComboBox</extends>
702
- <header>device_combobox</header>
756
+ <header>dap_combo_box</header>
703
757
  </customwidget>
704
758
  <customwidget>
705
- <class>LMFitDialog</class>
759
+ <class>PositionerBox</class>
706
760
  <extends>QWidget</extends>
707
- <header>lm_fit_dialog</header>
761
+ <header>positioner_box</header>
708
762
  </customwidget>
709
763
  </customwidgets>
710
764
  <tabstops>
bec_widgets/cli/client.py CHANGED
@@ -1135,6 +1135,15 @@ class BECImageShow(RPCBase):
1135
1135
  y(bool): Show grid on the y-axis.
1136
1136
  """
1137
1137
 
1138
+ @rpc_call
1139
+ def enable_fps_monitor(self, enable: "bool" = True):
1140
+ """
1141
+ Enable the FPS monitor.
1142
+
1143
+ Args:
1144
+ enable(bool): True to enable, False to disable.
1145
+ """
1146
+
1138
1147
  @rpc_call
1139
1148
  def lock_aspect_ratio(self, lock):
1140
1149
  """
@@ -1330,6 +1339,15 @@ class BECImageWidget(RPCBase):
1330
1339
  y_grid(bool): Visibility of the y-axis grid.
1331
1340
  """
1332
1341
 
1342
+ @rpc_call
1343
+ def enable_fps_monitor(self, enabled: "bool"):
1344
+ """
1345
+ Enable the FPS monitor of the plot widget.
1346
+
1347
+ Args:
1348
+ enabled(bool): If True, enable the FPS monitor.
1349
+ """
1350
+
1333
1351
  @rpc_call
1334
1352
  def lock_aspect_ratio(self, lock: "bool"):
1335
1353
  """
@@ -1666,6 +1684,15 @@ class BECPlotBase(RPCBase):
1666
1684
  show(bool): Show the outer axes.
1667
1685
  """
1668
1686
 
1687
+ @rpc_call
1688
+ def enable_fps_monitor(self, enable: "bool" = True):
1689
+ """
1690
+ Enable the FPS monitor.
1691
+
1692
+ Args:
1693
+ enable(bool): True to enable, False to disable.
1694
+ """
1695
+
1669
1696
  @rpc_call
1670
1697
  def lock_aspect_ratio(self, lock):
1671
1698
  """
@@ -2069,6 +2096,15 @@ class BECWaveform(RPCBase):
2069
2096
  colormap(str, optional): Scale the colors of curves to colormap. If None, use the default color palette.
2070
2097
  """
2071
2098
 
2099
+ @rpc_call
2100
+ def enable_fps_monitor(self, enable: "bool" = True):
2101
+ """
2102
+ Enable the FPS monitor.
2103
+
2104
+ Args:
2105
+ enable(bool): True to enable, False to disable.
2106
+ """
2107
+
2072
2108
  @rpc_call
2073
2109
  def lock_aspect_ratio(self, lock):
2074
2110
  """
@@ -2374,6 +2410,15 @@ class BECWaveformWidget(RPCBase):
2374
2410
  y_grid(bool): Visibility of the y-axis grid.
2375
2411
  """
2376
2412
 
2413
+ @rpc_call
2414
+ def enable_fps_monitor(self, enabled: "bool"):
2415
+ """
2416
+ Enable the FPS monitor of the plot widget.
2417
+
2418
+ Args:
2419
+ enabled(bool): If True, enable the FPS monitor.
2420
+ """
2421
+
2377
2422
  @rpc_call
2378
2423
  def lock_aspect_ratio(self, lock: "bool"):
2379
2424
  """
@@ -0,0 +1,223 @@
1
+ from types import SimpleNamespace
2
+
3
+ from bec_qthemes import material_icon
4
+ from qtpy.QtCore import Property, Qt
5
+ from qtpy.QtGui import QColor
6
+ from qtpy.QtWidgets import (
7
+ QDialog,
8
+ QHBoxLayout,
9
+ QLabel,
10
+ QPushButton,
11
+ QSizePolicy,
12
+ QVBoxLayout,
13
+ QWidget,
14
+ )
15
+
16
+ from bec_widgets.utils.colors import get_accent_colors
17
+
18
+
19
+ class LedLabel(QLabel):
20
+ success_led = "color: white;border-radius: 10;background-color: qlineargradient(spread:pad, x1:0.145, y1:0.16, x2:1, y2:1, stop:0 %s, stop:1 %s);"
21
+ emergency_led = "color: white;border-radius: 10;background-color: qlineargradient(spread:pad, x1:0.145, y1:0.16, x2:0.92, y2:0.988636, stop:0 %s, stop:1 %s);"
22
+ warning_led = "color: white;border-radius: 10;background-color: qlineargradient(spread:pad, x1:0.232, y1:0.272, x2:0.98, y2:0.959773, stop:0 %s, stop:1 %s);"
23
+ default_led = "color: white;border-radius: 10;background-color: qlineargradient(spread:pad, x1:0.04, y1:0.0565909, x2:0.799, y2:0.795, stop:0 %s, stop:1 %s);"
24
+
25
+ def __init__(self, parent=None):
26
+ super().__init__(parent)
27
+
28
+ self.palette = get_accent_colors()
29
+ if self.palette is None:
30
+ # no theme!
31
+ self.palette = SimpleNamespace(
32
+ default=QColor("blue"),
33
+ success=QColor("green"),
34
+ warning=QColor("orange"),
35
+ emergency=QColor("red"),
36
+ )
37
+ self.setState("default")
38
+ self.setFixedSize(20, 20)
39
+
40
+ def setState(self, state: str):
41
+ match state:
42
+ case "success":
43
+ r, g, b, a = self.palette.success.getRgb()
44
+ self.setStyleSheet(
45
+ LedLabel.success_led
46
+ % (
47
+ f"rgba({r},{g},{b},{a})",
48
+ f"rgba({int(r*0.8)},{int(g*0.8)},{int(b*0.8)},{a})",
49
+ )
50
+ )
51
+ case "default":
52
+ r, g, b, a = self.palette.default.getRgb()
53
+ self.setStyleSheet(
54
+ LedLabel.default_led
55
+ % (
56
+ f"rgba({r},{g},{b},{a})",
57
+ f"rgba({int(r*0.8)},{int(g*0.8)},{int(b*0.8)},{a})",
58
+ )
59
+ )
60
+ case "warning":
61
+ r, g, b, a = self.palette.warning.getRgb()
62
+ self.setStyleSheet(
63
+ LedLabel.warning_led
64
+ % (
65
+ f"rgba({r},{g},{b},{a})",
66
+ f"rgba({int(r*0.8)},{int(g*0.8)},{int(b*0.8)},{a})",
67
+ )
68
+ )
69
+ case "emergency":
70
+ r, g, b, a = self.palette.emergency.getRgb()
71
+ self.setStyleSheet(
72
+ LedLabel.emergency_led
73
+ % (
74
+ f"rgba({r},{g},{b},{a})",
75
+ f"rgba({int(r*0.8)},{int(g*0.8)},{int(b*0.8)},{a})",
76
+ )
77
+ )
78
+ case unknown_state:
79
+ raise ValueError(
80
+ f"Unknown state {repr(unknown_state)}, must be one of default, success, warning or emergency"
81
+ )
82
+
83
+
84
+ class PopupDialog(QDialog):
85
+ def __init__(self, content_widget):
86
+ self.parent = content_widget.parent()
87
+ self.content_widget = content_widget
88
+
89
+ super().__init__(self.parent)
90
+
91
+ self.setAttribute(Qt.WA_DeleteOnClose)
92
+
93
+ self.content_widget.setParent(self)
94
+ QVBoxLayout(self)
95
+ self.layout().addWidget(self.content_widget)
96
+ self.content_widget.setVisible(True)
97
+
98
+ def closeEvent(self, event):
99
+ self.content_widget.setVisible(False)
100
+ self.content_widget.setParent(self.parent)
101
+
102
+
103
+ class CompactPopupWidget(QWidget):
104
+ """Container widget, that can display its content or have a compact form,
105
+ in this case clicking on a small button pops the contained widget up.
106
+
107
+ In the compact form, a LED-like indicator shows a status indicator.
108
+ """
109
+
110
+ def __init__(self, parent=None, layout=QVBoxLayout):
111
+ super().__init__(parent)
112
+
113
+ self._popup_window = None
114
+
115
+ QVBoxLayout(self)
116
+ self.compact_view = QWidget(self)
117
+ self.compact_view.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
118
+ QHBoxLayout(self.compact_view)
119
+ self.compact_view.layout().setSpacing(0)
120
+ self.compact_view.layout().setContentsMargins(0, 0, 0, 0)
121
+ self.compact_label = QLabel(self.compact_view)
122
+ self.compact_status = LedLabel(self.compact_view)
123
+ self.compact_show_popup = QPushButton(self.compact_view)
124
+ self.compact_show_popup.setFlat(True)
125
+ self.compact_show_popup.setIcon(
126
+ material_icon(icon_name="pan_zoom", size=(10, 10), convert_to_pixmap=False)
127
+ )
128
+ self.compact_view.layout().addWidget(self.compact_label)
129
+ self.compact_view.layout().addWidget(self.compact_status)
130
+ self.compact_view.layout().addWidget(self.compact_show_popup)
131
+ self.compact_view.setVisible(False)
132
+ self.layout().addWidget(self.compact_view)
133
+ self.container = QWidget(self)
134
+ self.layout().addWidget(self.container)
135
+ self.container.setVisible(True)
136
+ layout(self.container)
137
+ self.layout = self.container.layout()
138
+
139
+ self.compact_show_popup.clicked.connect(self.show_popup)
140
+
141
+ def set_global_state(self, state: str):
142
+ """Set the LED-indicator state
143
+
144
+ The LED indicator represents the 'global' state. State can be one of the
145
+ following: "default", "success", "warning", "emergency"
146
+ """
147
+ self.compact_status.setState(state)
148
+
149
+ def show_popup(self):
150
+ """Display the contained widgets in a popup dialog"""
151
+ self._popup_window = PopupDialog(self.container)
152
+ self._popup_window.show()
153
+
154
+ def setSizePolicy(self, size_policy1, size_policy2=None):
155
+ # setting size policy on the compact popup widget will set
156
+ # the policy for the container, and for itself
157
+ if size_policy2 is None:
158
+ # assuming first form: setSizePolicy(QSizePolicy)
159
+ self.container.setSizePolicy(size_policy1)
160
+ QWidget.setSizePolicy(self, size_policy1)
161
+ else:
162
+ self.container.setSizePolicy(size_policy1, size_policy2)
163
+ QWidget.setSizePolicy(self, size_policy1, size_policy2)
164
+
165
+ def addWidget(self, widget):
166
+ """Add a widget to the popup container
167
+
168
+ The popup container corresponds to the "full view" (not compact)
169
+ The widget is reparented to the container, and added to the container layout
170
+ """
171
+ widget.setParent(self.container)
172
+ self.container.layout().addWidget(widget)
173
+
174
+ @Property(bool)
175
+ def compact(self):
176
+ return self.compact_view.isVisible()
177
+
178
+ @compact.setter
179
+ def compact(self, set_compact: bool):
180
+ """Sets the compact form
181
+
182
+ If set_compact is True, the compact view is displayed ; otherwise,
183
+ the full view is displayed. This is handled by toggling visibility of
184
+ the container widget or the compact view widget.
185
+ """
186
+ if set_compact:
187
+ self.compact_view.setVisible(True)
188
+ self.container.setVisible(False)
189
+ QWidget.setSizePolicy(self, QSizePolicy.Fixed, QSizePolicy.Fixed)
190
+ else:
191
+ self.compact_view.setVisible(False)
192
+ self.container.setVisible(True)
193
+ QWidget.setSizePolicy(self, self.container.sizePolicy())
194
+ if self.parentWidget():
195
+ self.parentWidget().adjustSize()
196
+ else:
197
+ self.adjustSize()
198
+
199
+ @Property(str)
200
+ def label(self):
201
+ return self.compact_label.text()
202
+
203
+ @label.setter
204
+ def label(self, compact_label_text: str):
205
+ """Set the label text associated to the compact view"""
206
+ self.compact_label.setText(compact_label_text)
207
+
208
+ @Property(str)
209
+ def tooltip(self):
210
+ return self.compact_label.toolTip()
211
+
212
+ @tooltip.setter
213
+ def tooltip(self, tooltip: str):
214
+ """Set the tooltip text associated to the compact view"""
215
+ self.compact_label.setToolTip(tooltip)
216
+ self.compact_status.setToolTip(tooltip)
217
+
218
+ def closeEvent(self, event):
219
+ # Called by Qt, on closing - since the children widgets can be
220
+ # BECWidgets, it is good to explicitely call 'close' on them,
221
+ # to ensure proper resources cleanup
222
+ for child in self.container.findChildren(QWidget, options=Qt.FindDirectChildrenOnly):
223
+ child.close()
@@ -0,0 +1,84 @@
1
+ """
2
+ This module provides a utility class for counting and reporting frames per second (FPS) in a PyQtGraph application.
3
+
4
+ Classes:
5
+ FPSCounter: A class that monitors the paint events of a `ViewBox` to calculate and emit FPS values.
6
+
7
+ Usage:
8
+ The `FPSCounter` class can be used to monitor the rendering performance of a `ViewBox` in a PyQtGraph application.
9
+ It connects to the `ViewBox`'s paint event and calculates the FPS over a specified interval, emitting the FPS value
10
+ at regular intervals.
11
+
12
+ Example:
13
+ from qtpy import QtWidgets, QtCore
14
+ import pyqtgraph as pg
15
+ from fps_counter import FPSCounter
16
+
17
+ app = pg.mkQApp("FPS Counter Example")
18
+ win = pg.GraphicsLayoutWidget()
19
+ win.show()
20
+
21
+ vb = pg.ViewBox()
22
+ plot_item = pg.PlotItem(viewBox=vb)
23
+ win.addItem(plot_item)
24
+
25
+ fps_counter = FPSCounter(vb)
26
+ fps_counter.sigFpsUpdate.connect(lambda fps: print(f"FPS: {fps:.2f}"))
27
+
28
+ sys.exit(app.exec_())
29
+ """
30
+
31
+ from time import perf_counter
32
+
33
+ import pyqtgraph as pg
34
+ from qtpy import QtCore
35
+
36
+
37
+ class FPSCounter(QtCore.QObject):
38
+ """
39
+ A utility class for counting and reporting frames per second (FPS).
40
+
41
+ This class connects to a `ViewBox`'s paint event to count the number of
42
+ frames rendered and calculates the FPS over a specified interval. It emits
43
+ a signal with the FPS value at regular intervals.
44
+
45
+ Attributes:
46
+ sigFpsUpdate (QtCore.Signal): Signal emitted with the FPS value.
47
+ view_box (pg.ViewBox): The `ViewBox` instance to monitor.
48
+ """
49
+
50
+ sigFpsUpdate = QtCore.Signal(float)
51
+
52
+ def __init__(self, view_box):
53
+ super().__init__()
54
+ self.view_box = view_box
55
+ self.view_box.sigPaint.connect(self.increment_count)
56
+ self.count = 0
57
+ self.last_update = perf_counter()
58
+ self.timer = QtCore.QTimer()
59
+ self.timer.timeout.connect(self.calculate_fps)
60
+ self.timer.start(1000)
61
+
62
+ def increment_count(self):
63
+ """
64
+ Increment the frame count when the `ViewBox` is painted.
65
+ """
66
+ self.count += 1
67
+
68
+ def calculate_fps(self):
69
+ """
70
+ Calculate the frames per second (FPS) based on the number of frames
71
+ """
72
+ now = perf_counter()
73
+ elapsed = now - self.last_update
74
+ fps = self.count / elapsed if elapsed > 0 else 0.0
75
+ self.last_update = now
76
+ self.count = 0
77
+ self.sigFpsUpdate.emit(fps)
78
+
79
+ def cleanup(self):
80
+ """
81
+ Clean up the FPS counter by stopping the timer and disconnecting the signal.
82
+ """
83
+ self.timer.stop()
84
+ self.timer.timeout.disconnect(self.calculate_fps)
@@ -2,10 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from bec_lib.endpoints import MessageEndpoints
4
4
  from bec_qthemes import material_icon
5
- from qtpy.QtCore import Property, Qt, Slot
5
+ from qtpy.QtCore import Property, Qt, Signal, Slot
6
6
  from qtpy.QtGui import QColor
7
7
  from qtpy.QtWidgets import QHeaderView, QLabel, QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget
8
8
 
9
+ from bec_widgets.qt_utils.compact_popup import CompactPopupWidget
9
10
  from bec_widgets.qt_utils.toolbar import ModularToolBar, SeparatorAction, WidgetAction
10
11
  from bec_widgets.utils.bec_connector import ConnectionConfig
11
12
  from bec_widgets.utils.bec_widget import BECWidget
@@ -15,7 +16,7 @@ from bec_widgets.widgets.button_resume.button_resume import ResumeButton
15
16
  from bec_widgets.widgets.stop_button.stop_button import StopButton
16
17
 
17
18
 
18
- class BECQueue(BECWidget, QWidget):
19
+ class BECQueue(BECWidget, CompactPopupWidget):
19
20
  """
20
21
  Widget to display the BEC queue.
21
22
  """
@@ -31,6 +32,8 @@ class BECQueue(BECWidget, QWidget):
31
32
  "COMPLETED": "blue",
32
33
  }
33
34
 
35
+ queue_busy = Signal(bool)
36
+
34
37
  def __init__(
35
38
  self,
36
39
  parent: QWidget | None = None,
@@ -40,22 +43,24 @@ class BECQueue(BECWidget, QWidget):
40
43
  refresh_upon_start: bool = True,
41
44
  ):
42
45
  super().__init__(client, config, gui_id)
43
- QWidget.__init__(self, parent=parent)
44
- self.layout = QVBoxLayout(self)
46
+ CompactPopupWidget.__init__(self, parent=parent, layout=QVBoxLayout)
45
47
  self.layout.setSpacing(0)
46
48
  self.layout.setContentsMargins(0, 0, 0, 0)
47
49
 
48
50
  # Set up the toolbar
49
51
  self.set_toolbar()
50
-
51
52
  # Set up the table
52
53
  self.table = QTableWidget(self)
53
- self.layout.addWidget(self.table)
54
+ # self.layout.addWidget(self.table)
54
55
  self.table.setColumnCount(4)
55
56
  self.table.setHorizontalHeaderLabels(["Scan Number", "Type", "Status", "Cancel"])
56
57
  header = self.table.horizontalHeader()
57
58
  header.setSectionResizeMode(QHeaderView.Stretch)
58
59
 
60
+ self.addWidget(self.table)
61
+ self.label = "BEC Queue"
62
+ self.tooltip = "BEC Queue status"
63
+
59
64
  self.bec_dispatcher.connect_slot(self.update_queue, MessageEndpoints.scan_queue_status())
60
65
  self.reset_content()
61
66
  if refresh_upon_start:
@@ -78,7 +83,7 @@ class BECQueue(BECWidget, QWidget):
78
83
  target_widget=self,
79
84
  )
80
85
 
81
- self.layout.addWidget(self.toolbar)
86
+ self.addWidget(self.toolbar)
82
87
 
83
88
  @Property(bool)
84
89
  def hide_toolbar(self):
@@ -109,6 +114,9 @@ class BECQueue(BECWidget, QWidget):
109
114
  Refresh the queue.
110
115
  """
111
116
  msg = self.client.connector.get(MessageEndpoints.scan_queue_status())
117
+ if msg is None:
118
+ # msg is None if no scan has been run yet (fresh start)
119
+ return
112
120
  self.update_queue(msg.content, msg.metadata)
113
121
 
114
122
  @Slot(dict, dict)
@@ -152,6 +160,13 @@ class BECQueue(BECWidget, QWidget):
152
160
  if scan_ids:
153
161
  scan_ids = ", ".join(scan_ids)
154
162
  self.set_row(index, scan_numbers, scan_types, status, scan_ids)
163
+ busy = (
164
+ False
165
+ if all(item.get("status") in ("STOPPED", "COMPLETED", "IDLE") for item in queue_info)
166
+ else True
167
+ )
168
+ self.set_global_state("warning" if busy else "default")
169
+ self.queue_busy.emit(busy)
155
170
 
156
171
  def format_item(self, content: str, status=False) -> QTableWidgetItem:
157
172
  """
@@ -13,8 +13,8 @@ from bec_lib.utils.import_utils import lazy_import_from
13
13
  from qtpy.QtCore import QObject, QTimer, Signal, Slot
14
14
  from qtpy.QtWidgets import QHBoxLayout, QTreeWidget, QTreeWidgetItem, QWidget
15
15
 
16
+ from bec_widgets.qt_utils.compact_popup import CompactPopupWidget
16
17
  from bec_widgets.utils.bec_widget import BECWidget
17
- from bec_widgets.utils.colors import set_theme
18
18
  from bec_widgets.widgets.bec_status_box.status_item import StatusItem
19
19
 
20
20
  if TYPE_CHECKING:
@@ -64,7 +64,7 @@ class BECServiceStatusMixin(QObject):
64
64
  self._service_update_timer.deleteLater()
65
65
 
66
66
 
67
- class BECStatusBox(BECWidget, QWidget):
67
+ class BECStatusBox(BECWidget, CompactPopupWidget):
68
68
  """An autonomous widget to display the status of BEC services.
69
69
 
70
70
  Args:
@@ -83,15 +83,13 @@ class BECStatusBox(BECWidget, QWidget):
83
83
  def __init__(
84
84
  self,
85
85
  parent=None,
86
- box_name: str = "BEC Server",
86
+ box_name: str = "BEC Servers",
87
87
  client: BECClient = None,
88
88
  bec_service_status_mixin: BECServiceStatusMixin = None,
89
89
  gui_id: str = None,
90
90
  ):
91
91
  super().__init__(client=client, gui_id=gui_id)
92
- QWidget.__init__(self, parent=parent)
93
- self.tree = QTreeWidget(self)
94
- self.layout = QHBoxLayout(self)
92
+ CompactPopupWidget.__init__(self, parent=parent, layout=QHBoxLayout)
95
93
 
96
94
  self.box_name = box_name
97
95
  self.status_container = defaultdict(lambda: {"info": None, "item": None, "widget": None})
@@ -100,11 +98,13 @@ class BECStatusBox(BECWidget, QWidget):
100
98
  bec_service_status_mixin = BECServiceStatusMixin(self, client=self.client)
101
99
  self.bec_service_status = bec_service_status_mixin
102
100
 
101
+ self.label = box_name
102
+ self.tooltip = "BEC servers health status"
103
103
  self.init_ui()
104
104
  self.bec_service_status.services_update.connect(self.update_service_status)
105
105
  self.bec_core_state.connect(self.update_top_item_status)
106
106
  self.tree.itemDoubleClicked.connect(self.on_tree_item_double_clicked)
107
- self.layout.addWidget(self.tree)
107
+ self.addWidget(self.tree)
108
108
 
109
109
  def init_ui(self) -> None:
110
110
  """Init the UI for the BECStatusBox widget, should only take place once."""
@@ -121,6 +121,7 @@ class BECStatusBox(BECWidget, QWidget):
121
121
 
122
122
  def init_ui_tree_widget(self) -> None:
123
123
  """Initialise the tree widget for the status box."""
124
+ self.tree = QTreeWidget(self)
124
125
  self.tree.setHeaderHidden(True)
125
126
  # TODO probably here is a problem still with setting the stylesheet
126
127
  self.tree.setStyleSheet(
@@ -163,6 +164,7 @@ class BECStatusBox(BECWidget, QWidget):
163
164
  status (BECStatus): The state of the core services.
164
165
  """
165
166
  self.status_container[self.box_name]["info"].status = status
167
+ self.set_global_state("emergency" if status == "NOTCONNECTED" else "success")
166
168
  self.service_update.emit(self.status_container[self.box_name]["info"])
167
169
 
168
170
  def _update_status_container(
@@ -308,6 +310,8 @@ if __name__ == "__main__": # pragma: no cover
308
310
 
309
311
  from qtpy.QtWidgets import QApplication
310
312
 
313
+ from bec_widgets.utils.colors import set_theme
314
+
311
315
  app = QApplication(sys.argv)
312
316
  set_theme("dark")
313
317
  main_window = BECStatusBox()
@@ -57,6 +57,7 @@ class BECImageShow(BECPlotBase):
57
57
  "set_x_lim",
58
58
  "set_y_lim",
59
59
  "set_grid",
60
+ "enable_fps_monitor",
60
61
  "lock_aspect_ratio",
61
62
  "export",
62
63
  "remove",
@@ -307,7 +307,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
307
307
  if vrange is not None:
308
308
  self.color_bar.setLevels(low=vrange[0], high=vrange[1])
309
309
  self.color_bar.setImageItem(self)
310
- self.parent_image.addItem(self.color_bar) # , row=0, col=1)
310
+ self.parent_image.addItem(self.color_bar, row=1, col=1)
311
311
  self.config.color_bar = "simple"
312
312
  elif color_bar_style == "full":
313
313
  # Setting histogram
@@ -321,7 +321,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
321
321
  )
322
322
 
323
323
  # Adding histogram to the layout
324
- self.parent_image.addItem(self.color_bar) # , row=0, col=1)
324
+ self.parent_image.addItem(self.color_bar, row=1, col=1)
325
325
 
326
326
  # save settings
327
327
  self.config.color_bar = "full"
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections import defaultdict
4
3
  from typing import Literal, Optional
5
4
 
6
5
  import bec_qthemes
@@ -12,6 +11,7 @@ from qtpy.QtWidgets import QApplication, QWidget
12
11
 
13
12
  from bec_widgets.utils import BECConnector, ConnectionConfig
14
13
  from bec_widgets.utils.crosshair import Crosshair
14
+ from bec_widgets.utils.fps_counter import FPSCounter
15
15
  from bec_widgets.utils.plot_indicator_items import BECArrowItem, BECTickItem
16
16
 
17
17
  logger = bec_logger.logger
@@ -51,6 +51,11 @@ class SubplotConfig(ConnectionConfig):
51
51
 
52
52
 
53
53
  class BECViewBox(pg.ViewBox):
54
+ sigPaint = Signal()
55
+
56
+ def paint(self, painter, opt, widget):
57
+ super().paint(painter, opt, widget)
58
+ self.sigPaint.emit()
54
59
 
55
60
  def itemBoundsChanged(self, item):
56
61
  self._itemBoundsCache.pop(item, None)
@@ -79,6 +84,7 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
79
84
  "set_y_lim",
80
85
  "set_grid",
81
86
  "set_outer_axes",
87
+ "enable_fps_monitor",
82
88
  "lock_aspect_ratio",
83
89
  "export",
84
90
  "remove",
@@ -100,12 +106,13 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
100
106
 
101
107
  self.figure = parent_figure
102
108
 
103
- # self.plot_item = self.addPlot(row=0, col=0)
104
109
  self.plot_item = pg.PlotItem(viewBox=BECViewBox(parent=self, enableMenu=True), parent=self)
105
- self.addItem(self.plot_item, row=0, col=0)
110
+ self.addItem(self.plot_item, row=1, col=0)
106
111
 
107
112
  self.add_legend()
108
113
  self.crosshair = None
114
+ self.fps_monitor = None
115
+ self.fps_label = None
109
116
  self.tick_item = BECTickItem(parent=self, plot_item=self.plot_item)
110
117
  self.arrow_item = BECArrowItem(parent=self, plot_item=self.plot_item)
111
118
  self._connect_to_theme_change()
@@ -379,6 +386,10 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
379
386
  """
380
387
  self.plot_item.enableAutoRange(axis, enabled)
381
388
 
389
+ ############################################################
390
+ ###################### Crosshair ###########################
391
+ ############################################################
392
+
382
393
  def hook_crosshair(self) -> None:
383
394
  """Hook the crosshair to all plots."""
384
395
  if self.crosshair is None:
@@ -417,6 +428,54 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
417
428
  self.crosshair.clear_markers()
418
429
  self.crosshair.update_markers()
419
430
 
431
+ ############################################################
432
+ ##################### FPS Counter ##########################
433
+ ############################################################
434
+
435
+ def update_fps_label(self, fps: float) -> None:
436
+ """
437
+ Update the FPS label.
438
+
439
+ Args:
440
+ fps(float): The frames per second.
441
+ """
442
+ if self.fps_label:
443
+ self.fps_label.setText(f"FPS: {fps:.2f}")
444
+
445
+ def hook_fps_monitor(self):
446
+ """Hook the FPS monitor to the plot."""
447
+ if self.fps_monitor is None:
448
+ # text_color = self.get_text_color()#TODO later
449
+ self.fps_monitor = FPSCounter(self.plot_item.vb) # text_color=text_color)
450
+ self.fps_label = pg.LabelItem(justify="right")
451
+ self.addItem(self.fps_label, row=0, col=0)
452
+
453
+ self.fps_monitor.sigFpsUpdate.connect(self.update_fps_label)
454
+
455
+ def unhook_fps_monitor(self):
456
+ """Unhook the FPS monitor from the plot."""
457
+ if self.fps_monitor is not None:
458
+ # Remove Monitor
459
+ self.fps_monitor.cleanup()
460
+ self.fps_monitor.deleteLater()
461
+ self.fps_monitor = None
462
+ # Remove Label
463
+ self.removeItem(self.fps_label)
464
+ self.fps_label.deleteLater()
465
+ self.fps_label = None
466
+
467
+ def enable_fps_monitor(self, enable: bool = True):
468
+ """
469
+ Enable the FPS monitor.
470
+
471
+ Args:
472
+ enable(bool): True to enable, False to disable.
473
+ """
474
+ if enable and self.fps_monitor is None:
475
+ self.hook_fps_monitor()
476
+ elif not enable and self.fps_monitor is not None:
477
+ self.unhook_fps_monitor()
478
+
420
479
  def export(self):
421
480
  """Show the Export Dialog of the plot widget."""
422
481
  scene = self.plot_item.scene()
@@ -72,6 +72,7 @@ class BECWaveform(BECPlotBase):
72
72
  "set_y_lim",
73
73
  "set_grid",
74
74
  "set_colormap",
75
+ "enable_fps_monitor",
75
76
  "lock_aspect_ratio",
76
77
  "export",
77
78
  "remove",
@@ -40,6 +40,7 @@ class BECImageWidget(BECWidget, QWidget):
40
40
  "set_rotation",
41
41
  "set_log",
42
42
  "set_grid",
43
+ "enable_fps_monitor",
43
44
  "lock_aspect_ratio",
44
45
  ]
45
46
 
@@ -104,6 +105,9 @@ class BECImageWidget(BECWidget, QWidget):
104
105
  icon_name="reset_settings", tooltip="Reset Image Settings"
105
106
  ),
106
107
  "separator_3": SeparatorAction(),
108
+ "fps_monitor": MaterialIconAction(
109
+ icon_name="speed", tooltip="Show FPS Monitor", checkable=True
110
+ ),
107
111
  "axis_settings": MaterialIconAction(
108
112
  icon_name="settings", tooltip="Open Configuration Dialog"
109
113
  ),
@@ -150,6 +154,7 @@ class BECImageWidget(BECWidget, QWidget):
150
154
  self.toolbar.widgets["reset"].action.triggered.connect(self.reset_settings)
151
155
  # sepatator
152
156
  self.toolbar.widgets["axis_settings"].action.triggered.connect(self.show_axis_settings)
157
+ self.toolbar.widgets["fps_monitor"].action.toggled.connect(self.enable_fps_monitor)
153
158
 
154
159
  ###################################
155
160
  # Dialog Windows
@@ -450,6 +455,18 @@ class BECImageWidget(BECWidget, QWidget):
450
455
  self.toolbar.widgets["rectangle_mode"].action.setChecked(False)
451
456
  self._image.plot_item.getViewBox().setMouseMode(pg.ViewBox.PanMode)
452
457
 
458
+ @SafeSlot()
459
+ def enable_fps_monitor(self, enabled: bool):
460
+ """
461
+ Enable the FPS monitor of the plot widget.
462
+
463
+ Args:
464
+ enabled(bool): If True, enable the FPS monitor.
465
+ """
466
+ self._image.enable_fps_monitor(enabled)
467
+ if self.toolbar.widgets["fps_monitor"].action.isChecked() != enabled:
468
+ self.toolbar.widgets["fps_monitor"].action.setChecked(enabled)
469
+
453
470
  def export(self):
454
471
  """
455
472
  Show the export dialog for the plot widget.
@@ -52,6 +52,7 @@ class BECWaveformWidget(BECWidget, QWidget):
52
52
  "set_legend_label_size",
53
53
  "set_auto_range",
54
54
  "set_grid",
55
+ "enable_fps_monitor",
55
56
  "lock_aspect_ratio",
56
57
  "export",
57
58
  "export_to_matplotlib",
@@ -118,9 +119,7 @@ class BECWaveformWidget(BECWidget, QWidget):
118
119
  "fit_params": MaterialIconAction(
119
120
  icon_name="monitoring", tooltip="Open Fitting Parameters"
120
121
  ),
121
- "axis_settings": MaterialIconAction(
122
- icon_name="settings", tooltip="Open Configuration Dialog"
123
- ),
122
+ "separator_3": SeparatorAction(),
124
123
  "crosshair": MaterialIconAction(
125
124
  icon_name="point_scan", tooltip="Show Crosshair", checkable=True
126
125
  ),
@@ -129,6 +128,13 @@ class BECWaveformWidget(BECWidget, QWidget):
129
128
  tooltip="Add ROI region for DAP",
130
129
  checkable=True,
131
130
  ),
131
+ "separator_4": SeparatorAction(),
132
+ "fps_monitor": MaterialIconAction(
133
+ icon_name="speed", tooltip="Show FPS Monitor", checkable=True
134
+ ),
135
+ "axis_settings": MaterialIconAction(
136
+ icon_name="settings", tooltip="Open Configuration Dialog"
137
+ ),
132
138
  },
133
139
  target_widget=self,
134
140
  )
@@ -186,6 +192,7 @@ class BECWaveformWidget(BECWidget, QWidget):
186
192
  self.toolbar.widgets["axis_settings"].action.triggered.connect(self.show_axis_settings)
187
193
  self.toolbar.widgets["crosshair"].action.triggered.connect(self.waveform.toggle_crosshair)
188
194
  self.toolbar.widgets["roi_select"].action.toggled.connect(self.waveform.toggle_roi)
195
+ self.toolbar.widgets["fps_monitor"].action.toggled.connect(self.enable_fps_monitor)
189
196
  # self.toolbar.widgets["import"].action.triggered.connect(
190
197
  # lambda: self.load_config(path=None, gui=True)
191
198
  # )
@@ -594,6 +601,8 @@ class BECWaveformWidget(BECWidget, QWidget):
594
601
  checked(bool): If True, enable the linear region selector.
595
602
  """
596
603
  self.waveform.toggle_roi(checked)
604
+ if self.toolbar.widgets["roi_select"].action.isChecked() != checked:
605
+ self.toolbar.widgets["roi_select"].action.setChecked(checked)
597
606
 
598
607
  def select_roi(self, region: tuple):
599
608
  """
@@ -604,6 +613,17 @@ class BECWaveformWidget(BECWidget, QWidget):
604
613
  """
605
614
  self.waveform.select_roi(region)
606
615
 
616
+ def enable_fps_monitor(self, enabled: bool):
617
+ """
618
+ Enable the FPS monitor of the plot widget.
619
+
620
+ Args:
621
+ enabled(bool): If True, enable the FPS monitor.
622
+ """
623
+ self.waveform.enable_fps_monitor(enabled)
624
+ if self.toolbar.widgets["fps_monitor"].action.isChecked() != enabled:
625
+ self.toolbar.widgets["fps_monitor"].action.setChecked(enabled)
626
+
607
627
  @SafeSlot()
608
628
  def _auto_range_from_toolbar(self):
609
629
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.115.0
3
+ Version: 0.117.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
@@ -32,4 +32,4 @@ Provides-Extra: pyqt6
32
32
  Requires-Dist: pyqt6-webengine>=6.7; extra == 'pyqt6'
33
33
  Requires-Dist: pyqt6>=6.7; extra == 'pyqt6'
34
34
  Provides-Extra: pyside6
35
- Requires-Dist: pyside6>=6.7; extra == 'pyside6'
35
+ Requires-Dist: pyside6~=6.7.2; extra == 'pyside6'
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=Dc1iDjsc72UxdUtihx4uSZU0lrTQeR8hZwGx1MQBfKE,8432
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=xNxt0fWzJsTPx07OGBYWsh3-b6Xyeo9NiZhKFZwoh9U,7301
5
+ CHANGELOG.md,sha256=P2FvQRmkrzZeRyx-2Okb6dqpPawiUEEua4eQjG0ffNI,7492
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=F0J9ZxDrWcTo3KqHlmYp2bBktM7rUMVBHFSp2aXhyug,1332
7
+ PKG-INFO,sha256=qzAx_QzNjl7iO4_f8Bz9YYq3XG9n3Lv2VGGKUY5X48M,1334
8
8
  README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
9
- pyproject.toml,sha256=Ugkyy648qWHFcUST0xsGFrK3TFSnz9EyC3ifu5L8IUg,2592
9
+ pyproject.toml,sha256=kyGOtgZp4UfDCXDm7NnGWUox2z3FHW5lri9ZUU-g3Yg,2594
10
10
  .git_hooks/pre-commit,sha256=n3RofIZHJl8zfJJIUomcMyYGFi_rwq4CC19z0snz3FI,286
11
11
  .gitlab/issue_templates/bug_report_template.md,sha256=gAuyEwl7XlnebBrkiJ9AqffSNOywmr8vygUFWKTuQeI,386
12
12
  .gitlab/issue_templates/documentation_update_template.md,sha256=FHLdb3TS_D9aL4CYZCjyXSulbaW5mrN2CmwTaeLPbNw,860
@@ -18,13 +18,13 @@ bec_widgets/applications/bec_app.py,sha256=PptBknsnhWm5GSE-xBps4EAYP3f9i-q9YmUMR
18
18
  bec_widgets/applications/alignment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  bec_widgets/applications/alignment/alignment_1d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  bec_widgets/applications/alignment/alignment_1d/alignment_1d.py,sha256=eGqEXKVz4kSdaIBZAKfuI4pwYoLWwKyLjTY1eB31kgk,11200
21
- bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui,sha256=qgYZnT78MZmugVdTWUJSQNlAOJW278d2wEZ9bGXe94s,27537
21
+ bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui,sha256=CQz0UAXhWI4As_dsgfZUqCtYoqEAgEJl7IZWP7YxA5I,28940
22
22
  bec_widgets/assets/app_icons/BEC-General-App.png,sha256=hc2ktly53DZAbl_rE3cb-vdRa5gtdCmBEjfwm2y5P4g,447581
23
23
  bec_widgets/assets/app_icons/alignment_1d.png,sha256=5VouaWieb4lVv3wUBNHaO5ovUW2Fk25aTKYQzOWy0mg,2071069
24
24
  bec_widgets/assets/app_icons/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3igZdc20pkYM,1747017
25
25
  bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
26
26
  bec_widgets/cli/auto_updates.py,sha256=DwzRChcFIWPH2kCYvp8H7dXvyYSKGYv6LwCmK2sDR2E,5676
27
- bec_widgets/cli/client.py,sha256=PaNh9ih6HXlOEDo6MHa4L3Yl3pjZSWZ0bCk-2G7RWiU,82154
27
+ bec_widgets/cli/client.py,sha256=5fPUNDWvzBii-0oMOL76IhKffN88q1vbrx2P7Uh90TE,83195
28
28
  bec_widgets/cli/client_utils.py,sha256=EdDfo3uuYAWtZiDGGu3_GPnl94FSLkNG2N_4I9FNfMc,11809
29
29
  bec_widgets/cli/generate_cli.py,sha256=C5SOlUeDzFgEptgpa5vdiF6c-YILLcfILZZtk9jr_H0,6637
30
30
  bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
@@ -45,6 +45,7 @@ bec_widgets/examples/plugin_example_pyside/tictactoe.py,sha256=s3rCurXloVcmMdzZi
45
45
  bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=MFMwONn4EZ3V8DboEG4I3BXpURE9JDbKB7XTzzfZl5w,1978
46
46
  bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=V6OVnBTS-60zjQ2FAs88Ldjm1MfoMROfiQZZu6Guav8,2379
47
47
  bec_widgets/qt_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ bec_widgets/qt_utils/compact_popup.py,sha256=DQel3iVjRYn6C5cR33ADSPyZObuMWSKwJYbwudb0o1k,8474
48
49
  bec_widgets/qt_utils/error_popups.py,sha256=y9gKKWaafp468ioHr96nBhf02ZpEgjDc-BAVOTWh-e8,7680
49
50
  bec_widgets/qt_utils/palette_viewer.py,sha256=PGhJ-4XI0f7gNnC1djNgcIHYg6HDO6hPju4pfWj9rvg,6311
50
51
  bec_widgets/qt_utils/redis_message_waiter.py,sha256=fvL_QgC0cTDv_FPJdRyp5AKjf401EJU4z3r38p47ydY,1745
@@ -61,6 +62,7 @@ bec_widgets/utils/colors.py,sha256=aUQkDMTRjTjS9lQfgO5NrUllU2r4ygDTTSUROtTBX90,1
61
62
  bec_widgets/utils/container_utils.py,sha256=0wr3ZfuMiAFKCrQHVjxjw-Vuk8wsHdridqcjy2eY840,1531
62
63
  bec_widgets/utils/crosshair.py,sha256=8lik9k69WI2WMj5FGLbrKtny9duxqXUJAhpX8tHoyp0,11543
63
64
  bec_widgets/utils/entry_validator.py,sha256=3skJIsUwTYicT76AMHm_M78RiWtUgyD2zb-Rxo2HdHQ,1313
65
+ bec_widgets/utils/fps_counter.py,sha256=seuCWwiNP5q2e2OEztloa66pNb3Sygh-0lEHAcYaDfc,2612
64
66
  bec_widgets/utils/generate_designer_plugin.py,sha256=eidqauS8YLgoxkPntPL0oSG_lYqI2D7fSyOZvOtCU_U,5891
65
67
  bec_widgets/utils/layout_manager.py,sha256=H0nKsIMaPxRkof1MEXlSmW6w1dFxA6astaGzf4stI84,4727
66
68
  bec_widgets/utils/linear_region_selector.py,sha256=83qMSGnxCePdI5UL8_M4sMeK_BU9sZBzcw0o3Z_Jgxw,3286
@@ -84,12 +86,12 @@ bec_widgets/widgets/bec_progressbar/bec_progress_bar_plugin.py,sha256=b0b0F37Hrw
84
86
  bec_widgets/widgets/bec_progressbar/bec_progressbar.py,sha256=kkmpe5o9RPOCIKKPp0jjfQORLT8XKW_e-ETEl5Zmltk,7980
85
87
  bec_widgets/widgets/bec_progressbar/register_bec_progress_bar.py,sha256=ZcGLPYEwkrbSOpxQeuZJRRVV3yXvcFh133hnlKIQGKw,488
86
88
  bec_widgets/widgets/bec_queue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- bec_widgets/widgets/bec_queue/bec_queue.py,sha256=8gayhWyIB3_gdpeongGTDQxHs37Waf3H5TDjySCctho,7998
89
+ bec_widgets/widgets/bec_queue/bec_queue.py,sha256=5_uhcstSnbcQ__SmkWRfomEvDfwaJv3vTetXUV6w7Eo,8585
88
90
  bec_widgets/widgets/bec_queue/bec_queue.pyproject,sha256=VhoNmAv1DQUl9dg7dELyf5i4pZ5k65N3GnqOYiSwbQo,27
89
91
  bec_widgets/widgets/bec_queue/bec_queue_plugin.py,sha256=cotVUNKphAXQAp6mWtKoyzJ-vyAnfd86tGf4yNp0CjU,1341
90
92
  bec_widgets/widgets/bec_queue/register_bec_queue.py,sha256=XnwtUSa1asK1b80knKWodcyX9qJy4DnKsQL_FoDfZy4,463
91
93
  bec_widgets/widgets/bec_status_box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
- bec_widgets/widgets/bec_status_box/bec_status_box.py,sha256=4ZZ3rgimxBpTHzQcjHXLdmmygmcsNpO6mZy0u6ZFr3k,13038
94
+ bec_widgets/widgets/bec_status_box/bec_status_box.py,sha256=udxOoTqO_qVO08B8kmVzn6YmAMLsAZBOERdm18hZIUc,13272
93
95
  bec_widgets/widgets/bec_status_box/bec_status_box.pyproject,sha256=JWtx3Csfn2h7ODtk10HtyBNLf6tFIqyU6g04rMWOO1U,32
94
96
  bec_widgets/widgets/bec_status_box/bec_status_box_plugin.py,sha256=UmsXAmeHg7FRkzirOLBPi3LwbRbG75w8LglYt4jn6Fc,1412
95
97
  bec_widgets/widgets/bec_status_box/register_bec_status_box.py,sha256=EiQITnkNw7IU7hE776wAeXro97eZd9XlsB9essgCebE,481
@@ -162,20 +164,20 @@ bec_widgets/widgets/figure/figure.py,sha256=IzQaV_9utjViJyjMydOa3-EJ9k-FVG2PTVm8
162
164
  bec_widgets/widgets/figure/plots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
165
  bec_widgets/widgets/figure/plots/axis_settings.py,sha256=grgrX4t4eAzccW4jj4HYtMSxy8Wgcd9N9J1aU7UHtIs,3723
164
166
  bec_widgets/widgets/figure/plots/axis_settings.ui,sha256=ye-guaRU_jhu7sHZS-9AjBjLrCtA1msOD0dszu4o9x8,11785
165
- bec_widgets/widgets/figure/plots/plot_base.py,sha256=pWaeT35pPYh5L3_N_Fw5fzC7t5TlsRdNZCVBMpC3BBc,15974
167
+ bec_widgets/widgets/figure/plots/plot_base.py,sha256=FIPvigIHenScR3T1NitRdlY_CXJmuAjCY8fv3GQzrRM,18053
166
168
  bec_widgets/widgets/figure/plots/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
167
- bec_widgets/widgets/figure/plots/image/image.py,sha256=rq2zy-vwZLd3___rFRNEBnPFGKSu88T5T4ngrTkcbr0,25014
168
- bec_widgets/widgets/figure/plots/image/image_item.py,sha256=DhlBbI-c8nVbJ8tREQhyNr8Qk4W6PXF0HgBhrIoYQPo,11012
169
+ bec_widgets/widgets/figure/plots/image/image.py,sha256=FmvVVKLtMOAvaRL1tF30KLO0yQVCZqxCqydQIesKgdc,25044
170
+ bec_widgets/widgets/figure/plots/image/image_item.py,sha256=TwHo6FwCiQgJBdr-KKy_7Y_vYSB0pPjBl1AubuZSrE0,11002
169
171
  bec_widgets/widgets/figure/plots/image/image_processor.py,sha256=GeTtWjbldy6VejMwPGQgM-o3d6bmLglCjdoktu19xfA,5262
170
172
  bec_widgets/widgets/figure/plots/motor_map/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
173
  bec_widgets/widgets/figure/plots/motor_map/motor_map.py,sha256=AiDq4bmcEoj9PlkRQtHCk-5cwvOFGPBcfxPNNr8E53Y,18399
172
174
  bec_widgets/widgets/figure/plots/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
- bec_widgets/widgets/figure/plots/waveform/waveform.py,sha256=N3sF0TWJblsznTU8ZSt8QNvoXo7CZEiuDZymJsQUZzA,56708
175
+ bec_widgets/widgets/figure/plots/waveform/waveform.py,sha256=WmFOwtDL5i7RSamB2B-PQS3T8BaKv-pv2Mp2ggh-t3U,56738
174
176
  bec_widgets/widgets/figure/plots/waveform/waveform_curve.py,sha256=RUo4GfXwlaCei8BBvWBQF3IEB8h-KgpvaHur6JUvT6s,8682
175
177
  bec_widgets/widgets/image/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
176
178
  bec_widgets/widgets/image/bec_image_widget.pyproject,sha256=PHisdBo5_5UCApd27GkizzqgfdjsDx2bFZa_p9LiSW8,30
177
179
  bec_widgets/widgets/image/bec_image_widget_plugin.py,sha256=3nOHJTukHsEkaCLAivPHIY4qdshAj86LUKb5fYF3C-Y,1369
178
- bec_widgets/widgets/image/image_widget.py,sha256=8a4nR9JsPstIJSBrFiA1nkazTSmYuXaau9WecqI0t9A,16349
180
+ bec_widgets/widgets/image/image_widget.py,sha256=va8IwMWgKUhtmmig2npFjVWJgTnIdnGMFhqFHuoQvWo,17040
179
181
  bec_widgets/widgets/image/register_bec_image_widget.py,sha256=01YLZQTMSSIXvH1TSL-1AYsRs1a4EbSwKLVAwh9AjeA,478
180
182
  bec_widgets/widgets/jupyter_console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
181
183
  bec_widgets/widgets/jupyter_console/jupyter_console.py,sha256=-e7HQOECeH5eDrJYh4BFIzRL78LDkooU4otabyN0aX4,2343
@@ -251,7 +253,7 @@ bec_widgets/widgets/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
251
253
  bec_widgets/widgets/waveform/bec_waveform_widget.pyproject,sha256=GLD8GN9dXx9wNbtnevrxqqcwk7vKV-Uv8QYSycdaoaI,33
252
254
  bec_widgets/widgets/waveform/bec_waveform_widget_plugin.py,sha256=qSQTeCzIUn8rgDkjIM47Rr3-fqg1uk8rDf_lCoY9gZw,1402
253
255
  bec_widgets/widgets/waveform/register_bec_waveform_widget.py,sha256=qZHVZH_lP2hvzkG1Ra0EyrXlMeLkRCy0aceH-bfJ1cs,490
254
- bec_widgets/widgets/waveform/waveform_widget.py,sha256=jUyeGZCKKqtyk219Xkj_IMca-nvPELW23FqjMn-SMhc,24736
256
+ bec_widgets/widgets/waveform/waveform_widget.py,sha256=HnBRkAPecKV1U_gqLF6seyQnNcqFeR3D4olLqNeNQAw,25664
255
257
  bec_widgets/widgets/waveform/waveform_popups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
258
  bec_widgets/widgets/waveform/waveform_popups/curve_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
257
259
  bec_widgets/widgets/waveform/waveform_popups/curve_dialog/curve_dialog.py,sha256=VxbAtI_ZLfkrkTXqImQcNPwKDqFRWEj-vI8v6mmVMJ8,13025
@@ -263,8 +265,8 @@ bec_widgets/widgets/website/register_website_widget.py,sha256=LIQJpV9uqcBiPR9cEA
263
265
  bec_widgets/widgets/website/website.py,sha256=42pncCc_zI2eqeMArIurVmPUukRo5bTxa2h1Skah-io,3012
264
266
  bec_widgets/widgets/website/website_widget.pyproject,sha256=scOiV3cV1_BjbzpPzy2N8rIJL5P2qIZz8ObTJ-Uvdtg,25
265
267
  bec_widgets/widgets/website/website_widget_plugin.py,sha256=pz38_C2cZ0yvPPS02wdIPcmhFo_yiwUhflsASocAPQQ,1341
266
- bec_widgets-0.115.0.dist-info/METADATA,sha256=F0J9ZxDrWcTo3KqHlmYp2bBktM7rUMVBHFSp2aXhyug,1332
267
- bec_widgets-0.115.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
268
- bec_widgets-0.115.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
269
- bec_widgets-0.115.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
270
- bec_widgets-0.115.0.dist-info/RECORD,,
268
+ bec_widgets-0.117.0.dist-info/METADATA,sha256=qzAx_QzNjl7iO4_f8Bz9YYq3XG9n3Lv2VGGKUY5X48M,1334
269
+ bec_widgets-0.117.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
270
+ bec_widgets-0.117.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
271
+ bec_widgets-0.117.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
272
+ bec_widgets-0.117.0.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "0.115.0"
7
+ version = "0.117.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -38,7 +38,7 @@ dev = [
38
38
  "pytest~=8.0",
39
39
  ]
40
40
  pyqt6 = ["PyQt6>=6.7", "PyQt6-WebEngine>=6.7"]
41
- pyside6 = ["PySide6>=6.7"]
41
+ pyside6 = ["PySide6~=6.7.2"]
42
42
 
43
43
  [project.urls]
44
44
  "Bug Tracker" = "https://gitlab.psi.ch/bec/bec_widgets/issues"