bec-widgets 0.68.0__py3-none-any.whl → 0.69.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 +10 -12
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +4 -0
- bec_widgets/cli/generate_cli.py +3 -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.69.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.69.0.dist-info}/RECORD +14 -11
- pyproject.toml +1 -1
- tests/unit_tests/test_vscode_widget.py +61 -0
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.69.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.69.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.68.0.dist-info → bec_widgets-0.69.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v0.69.0 (2024-06-21)
|
4
|
+
|
5
|
+
### Feature
|
6
|
+
|
7
|
+
* feat(widgets): added vscode widget ([`48ae950`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/48ae950d57b454307ce409e2511f7b7adf3cfc6b))
|
8
|
+
|
9
|
+
### Fix
|
10
|
+
|
11
|
+
* fix(generate_cli): fixed rpc generate for classes without user access; closes #226 ([`925c893`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/925c893f3ff4337fc8b4d237c8ffc19a597b0996))
|
12
|
+
|
3
13
|
## v0.68.0 (2024-06-21)
|
4
14
|
|
5
15
|
### Feature
|
@@ -166,15 +176,3 @@ on SIGTERM ([`9263f8e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9263f8ef5
|
|
166
176
|
### Test
|
167
177
|
|
168
178
|
* 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,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=48HpbXwe-Y_t9YJAntu3fJ8MU4DN6bSWSJWj-1bJAEY,7057
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=GEZve_LqjICZHq1bEuZ7S1woFWxfQSVUsGmIVgudNYE,1302
|
8
8
|
README.md,sha256=y4jB6wvArS7N8_iTbKWnSM_oRAqLA2GqgzUR-FMh5sU,2645
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=Td8223p7vyZ4AVOTaqH50fwtAgfFtSIRq-QYp625MOA,2162
|
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
|
@@ -100,8 +100,10 @@ bec_widgets/widgets/text_box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
100
100
|
bec_widgets/widgets/text_box/text_box.py,sha256=kykQ_Zcxh8IGcPEP5-oGGQwoZEpY9vhxRIM8TY8kTYg,4240
|
101
101
|
bec_widgets/widgets/toolbar/__init__.py,sha256=d-TP4_cr_VbpwreMM4ePnfZ5YXsEPQ45ibEf75nuGoE,36
|
102
102
|
bec_widgets/widgets/toolbar/toolbar.py,sha256=e0zCD_0q7K4NVhrzD8001Qvfxt-VhqHTgofchS9NgCM,5125
|
103
|
+
bec_widgets/widgets/vscode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
104
|
+
bec_widgets/widgets/vscode/vscode.py,sha256=k4Y54zp9jGfeUKsFc482TnUJQd3pj-jdIb3i_dLiWUA,2376
|
103
105
|
bec_widgets/widgets/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
104
|
-
bec_widgets/widgets/website/website.py,sha256=
|
106
|
+
bec_widgets/widgets/website/website.py,sha256=Scvpl4I52qpL7s69tnNBRQSG6GcRI9jzoR3RsSTXfPE,1722
|
105
107
|
docs/Makefile,sha256=i2WHuFlgfyAPEW4ssEP8NY4cOibDJrVjvzSEU8_Ggwc,634
|
106
108
|
docs/conf.py,sha256=HxLxupNGu0Smhwn57g1kFdjZzFuaWVREgRJKhT1zi2k,2464
|
107
109
|
docs/index.md,sha256=8ZCgaLIbJsYvt-jwi--QxsNwnK4-k3rejIeOOLclG40,1101
|
@@ -177,6 +179,7 @@ tests/unit_tests/test_scan_control.py,sha256=Xf8bGt8lRJobRwBoqUdVXxsHno8ejvC77Fq
|
|
177
179
|
tests/unit_tests/test_spiral_progress_bar.py,sha256=n5aLSZ2B6K5a1vQuKTERnCSmIz9hYGFyk7jP3TU0AwQ,12438
|
178
180
|
tests/unit_tests/test_stop_button.py,sha256=2OH9dhs_-S5QovPPgU-5hJoViE1YKZa0gxisb4vOY28,712
|
179
181
|
tests/unit_tests/test_text_box_widget.py,sha256=cT0uEHt_6d-FwST0A_wE9sFW9E3F_nJbKhuBAeU4yHg,1862
|
182
|
+
tests/unit_tests/test_vscode_widget.py,sha256=sCVNAuWVMiPFinh9mDqz_ulBay_H3qwHyEwkHsbWh4c,2173
|
180
183
|
tests/unit_tests/test_waveform1d.py,sha256=I3_pF0ieltcTWtweOBjICaOxJ8NCQ0-NWxpKg8Pas3E,15893
|
181
184
|
tests/unit_tests/test_website_widget.py,sha256=fBADIJJBAHU4Ro7u95kdemFVNv196UOcuO9oLHuHt8A,761
|
182
185
|
tests/unit_tests/test_widget_io.py,sha256=FeL3ZYSBQnRt6jxj8VGYw1cmcicRQyHKleahw7XIyR0,3475
|
@@ -186,8 +189,8 @@ tests/unit_tests/test_configs/config_device_no_entry.yaml,sha256=hdvue9KLc_kfNzG
|
|
186
189
|
tests/unit_tests/test_configs/config_scan.yaml,sha256=vo484BbWOjA_e-h6bTjSV9k7QaQHrlAvx-z8wtY-P4E,1915
|
187
190
|
tests/unit_tests/test_msgs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
188
191
|
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.
|
192
|
+
bec_widgets-0.69.0.dist-info/METADATA,sha256=GEZve_LqjICZHq1bEuZ7S1woFWxfQSVUsGmIVgudNYE,1302
|
193
|
+
bec_widgets-0.69.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
194
|
+
bec_widgets-0.69.0.dist-info/entry_points.txt,sha256=OvoqiNzNF9bizFQNhbAmmdc_njHrnVewLE-Kl-u9sh0,115
|
195
|
+
bec_widgets-0.69.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
196
|
+
bec_widgets-0.69.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
@@ -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
|
File without changes
|