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.
- CHANGELOG.md +32 -45
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +4 -0
- bec_widgets/cli/generate_cli.py +3 -0
- bec_widgets/examples/plugin_example_pyside/__init__.py +0 -0
- bec_widgets/examples/plugin_example_pyside/main.py +17 -0
- bec_widgets/examples/plugin_example_pyside/registertictactoe.py +12 -0
- bec_widgets/examples/plugin_example_pyside/taskmenuextension.pyproject +4 -0
- bec_widgets/examples/plugin_example_pyside/tictactoe.py +135 -0
- bec_widgets/examples/plugin_example_pyside/tictactoeplugin.py +68 -0
- bec_widgets/examples/plugin_example_pyside/tictactoetaskmenu.py +67 -0
- bec_widgets/utils/bec_designer.py +87 -0
- bec_widgets/widgets/device_inputs/device_combobox/device_combobox.py +0 -11
- bec_widgets/widgets/device_inputs/device_combobox/device_combobox.pyproject +4 -0
- bec_widgets/widgets/device_inputs/device_combobox/device_combobox_plugin.py +54 -0
- bec_widgets/widgets/device_inputs/device_combobox/launch_device_combobox.py +11 -0
- bec_widgets/widgets/device_inputs/device_combobox/register_device_combobox.py +17 -0
- bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.py +0 -11
- bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit.pyproject +4 -0
- bec_widgets/widgets/device_inputs/device_line_edit/device_line_edit_plugin.py +54 -0
- bec_widgets/widgets/device_inputs/device_line_edit/launch_device_line_edit.py +11 -0
- bec_widgets/widgets/device_inputs/device_line_edit/register_device_line_edit.py +17 -0
- bec_widgets/widgets/vscode/__init__.py +0 -0
- bec_widgets/widgets/vscode/vscode.py +86 -0
- bec_widgets/widgets/website/website.py +10 -1
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/RECORD +33 -14
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/entry_points.txt +1 -0
- docs/user/widgets/bec_status_box.md +1 -1
- pyproject.toml +2 -1
- tests/unit_tests/test_vscode_widget.py +61 -0
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.70.0.dist-info}/WHEEL +0 -0
- {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
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:
|
bec_widgets/cli/generate_cli.py
CHANGED
@@ -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,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,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,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
|
@@ -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=
|
5
|
+
CHANGELOG.md,sha256=6oy9Q1sEpauu8fxOvqkRcOD4rfjav0_yz6O9LJtNdeo,7057
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=_ms-ExvgDL4QW0BhYZRxe6u3FemzKVPW05H22vFUgMo,1302
|
8
8
|
README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
|
9
|
-
pyproject.toml,sha256=
|
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=
|
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=
|
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=
|
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
|
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=
|
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=
|
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.
|
190
|
-
bec_widgets-0.
|
191
|
-
bec_widgets-0.
|
192
|
-
bec_widgets-0.
|
193
|
-
bec_widgets-0.
|
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,,
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# BEC Status Box
|
3
3
|
**Purpose:**
|
4
4
|
|
5
|
-
The [BECStatusBox]
|
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.
|
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()
|
File without changes
|
File without changes
|