bec-widgets 0.82.2__py3-none-any.whl → 0.83.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 (40) hide show
  1. .gitlab-ci.yml +3 -0
  2. CHANGELOG.md +26 -28
  3. PKG-INFO +1 -1
  4. bec_widgets/utils/bec_connector.py +2 -0
  5. bec_widgets/utils/bec_widget.py +6 -0
  6. bec_widgets/utils/generate_designer_plugin.py +6 -5
  7. bec_widgets/utils/reference_utils.py +92 -0
  8. bec_widgets/widgets/console/console.py +1 -1
  9. bec_widgets/widgets/device_box/__init__.py +0 -0
  10. bec_widgets/widgets/device_box/device_box.py +197 -0
  11. bec_widgets/widgets/device_box/device_box.pyproject +1 -0
  12. bec_widgets/widgets/device_box/device_box.ui +179 -0
  13. bec_widgets/widgets/device_box/device_box_plugin.py +54 -0
  14. bec_widgets/widgets/device_box/register_device_box.py +15 -0
  15. bec_widgets/widgets/figure/figure.py +3 -3
  16. bec_widgets/widgets/position_indicator/position_indicator.py +71 -0
  17. bec_widgets/widgets/position_indicator/position_indicator.pyproject +1 -0
  18. bec_widgets/widgets/position_indicator/position_indicator_plugin.py +54 -0
  19. bec_widgets/widgets/position_indicator/register_position_indicator.py +17 -0
  20. bec_widgets/widgets/spinner/__init__.py +0 -0
  21. bec_widgets/widgets/spinner/register_spinner_widget.py +15 -0
  22. bec_widgets/widgets/spinner/spinner.py +85 -0
  23. bec_widgets/widgets/spinner/spinner_widget.pyproject +1 -0
  24. bec_widgets/widgets/spinner/spinner_widget_plugin.py +54 -0
  25. bec_widgets/widgets/vscode/vscode.py +0 -14
  26. bec_widgets/widgets/website/website.py +1 -1
  27. {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.0.dist-info}/METADATA +1 -1
  28. {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.0.dist-info}/RECORD +40 -18
  29. pyproject.toml +1 -1
  30. tests/references/SpinnerWidget/SpinnerWidget_darwin.png +0 -0
  31. tests/references/SpinnerWidget/SpinnerWidget_linux.png +0 -0
  32. tests/references/SpinnerWidget/SpinnerWidget_started_darwin.png +0 -0
  33. tests/references/SpinnerWidget/SpinnerWidget_started_linux.png +0 -0
  34. tests/unit_tests/client_mocks.py +9 -1
  35. tests/unit_tests/test_device_box.py +98 -0
  36. tests/unit_tests/test_spinner.py +30 -0
  37. tests/unit_tests/test_vscode_widget.py +27 -32
  38. {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.0.dist-info}/WHEEL +0 -0
  39. {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.0.dist-info}/entry_points.txt +0 -0
  40. {bec_widgets-0.82.2.dist-info → bec_widgets-0.83.0.dist-info}/licenses/LICENSE +0 -0
@@ -830,6 +830,6 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
830
830
  widget_class=self.__class__.__name__, gui_id=self.gui_id, theme=theme
831
831
  )
832
832
 
