bec-widgets 0.68.0__py3-none-any.whl → 0.70.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 (33) hide show
  1. CHANGELOG.md +32 -45
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +4 -0
  4. bec_widgets/cli/generate_cli.py +3 -0
  5. bec_widgets/examples/plugin_example_pyside/__init__.py +0 -0
  6. bec_widgets/examples/plugin_example_pyside/main.py +17 -0
  7. bec_widgets/examples/plugin_example_pyside/registertictactoe.py +12 -0
  8. bec_widgets/examples/plugin_example_pyside/taskmenuextension.pyproject +4 -0
  9. bec_widgets/examples/plugin_example_pyside/tictactoe.py +135 -0
  10. bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py +68 -0
  11. bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +67 -0
  12. bec_widgets/utils/bec_designer.py +87 -0
  13. bec_widgets/widgets/device_inputs/device_combobox/device_combobox.py +0 -11
  14. bec_widgets/widgets/device_inputs/device_combobox/device_combobox.pyproject +4 -0
  15. bec_widgets/widgets/device_inputs/device_combobox/device_combobox_plugin.py +54 -0
  16. bec_widgets/widgets/device_inputs/device_combobox/launch_device_combobox.py +11 -0
  17. bec_widgets/widgets/device_inputs/device_combobox/register_device_combobox.py +17 -0
  18. bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.py +0 -11
  19. bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.pyproject +4 -0
  20. bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit_plugin.py +54 -0
  21. bec_widgets/widgets/device_inputs/device_line_edit/launch_device_line_edit.py +11 -0
  22. bec_widgets/widgets/device_inputs/device_line_edit/register_device_line_edit.py +17 -0
  23. bec_widgets/widgets/vscode/__init__.py +0 -0
  24. bec_widgets/widgets/vscode/vscode.py +86 -0
  25. bec_widgets/widgets/website/website.py +10 -1
  26. {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/METADATA +1 -1
  27. {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/RECORD +33 -14
  28. {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/entry_points.txt +1 -0
  29. docs/user/widgets/bec_status_box.md +1 -1
  30. pyproject.toml +2 -1
  31. tests/unit_tests/test_vscode_widget.py +61 -0
  32. {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/WHEEL +0 -0
  33. {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.70.0 (2024-06-21)
4
+
5
+ ### Documentation
6
+
7
+ * docs: fix typo in link ([`fdf11d8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fdf11d8147750e379af9b17792761a267b49ae53))
8
+
9
+ ### Feature
10
+
11
+ * feat(bec-designer): automatic plugin discovery ([`4639eee`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4639eee0b975ebd7a946e0e290449f5b88c372eb))
12
+
13
+ * feat(device_line_edit): plugin added to bec-designer ([`b4b27ae`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b4b27aea3d8c08fa3d5d5514c69dbde32721d1dc))
14
+
15
+ * feat(device_combobox): plugin added to bec-designer ([`e483b28`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e483b282db20a81182b87938ea172654092419b5))
16
+
17
+ * feat: added entry point for bec-designer ([`36391db`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/36391db60735d57b371211791ddf8d3d00cebcf1))
18
+
19
+ * feat(utils/bec-designer): added startup script to launched QtDesigner compatible with conda environments ([`5362334`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5362334ff3b07fc83653323a084a4b6946bade96))
20
+
21
+ ### Fix
22
+
23
+ * fix(bec-desiger+plugins): imports fixed, PYSIDE6 check to not enable run plugins with pyqt6 ([`50b3422`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/50b3422528d46d74317e8c903b6286e868ab7fe0))
24
+
25
+ ## v0.69.0 (2024-06-21)
26
+
27
+ ### Feature
28
+
29
+ * feat(widgets): added vscode widget ([`48ae950`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/48ae950d57b454307ce409e2511f7b7adf3cfc6b))
30
+
31
+ ### Fix
32
+
33
+ * fix(generate_cli): fixed rpc generate for classes without user access; closes #226 ([`925c893`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/925c893f3ff4337fc8b4d237c8ffc19a597b0996))
34
+
3
35
  ## v0.68.0 (2024-06-21)
4
36
 
5
37
  ### Feature
@@ -133,48 +165,3 @@ in their parent process ([`ce37416`](https://gitlab.psi.ch/bec/bec_widgets/-/com
133
165
  * fix: do not import "server" in client, prevents from having trouble with QApplication creation order
134
166
 
135
167
  Like with QtWebEngine ([`6f96498`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6f96498de66358b89f3a2035627eed2e02dde5a1))
136
-
137
- ### Unknown
138
-
139
- * Reapply "feat: implement non-polling, interruptible waiting of gui instruction response with timeout"
140
-
141
- This reverts commit fe04dd80e59a0e74f7fdea603e0642707ecc7c2a. ([`836b6e6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/836b6e64f694916d6b6f909dedf11a4a6d2c86a4))
142
-
143
- ## v0.63.1 (2024-06-13)
144
-
145
- ### Fix
146
-
147
- * fix: just terminate the remote process in close() instead of communicating
148
-
149
- The proper finalization sequence will be executed by the remote process
150
- on SIGTERM ([`9263f8e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9263f8ef5c17ae7a007a1a564baf787b39061756))
151
-
152
- ## v0.63.0 (2024-06-13)
153
-
154
- ### Documentation
155
-
156
- * docs: add documentation ([`bc709c4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/bc709c4184c985d4e721f9ea7d1b3dad5e9153a7))
157
-
158
- ### Feature
159
-
160
- * feat: add textbox widget ([`d9d4e3c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d9d4e3c9bf73ab2a5629c2867b50fc91e69489ec))
161
-
162
- ### Refactor
163
-
164
- * refactor: add pydantic config, add change_theme ([`6b8432f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6b8432f5b20a71175a3537b5f6832b76e3b67d73))
165
-
166
- ### Test
167
-
168
- * test: add test for text box ([`b49462a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b49462abeb186e56bac79d2ef0b0add1ef28a1a5))
169
-
170
- ### Unknown
171
-
172
- * Revert "feat: implement non-polling, interruptible waiting of gui instruction response with timeout"
173
-
174
- This reverts commit abc6caa2d0b6141dfbe1f3d025f78ae14deddcb3 ([`fe04dd8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fe04dd80e59a0e74f7fdea603e0642707ecc7c2a))
175
-
176
- ## v0.62.0 (2024-06-12)
177
-
178
- ### Unknown
179
-
180
- * doc: add documentation about creating custom GUI applications embedding BEC Widgets ([`17a0068`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/17a00687579f5efab1990cd83862ec0e78198633))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.68.0
3
+ Version: 0.70.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,7 @@ class Widgets(str, enum.Enum):
19
19
  BECFigure = "BECFigure"
20
20
  SpiralProgressBar = "SpiralProgressBar"
21
21
  TextBox = "TextBox"
22
+ VSCodeEditor = "VSCodeEditor"
22
23
  WebsiteWidget = "WebsiteWidget"
23
24
 
24
25
 
@@ -2049,6 +2050,9 @@ class TextBox(RPCBase):
2049
2050
  """
2050
2051
 
2051
2052
 
2053
+ class VSCodeEditor(RPCBase): ...
2054
+
2055
+
2052
2056
  class WebsiteWidget(RPCBase):
2053
2057
  @rpc_call
2054
2058
  def set_url(self, url: str) -> None:
@@ -83,6 +83,9 @@ class {class_name}(RPCBase, BECGuiClientMixin):"""
83
83
  else:
84
84
  self.content += f"""
85
85
  class {class_name}(RPCBase):"""
86
+ if not cls.USER_ACCESS:
87
+ self.content += """...
88
+ """
86
89
  for method in cls.USER_ACCESS:
87
90
  obj = getattr(cls, method)
88
91
  if isinstance(obj, property):
File without changes
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2022 The Qt Company Ltd.
2
+ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
+
4
+ """PySide6 port of the Qt Designer taskmenuextension example from Qt v6.x"""
5
+
6
+ import sys
7
+
8
+ from bec_ipython_client.main import BECIPythonClient
9
+ from qtpy.QtWidgets import QApplication
10
+ from tictactoe import TicTacToe
11
+
12
+ if __name__ == "__main__": # pragma: no cover
13
+ app = QApplication(sys.argv)
14
+ window = TicTacToe()
15
+ window.state = "-X-XO----"
16
+ window.show()
17
+ sys.exit(app.exec())
@@ -0,0 +1,12 @@
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 QPyDesignerCustomWidgetCollection
5
+ from tictactoe import TicTacToe
6
+ from tictactoeplugin import TicTacToePlugin
7
+
8
+ # Set PYSIDE_DESIGNER_PLUGINS to point to this directory and load the plugin
9
+
10
+
11
+ if __name__ == "__main__": # pragma: no cover
12
+ QPyDesignerCustomWidgetCollection.addCustomWidget(TicTacToePlugin())
@@ -0,0 +1,4 @@
1
+ {
2
+ "files": ["tictactoe.py", "main.py", "registertictactoe.py", "tictactoeplugin.py",
3
+ "tictactoetaskmenu.py"]
4
+ }
@@ -0,0 +1,135 @@
1
+ # Copyright (C) 2022 The Qt Company Ltd.
2
+ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
+
4
+ from qtpy.QtCore import Property, QPoint, QRect, QSize, Qt, Slot
5
+ from qtpy.QtGui import QPainter, QPen
6
+ from qtpy.QtWidgets import QWidget
7
+
8
+ EMPTY = "-"
9
+ CROSS = "X"
10
+ NOUGHT = "O"
11
+ DEFAULT_STATE = "---------"
12
+
13
+
14
+ class TicTacToe(QWidget): # pragma: no cover
15
+ def __init__(self, parent=None):
16
+ super().__init__(parent)
17
+ self._state = DEFAULT_STATE
18
+ self._turn_number = 0
19
+
20
+ def minimumSizeHint(self):
21
+ return QSize(200, 200)
22
+
23
+ def sizeHint(self):
24
+ return QSize(200, 200)
25
+
26
+ def setState(self, new_state):
27
+ self._turn_number = 0
28
+ self._state = DEFAULT_STATE
29
+ for position in range(min(9, len(new_state))):
30
+ mark = new_state[position]
31
+ if mark == CROSS or mark == NOUGHT:
32
+ self._turn_number += 1
33
+ self._change_state_at(position, mark)
34
+ position += 1
35
+ self.update()
36
+
37
+ def state(self):
38
+ return self._state
39
+
40
+ @Slot()
41
+ def clear_board(self):
42
+ self._state = DEFAULT_STATE
43
+ self._turn_number = 0
44
+ self.update()
45
+
46
+ def _change_state_at(self, pos, new_state):
47
+ self._state = self._state[:pos] + new_state + self._state[pos + 1 :]
48
+
49
+ def mousePressEvent(self, event):
50
+ if self._turn_number == 9:
51
+ self.clear_board()
52
+ return
53
+ for position in range(9):
54
+ cell = self._cell_rect(position)
55
+ if cell.contains(event.position().toPoint()):
56
+ if self._state[position] == EMPTY:
57
+ new_state = CROSS if self._turn_number % 2 == 0 else NOUGHT
58
+ self._change_state_at(position, new_state)
59
+ self._turn_number += 1
60
+ self.update()
61
+
62
+ def paintEvent(self, event):
63
+ with QPainter(self) as painter:
64
+ painter.setRenderHint(QPainter.Antialiasing)
65
+
66
+ painter.setPen(QPen(Qt.darkGreen, 1))
67
+ painter.drawLine(self._cell_width(), 0, self._cell_width(), self.height())
68
+ painter.drawLine(2 * self._cell_width(), 0, 2 * self._cell_width(), self.height())
69
+ painter.drawLine(0, self._cell_height(), self.width(), self._cell_height())
70
+ painter.drawLine(0, 2 * self._cell_height(), self.width(), 2 * self._cell_height())
71
+
72
+ painter.setPen(QPen(Qt.darkBlue, 2))
73
+
74
+ for position in range(9):
75
+ cell = self._cell_rect(position)
76
+ if self._state[position] == CROSS:
77
+ painter.drawLine(cell.topLeft(), cell.bottomRight())
78
+ painter.drawLine(cell.topRight(), cell.bottomLeft())
79
+ elif self._state[position] == NOUGHT:
80
+ painter.drawEllipse(cell)
81
+
82
+ painter.setPen(QPen(Qt.yellow, 3))
83
+
84
+ for position in range(0, 8, 3):
85
+ if (
86
+ self._state[position] != EMPTY
87
+ and self._state[position + 1] == self._state[position]
88
+ and self._state[position + 2] == self._state[position]
89
+ ):
90
+ y = self._cell_rect(position).center().y()
91
+ painter.drawLine(0, y, self.width(), y)
92
+ self._turn_number = 9
93
+
94
+ for position in range(3):
95
+ if (
96
+ self._state[position] != EMPTY
97
+ and self._state[position + 3] == self._state[position]
98
+ and self._state[position + 6] == self._state[position]
99
+ ):
100
+ x = self._cell_rect(position).center().x()
101
+ painter.drawLine(x, 0, x, self.height())
102
+ self._turn_number = 9
103
+
104
+ if (
105
+ self._state[0] != EMPTY
106
+ and self._state[4] == self._state[0]
107
+ and self._state[8] == self._state[0]
108
+ ):
109
+ painter.drawLine(0, 0, self.width(), self.height())
110
+ self._turn_number = 9
111
+
112
+ if (
113
+ self._state[2] != EMPTY
114
+ and self._state[4] == self._state[2]
115
+ and self._state[6] == self._state[2]
116
+ ):
117
+ painter.drawLine(0, self.height(), self.width(), 0)
118
+ self._turn_number = 9
119
+
120
+ def _cell_rect(self, position):
121
+ h_margin = self.width() / 30
122
+ v_margin = self.height() / 30
123
+ row = int(position / 3)
124
+ column = position - 3 * row
125
+ pos = QPoint(column * self._cell_width() + h_margin, row * self._cell_height() + v_margin)
126
+ size = QSize(self._cell_width() - 2 * h_margin, self._cell_height() - 2 * v_margin)
127
+ return QRect(pos, size)
128
+
129
+ def _cell_width(self):
130
+ return self.width() / 3
131
+
132
+ def _cell_height(self):
133
+ return self.height() / 3
134
+
135
+ state = Property(str, state, setState)
@@ -0,0 +1,68 @@
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
+ from tictactoe import TicTacToe
7
+ from tictactoetaskmenu import TicTacToeTaskMenuFactory
8
+
9
+ DOM_XML = """
10
+ <ui language='c++'>
11
+ <widget class='TicTacToe' name='ticTacToe'>
12
+ <property name='geometry'>
13
+ <rect>
14
+ <x>0</x>
15
+ <y>0</y>
16
+ <width>200</width>
17
+ <height>200</height>
18
+ </rect>
19
+ </property>
20
+ <property name='state'>
21
+ <string>-X-XO----</string>
22
+ </property>
23
+ </widget>
24
+ </ui>
25
+ """
26
+
27
+
28
+ class TicTacToePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
29
+ def __init__(self):
30
+ super().__init__()
31
+ self._form_editor = None
32
+
33
+ def createWidget(self, parent):
34
+ t = TicTacToe(parent)
35
+ return t
36
+
37
+ def domXml(self):
38
+ return DOM_XML
39
+
40
+ def group(self):
41
+ return ""
42
+
43
+ def icon(self):
44
+ return QIcon()
45
+
46
+ def includeFile(self):
47
+ return "tictactoe"
48
+
49
+ def initialize(self, form_editor):
50
+ self._form_editor = form_editor
51
+ manager = form_editor.extensionManager()
52
+ iid = TicTacToeTaskMenuFactory.task_menu_iid()
53
+ manager.registerExtensions(TicTacToeTaskMenuFactory(manager), iid)
54
+
55
+ def isContainer(self):
56
+ return False
57
+
58
+ def isInitialized(self):
59
+ return self._form_editor is not None
60
+
61
+ def name(self):
62
+ return "TicTacToe"
63
+
64
+ def toolTip(self):
65
+ return "Tic Tac Toe Example, demonstrating class QDesignerTaskMenuExtension (Python)"
66
+
67
+ def whatsThis(self):
68
+ return self.toolTip()
@@ -0,0 +1,67 @@
1
+ # Copyright (C) 2022 The Qt Company Ltd.
2
+ # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
3
+
4
+ from qtpy.QtCore import Slot
5
+ from qtpy.QtDesigner import QExtensionFactory, QPyDesignerTaskMenuExtension
6
+ from qtpy.QtGui import QAction
7
+ from qtpy.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout
8
+ from tictactoe import TicTacToe
9
+
10
+
11
+ class TicTacToeDialog(QDialog): # pragma: no cover
12
+ def __init__(self, parent):
13
+ super().__init__(parent)
14
+ layout = QVBoxLayout(self)
15
+ self._ticTacToe = TicTacToe(self)
16
+ layout.addWidget(self._ticTacToe)
17
+ button_box = QDialogButtonBox(
18
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Reset
19
+ )
20
+ button_box.accepted.connect(self.accept)
21
+ button_box.rejected.connect(self.reject)
22
+ reset_button = button_box.button(QDialogButtonBox.Reset)
23
+ reset_button.clicked.connect(self._ticTacToe.clear_board)
24
+ layout.addWidget(button_box)
25
+
26
+ def set_state(self, new_state):
27
+ self._ticTacToe.setState(new_state)
28
+
29
+ def state(self):
30
+ return self._ticTacToe.state
31
+
32
+
33
+ class TicTacToeTaskMenu(QPyDesignerTaskMenuExtension):
34
+ def __init__(self, ticTacToe, parent):
35
+ super().__init__(parent)
36
+ self._ticTacToe = ticTacToe
37
+ self._edit_state_action = QAction("Edit State...", None)
38
+ self._edit_state_action.triggered.connect(self._edit_state)
39
+
40
+ def taskActions(self):
41
+ return [self._edit_state_action]
42
+
43
+ def preferredEditAction(self):
44
+ return self._edit_state_action
45
+
46
+ @Slot()
47
+ def _edit_state(self):
48
+ dialog = TicTacToeDialog(self._ticTacToe)
49
+ dialog.set_state(self._ticTacToe.state)
50
+ if dialog.exec() == QDialog.Accepted:
51
+ self._ticTacToe.state = dialog.state()
52
+
53
+
54
+ class TicTacToeTaskMenuFactory(QExtensionFactory):
55
+ def __init__(self, extension_manager):
56
+ super().__init__(extension_manager)
57
+
58
+ @staticmethod
59
+ def task_menu_iid():
60
+ return "org.qt-project.Qt.Designer.TaskMenu"
61
+
62
+ def createExtension(self, object, iid, parent):
63
+ if iid != TicTacToeTaskMenuFactory.task_menu_iid():
64
+ return None
65
+ if object.__class__.__name__ != "TicTacToe":
66
+ return None
67
+ return TicTacToeTaskMenu(object, parent)
@@ -0,0 +1,87 @@
1
+ import os
2
+ import sys
3
+ import sysconfig
4
+ from pathlib import Path
5
+
6
+ from qtpy import PYSIDE6
7
+
8
+ if PYSIDE6:
9
+ from PySide6.scripts.pyside_tool import (
10
+ _extend_path_var,
11
+ init_virtual_env,
12
+ is_pyenv_python,
13
+ is_virtual_env,
14
+ qt_tool_wrapper,
15
+ ui_tool_binary,
16
+ )
17
+
18
+ import bec_widgets
19
+
20
+
21
+ def patch_designer(): # pragma: no cover
22
+ if not PYSIDE6:
23
+ print("PYSIDE6 is not available in the environment. Cannot patch designer.")
24
+ return
25
+
26
+ init_virtual_env()
27
+
28
+ major_version = sys.version_info[0]
29
+ minor_version = sys.version_info[1]
30
+ os.environ["PY_MAJOR_VERSION"] = str(major_version)
31
+ os.environ["PY_MINOR_VERSION"] = str(minor_version)
32
+
33
+ if sys.platform == "linux":
34
+ version = f"{major_version}.{minor_version}"
35
+ library_name = f"libpython{version}{sys.abiflags}.so"
36
+ if is_pyenv_python():
37
+ library_name = str(Path(sysconfig.get_config_var("LIBDIR")) / library_name)
38
+ os.environ["LD_PRELOAD"] = library_name
39
+ elif sys.platform == "darwin":
40
+ library_name = f"libpython{major_version}.{minor_version}.dylib"
41
+ lib_path = str(Path(sysconfig.get_config_var("LIBDIR")) / library_name)
42
+ os.environ["DYLD_INSERT_LIBRARIES"] = lib_path
43
+ elif sys.platform == "win32":
44
+ if is_virtual_env():
45
+ _extend_path_var("PATH", os.fspath(Path(sys._base_executable).parent), True)
46
+
47
+ qt_tool_wrapper(ui_tool_binary("designer"), sys.argv[1:])
48
+
49
+
50
+ def find_plugin_paths(base_path: Path):
51
+ """
52
+ Recursively find all directories containing a .pyproject file.
53
+ """
54
+ plugin_paths = []
55
+ for path in base_path.rglob("*.pyproject"):
56
+ plugin_paths.append(str(path.parent))
57
+ return plugin_paths
58
+
59
+
60
+ def set_plugin_environment_variable(plugin_paths):
61
+ """
62
+ Set the PYSIDE_DESIGNER_PLUGINS environment variable with the given plugin paths.
63
+ """
64
+ current_paths = os.environ.get("PYSIDE_DESIGNER_PLUGINS", "")
65
+ if current_paths:
66
+ current_paths = current_paths.split(os.pathsep)
67
+ else:
68
+ current_paths = []
69
+
70
+ current_paths.extend(plugin_paths)
71
+ os.environ["PYSIDE_DESIGNER_PLUGINS"] = os.pathsep.join(current_paths)
72
+
73
+
74
+ # Patch the designer function
75
+ def main(): # pragma: no cover
76
+ if not PYSIDE6:
77
+ print("PYSIDE6 is not available in the environment. Exiting...")
78
+ return
79
+ base_dir = Path(os.path.dirname(bec_widgets.__file__)).resolve()
80
+ plugin_paths = find_plugin_paths(base_dir)
81
+ set_plugin_environment_variable(plugin_paths)
82
+
83
+ patch_designer()
84
+
85
+
86
+ if __name__ == "__main__": # pragma: no cover
87
+ main()
@@ -82,14 +82,3 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
82
82
  if device_obj is None:
83
83
  raise ValueError(f"Device {device_name} is not found.")
84
84
  return device_obj
85
-
86
-
87
- if __name__ == "__main__": # pragma: no cover
88
- import sys
89
-
90
- from qtpy.QtWidgets import QApplication
91
-
92
- app = QApplication(sys.argv)
93
- w = DeviceComboBox(default_device="samx")
94
- w.show()
95
- sys.exit(app.exec_())
@@ -0,0 +1,4 @@
1
+ {
2
+ "files": ["device_combobox.py", "launch_device_combobox.py",
3
+ ]
4
+ }
@@ -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.device_inputs import DeviceComboBox
8
+
9
+ DOM_XML = """
10
+ <ui language='c++'>
11
+ <widget class='DeviceComboBox' name='device_combobox'>
12
+ </widget>
13
+ </ui>
14
+ """
15
+
16
+
17
+ class DeviceComboBoxPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
18
+ def __init__(self):
19
+ super().__init__()
20
+ self._form_editor = None
21
+
22
+ def createWidget(self, parent):
23
+ t = DeviceComboBox(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 "device_combobox"
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 "DeviceComboBox"
49
+
50
+ def toolTip(self):
51
+ return "Device ComboBox Example for BEC Widgets"
52
+
53
+ def whatsThis(self):
54
+ return self.toolTip()
@@ -0,0 +1,11 @@
1
+ from bec_widgets.widgets.device_inputs import DeviceComboBox
2
+
3
+ if __name__ == "__main__": # pragma: no cover
4
+ import sys
5
+
6
+ from qtpy.QtWidgets import QApplication
7
+
8
+ app = QApplication(sys.argv)
9
+ w = DeviceComboBox()
10
+ w.show()
11
+ sys.exit(app.exec_())
@@ -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.device_inputs.device_combobox.device_combobox_plugin import (
10
+ DeviceComboBoxPlugin,
11
+ )
12
+
13
+ QPyDesignerCustomWidgetCollection.addCustomWidget(DeviceComboBoxPlugin())
14
+
15
+
16
+ if __name__ == "__main__": # pragma: no cover
17
+ main()
@@ -89,14 +89,3 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
89
89
  if device_obj is None:
90
90
  raise ValueError(f"Device {device_name} is not found.")
91
91
  return device_obj
92
-
93
-
94
- if __name__ == "__main__": # pragma: no cover
95
- import sys
96
-
97
- from qtpy.QtWidgets import QApplication
98
-
99
- app = QApplication(sys.argv)
100
- w = DeviceLineEdit(default_device="samx")
101
- w.show()
102
- sys.exit(app.exec_())
@@ -0,0 +1,4 @@
1
+ {
2
+ "files": ["device_line_edit.py", "launch_device_line_edit.py",
3
+ ]
4
+ }
@@ -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.device_inputs import DeviceLineEdit
8
+
9
+ DOM_XML = """
10
+ <ui language='c++'>
11
+ <widget class='DeviceLineEdit' name='device_line_edit'>
12
+ </widget>
13
+ </ui>
14
+ """
15
+
16
+
17
+ class DeviceLineEditPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
18
+ def __init__(self):
19
+ super().__init__()
20
+ self._form_editor = None
21
+
22
+ def createWidget(self, parent):
23
+ t = DeviceLineEdit(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 "device_line_edit"
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 "DeviceLineEdit"
49
+
50
+ def toolTip(self):
51
+ return "Device LineEdit Example for BEC Widgets with autocomplete."
52
+
53
+ def whatsThis(self):
54
+ return self.toolTip()
@@ -0,0 +1,11 @@
1
+ from bec_widgets.widgets.device_inputs import DeviceLineEdit
2
+
3
+ if __name__ == "__main__": # pragma: no cover
4
+ import sys
5
+
6
+ from qtpy.QtWidgets import QApplication
7
+
8
+ app = QApplication(sys.argv)
9
+ w = DeviceLineEdit()
10
+ w.show()
11
+ sys.exit(app.exec_())
@@ -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.device_inputs.device_line_edit.device_line_edit_plugin import (
10
+ DeviceLineEditPlugin,
11
+ )
12
+
13
+ QPyDesignerCustomWidgetCollection.addCustomWidget(DeviceLineEditPlugin())
14
+
15
+
16
+ if __name__ == "__main__": # pragma: no cover
17
+ main()
File without changes
@@ -0,0 +1,86 @@
1
+ import os
2
+ import select
3
+ import shlex
4
+ import signal
5
+ import subprocess
6
+ import sys
7
+
8
+ from bec_widgets.widgets.website.website import WebsiteWidget
9
+
10
+
11
+ class VSCodeEditor(WebsiteWidget):
12
+ """
13
+ A widget to display the VSCode editor.
14
+ """
15
+
16
+ token = "bec"
17
+ host = "127.0.0.1"
18
+ port = 7000
19
+
20
+ USER_ACCESS = []
21
+
22
+ def __init__(self, parent=None, config=None, client=None, gui_id=None):
23
+
24
+ self.process = None
25
+ self._url = f"http://{self.host}:{self.port}?tkn={self.token}"
26
+ super().__init__(parent=parent, config=config, client=client, gui_id=gui_id)
27
+ self.start_server()
28
+
29
+ def start_server(self):
30
+ """
31
+ Start the server.
32
+
33
+ This method starts the server for the VSCode editor in a subprocess.
34
+ """
35
+
36
+ cmd = shlex.split(
37
+ f"code serve-web --port {self.port} --connection-token={self.token} --accept-server-license-terms"
38
+ )
39
+ self.process = subprocess.Popen(
40
+ cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, preexec_fn=os.setsid
41
+ )
42
+
43
+ os.set_blocking(self.process.stdout.fileno(), False)
44
+ while self.process.poll() is None:
45
+ readylist, _, _ = select.select([self.process.stdout], [], [], 1)
46
+ if self.process.stdout in readylist:
47
+ output = self.process.stdout.read(1024)
48
+ if output and f"available at {self._url}" in output:
49
+ break
50
+ self.set_url(self._url)
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
+ def cleanup_vscode(self):
60
+ """
61
+ Cleanup the VSCode editor.
62
+ """
63
+ if not self.process:
64
+ return
65
+ os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
66
+ self.process.wait()
67
+
68
+ def cleanup(self):
69
+ """
70
+ Cleanup the widget. This method is called from the dock area when the widget is removed.
71
+ """
72
+ self.cleanup_vscode()
73
+ return super().cleanup()
74
+
75
+
76
+ if __name__ == "__main__": # pragma: no cover
77
+ import sys
78
+
79
+ from qtpy.QtWidgets import QApplication
80
+
81
+ app = QApplication(sys.argv)
82
+ widget = VSCodeEditor()
83
+ widget.show()
84
+ app.exec_()
85
+ widget.bec_dispatcher.disconnect_all()
86
+ widget.client.shutdown()
@@ -1,10 +1,19 @@
1
- from qtpy.QtCore import QUrl
1
+ from qtpy.QtCore import QUrl, qInstallMessageHandler
2
2
  from qtpy.QtWebEngineWidgets import QWebEngineView
3
3
  from qtpy.QtWidgets import QApplication
4
4
 
5
5
  from bec_widgets.utils import BECConnector
6
6
 
7
7
 
8
+ def suppress_qt_messages(type_, context, msg):
9
+ if context.category in ["js", "default"]:
10
+ return
11
+ print(msg)
12
+
13
+
14
+ qInstallMessageHandler(suppress_qt_messages)
15
+
16
+
8
17
  class WebsiteWidget(BECConnector, QWebEngineView):
9
18
  """
10
19
  A simple widget to display a website
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.68.0
3
+ Version: 0.70.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
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=RnYDz4zKXjlqltTryprlB1s5vLXxI2-seW-Vb70NNF0,8162
3
3
  .pylintrc,sha256=OstrgmEyP0smNFBKoIN5_26-UmNZgMHnbjvAWX0UrLs,18535
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=EhWy5RDQRny8SHputsNnGM7Hy1HOZoSmZ6cJB2Gw8Cw,7198
5
+ CHANGELOG.md,sha256=6oy9Q1sEpauu8fxOvqkRcOD4rfjav0_yz6O9LJtNdeo,7057
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=I5LEt7gWZyuBZpnkwiIXlKyxz5sQOmtI-RAIDZjzD9s,1302
7
+ PKG-INFO,sha256=_ms-ExvgDL4QW0BhYZRxe6u3FemzKVPW05H22vFUgMo,1302
8
8
  README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
9
- pyproject.toml,sha256=wCSTnyvyFrQnQiDgC5Pn18_zeLOguN86bLfJVSIboz8,2162
9
+ pyproject.toml,sha256=nosm73wZVCwou86NLT-6_lqc0iSTx09_FUvSoSqWqVQ,2215
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
@@ -17,9 +17,9 @@ bec_widgets/assets/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3i
17
17
  bec_widgets/assets/terminal_icon.png,sha256=bJl7Tft4Fi2uxvuXI8o14uMHnI9eAWKSU2uftXCH9ws,3889
18
18
  bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
19
19
  bec_widgets/cli/auto_updates.py,sha256=DyBV3HnjMSH-cvVkYNcDiYKVf0Xut4Qy2qGQqkW47Bw,4833
20
- bec_widgets/cli/client.py,sha256=Zd4oMSE5-HY3IBUIVcparGGV2Ew86gaWAFTd4OjFVmg,58005
20
+ bec_widgets/cli/client.py,sha256=DNsCueEdVwW0MWjBIIg-vhTu_p64qr0QurT7mHM79is,58074
21
21
  bec_widgets/cli/client_utils.py,sha256=_Hb2nl1rKEf7k4By9VZDYl5YyGFczxMuYIFMVrOAZD0,12182
22
- bec_widgets/cli/generate_cli.py,sha256=Bi8HxHhge1I87vbdYHZUZiZwvbB-OSkLYS5Xfmwiz9M,4922
22
+ bec_widgets/cli/generate_cli.py,sha256=InKBVYM7DRfAVLNJhRJbWWSSPBQBHI8Ek6v7NCsK0ME,4997
23
23
  bec_widgets/cli/rpc_register.py,sha256=QxXUZu5XNg00Yf5O3UHWOXg3-f_pzKjjoZYMOa-MOJc,2216
24
24
  bec_widgets/cli/rpc_wigdet_handler.py,sha256=1qQOGrM8rozaWLkoxAW8DTVLv_L_DZdZgUMDPy5MOek,1486
25
25
  bec_widgets/cli/server.py,sha256=3bFBPmtXKXFMjeja18d0hF3CO66Jo0-LEDtcF7lYb7k,7166
@@ -30,8 +30,16 @@ bec_widgets/examples/jupyter_console/jupyter_console_window.ui,sha256=2A2mNTUMZB
30
30
  bec_widgets/examples/motor_movement/__init__.py,sha256=LzPJkxLAxOsZCbXR-fRCPmeYobp7Yqds6tDxW4W1gSw,214
31
31
  bec_widgets/examples/motor_movement/motor_control_compilations.py,sha256=8rpA7a2xVZTDMrx7YQIj3IJew78J1gcVMkHvloS0U_Q,9055
32
32
  bec_widgets/examples/motor_movement/motor_controller.ui,sha256=83XX6NGILwntoUIghvzWnMuGf80O8khK3SduVKTAEFM,29105
33
+ bec_widgets/examples/plugin_example_pyside/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ bec_widgets/examples/plugin_example_pyside/main.py,sha256=xdC6RWSRt1rW8Prj0CsDeCPct6-_j3__oJmdgogB5PI,505
35
+ bec_widgets/examples/plugin_example_pyside/registertictactoe.py,sha256=VNFkHc5Sc30lRKzOFJbhArCHGkp_wRxOeJjZbmaAHRU,448
36
+ bec_widgets/examples/plugin_example_pyside/taskmenuextension.pyproject,sha256=O_PSMFJlXxQuf55gIOfkpfZIZGpX8W1SfUVZ-mvtI_E,119
37
+ bec_widgets/examples/plugin_example_pyside/tictactoe.py,sha256=s3rCurXloVcmMdzZiSzDS7Lgj0Qe6x8-wkxCeiXYX80,4904
38
+ bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py,sha256=BBt3MD8oDLUMCCY3mioJa1QRR0WQdW6DuvVmK1Taovk,1734
39
+ bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py,sha256=LNwplI6deUdKY6FOhUuWBanotxk9asF2G-6k7lFfA8Y,2301
33
40
  bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
34
41
  bec_widgets/utils/bec_connector.py,sha256=RxHJNF7JjtY5pRbTMu2eQTiRXvoyJ53QuTYxHjZba38,5357
42
+ bec_widgets/utils/bec_designer.py,sha256=gaxNuxRu-3rQylUd5lGSysK1lqoRg8gtmfad0CnsUPU,2613
35
43
  bec_widgets/utils/bec_dispatcher.py,sha256=yM9PG04O7ABhiA9Nzk38Rv9Qbjc5O93wi2xfSbOlOxc,6202
36
44
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
37
45
  bec_widgets/utils/colors.py,sha256=GYSDe0ZxsJSwxvuy-yG2BH17qlf_Sjq8dhDcyp9IhBI,8532
@@ -56,9 +64,17 @@ bec_widgets/widgets/buttons/stop_button/stop_button.py,sha256=x4a7RvlMkHzOd05zKO
56
64
  bec_widgets/widgets/device_inputs/__init__.py,sha256=BcWvcSASPh6YdDu5jfC48xqI2_iBj1epUt4doYJQHEs,122
57
65
  bec_widgets/widgets/device_inputs/device_input_base.py,sha256=ew3G1WXX0srCOQ2uRSjYoK5zB-NBZ9EwwhO-rmR3K4M,3803
58
66
  bec_widgets/widgets/device_inputs/device_combobox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- bec_widgets/widgets/device_inputs/device_combobox/device_combobox.py,sha256=_XL40tD1AVuSMmjC2movK5tscZzxfRnKg3lyZbvE6ZE,2889
67
+ bec_widgets/widgets/device_inputs/device_combobox/device_combobox.py,sha256=i4ube17LiqfhgMHKF4BwKoC8L7KgO4_kot-wb0zjQD4,2661
68
+ bec_widgets/widgets/device_inputs/device_combobox/device_combobox.pyproject,sha256=AH6Oz_4aDCzsBbHWnCNK_ALvQ0Qwj-z24sUG1oNSK3I,75
69
+ bec_widgets/widgets/device_inputs/device_combobox/device_combobox_plugin.py,sha256=VfbP0PrLwME8OB1bz1kVmy8vJCZDRONvas5j5cPyd08,1218
70
+ bec_widgets/widgets/device_inputs/device_combobox/launch_device_combobox.py,sha256=jr9JJHtIPmbUNLpGLUESS_bYcITnbv-W3EdrZCHq2Rg,267
71
+ bec_widgets/widgets/device_inputs/device_combobox/register_device_combobox.py,sha256=V6QcxBU9Lli4qy2Vhf9bFfpTsofsc-bT-_vEp0heBsM,518
60
72
  bec_widgets/widgets/device_inputs/device_line_edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
- bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.py,sha256=cvvFg9HS7yN9kn-lWI_Uvj_dq2xlqNhuDi21e73k2xo,3249
73
+ bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.py,sha256=-haiyXSWBULxUBo4gbpfkFYHOBYQnR9ItnATXKvf0Yc,3021
74
+ bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.pyproject,sha256=0GTk3WfNUHl-6tFEAY0pel5XBpVR0oyRX2BU-LNzVps,77
75
+ bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit_plugin.py,sha256=J1MNrtXkgBrwEsUXnf0pGRoNJ1g_OWqk-xafSpV4ot8,1239
76
+ bec_widgets/widgets/device_inputs/device_line_edit/launch_device_line_edit.py,sha256=3sVZ-5Hoy1smhgBcnzO9SXHk5_oUEaWFnRfslXAW3_4,267
77
+ bec_widgets/widgets/device_inputs/device_line_edit/register_device_line_edit.py,sha256=GXHPRe1kSkJ2NKDWQJWabI4a6Maw__niA2g5wLiheX8,520
62
78
  bec_widgets/widgets/dock/__init__.py,sha256=B7foHt02gnhM7mFksa7GJVwT7n0j_JvYDCt6wc6XR5g,61
63
79
  bec_widgets/widgets/dock/dock.py,sha256=lEl6ppy0v-8X5bVX7EQIYka8rAakjkZyh6ufgr7YxKs,7595
64
80
  bec_widgets/widgets/dock/dock_area.py,sha256=9c_tLzyBRllLfc4H5o9-4bvasWp5hWe1NWg4mupXVtU,7911
@@ -100,8 +116,10 @@ bec_widgets/widgets/text_box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
100
116
  bec_widgets/widgets/text_box/text_box.py,sha256=kykQ_Zcxh8IGcPEP5-oGGQwoZEpY9vhxRIM8TY8kTYg,4240
101
117
  bec_widgets/widgets/toolbar/__init__.py,sha256=d-TP4_cr_VbpwreMM4ePnfZ5YXsEPQ45ibEf75nuGoE,36
102
118
  bec_widgets/widgets/toolbar/toolbar.py,sha256=e0zCD_0q7K4NVhrzD8001Qvfxt-VhqHTgofchS9NgCM,5125
119
+ bec_widgets/widgets/vscode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
+ bec_widgets/widgets/vscode/vscode.py,sha256=k4Y54zp9jGfeUKsFc482TnUJQd3pj-jdIb3i_dLiWUA,2376
103
121
  bec_widgets/widgets/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
- bec_widgets/widgets/website/website.py,sha256=C679bpGUoSqfLtu4_BcEQEmesNj_L9HaTu0buJzb4c0,1526
122
+ bec_widgets/widgets/website/website.py,sha256=Scvpl4I52qpL7s69tnNBRQSG6GcRI9jzoR3RsSTXfPE,1722
105
123
  docs/Makefile,sha256=i2WHuFlgfyAPEW4ssEP8NY4cOibDJrVjvzSEU8_Ggwc,634
106
124
  docs/conf.py,sha256=HxLxupNGu0Smhwn57g1kFdjZzFuaWVREgRJKhT1zi2k,2464
107
125
  docs/index.md,sha256=8ZCgaLIbJsYvt-jwi--QxsNwnK4-k3rejIeOOLclG40,1101
@@ -136,7 +154,7 @@ docs/user/getting_started/quick_start.md,sha256=VGU880GwamcIZcBE8tjxuqX2syE-71jq
136
154
  docs/user/widgets/BECFigure.png,sha256=8dQr4u0uk_y0VV-R1Jh9yTR3Vidd9HDEno_07R0swaE,1605920
137
155
  docs/user/widgets/bec_figure.md,sha256=BwcumbhZd6a2zKmoHTvwKr8kG8WxBx9lS_QwxNiBMpQ,5155
138
156
  docs/user/widgets/bec_status_box.gif,sha256=kLxf40HbS6fjdUIQ2b9SiduBEXdBd4DDWGEnQDOFMcY,1259044
139
- docs/user/widgets/bec_status_box.md,sha256=0ILY12UnSjiVCtd5qpC8G2dPBYhug3J_rmQLilDxulY,1164
157
+ docs/user/widgets/bec_status_box.md,sha256=YJ7uHuaHRsvP7tkC65O7siJOzm1LRtsrvH1ikeOH9xo,1156
140
158
  docs/user/widgets/buttons.md,sha256=Yci21PmxlRvfKcrvY7mVI7JkECPmE6j9WyWyH3j7Y2o,1221
141
159
  docs/user/widgets/image_plot.gif,sha256=_mVFhMTXGqwDOcEtrBHMZj5Thn2sLhDAHEeL2XyHN-s,14098977
142
160
  docs/user/widgets/motor.gif,sha256=FtaWdRHx4UZaGJPpq8LNhMMgX4PFcAB6IZ93JCMEh_w,2280719
@@ -177,6 +195,7 @@ tests/unit_tests/test_scan_control.py,sha256=Xf8bGt8lRJobRwBoqUdVXxsHno8ejvC77Fq
177
195
  tests/unit_tests/test_spiral_progress_bar.py,sha256=n5aLSZ2B6K5a1vQuKTERnCSmIz9hYGFyk7jP3TU0AwQ,12438
178
196
  tests/unit_tests/test_stop_button.py,sha256=2OH9dhs_-S5QovPPgU-5hJoViE1YKZa0gxisb4vOY28,712
179
197
  tests/unit_tests/test_text_box_widget.py,sha256=cT0uEHt_6d-FwST0A_wE9sFW9E3F_nJbKhuBAeU4yHg,1862
198
+ tests/unit_tests/test_vscode_widget.py,sha256=sCVNAuWVMiPFinh9mDqz_ulBay_H3qwHyEwkHsbWh4c,2173
180
199
  tests/unit_tests/test_waveform1d.py,sha256=I3_pF0ieltcTWtweOBjICaOxJ8NCQ0-NWxpKg8Pas3E,15893
181
200
  tests/unit_tests/test_website_widget.py,sha256=fBADIJJBAHU4Ro7u95kdemFVNv196UOcuO9oLHuHt8A,761
182
201
  tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
@@ -186,8 +205,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
186
205
  tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
187
206
  tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
188
207
  tests/unit_tests/test_msgs/available_scans_message.py,sha256=m_z97hIrjHXXMa2Ex-UvsPmTxOYXfjxyJaGkIY6StTY,46532
189
- bec_widgets-0.68.0.dist-info/METADATA,sha256=I5LEt7gWZyuBZpnkwiIXlKyxz5sQOmtI-RAIDZjzD9s,1302
190
- bec_widgets-0.68.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
191
- bec_widgets-0.68.0.dist-info/entry_points.txt,sha256=OvoqiNzNF9bizFQNhbAmmdc_njHrnVewLE-Kl-u9sh0,115
192
- bec_widgets-0.68.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
193
- bec_widgets-0.68.0.dist-info/RECORD,,
208
+ bec_widgets-0.70.0.dist-info/METADATA,sha256=_ms-ExvgDL4QW0BhYZRxe6u3FemzKVPW05H22vFUgMo,1302
209
+ bec_widgets-0.70.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
210
+ bec_widgets-0.70.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
211
+ bec_widgets-0.70.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
212
+ bec_widgets-0.70.0.dist-info/RECORD,,
@@ -1,3 +1,4 @@
1
1
  [console_scripts]
2
+ bec-designer = bec_widgets.utils.bec_designer:main
2
3
  bec-gui-server = bec_widgets.cli.server:main
3
4
  bw-generate-cli = bec_widgets.cli.generate_cli:main
@@ -2,7 +2,7 @@
2
2
  # BEC Status Box
3
3
  **Purpose:**
4
4
 
5
- The [BECStatusBox]](/api_reference/_autosummary/bec_widgets.cli.client.BECStatusBox) Widget is a widget that allows you to monitor the status/health of the all running BEC processes. The widget generates the view automatically and updates the status of the processes in real-time. The top level indicates the overall state of the BEC core services (DeviceServer, ScanServer, SciHub, ScanBundler and FileWriter), but you can also see the status of each individual process by opening the collapsed view. In the collapsed view, you can double click on each process to get a popup window with live updates of the metrics for each process in real-time.
5
+ The [BECStatusBox](/api_reference/_autosummary/bec_widgets.cli.client.BECStatusBox) is a widget that allows you to monitor the status/health of the all running BEC processes. The widget generates the view automatically and updates the status of the processes in real-time. The top level indicates the overall state of the BEC core services (DeviceServer, ScanServer, SciHub, ScanBundler and FileWriter), but you can also see the status of each individual process by opening the collapsed view. In the collapsed view, you can double click on each process to get a popup window with live updates of the metrics for each process in real-time.
6
6
 
7
7
  **Key Features:**
8
8
 
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "0.68.0"
7
+ version = "0.70.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [
@@ -49,6 +49,7 @@ Homepage = "https://gitlab.psi.ch/bec/bec_widgets"
49
49
  [project.scripts]
50
50
  bw-generate-cli = "bec_widgets.cli.generate_cli:main"
51
51
  bec-gui-server = "bec_widgets.cli.server:main"
52
+ bec-designer = "bec_widgets.utils.bec_designer:main"
52
53
 
53
54
  [tool.hatch.build.targets.wheel]
54
55
  include = ["*"]
@@ -0,0 +1,61 @@
1
+ import os
2
+ import shlex
3
+ import subprocess
4
+ from unittest import mock
5
+
6
+ import pytest
7
+
8
+ from bec_widgets.widgets.vscode.vscode import VSCodeEditor
9
+
10
+ from .client_mocks import mocked_client
11
+
12
+
13
+ @pytest.fixture
14
+ def vscode_widget(qtbot, mocked_client):
15
+ with mock.patch("bec_widgets.widgets.vscode.vscode.subprocess.Popen") as mock_popen:
16
+ widget = VSCodeEditor(client=mocked_client)
17
+ yield widget
18
+
19
+
20
+ def test_vscode_widget(qtbot, vscode_widget):
21
+ assert vscode_widget.process is not None
22
+ assert vscode_widget._url == "http://127.0.0.1:7000?tkn=bec"
23
+
24
+
25
+ def test_start_server(qtbot, mocked_client):
26
+
27
+ with mock.patch("bec_widgets.widgets.vscode.vscode.subprocess.Popen") as mock_popen:
28
+ mock_process = mock.Mock()
29
+ mock_process.stdout.fileno.return_value = 1
30
+ mock_process.poll.return_value = None
31
+ mock_process.stdout.read.return_value = (
32
+ f"available at http://{VSCodeEditor.host}:{VSCodeEditor.port}?tkn={VSCodeEditor.token}"
33
+ )
34
+ mock_popen.return_value = mock_process
35
+
36
+ widget = VSCodeEditor(client=mocked_client)
37
+
38
+ mock_popen.assert_called_once_with(
39
+ shlex.split(
40
+ f"code serve-web --port {widget.port} --connection-token={widget.token} --accept-server-license-terms"
41
+ ),
42
+ text=True,
43
+ stdout=subprocess.PIPE,
44
+ stderr=subprocess.DEVNULL,
45
+ preexec_fn=os.setsid,
46
+ )
47
+
48
+
49
+ def test_close_event(qtbot, vscode_widget):
50
+ with mock.patch("bec_widgets.widgets.vscode.vscode.os.killpg") as mock_killpg:
51
+ with mock.patch("bec_widgets.widgets.vscode.vscode.os.getpgid") as mock_getpgid:
52
+ with mock.patch(
53
+ "bec_widgets.widgets.website.website.WebsiteWidget.closeEvent"
54
+ ) as mock_close_event:
55
+ mock_getpgid.return_value = 123
56
+ vscode_widget.process = mock.Mock()
57
+ vscode_widget.process.pid = 123
58
+ vscode_widget.closeEvent(None)
59
+ mock_killpg.assert_called_once_with(123, 15)
60
+ vscode_widget.process.wait.assert_called_once()
61
+ mock_close_event.assert_called_once()