833
- def cleanup(self):
834
- self.clear_all()
835
- super().cleanup()
833
+ # def cleanup(self):
834
+ # self.clear_all()
835
+ # super().cleanup()
@@ -0,0 +1,71 @@
1
+ from qtpy.QtCore import Qt, Slot
2
+ from qtpy.QtGui import QPainter, QPen
3
+ from qtpy.QtWidgets import QWidget
4
+
5
+
6
+ class PositionIndicator(QWidget):
7
+
8
+ def __init__(self, parent=None):
9
+ super().__init__(parent)
10
+ self.position = 0.5
11
+ self.min_value = 0
12
+ self.max_value = 100
13
+ self.scaling_factor = 0.5
14
+ self.setMinimumHeight(10)
15
+
16
+ def set_range(self, min_value, max_value):
17
+ self.min_value = min_value
18
+ self.max_value = max_value
19
+
20
+ @Slot(float)
21
+ def on_position_update(self, position: float):
22
+ self.position = position
23
+ self.update()
24
+
25
+ def paintEvent(self, event):
26
+ painter = QPainter(self)
27
+ painter.setRenderHint(QPainter.Antialiasing)
28
+
29
+ width = self.width()
30
+ height = self.height()
31
+
32
+ # Draw horizontal line
33
+ painter.setPen(Qt.black)
34
+ painter.drawLine(0, height // 2, width, height // 2)
35
+
36
+ # Draw shorter vertical line at the current position
37
+ x_pos = int(self.position * width)
38
+ painter.setPen(QPen(Qt.red, 2))
39
+ short_line_height = int(height * self.scaling_factor)
40
+ painter.drawLine(
41
+ x_pos,
42
+ (height // 2) - (short_line_height // 2),
43
+ x_pos,
44
+ (height // 2) + (short_line_height // 2),
45
+ )
46
+
47
+ # Draw thicker vertical lines at the ends
48
+ end_line_pen = QPen(Qt.blue, 5)
49
+ painter.setPen(end_line_pen)
50
+ painter.drawLine(0, 0, 0, height)
51
+ painter.drawLine(width - 1, 0, width - 1, height)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ from qtpy.QtWidgets import QApplication, QSlider, QVBoxLayout
56
+
57
+ app = QApplication([])
58
+
59
+ position_indicator = PositionIndicator()
60
+ slider = QSlider(Qt.Horizontal)
61
+ slider.valueChanged.connect(lambda value: position_indicator.on_position_update(value / 100))
62
+
63
+ layout = QVBoxLayout()
64
+ layout.addWidget(position_indicator)
65
+ layout.addWidget(slider)
66
+
67
+ widget = QWidget()
68
+ widget.setLayout(layout)
69
+ widget.show()
70
+
71
+ app.exec_()
@@ -0,0 +1 @@
1
+ {'files': ['position_indicator.py']}
@@ -0,0 +1,54 @@
1
+ # Copyright (C) 2022 The Qt Company Ltd.
2
+ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
+
4
+ from qtpy.QtDesigner import QDesignerCustomWidgetInterface
5
+ from qtpy.QtGui import QIcon
6
+
7
+ from bec_widgets.widgets.position_indicator.position_indicator import PositionIndicator
8
+
9
+ DOM_XML = """
10
+ <ui language='c++'>
11
+ <widget class='PositionIndicator' name='position_indicator'>
12
+ </widget>
13
+ </ui>
14
+ """
15
+
16
+
17
+ class PositionIndicatorPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
18
+ def __init__(self):
19
+ super().__init__()
20
+ self._form_editor = None
21
+
22
+ def createWidget(self, parent):
23
+ t = PositionIndicator(parent)
24
+ return t
25
+
26
+ def domXml(self):
27
+ return DOM_XML
28
+
29
+ def group(self):
30
+ return ""
31
+
32
+ def icon(self):
33
+ return QIcon()
34
+
35
+ def includeFile(self):
36
+ return "position_indicator"
37
+
38
+ def initialize(self, form_editor):
39
+ self._form_editor = form_editor
40
+
41
+ def isContainer(self):
42
+ return False
43
+
44
+ def isInitialized(self):
45
+ return self._form_editor is not None
46
+
47
+ def name(self):
48
+ return "PositionIndicator"
49
+
50
+ def toolTip(self):
51
+ return "PositionIndicator"
52
+
53
+ def whatsThis(self):
54
+ 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.position_indicator.position_indicator_plugin import (
10
+ PositionIndicatorPlugin,
11
+ )
12
+
13
+ QPyDesignerCustomWidgetCollection.addCustomWidget(PositionIndicatorPlugin())
14
+
15
+
16
+ if __name__ == "__main__": # pragma: no cover
17
+ main()
File without changes
@@ -0,0 +1,15 @@
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.spinner.spinner_widget_plugin import SpinnerWidgetPlugin
10
+
11
+ QPyDesignerCustomWidgetCollection.addCustomWidget(SpinnerWidgetPlugin())
12
+
13
+
14
+ if __name__ == "__main__": # pragma: no cover
15
+ main()
@@ -0,0 +1,85 @@
1
+ import sys
2
+
3
+ import numpy as np
4
+ import qdarktheme
5
+ from qtpy.QtCore import QRect, Qt, QTimer
6
+ from qtpy.QtGui import QColor, QPainter, QPen
7
+ from qtpy.QtWidgets import QApplication, QMainWindow, QWidget
8
+
9
+
10
+ def ease_in_out_sine(t):
11
+ return 1 - np.sin(np.pi * t)
12
+
13
+
14
+ class SpinnerWidget(QWidget):
15
+ def __init__(self, parent=None):
16
+ super().__init__(parent)
17
+
18
+ self.angle = 0
19
+ self.timer = QTimer(self)
20
+ self.timer.timeout.connect(self.rotate)
21
+ self.time = 0
22
+ self.duration = 50
23
+ self.speed = 50
24
+ self._started = False
25
+
26
+ def start(self):
27
+ if self._started:
28
+ return
29
+ self.timer.start(self.speed)
30
+ self._started = True
31
+
32
+ def stop(self):
33
+ if not self._started:
34
+ return
35
+ self.timer.stop()
36
+ self._started = False
37
+ self.update()
38
+
39
+ def rotate(self):
40
+ self.time = (self.time + 1) % self.duration
41
+ t = self.time / self.duration
42
+ easing_value = ease_in_out_sine(t)
43
+ self.angle -= (20 * easing_value) % 360 + 10
44
+ self.update()
45
+
46
+ def paintEvent(self, event):
47
+ painter = QPainter(self)
48
+ painter.setRenderHint(QPainter.Antialiasing)
49
+ size = min(self.width(), self.height())
50
+ rect = QRect(0, 0, size, size)
51
+
52
+ background_color = QColor(200, 200, 200, 50)
53
+ line_width = 5
54
+
55
+ color_palette = qdarktheme.load_palette()
56
+
57
+ color = QColor(color_palette.accent().color())
58
+
59
+ rect.adjust(line_width, line_width, -line_width, -line_width)
60
+
61
+ # Background arc
62
+ painter.setPen(QPen(background_color, line_width, Qt.SolidLine))
63
+ adjusted_rect = QRect(rect.left(), rect.top(), rect.width(), rect.height())
64
+ painter.drawArc(adjusted_rect, 0, 360 * 16)
65
+
66
+ if self._started:
67
+ # Foreground arc
68
+ pen = QPen(color, line_width, Qt.SolidLine)
69
+ pen.setCapStyle(Qt.RoundCap)
70
+ painter.setPen(pen)
71
+ proportion = 1 / 4
72
+ angle_span = int(proportion * 360 * 16)
73
+ angle_span += angle_span * ease_in_out_sine(self.time / self.duration)
74
+ painter.drawArc(adjusted_rect, self.angle * 16, int(angle_span))
75
+ painter.end()
76
+
77
+
78
+ if __name__ == "__main__": # pragma: no cover
79
+ app = QApplication(sys.argv)
80
+ window = QMainWindow()
81
+ widget = SpinnerWidget()
82
+ widget.start()
83
+ window.setCentralWidget(widget)
84
+ window.show()
85
+ sys.exit(app.exec())
@@ -0,0 +1 @@
1
+ {'files': ['spinner.py']}
@@ -0,0 +1,54 @@
1
+ # Copyright (C) 2022 The Qt Company Ltd.
2
+ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
+
4
+ from qtpy.QtDesigner import QDesignerCustomWidgetInterface
5
+ from qtpy.QtGui import QIcon
6
+
7
+ from bec_widgets.widgets.spinner.spinner import SpinnerWidget
8
+
9
+ DOM_XML = """
10
+ <ui language='c++'>
11
+ <widget class='SpinnerWidget' name='spinner_widget'>
12
+ </widget>
13
+ </ui>
14
+ """
15
+
16
+
17
+ class SpinnerWidgetPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
18
+ def __init__(self):
19
+ super().__init__()
20
+ self._form_editor = None
21
+
22
+ def createWidget(self, parent):
23
+ t = SpinnerWidget(parent)
24
+ return t
25
+
26
+ def domXml(self):
27
+ return DOM_XML
28
+
29
+ def group(self):
30
+ return ""
31
+
32
+ def icon(self):
33
+ return QIcon()
34
+
35
+ def includeFile(self):
36
+ return "spinner_widget"
37
+
38
+ def initialize(self, form_editor):
39
+ self._form_editor = form_editor
40
+
41
+ def isContainer(self):
42
+ return False
43
+
44
+ def isInitialized(self):
45
+ return self._form_editor is not None
46
+
47
+ def name(self):
48
+ return "SpinnerWidget"
49
+
50
+ def toolTip(self):
51
+ return "SpinnerWidget"
52
+
53
+ def whatsThis(self):
54
+ return self.toolTip()
@@ -49,13 +49,6 @@ class VSCodeEditor(WebsiteWidget):
49
49
  break
50
50
  self.set_url(self._url)
51
51
 
52
- def closeEvent(self, event):
53
- """
54
- Hook for the close event to terminate the server.
55
- """
56
- self.cleanup_vscode()
57
- super().closeEvent(event)
58
-
59
52
  def cleanup_vscode(self):
60
53
  """
61
54
  Cleanup the VSCode editor.
@@ -72,13 +65,6 @@ class VSCodeEditor(WebsiteWidget):
72
65
  self.cleanup_vscode()
73
66
  return super().cleanup()
74
67
 
75
- def close(self):
76
- """
77
- Close the widget.
78
- """
79
- self.cleanup_vscode()
80
- return super().close()
81
-
82
68
 
83
69
  if __name__ == "__main__": # pragma: no cover
84
70
  import sys
@@ -69,6 +69,6 @@ if __name__ == "__main__":
69
69
  import sys
70
70
 
71
71
  app = QApplication(sys.argv)
72
- mainWin = WebsiteWidget("https://scilog.psi.ch")
72
+ mainWin = WebsiteWidget(url="https://scilog.psi.ch")
73
73
  mainWin.show()
74
74
  sys.exit(app.exec())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.82.2
3
+ Version: 0.83.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
@@ -1,12 +1,12 @@
1
1
  .gitignore,sha256=cMQ1MLmnoR88aMCCJwUyfoTnufzl4-ckmHtlFUqHcT4,3253
2
- .gitlab-ci.yml,sha256=vuDJPAYOK0995_H6fu0N5dIgIrOJgTvKr0HZkNWlxMw,8142
2
+ .gitlab-ci.yml,sha256=zvb4A6QI5lQTsdfI5nPPL-tUNfcrz__SQjxW03QZ5Ek,8204
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=7VVqe14x1w6butWm_Du8rzXRFtf3RQYV3CsURU1-uT0,6428
5
+ CHANGELOG.md,sha256=yrNwpjAjIHr7MeZHZe2O0EkihwIB__p1ZrFzl16iUqI,6680
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=9lGCW5x2H2CEYe7DJsHs1LlXB0Pn_hr7efNeRFUGzgo,1309
7
+ PKG-INFO,sha256=xSeHtjGGoakyD7GAov5Syu1U4lMGeuIPyORPJeEfz9o,1309
8
8
  README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
9
- pyproject.toml,sha256=Jsa50E-GnKAaTBdJplPyg-B7sHQsIPL2GS-IHwugDFA,2358
9
+ pyproject.toml,sha256=t0CTeL_fv-YSLp6OaG1DgQdwsSFZrYKnIzs9jFl-mNk,2358
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
@@ -38,18 +38,19 @@ bec_widgets/qt_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
38
38
  bec_widgets/qt_utils/settings_dialog.py,sha256=rR_Zk4RGTnI4dz5OEzCc13lVpxlOKuwOf4_7wqXSbRw,3373
39
39
  bec_widgets/qt_utils/toolbar.py,sha256=r6H4A8_3s2AItmdV8JS9UMSM4hV8Fx_162grYL2Pwzw,2111
40
40
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
41
- bec_widgets/utils/bec_connector.py,sha256=JScGWHEt4kh6C-C0O_JV_bOuiQlBjYTAPJktqgeAj70,9534
41
+ bec_widgets/utils/bec_connector.py,sha256=NypWbIrqb2ls3SIpflM6KihidV9fkroiJu2tQk6KwOA,9604
42
42
  bec_widgets/utils/bec_designer.py,sha256=ak3G8FdojUPjVBBwdPXw7tN5P2Uxr-SSoQt394jXeAA,4308
43
43
  bec_widgets/utils/bec_dispatcher.py,sha256=dghOw63Ql6rQ-zH1ZaYU6kT9Dg5kNDcZwEdu2YPBJeU,6150
44
44
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
45
- bec_widgets/utils/bec_widget.py,sha256=Y-t90XMe2PASvY96Xve5a_jevXqnxk1SJ-mfy6ULC9Y,59
45
+ bec_widgets/utils/bec_widget.py,sha256=eC7jhCqyABDf_wGbM6cCdtSF4__rpnVs2Ir1XzGHrCs,238
46
46
  bec_widgets/utils/colors.py,sha256=CP_lwj757CpwdVhWSfdNEXKFCEVVVF48DizD2WJKSwI,9759
47
47
  bec_widgets/utils/container_utils.py,sha256=m3VUyAYmSWkEwApP9tBvKxPYVtc2kHw4toxIpMryJy4,1495
48
48
  bec_widgets/utils/crosshair.py,sha256=SubY4FQCI6vUKsmMYGKHR7uYdGQJ6vhoYLuC1XlKS9I,9626
49
49
  bec_widgets/utils/entry_validator.py,sha256=3skJIsUwTYicT76AMHm_M78RiWtUgyD2zb-Rxo2HdHQ,1313
50
- bec_widgets/utils/generate_designer_plugin.py,sha256=T6DqL29sN4LcXLH8Hoa410UGnoRzjsZ-z0PRGTEzkRE,5809
50
+ bec_widgets/utils/generate_designer_plugin.py,sha256=JlkJo0Iu5dA_7HQ0silT8kLnYHhKZ8fKUFeysm1k0l0,5878
51
51
  bec_widgets/utils/layout_manager.py,sha256=H0nKsIMaPxRkof1MEXlSmW6w1dFxA6astaGzf4stI84,4727
52
52
  bec_widgets/utils/plugin_utils.py,sha256=njvVdvF-AR47Yn80ntpvFldEvLuFx9GV-qEX4p_n4AI,5263
53
+ bec_widgets/utils/reference_utils.py,sha256=8pq06TOvZBZdim0G6hvPJXzVDib7ve4o-Ptvfp563nk,2859
53
54
  bec_widgets/utils/rpc_decorator.py,sha256=pIvtqySQLnuS7l2Ti_UAe4WX7CRivZnsE5ZdKAihxh0,479
54
55
  bec_widgets/utils/thread_checker.py,sha256=rDNuA3X6KQyA7JPb67mccTg0z8YkInynLAENQDQpbuE,1607
55
56
  bec_widgets/utils/ui_loader.py,sha256=9C4DkMIM8PEGXs2LmbZMyLVt38EIO9-T9C2UnK2SAvE,4882
@@ -78,7 +79,13 @@ bec_widgets/widgets/color_button/color_button.pyproject,sha256=LUYF4VkDOB4ttVe7Y
78
79
  bec_widgets/widgets/color_button/color_button_plugin.py,sha256=CvO3L6r8pBd-0DNuqjQ7uKF9u1mU6xFs23sS_dsj4tw,1256
79
80
  bec_widgets/widgets/color_button/register_color_button.py,sha256=ZAx3t7G2VI2S-1qcEof31Xi9O0X8deLeZNgS651L1JI,475
80
81
  bec_widgets/widgets/color_button/assets/color_button.png,sha256=dkjgGHSdxu98nM68O16fGpxBjxkkeLN8iu9M_8i5-NY,5618
81
- bec_widgets/widgets/console/console.py,sha256=EgKYlbW3NrRPoQwIr8x739sLd1IRBWkEy286VDQw0gA,17838
82
+ bec_widgets/widgets/console/console.py,sha256=IW1OerycmS-cm8CKGFig44Qye8mxsmVcKvLHAc3lXco,17845
83
+ bec_widgets/widgets/device_box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
+ bec_widgets/widgets/device_box/device_box.py,sha256=c-g31zzO4XuQgcR2qnZiRRYllYIMWtx1lGZXYRSxGiI,6922
85
+ bec_widgets/widgets/device_box/device_box.pyproject,sha256=jtwvhaySJRdnuV99mEZT3htmWKVLphFeetEW4al7s-o,28
86
+ bec_widgets/widgets/device_box/device_box.ui,sha256=z7j60J4ZKYjH9eyHl6FnZ_Z8lkdq1LQftxveSZQ6g_w,4865
87
+ bec_widgets/widgets/device_box/device_box_plugin.py,sha256=n7l3OIKqtQGsL86ygDekb0wb4bBySJXMUo58eTtKIdQ,1212
88
+ bec_widgets/widgets/device_box/register_device_box.py,sha256=K7Hx4FIQDXasejaw6njwkFkkkwk63Smm6pHoOEdLWPw,467
82
89
  bec_widgets/widgets/device_combobox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
90
  bec_widgets/widgets/device_combobox/device_combobox.py,sha256=L6hPOk3LkTy9U_HRdU7bvqCH1L1Y_BuPYTTfl1eqShg,2824
84
91
  bec_widgets/widgets/device_combobox/device_combobox.pyproject,sha256=wI2eXR5ky_IM9-BCHJnH_9CEqYcZwIuLcgitSEr8OJU,40
@@ -95,7 +102,7 @@ bec_widgets/widgets/dock/__init__.py,sha256=B7foHt02gnhM7mFksa7GJVwT7n0j_JvYDCt6
95
102
  bec_widgets/widgets/dock/dock.py,sha256=joymi8NRoIuzuugUj9ccF9e1m57HwLQhhMmjaWiwTnM,7597
96
103
  bec_widgets/widgets/dock/dock_area.py,sha256=WKIt61v7w2YXahfIL4nddWHPfpTpw52uphX4QCbS3q0,7913
97
104
  bec_widgets/widgets/figure/__init__.py,sha256=3hGx_KOV7QHCYAV06aNuUgKq4QIYCjUTad-DrwkUaBM,44
98
- bec_widgets/widgets/figure/figure.py,sha256=bxaqwrgT4FXeFXA4dACIorWZ2ydJ5ddHux8Xghjw8Jk,32048
105
+ bec_widgets/widgets/figure/figure.py,sha256=lkU5mGtgi2_kF1FGrz_CYH7aifXxtan5xWNAdl3raXw,32054
99
106
  bec_widgets/widgets/figure/plots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
107
  bec_widgets/widgets/figure/plots/axis_settings.py,sha256=RMvbPgu8r1bk8zZz49hkUSHmcqRdw19zrsfLHcgDUIc,2261
101
108
  bec_widgets/widgets/figure/plots/axis_settings.ui,sha256=zMKZK6lH_3KJGO4RA_paXAH7UzZJ4Snlil3MK4pK3L8,11589
@@ -124,12 +131,21 @@ bec_widgets/widgets/motor_map/motor_map_dialog/__init__.py,sha256=47DEQpj8HBSa-_
124
131
  bec_widgets/widgets/motor_map/motor_map_dialog/motor_map_settings.py,sha256=terJ5NHRzAsiUwdqg-hTbkQ5gadnRT0Ol0_Fm66kPF4,2011
125
132
  bec_widgets/widgets/motor_map/motor_map_dialog/motor_map_settings.ui,sha256=nv5vPftt6vcIl60OOZLRvwD29rdHVWOoGmz168BnwKw,2685
126
133
  bec_widgets/widgets/motor_map/motor_map_dialog/motor_map_toolbar.py,sha256=R6pmxhB_1ouc6f6cNQQvbxTHb3Ko3K_gTGs5WIYgnRI,2194
134
+ bec_widgets/widgets/position_indicator/position_indicator.py,sha256=QVlWvs_RvEJe5IMxGYCpi-CXYF7vFeMqEMFCs6u5NGc,2020
135
+ bec_widgets/widgets/position_indicator/position_indicator.pyproject,sha256=s0JEf5YkpMag19ddFSYeRZ8erBau7erO3bqw05YrTyg,36
136
+ bec_widgets/widgets/position_indicator/position_indicator_plugin.py,sha256=h9EQU4t6KdKVAZh5ILalpl1K8b7JMHf-2AFYREej2-w,1241
137
+ bec_widgets/widgets/position_indicator/register_position_indicator.py,sha256=OZNiMgM_80TPSAXK_0hXAkne4vUh8DGvh_OdpOiMpwI,516
127
138
  bec_widgets/widgets/ring_progress_bar/__init__.py,sha256=_uoJKnDM2YAeUBfwc5WLbIHSJj7zm_FAurSKP3WRaCw,47
128
139
  bec_widgets/widgets/ring_progress_bar/ring.py,sha256=19zFj-6ZrIPLXYqvs5EPcrmDWnfnSLlEOmzJffL4d3A,11241
129
140
  bec_widgets/widgets/ring_progress_bar/ring_progress_bar.py,sha256=sU4Dur2XzBVfDYAYazI6pjOZOhzggoQIuc9VD3PWgac,24073
130
141
  bec_widgets/widgets/scan_control/__init__.py,sha256=IOfHl15vxb_uC6KN62-PeUzbBha_vQyqkkXbJ2HU674,38
131
142
  bec_widgets/widgets/scan_control/scan_control.py,sha256=EHibpeK8vpG6rlg8zhvDDz8GDUzDmvhYH0aX7-Xo4m8,7689
132
143
  bec_widgets/widgets/scan_control/scan_group_box.py,sha256=wrrJLfI0apfll0NKpqM8ymlbl5NaqA9cKNgyfVdYR00,7420
144
+ bec_widgets/widgets/spinner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
+ bec_widgets/widgets/spinner/register_spinner_widget.py,sha256=_zCPjLh4M7NTSHP1Atdn6yu33zJ3LJkcBy3KOJ5eSVY,476
146
+ bec_widgets/widgets/spinner/spinner.py,sha256=aWr0NnJVISwlBXGMnModJDwVNF4Xr01mDegGev29xPo,2465
147
+ bec_widgets/widgets/spinner/spinner_widget.pyproject,sha256=zzLajGB3DTgVnrSqMey2jRpBlxTq9cBXZL9tWJCKe-I,25
148
+ bec_widgets/widgets/spinner/spinner_widget_plugin.py,sha256=ZcG1QIwpbriapM5ZrR4gQ-WA2a7ARhsstk8EIY-OGTU,1187
133
149
  bec_widgets/widgets/stop_button/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
150
  bec_widgets/widgets/stop_button/register_stop_button.py,sha256=U7r3fEOH-uPhAQI-nTituHXDDXDWR4JQZ7_vD6b_dfM,471
135
151
  bec_widgets/widgets/stop_button/stop_button.py,sha256=icJQT1k5G_nvdHWZZkGEZVQupt-mWd2viK8zYg5B5dI,793
@@ -144,9 +160,9 @@ bec_widgets/widgets/toggle/toggle.py,sha256=JzCGYoyHBrlBWCoyL94QX4zSLyEwWbCNHMeg
144
160
  bec_widgets/widgets/toggle/toggle_switch.pyproject,sha256=Msa-AS5H5XlqW65r8GlX2AxD8FnFnDyDgGnbKcXqKOw,24
145
161
  bec_widgets/widgets/toggle/toggle_switch_plugin.py,sha256=8e8JjUx6T5CIx7ixWLwVdT9JOJTbQ8aWwd3_9VAc_Mw,1177
146
162
  bec_widgets/widgets/vscode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
- bec_widgets/widgets/vscode/vscode.py,sha256=ZyJJCJapYrGhqgudEt8JQn723DDqLdwjsXxXa5q3EkU,2544
163
+ bec_widgets/widgets/vscode/vscode.py,sha256=yV1D9PK2IYomq9yYfwqUOrZGIeBwMnNuBfXxBc2M8Qc,2231
148
164
  bec_widgets/widgets/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
149
- bec_widgets/widgets/website/website.py,sha256=iMOooGlZPQ5Y_T_7g4rJZXj-tKpbIupA1O6_WN27JuI,1722
165
+ bec_widgets/widgets/website/website.py,sha256=-5JG2dh7Ftw9xv8I4p08TxjaRzaql9XZDiv1iQOIFsU,1726
150
166
  docs/Makefile,sha256=i2WHuFlgfyAPEW4ssEP8NY4cOibDJrVjvzSEU8_Ggwc,634
151
167
  docs/conf.py,sha256=HxLxupNGu0Smhwn57g1kFdjZzFuaWVREgRJKhT1zi2k,2464
152
168
  docs/index.md,sha256=8ZCgaLIbJsYvt-jwi--QxsNwnK4-k3rejIeOOLclG40,1101
@@ -202,8 +218,12 @@ tests/end-2-end/test_bec_dock_rpc_e2e.py,sha256=uDhauuLEWg9Baf3N1KSeq6HPUWtuU6a9
202
218
  tests/end-2-end/test_bec_figure_rpc_e2e.py,sha256=mjg29huqTivLnukG_XyoMtjOy2P_7JACIhMEzahLXb8,6601
203
219
  tests/end-2-end/test_rpc_register_e2e.py,sha256=blhMiW7HVHX1kGm5dg8Sv0PeCuJ0gnBz3evznQFz_B8,1619
204
220
  tests/end-2-end/test_scan_control_e2e.py,sha256=u7oLgFyltkMW2apSZKDukMIXvYrbhHrU32p4mBdn8VE,2276
221
+ tests/references/SpinnerWidget/SpinnerWidget_darwin.png,sha256=-Tf5x0xY0pb-8sXH6Pk8cZL5PAxZ4vMR5RgyGCxisM4,9490
222
+ tests/references/SpinnerWidget/SpinnerWidget_linux.png,sha256=-Tf5x0xY0pb-8sXH6Pk8cZL5PAxZ4vMR5RgyGCxisM4,9490
223
+ tests/references/SpinnerWidget/SpinnerWidget_started_darwin.png,sha256=wPc65MrSSKJib2aBlPI20mBE-zfamOh-SORGRD27dhQ,14773
224
+ tests/references/SpinnerWidget/SpinnerWidget_started_linux.png,sha256=gE1L3nVuypmsW8vcnV9kX4mDgQGGXYjiEIYiRifr5DM,15265
205
225
  tests/unit_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
206
- tests/unit_tests/client_mocks.py,sha256=BTBpA75CqeJhqYCI5M5XSX2-e4wMoHxi2rnc1PnmsuE,4549
226
+ tests/unit_tests/client_mocks.py,sha256=_gtf12kTxmwaXKwSOG5Cc0dqVEY1PHi-hmqdTdLhLTk,4752
207
227
  tests/unit_tests/conftest.py,sha256=KrnktXPWmZhnKNue-xGWOLD1XGEvdz9Vf7V2eO3XQ3A,596
208
228
  tests/unit_tests/test_bec_connector.py,sha256=zGDfNHwLFZTbpyX6-yc7Pwzr2jWO_HGZ8T4NFCNo4IE,2444
209
229
  tests/unit_tests/test_bec_dispatcher.py,sha256=rYPiRizHaswhGZw55IBMneDFxmPiCCLAZQBqjEkpdyY,3992
@@ -216,6 +236,7 @@ tests/unit_tests/test_bec_status_box.py,sha256=gZdjyy9DNuUP9UwleTLj2Dp5HUImiqnkH
216
236
  tests/unit_tests/test_client_utils.py,sha256=CBdWIVJ_UiyFzTJnX3XJm4PGw2uXhFvRCP_Y9ifckbw,2630
217
237
  tests/unit_tests/test_color_validation.py,sha256=xbFbtFDia36XLgaNrX2IwvAX3IDC_Odpj5BGoJSgiIE,2389
218
238
  tests/unit_tests/test_crosshair.py,sha256=3OMAJ2ZaISYXMOtkXf1rPdy94vCr8njeLi6uHblBL9Q,5045
239
+ tests/unit_tests/test_device_box.py,sha256=q9IVFpt1NF3TBF0Jhk-I-LRiuvvHG3FGUalw4jEYwVo,3431
219
240
  tests/unit_tests/test_device_input_base.py,sha256=r1tI7BFAhpv7V7gf_n5gjusyrBFOfuCqIkdVg7YA7vY,2444
220
241
  tests/unit_tests/test_device_input_widgets.py,sha256=Q40xNKGvJT2dvNttRH68WZi0au8PwpUgBi2EB4LXfC8,5841
221
242
  tests/unit_tests/test_generate_cli_client.py,sha256=ng-eV5iF7Dhm-6YpxYo99CMY0KgqoaZBQNkMeKULDBU,3355
@@ -230,10 +251,11 @@ tests/unit_tests/test_rpc_widget_handler.py,sha256=ceQ3BPnBIFY2Hy-sDPw0wxxREVTTp
230
251
  tests/unit_tests/test_scan_control.py,sha256=Wr6KcE8av4sEIOx5VgYbzVCem3Jgb4Kzx_oOuvwlmkE,13459
231
252
  tests/unit_tests/test_scan_control_group_box.py,sha256=HNqjP10B_NonikspNwKz9upJU-t7xf6hwBerNhbC-uo,5563
232
253
  tests/unit_tests/test_setting_dialog.py,sha256=QbHWwLa1VGFwYie-SN3rjS3ICo7A44S4AKN2qeNvIKY,3137
254
+ tests/unit_tests/test_spinner.py,sha256=4_wQs56vQb6Sr1aTXMdW6LKpce2uM0tWMO3LPtAhSwU,774
233
255
  tests/unit_tests/test_stop_button.py,sha256=tpQanzBUyl7qLXjbMUQqm3U3vShbKKARcnLpgsu3P0E,789
234
256
  tests/unit_tests/test_text_box_widget.py,sha256=cT0uEHt_6d-FwST0A_wE9sFW9E3F_nJbKhuBAeU4yHg,1862
235
257
  tests/unit_tests/test_toggle.py,sha256=Amzgres7te0tTQIDR2WMKSx9Kce44TxMpIPR6HZygXQ,832
236
- tests/unit_tests/test_vscode_widget.py,sha256=NVB23ppLqIj-Opd8QnapqUJzDMz6mIZfm3wGPnFXyUE,2788
258
+ tests/unit_tests/test_vscode_widget.py,sha256=G1G7nCQGXFUn0BnMECE7mHmAm0C6pYx1JpEi_XEhodY,2682
237
259
  tests/unit_tests/test_waveform1d.py,sha256=inc-CyRA_3-PE99WnR6vDdfRE1vgp-ZoLVJ-Qbq0qFY,15967
238
260
  tests/unit_tests/test_website_widget.py,sha256=fBADIJJBAHU4Ro7u95kdemFVNv196UOcuO9oLHuHt8A,761
239
261
  tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
@@ -243,8 +265,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
243
265
  tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
244
266
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
267
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
246
- bec_widgets-0.82.2.dist-info/METADATA,sha256=9lGCW5x2H2CEYe7DJsHs1LlXB0Pn_hr7efNeRFUGzgo,1309
247
- bec_widgets-0.82.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
248
- bec_widgets-0.82.2.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
249
- bec_widgets-0.82.2.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
250
- bec_widgets-0.82.2.dist-info/RECORD,,
268
+ bec_widgets-0.83.0.dist-info/METADATA,sha256=xSeHtjGGoakyD7GAov5Syu1U4lMGeuIPyORPJeEfz9o,1309
269
+ bec_widgets-0.83.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
270
+ bec_widgets-0.83.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
271
+ bec_widgets-0.83.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
272
+ bec_widgets-0.83.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.82.2"
7
+ version = "0.83.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -49,11 +49,19 @@ class FakePositioner(FakeDevice):
49
49
  self.read_value = read_value
50
50
  self.name = name
51
51
 
52
+ @property
53
+ def precision(self):
54
+ return 3
55
+
52
56
  def set_read_value(self, value):
53
57
  self.read_value = value
54
58
 
55
59
  def read(self):
56
- return {self.name: {"value": self.read_value}}
60
+ return {
61
+ self.name: {"value": self.read_value},
62
+ f"{self.name}_setpoint": {"value": self.read_value},
63
+ f"{self.name}_motor_is_moving": {"value": 0},
64
+ }
57
65
 
58
66
  def set_limits(self, limits):
59
67
  self.limits = limits
@@ -0,0 +1,98 @@
1
+ from unittest import mock
2
+
3
+ import pytest
4
+ from bec_lib.endpoints import MessageEndpoints
5
+ from bec_lib.messages import ScanQueueMessage
6
+ from qtpy.QtGui import QValidator
7
+
8
+ from bec_widgets.widgets.device_box.device_box import DeviceBox
9
+
10
+ from .client_mocks import mocked_client
11
+
12
+
13
+ @pytest.fixture
14
+ def device_box(qtbot, mocked_client):
15
+ with mock.patch("bec_widgets.widgets.device_box.device_box.uuid.uuid4") as mock_uuid:
16
+ mock_uuid.return_value = "fake_uuid"
17
+ db = DeviceBox(device="samx", client=mocked_client)
18
+ qtbot.addWidget(db)
19
+ yield db
20
+
21
+
22
+ def test_device_box(device_box):
23
+ assert device_box.device == "samx"
24
+ data = device_box.dev["samx"].read()
25
+
26
+ setpoint_text = device_box.ui.setpoint.text()
27
+ # check that the setpoint is taken correctly after init
28
+ assert float(setpoint_text) == data["samx_setpoint"]["value"]
29
+
30
+ # check that the precision is taken correctly after init
31
+ precision = device_box.dev["samx"].precision
32
+ assert setpoint_text == f"{data['samx_setpoint']['value']:.{precision}f}"
33
+
34
+ # check that the step size is set according to the device precision
35
+ assert device_box.ui.step_size.value() == 10**-precision * 10
36
+
37
+
38
+ def test_device_box_update_limits(device_box):
39
+ device_box._limits = None
40
+ device_box.update_limits([0, 10])
41
+ assert device_box._limits == [0, 10]
42
+ assert device_box.setpoint_validator.bottom() == 0
43
+ assert device_box.setpoint_validator.top() == 10
44
+ assert device_box.setpoint_validator.validate("100", 0) == (
45
+ QValidator.State.Intermediate,
46
+ "100",
47
+ 0,
48
+ )
49
+
50
+ device_box.update_limits(None)
51
+ assert device_box._limits is None
52
+ assert device_box.setpoint_validator.validate("100", 0) == (
53
+ QValidator.State.Acceptable,
54
+ "100",
55
+ 0,
56
+ )
57
+
58
+
59
+ def test_device_box_on_stop(device_box):
60
+ with mock.patch.object(device_box.client.connector, "send") as mock_send:
61
+ device_box.on_stop()
62
+ params = {"device": "samx", "rpc_id": "fake_uuid", "func": "stop", "args": [], "kwargs": {}}
63
+ msg = ScanQueueMessage(
64
+ scan_type="device_rpc",
65
+ parameter=params,
66
+ queue="emergency",
67
+ metadata={"RID": "fake_uuid", "response": False},
68
+ )
69
+ mock_send.assert_called_once_with(MessageEndpoints.scan_queue_request(), msg)
70
+
71
+
72
+ def test_device_box_setpoint_change(device_box):
73
+ with mock.patch.object(device_box.dev["samx"], "move") as mock_move:
74
+ device_box.ui.setpoint.setText("100")
75
+ device_box.on_setpoint_change()
76
+ mock_move.assert_called_once_with(100, relative=False)
77
+
78
+
79
+ def test_device_box_on_tweak_right(device_box):
80
+ with mock.patch.object(device_box.dev["samx"], "move") as mock_move:
81
+ device_box.ui.step_size.setValue(0.1)
82
+ device_box.on_tweak_right()
83
+ mock_move.assert_called_once_with(0.1, relative=True)
84
+
85
+
86
+ def test_device_box_on_tweak_left(device_box):
87
+ with mock.patch.object(device_box.dev["samx"], "move") as mock_move:
88
+ device_box.ui.step_size.setValue(0.1)
89
+ device_box.on_tweak_left()
90
+ mock_move.assert_called_once_with(-0.1, relative=True)
91
+
92
+
93
+ def test_device_box_setpoint_out_of_range(device_box):
94
+ device_box.update_limits([0, 10])
95
+ device_box.ui.setpoint.setText("100")
96
+ device_box.on_setpoint_change()
97
+ assert device_box.ui.setpoint.text() == "100"
98
+ assert device_box.ui.setpoint.hasAcceptableInput() == False