bec-widgets 1.5.3__py3-none-any.whl → 1.7.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 +81 -82
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +166 -0
- bec_widgets/cli/client_utils.py +77 -14
- bec_widgets/cli/server.py +14 -12
- bec_widgets/utils/bec_connector.py +1 -1
- bec_widgets/widgets/containers/dock/dock_area.py +14 -0
- bec_widgets/widgets/containers/main_window/__init__.py +0 -0
- bec_widgets/widgets/containers/main_window/main_window.py +9 -0
- bec_widgets/widgets/editors/console/console.py +140 -5
- bec_widgets/widgets/services/device_browser/device_item/device_item.py +18 -6
- {bec_widgets-1.5.3.dist-info → bec_widgets-1.7.0.dist-info}/METADATA +1 -1
- {bec_widgets-1.5.3.dist-info → bec_widgets-1.7.0.dist-info}/RECORD +17 -15
- pyproject.toml +1 -1
- {bec_widgets-1.5.3.dist-info → bec_widgets-1.7.0.dist-info}/WHEEL +0 -0
- {bec_widgets-1.5.3.dist-info → bec_widgets-1.7.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-1.5.3.dist-info → bec_widgets-1.7.0.dist-info}/licenses/LICENSE +0 -0
CHANGELOG.md
CHANGED
@@ -1,6 +1,87 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
|
4
|
+
## v1.7.0 (2024-12-02)
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
- **tests**: Add test for Console widget
|
9
|
+
([`da579b6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/da579b6d213bcdf28c40c1a9e4e2535fdde824fb))
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
- **console**: Add "prompt" signal to inform when shell is at prompt
|
14
|
+
([`3aeb0b6`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3aeb0b66fbeb03d3d0ee60e108cc6b98fd9aa9b9))
|
15
|
+
|
16
|
+
- **console**: Add 'terminate' and 'send_ctrl_c' methods to Console
|
17
|
+
([`02086ae`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/02086aeae09233ec4e6ccc0e6a17f2b078d500b8))
|
18
|
+
|
19
|
+
.terminate() ends the started process, sending SIGTERM signal. If process is not dead after optional
|
20
|
+
timeout, SIGKILL is sent. .send_ctrl_c() sends SIGINT to the child process, and waits for prompt
|
21
|
+
until optional timeout is reached. Timeouts raise 'TimeoutError' exception.
|
22
|
+
|
23
|
+
|
24
|
+
## v1.6.0 (2024-11-27)
|
25
|
+
|
26
|
+
### Bug Fixes
|
27
|
+
|
28
|
+
- Add back accidentally removed variables
|
29
|
+
([`e998352`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e9983521ed2a1c04af048a55ece70a1943a84313))
|
30
|
+
|
31
|
+
- Differentiate click and drag for DeviceItem, adapt tests accordingly
|
32
|
+
([`cffcdf2`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cffcdf292363249bcc7efa9d130431d0bc727fda))
|
33
|
+
|
34
|
+
This fixes the blocking "QDrag.exec_()" on Linux, indeed before the drag'n'drop operation was
|
35
|
+
started with a simple click and it was waiting for drop forever. Now there are 2 different cases,
|
36
|
+
click or drag'n'drop - the drag'n'drop test actually moves the mouse and releases the button.
|
37
|
+
|
38
|
+
- Do not quit automatically when last window is "closed"
|
39
|
+
([`96e255e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/96e255e4ef394eb79006a66d13e06775ae235667))
|
40
|
+
|
41
|
+
Qt confuses closed and hidden
|
42
|
+
|
43
|
+
- No need to call inspect.signature - it can fail on methods coming from C (like Qt methods)
|
44
|
+
([`6029246`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/60292465e9e52d3248ae681c68c07298b9b3ce14))
|
45
|
+
|
46
|
+
- **rpc**: Gui hide/show also hide/show all floating docks
|
47
|
+
([`c27d058`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c27d058b01fe604eccec76454e39360122e48515))
|
48
|
+
|
49
|
+
- **server**: Use dock area by default
|
50
|
+
([`2fe7f5e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2fe7f5e1510a5ea72676045e6ea3485e6b11c220))
|
51
|
+
|
52
|
+
- **tests**: Make use of BECDockArea with client mixin to start server and use it in tests
|
53
|
+
([`da18c2c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/da18c2ceecf9aeaf0e0ea9b78f4c867b27b9c314))
|
54
|
+
|
55
|
+
Depending on the test, auto-updates are enabled or not.
|
56
|
+
|
57
|
+
### Features
|
58
|
+
|
59
|
+
- '._auto_updates_enabled' attribute can be used to activate auto updates installation in
|
60
|
+
BECDockArea
|
61
|
+
([`31d8703`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/31d87036c9801e639a7ca6fc003c90e0c4edb19d))
|
62
|
+
|
63
|
+
- Add '--hide' argument to BEC GUI server
|
64
|
+
([`1f60fec`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1f60fec7201ed252d7e49bf16f2166ee7f6bed6a))
|
65
|
+
|
66
|
+
- Add main window container widget
|
67
|
+
([`f80ec33`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f80ec33ae5a261dbcab901ae30f4cc802316e554))
|
68
|
+
|
69
|
+
- Add rpc_id member to client objects
|
70
|
+
([`3ba0b1d`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/3ba0b1daf5b83da840e90fbbc063ed7b86ebe99b))
|
71
|
+
|
72
|
+
- Asynchronous .start() for GUI
|
73
|
+
([`2047e48`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2047e484d5a4b2f5ea494a1e49035b35b1bbde35))
|
74
|
+
|
75
|
+
- Do not take focus when GUI is loaded
|
76
|
+
([`1f71d8e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/1f71d8e5eded9952f9b34bfc427e2ff44cf5fc18))
|
77
|
+
|
78
|
+
- **client**: Add show()/hide() methods to "gui" object
|
79
|
+
([`e68e2b5`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e68e2b5978339475b97555c3e20795807932fbc9))
|
80
|
+
|
81
|
+
- **server**: Add main window, with proper gui_id derived from given id
|
82
|
+
([`daf6ea0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/daf6ea0159c9ffc7b53bb7ae6b9abc16a302972c))
|
83
|
+
|
84
|
+
|
4
85
|
## v1.5.3 (2024-11-21)
|
5
86
|
|
6
87
|
### Bug Fixes
|
@@ -131,85 +212,3 @@
|
|
131
212
|
|
132
213
|
- **colormap_button**: Colormap button with menu to select colormap filtered by the colormap type
|
133
214
|
([`b039933`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b039933405e2fbe92bd81bd0748e79e8d443a741))
|
134
|
-
|
135
|
-
|
136
|
-
## v1.2.0 (2024-10-25)
|
137
|
-
|
138
|
-
### Features
|
139
|
-
|
140
|
-
- **colors**: Evenly spaced color generation + new golden ratio calculation
|
141
|
-
([`40c9fea`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/40c9fea35f869ef52e05948dd1989bcd99f602e0))
|
142
|
-
|
143
|
-
### Refactoring
|
144
|
-
|
145
|
-
- Add bec_lib version to statusbox
|
146
|
-
([`5d4b86e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5d4b86e1c6e1800051afce4f991153e370767fa6))
|
147
|
-
|
148
|
-
|
149
|
-
## v1.1.0 (2024-10-25)
|
150
|
-
|
151
|
-
### Features
|
152
|
-
|
153
|
-
- Add filter i/o utility class
|
154
|
-
([`0350833`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/0350833f36e0a7cadce4173f9b1d1fbfdf985375))
|
155
|
-
|
156
|
-
### Refactoring
|
157
|
-
|
158
|
-
- Do not flush selection upon receiving config update; allow widgetIO to receive kwargs to be able
|
159
|
-
to use get_value to receive string instead of int for QComboBox
|
160
|
-
([`91959e8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/91959e82de8586934af3ebb5aaa0923930effc51))
|
161
|
-
|
162
|
-
- Allow to set selection in DeviceInput; automatic update of selection on device config update;
|
163
|
-
cleanup
|
164
|
-
([`5eb15b7`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5eb15b785f12e30eb8ccbc56d4ad9e759a4cf5eb))
|
165
|
-
|
166
|
-
- Cleanup, added device_signal for signal inputs
|
167
|
-
([`6fb2055`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6fb20552ff57978f4aeb79fd7f062f8d6b5581e7))
|
168
|
-
|
169
|
-
### Testing
|
170
|
-
|
171
|
-
- **scan_control**: Tests added for grid_scan to ensure scan_args signal validity
|
172
|
-
([`acb7902`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/acb79020d4be546efc001ff47b6f5cdba2ee9375))
|
173
|
-
|
174
|
-
|
175
|
-
## v1.0.2 (2024-10-22)
|
176
|
-
|
177
|
-
### Bug Fixes
|
178
|
-
|
179
|
-
- **scan_control**: Scan args signal fixed to emit list instead of hardcoded structure
|
180
|
-
([`4f5448c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4f5448cf51a204e077af162c7f0aed1f1a60e57a))
|
181
|
-
|
182
|
-
|
183
|
-
## v1.0.1 (2024-10-22)
|
184
|
-
|
185
|
-
### Bug Fixes
|
186
|
-
|
187
|
-
- **waveform**: Added support for live_data and data access
|
188
|
-
([`7469c89`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/7469c892c8076fc09e61f173df6920c551241cec))
|
189
|
-
|
190
|
-
|
191
|
-
## v1.0.0 (2024-10-18)
|
192
|
-
|
193
|
-
### Bug Fixes
|
194
|
-
|
195
|
-
- **crosshair**: Downsample clear markers
|
196
|
-
([`f9a889f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/f9a889fc6d380b9e587edcb465203122ea0bffc1))
|
197
|
-
|
198
|
-
### Features
|
199
|
-
|
200
|
-
- Ability to disable scatter from waveform & compatible crosshair with down sampling
|
201
|
-
([`2ab12ed`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2ab12ed60abb995abc381d9330fdcf399796d9e5))
|
202
|
-
|
203
|
-
|
204
|
-
## v0.119.0 (2024-10-17)
|
205
|
-
|
206
|
-
### Bug Fixes
|
207
|
-
|
208
|
-
- Fix syntax due to change of api for simulated devices
|
209
|
-
([`19f4e40`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/19f4e407e00ee242973ca4c3f90e4e41a4d3e315))
|
210
|
-
|
211
|
-
- Remove wrongly scoped test
|
212
|
-
([`a23841b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/a23841b2553dc7162da943715d58275c7dc39ed9))
|
213
|
-
|
214
|
-
- Rename 'compact' property -> 'compact_view'
|
215
|
-
([`6982711`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/6982711fea5fb8a73845ed7c0692e3ec53ef7871))
|
PKG-INFO
CHANGED
bec_widgets/cli/client.py
CHANGED
@@ -15,6 +15,37 @@ class Widgets(str, enum.Enum):
|
|
15
15
|
Enum for the available widgets.
|
16
16
|
"""
|
17
17
|
|
18
|
+
AbortButton = "AbortButton"
|
19
|
+
BECColorMapWidget = "BECColorMapWidget"
|
20
|
+
BECDockArea = "BECDockArea"
|
21
|
+
BECImageWidget = "BECImageWidget"
|
22
|
+
BECMainWindow = "BECMainWindow"
|
23
|
+
BECMotorMapWidget = "BECMotorMapWidget"
|
24
|
+
BECMultiWaveformWidget = "BECMultiWaveformWidget"
|
25
|
+
BECProgressBar = "BECProgressBar"
|
26
|
+
BECQueue = "BECQueue"
|
27
|
+
BECStatusBox = "BECStatusBox"
|
28
|
+
BECWaveformWidget = "BECWaveformWidget"
|
29
|
+
DapComboBox = "DapComboBox"
|
30
|
+
DarkModeButton = "DarkModeButton"
|
31
|
+
DeviceBrowser = "DeviceBrowser"
|
32
|
+
DeviceComboBox = "DeviceComboBox"
|
33
|
+
DeviceLineEdit = "DeviceLineEdit"
|
34
|
+
LMFitDialog = "LMFitDialog"
|
35
|
+
PositionIndicator = "PositionIndicator"
|
36
|
+
PositionerBox = "PositionerBox"
|
37
|
+
PositionerControlLine = "PositionerControlLine"
|
38
|
+
ResetButton = "ResetButton"
|
39
|
+
ResumeButton = "ResumeButton"
|
40
|
+
RingProgressBar = "RingProgressBar"
|
41
|
+
ScanControl = "ScanControl"
|
42
|
+
SignalComboBox = "SignalComboBox"
|
43
|
+
SignalLineEdit = "SignalLineEdit"
|
44
|
+
StopButton = "StopButton"
|
45
|
+
TextBox = "TextBox"
|
46
|
+
VSCodeEditor = "VSCodeEditor"
|
47
|
+
WebsiteWidget = "WebsiteWidget"
|
48
|
+
|
18
49
|
|
19
50
|
class AbortButton(RPCBase):
|
20
51
|
@property
|
@@ -431,6 +462,18 @@ class BECDockArea(RPCBase, BECGuiClientMixin):
|
|
431
462
|
list: The temporary areas in the dock area.
|
432
463
|
"""
|
433
464
|
|
465
|
+
@rpc_call
|
466
|
+
def show(self):
|
467
|
+
"""
|
468
|
+
Show all windows including floating docks.
|
469
|
+
"""
|
470
|
+
|
471
|
+
@rpc_call
|
472
|
+
def hide(self):
|
473
|
+
"""
|
474
|
+
Hide all windows including floating docks.
|
475
|
+
"""
|
476
|
+
|
434
477
|
|
435
478
|
class BECFigure(RPCBase):
|
436
479
|
@property
|
@@ -1343,6 +1386,31 @@ class BECImageWidget(RPCBase):
|
|
1343
1386
|
"""
|
1344
1387
|
|
1345
1388
|
|
1389
|
+
class BECMainWindow(RPCBase):
|
1390
|
+
@property
|
1391
|
+
@rpc_call
|
1392
|
+
def _config_dict(self) -> "dict":
|
1393
|
+
"""
|
1394
|
+
Get the configuration of the widget.
|
1395
|
+
|
1396
|
+
Returns:
|
1397
|
+
dict: The configuration of the widget.
|
1398
|
+
"""
|
1399
|
+
|
1400
|
+
@rpc_call
|
1401
|
+
def _get_all_rpc(self) -> "dict":
|
1402
|
+
"""
|
1403
|
+
Get all registered RPC objects.
|
1404
|
+
"""
|
1405
|
+
|
1406
|
+
@property
|
1407
|
+
@rpc_call
|
1408
|
+
def _rpc_id(self) -> "str":
|
1409
|
+
"""
|
1410
|
+
Get the RPC ID of the widget.
|
1411
|
+
"""
|
1412
|
+
|
1413
|
+
|
1346
1414
|
class BECMotorMap(RPCBase):
|
1347
1415
|
@property
|
1348
1416
|
@rpc_call
|
@@ -2207,6 +2275,13 @@ class BECQueue(RPCBase):
|
|
2207
2275
|
Get all registered RPC objects.
|
2208
2276
|
"""
|
2209
2277
|
|
2278
|
+
@property
|
2279
|
+
@rpc_call
|
2280
|
+
def _rpc_id(self) -> "str":
|
2281
|
+
"""
|
2282
|
+
Get the RPC ID of the widget.
|
2283
|
+
"""
|
2284
|
+
|
2210
2285
|
|
2211
2286
|
class BECStatusBox(RPCBase):
|
2212
2287
|
@property
|
@@ -2225,6 +2300,13 @@ class BECStatusBox(RPCBase):
|
|
2225
2300
|
Get all registered RPC objects.
|
2226
2301
|
"""
|
2227
2302
|
|
2303
|
+
@property
|
2304
|
+
@rpc_call
|
2305
|
+
def _rpc_id(self) -> "str":
|
2306
|
+
"""
|
2307
|
+
Get the RPC ID of the widget.
|
2308
|
+
"""
|
2309
|
+
|
2228
2310
|
|
2229
2311
|
class BECWaveform(RPCBase):
|
2230
2312
|
@property
|
@@ -2947,6 +3029,13 @@ class DeviceBrowser(RPCBase):
|
|
2947
3029
|
Get all registered RPC objects.
|
2948
3030
|
"""
|
2949
3031
|
|
3032
|
+
@property
|
3033
|
+
@rpc_call
|
3034
|
+
def _rpc_id(self) -> "str":
|
3035
|
+
"""
|
3036
|
+
Get the RPC ID of the widget.
|
3037
|
+
"""
|
3038
|
+
|
2950
3039
|
|
2951
3040
|
class DeviceComboBox(RPCBase):
|
2952
3041
|
@property
|
@@ -2965,6 +3054,13 @@ class DeviceComboBox(RPCBase):
|
|
2965
3054
|
Get all registered RPC objects.
|
2966
3055
|
"""
|
2967
3056
|
|
3057
|
+
@property
|
3058
|
+
@rpc_call
|
3059
|
+
def _rpc_id(self) -> "str":
|
3060
|
+
"""
|
3061
|
+
Get the RPC ID of the widget.
|
3062
|
+
"""
|
3063
|
+
|
2968
3064
|
|
2969
3065
|
class DeviceInputBase(RPCBase):
|
2970
3066
|
@property
|
@@ -2983,6 +3079,13 @@ class DeviceInputBase(RPCBase):
|
|
2983
3079
|
Get all registered RPC objects.
|
2984
3080
|
"""
|
2985
3081
|
|
3082
|
+
@property
|
3083
|
+
@rpc_call
|
3084
|
+
def _rpc_id(self) -> "str":
|
3085
|
+
"""
|
3086
|
+
Get the RPC ID of the widget.
|
3087
|
+
"""
|
3088
|
+
|
2986
3089
|
|
2987
3090
|
class DeviceLineEdit(RPCBase):
|
2988
3091
|
@property
|
@@ -3001,6 +3104,13 @@ class DeviceLineEdit(RPCBase):
|
|
3001
3104
|
Get all registered RPC objects.
|
3002
3105
|
"""
|
3003
3106
|
|
3107
|
+
@property
|
3108
|
+
@rpc_call
|
3109
|
+
def _rpc_id(self) -> "str":
|
3110
|
+
"""
|
3111
|
+
Get the RPC ID of the widget.
|
3112
|
+
"""
|
3113
|
+
|
3004
3114
|
|
3005
3115
|
class DeviceSignalInputBase(RPCBase):
|
3006
3116
|
@property
|
@@ -3019,6 +3129,13 @@ class DeviceSignalInputBase(RPCBase):
|
|
3019
3129
|
Get all registered RPC objects.
|
3020
3130
|
"""
|
3021
3131
|
|
3132
|
+
@property
|
3133
|
+
@rpc_call
|
3134
|
+
def _rpc_id(self) -> "str":
|
3135
|
+
"""
|
3136
|
+
Get the RPC ID of the widget.
|
3137
|
+
"""
|
3138
|
+
|
3022
3139
|
|
3023
3140
|
class LMFitDialog(RPCBase):
|
3024
3141
|
@property
|
@@ -3037,6 +3154,13 @@ class LMFitDialog(RPCBase):
|
|
3037
3154
|
Get all registered RPC objects.
|
3038
3155
|
"""
|
3039
3156
|
|
3157
|
+
@property
|
3158
|
+
@rpc_call
|
3159
|
+
def _rpc_id(self) -> "str":
|
3160
|
+
"""
|
3161
|
+
Get the RPC ID of the widget.
|
3162
|
+
"""
|
3163
|
+
|
3040
3164
|
|
3041
3165
|
class PositionIndicator(RPCBase):
|
3042
3166
|
@rpc_call
|
@@ -3126,6 +3250,13 @@ class ResetButton(RPCBase):
|
|
3126
3250
|
Get all registered RPC objects.
|
3127
3251
|
"""
|
3128
3252
|
|
3253
|
+
@property
|
3254
|
+
@rpc_call
|
3255
|
+
def _rpc_id(self) -> "str":
|
3256
|
+
"""
|
3257
|
+
Get the RPC ID of the widget.
|
3258
|
+
"""
|
3259
|
+
|
3129
3260
|
|
3130
3261
|
class ResumeButton(RPCBase):
|
3131
3262
|
@property
|
@@ -3144,6 +3275,13 @@ class ResumeButton(RPCBase):
|
|
3144
3275
|
Get all registered RPC objects.
|
3145
3276
|
"""
|
3146
3277
|
|
3278
|
+
@property
|
3279
|
+
@rpc_call
|
3280
|
+
def _rpc_id(self) -> "str":
|
3281
|
+
"""
|
3282
|
+
Get the RPC ID of the widget.
|
3283
|
+
"""
|
3284
|
+
|
3147
3285
|
|
3148
3286
|
class Ring(RPCBase):
|
3149
3287
|
@rpc_call
|
@@ -3441,6 +3579,13 @@ class ScanControl(RPCBase):
|
|
3441
3579
|
Get all registered RPC objects.
|
3442
3580
|
"""
|
3443
3581
|
|
3582
|
+
@property
|
3583
|
+
@rpc_call
|
3584
|
+
def _rpc_id(self) -> "str":
|
3585
|
+
"""
|
3586
|
+
Get the RPC ID of the widget.
|
3587
|
+
"""
|
3588
|
+
|
3444
3589
|
|
3445
3590
|
class SignalComboBox(RPCBase):
|
3446
3591
|
@property
|
@@ -3459,6 +3604,13 @@ class SignalComboBox(RPCBase):
|
|
3459
3604
|
Get all registered RPC objects.
|
3460
3605
|
"""
|
3461
3606
|
|
3607
|
+
@property
|
3608
|
+
@rpc_call
|
3609
|
+
def _rpc_id(self) -> "str":
|
3610
|
+
"""
|
3611
|
+
Get the RPC ID of the widget.
|
3612
|
+
"""
|
3613
|
+
|
3462
3614
|
|
3463
3615
|
class SignalLineEdit(RPCBase):
|
3464
3616
|
@property
|
@@ -3477,6 +3629,13 @@ class SignalLineEdit(RPCBase):
|
|
3477
3629
|
Get all registered RPC objects.
|
3478
3630
|
"""
|
3479
3631
|
|
3632
|
+
@property
|
3633
|
+
@rpc_call
|
3634
|
+
def _rpc_id(self) -> "str":
|
3635
|
+
"""
|
3636
|
+
Get the RPC ID of the widget.
|
3637
|
+
"""
|
3638
|
+
|
3480
3639
|
|
3481
3640
|
class StopButton(RPCBase):
|
3482
3641
|
@property
|
@@ -3495,6 +3654,13 @@ class StopButton(RPCBase):
|
|
3495
3654
|
Get all registered RPC objects.
|
3496
3655
|
"""
|
3497
3656
|
|
3657
|
+
@property
|
3658
|
+
@rpc_call
|
3659
|
+
def _rpc_id(self) -> "str":
|
3660
|
+
"""
|
3661
|
+
Get the RPC ID of the widget.
|
3662
|
+
"""
|
3663
|
+
|
3498
3664
|
|
3499
3665
|
class TextBox(RPCBase):
|
3500
3666
|
@rpc_call
|
bec_widgets/cli/client_utils.py
CHANGED
@@ -92,11 +92,11 @@ def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger
|
|
92
92
|
process will not be captured.
|
93
93
|
"""
|
94
94
|
# pylint: disable=subprocess-run-check
|
95
|
-
command = ["bec-gui-server", "--id", gui_id, "--gui_class", gui_class.__name__]
|
95
|
+
command = ["bec-gui-server", "--id", gui_id, "--gui_class", gui_class.__name__, "--hide"]
|
96
96
|
if config:
|
97
97
|
if isinstance(config, dict):
|
98
98
|
config = json.dumps(config)
|
99
|
-
command.extend(["--config", config])
|
99
|
+
command.extend(["--config", str(config)])
|
100
100
|
|
101
101
|
env_dict = os.environ.copy()
|
102
102
|
env_dict["PYTHONUNBUFFERED"] = "1"
|
@@ -126,15 +126,36 @@ def _start_plot_process(gui_id: str, gui_class: type, config: dict | str, logger
|
|
126
126
|
return process, process_output_processing_thread
|
127
127
|
|
128
128
|
|
129
|
+
class RepeatTimer(threading.Timer):
|
130
|
+
def run(self):
|
131
|
+
while not self.finished.wait(self.interval):
|
132
|
+
self.function(*self.args, **self.kwargs)
|
133
|
+
|
134
|
+
|
129
135
|
class BECGuiClientMixin:
|
130
136
|
def __init__(self, **kwargs) -> None:
|
131
137
|
super().__init__(**kwargs)
|
138
|
+
self._auto_updates_enabled = True
|
139
|
+
self._auto_updates = None
|
140
|
+
self._gui_started_timer = None
|
141
|
+
self._gui_started_event = threading.Event()
|
132
142
|
self._process = None
|
133
143
|
self._process_output_processing_thread = None
|
134
|
-
self.auto_updates = self._get_update_script()
|
135
144
|
self._target_endpoint = MessageEndpoints.scan_status()
|
136
145
|
self._selected_device = None
|
137
146
|
|
147
|
+
@property
|
148
|
+
def auto_updates(self):
|
149
|
+
if self._auto_updates_enabled:
|
150
|
+
self._gui_started_event.wait()
|
151
|
+
return self._auto_updates
|
152
|
+
|
153
|
+
def shutdown_auto_updates(self):
|
154
|
+
if self._auto_updates_enabled:
|
155
|
+
if self._auto_updates is not None:
|
156
|
+
self._auto_updates.shutdown()
|
157
|
+
self._auto_updates = None
|
158
|
+
|
138
159
|
def _get_update_script(self) -> AutoUpdates | None:
|
139
160
|
eps = imd.entry_points(group="bec.widgets.auto_updates")
|
140
161
|
for ep in eps:
|
@@ -180,38 +201,80 @@ class BECGuiClientMixin:
|
|
180
201
|
if isinstance(msg, messages.ScanStatusMessage):
|
181
202
|
if not self.gui_is_alive():
|
182
203
|
return
|
183
|
-
self.
|
184
|
-
|
185
|
-
|
204
|
+
if self._auto_updates_enabled:
|
205
|
+
self.auto_updates.msg_queue.put(msg)
|
206
|
+
|
207
|
+
def _gui_post_startup(self):
|
208
|
+
if self._auto_updates_enabled:
|
209
|
+
if self._auto_updates is None:
|
210
|
+
auto_updates = self._get_update_script()
|
211
|
+
if auto_updates is None:
|
212
|
+
AutoUpdates.create_default_dock = True
|
213
|
+
AutoUpdates.enabled = True
|
214
|
+
auto_updates = AutoUpdates(gui=self)
|
215
|
+
if auto_updates.create_default_dock:
|
216
|
+
auto_updates.start_default_dock()
|
217
|
+
# fig = auto_updates.get_default_figure()
|
218
|
+
self._auto_updates = auto_updates
|
219
|
+
self._gui_started_event.set()
|
220
|
+
self.show_all()
|
221
|
+
|
222
|
+
def start_server(self, wait=False) -> None:
|
186
223
|
"""
|
187
|
-
|
224
|
+
Start the GUI server, and execute callback when it is launched
|
188
225
|
"""
|
189
226
|
if self._process is None or self._process.poll() is not None:
|
227
|
+
logger.success("GUI starting...")
|
228
|
+
self._gui_started_event.clear()
|
190
229
|
self._start_update_script()
|
191
230
|
self._process, self._process_output_processing_thread = _start_plot_process(
|
192
231
|
self._gui_id, self.__class__, self._client._service_config.config, logger=logger
|
193
232
|
)
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
233
|
+
|
234
|
+
def gui_started_callback(callback):
|
235
|
+
try:
|
236
|
+
if callable(callback):
|
237
|
+
callback()
|
238
|
+
finally:
|
239
|
+
threading.current_thread().cancel()
|
240
|
+
|
241
|
+
self._gui_started_timer = RepeatTimer(
|
242
|
+
1, lambda: self.gui_is_alive() and gui_started_callback(self._gui_post_startup)
|
243
|
+
)
|
244
|
+
self._gui_started_timer.start()
|
245
|
+
|
246
|
+
if wait:
|
247
|
+
self._gui_started_event.wait()
|
248
|
+
|
249
|
+
def show_all(self):
|
250
|
+
self._gui_started_event.wait()
|
251
|
+
rpc_client = RPCBase(gui_id=f"{self._gui_id}:window", parent=self)
|
252
|
+
rpc_client._run_rpc("show")
|
253
|
+
|
254
|
+
def hide_all(self):
|
255
|
+
self._gui_started_event.wait()
|
256
|
+
rpc_client = RPCBase(gui_id=f"{self._gui_id}:window", parent=self)
|
257
|
+
rpc_client._run_rpc("hide")
|
198
258
|
|
199
259
|
def close(self) -> None:
|
200
260
|
"""
|
201
261
|
Close the gui window.
|
202
262
|
"""
|
263
|
+
if self._gui_started_timer is not None:
|
264
|
+
self._gui_started_timer.cancel()
|
265
|
+
self._gui_started_timer.join()
|
266
|
+
|
203
267
|
if self._process is None:
|
204
268
|
return
|
205
269
|
|
206
|
-
self._client.shutdown()
|
207
270
|
if self._process:
|
271
|
+
logger.success("Stopping GUI...")
|
208
272
|
self._process.terminate()
|
209
273
|
if self._process_output_processing_thread:
|
210
274
|
self._process_output_processing_thread.join()
|
211
275
|
self._process.wait()
|
212
276
|
self._process = None
|
213
|
-
|
214
|
-
self.auto_updates.shutdown()
|
277
|
+
self.shutdown_auto_updates()
|
215
278
|
|
216
279
|
|
217
280
|
class RPCResponseTimeoutError(Exception):
|
bec_widgets/cli/server.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import inspect
|
4
3
|
import json
|
5
4
|
import signal
|
6
5
|
import sys
|
@@ -11,13 +10,14 @@ from bec_lib.endpoints import MessageEndpoints
|
|
11
10
|
from bec_lib.logger import bec_logger
|
12
11
|
from bec_lib.service_config import ServiceConfig
|
13
12
|
from bec_lib.utils.import_utils import lazy_import
|
14
|
-
from qtpy.QtCore import QTimer
|
13
|
+
from qtpy.QtCore import Qt, QTimer
|
15
14
|
|
16
15
|
from bec_widgets.cli.rpc_register import RPCRegister
|
17
16
|
from bec_widgets.utils import BECDispatcher
|
18
17
|
from bec_widgets.utils.bec_connector import BECConnector
|
19
18
|
from bec_widgets.widgets.containers.dock import BECDockArea
|
20
19
|
from bec_widgets.widgets.containers.figure import BECFigure
|
20
|
+
from bec_widgets.widgets.containers.main_window.main_window import BECMainWindow
|
21
21
|
|
22
22
|
messages = lazy_import("bec_lib.messages")
|
23
23
|
logger = bec_logger.logger
|
@@ -95,11 +95,7 @@ class BECWidgetsCLIServer:
|
|
95
95
|
setattr(obj, method, args[0])
|
96
96
|
res = None
|
97
97
|
else:
|
98
|
-
|
99
|
-
if sig.parameters:
|
100
|
-
res = method_obj(*args, **kwargs)
|
101
|
-
else:
|
102
|
-
res = method_obj()
|
98
|
+
res = method_obj(*args, **kwargs)
|
103
99
|
|
104
100
|
if isinstance(res, list):
|
105
101
|
res = [self.serialize_object(obj) for obj in res]
|
@@ -181,7 +177,7 @@ def main():
|
|
181
177
|
|
182
178
|
from qtpy.QtCore import QSize
|
183
179
|
from qtpy.QtGui import QIcon
|
184
|
-
from qtpy.QtWidgets import QApplication
|
180
|
+
from qtpy.QtWidgets import QApplication
|
185
181
|
|
186
182
|
import bec_widgets
|
187
183
|
|
@@ -199,6 +195,7 @@ def main():
|
|
199
195
|
help="Name of the gui class to be rendered. Possible values: \n- BECFigure\n- BECDockArea",
|
200
196
|
)
|
201
197
|
parser.add_argument("--config", type=str, help="Config file or config string.")
|
198
|
+
parser.add_argument("--hide", action="store_true", help="Hide on startup")
|
202
199
|
|
203
200
|
args = parser.parse_args()
|
204
201
|
|
@@ -211,11 +208,12 @@ def main():
|
|
211
208
|
"Please specify a valid gui_class to run. Use -h for help."
|
212
209
|
"\n Starting with default gui_class BECFigure."
|
213
210
|
)
|
214
|
-
gui_class =
|
211
|
+
gui_class = BECDockArea
|
215
212
|
|
216
213
|
with redirect_stdout(SimpleFileLikeFromLogOutputFunc(logger.info)):
|
217
214
|
with redirect_stderr(SimpleFileLikeFromLogOutputFunc(logger.error)):
|
218
215
|
app = QApplication(sys.argv)
|
216
|
+
app.setQuitOnLastWindowClosed(False)
|
219
217
|
app.setApplicationName("BEC Figure")
|
220
218
|
module_path = os.path.dirname(bec_widgets.__file__)
|
221
219
|
icon = QIcon()
|
@@ -225,15 +223,19 @@ def main():
|
|
225
223
|
)
|
226
224
|
app.setWindowIcon(icon)
|
227
225
|
|
228
|
-
|
226
|
+
server = _start_server(args.id, gui_class, args.config)
|
227
|
+
|
228
|
+
win = BECMainWindow(gui_id=f"{server.gui_id}:window")
|
229
|
+
win.setAttribute(Qt.WA_ShowWithoutActivating)
|
229
230
|
win.setWindowTitle("BEC Widgets")
|
230
231
|
|
231
|
-
|
232
|
+
RPCRegister().add_rpc(win)
|
232
233
|
|
233
234
|
gui = server.gui
|
234
235
|
win.setCentralWidget(gui)
|
235
236
|
win.resize(800, 600)
|
236
|
-
|
237
|
+
if not args.hide:
|
238
|
+
win.show()
|
237
239
|
|
238
240
|
app.aboutToQuit.connect(server.shutdown)
|
239
241
|
|
@@ -69,7 +69,7 @@ class Worker(QRunnable):
|
|
69
69
|
class BECConnector:
|
70
70
|
"""Connection mixin class to handle BEC client and device manager"""
|
71
71
|
|
72
|
-
USER_ACCESS = ["_config_dict", "_get_all_rpc"]
|
72
|
+
USER_ACCESS = ["_config_dict", "_get_all_rpc", "_rpc_id"]
|
73
73
|
EXIT_HANDLERS = {}
|
74
74
|
|
75
75
|
def __init__(self, client=None, config: ConnectionConfig = None, gui_id: str = None):
|
@@ -53,6 +53,8 @@ class BECDockArea(BECWidget, QWidget):
|
|
53
53
|
"attach_all",
|
54
54
|
"_get_all_rpc",
|
55
55
|
"temp_areas",
|
56
|
+
"show",
|
57
|
+
"hide",
|
56
58
|
]
|
57
59
|
|
58
60
|
def __init__(
|
@@ -412,6 +414,18 @@ class BECDockArea(BECWidget, QWidget):
|
|
412
414
|
self.cleanup()
|
413
415
|
super().close()
|
414
416
|
|
417
|
+
def show(self):
|
418
|
+
"""Show all windows including floating docks."""
|
419
|
+
super().show()
|
420
|
+
for docks in self.panels.values():
|
421
|
+
docks.window().show()
|
422
|
+
|
423
|
+
def hide(self):
|
424
|
+
"""Hide all windows including floating docks."""
|
425
|
+
super().hide()
|
426
|
+
for docks in self.panels.values():
|
427
|
+
docks.window().hide()
|
428
|
+
|
415
429
|
|
416
430
|
if __name__ == "__main__":
|
417
431
|
from qtpy.QtWidgets import QApplication
|
File without changes
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from qtpy.QtWidgets import QMainWindow
|
2
|
+
|
3
|
+
from bec_widgets.utils import BECConnector
|
4
|
+
|
5
|
+
|
6
|
+
class BECMainWindow(QMainWindow, BECConnector):
|
7
|
+
def __init__(self, *args, **kwargs):
|
8
|
+
BECConnector.__init__(self, **kwargs)
|
9
|
+
QMainWindow.__init__(self, *args, **kwargs)
|
@@ -10,9 +10,13 @@ import fcntl
|
|
10
10
|
import html
|
11
11
|
import os
|
12
12
|
import pty
|
13
|
+
import re
|
14
|
+
import signal
|
13
15
|
import sys
|
16
|
+
import time
|
14
17
|
|
15
18
|
import pyte
|
19
|
+
from pygments.token import Token
|
16
20
|
from pyte.screens import History
|
17
21
|
from qtpy import QtCore, QtGui, QtWidgets
|
18
22
|
from qtpy.QtCore import Property as pyqtProperty
|
@@ -235,10 +239,14 @@ class BECConsole(QtWidgets.QWidget):
|
|
235
239
|
PLUGIN = True
|
236
240
|
ICON_NAME = "terminal"
|
237
241
|
|
242
|
+
prompt = pyqtSignal(bool)
|
243
|
+
|
238
244
|
def __init__(self, parent=None, cols=132):
|
239
245
|
super().__init__(parent)
|
240
246
|
|
241
247
|
self.term = _TerminalWidget(self, cols, rows=43)
|
248
|
+
self.term.prompt.connect(self.prompt) # forward signal from term to this widget
|
249
|
+
|
242
250
|
self.scroll_bar = QScrollBar(Qt.Vertical, self)
|
243
251
|
# self.scroll_bar.hide()
|
244
252
|
layout = QHBoxLayout(self)
|
@@ -320,9 +328,38 @@ class BECConsole(QtWidgets.QWidget):
|
|
320
328
|
def start(self, deactivate_ctrl_d=True):
|
321
329
|
self.term.start(deactivate_ctrl_d=deactivate_ctrl_d)
|
322
330
|
|
323
|
-
def push(self, text):
|
331
|
+
def push(self, text, hit_return=False):
|
324
332
|
"""Push some text to the terminal"""
|
325
|
-
return self.term.push(text)
|
333
|
+
return self.term.push(text, hit_return=hit_return)
|
334
|
+
|
335
|
+
def execute_command(self, command):
|
336
|
+
self.push(command, hit_return=True)
|
337
|
+
|
338
|
+
def set_prompt_tokens(self, *tokens):
|
339
|
+
"""Prepare regexp to identify prompt, based on tokens
|
340
|
+
|
341
|
+
Tokens are returned from get_ipython().prompts.in_prompt_tokens()
|
342
|
+
"""
|
343
|
+
regex_parts = []
|
344
|
+
for token_type, token_value in tokens:
|
345
|
+
if token_type == Token.PromptNum: # Handle dynamic prompt number
|
346
|
+
regex_parts.append(r"[\d\?]+") # Match one or more digits or '?'
|
347
|
+
else:
|
348
|
+
# Escape other prompt parts (e.g., "In [", "]: ")
|
349
|
+
if not token_value:
|
350
|
+
regex_parts.append(".+?") # arbitrary string
|
351
|
+
else:
|
352
|
+
regex_parts.append(re.escape(token_value))
|
353
|
+
|
354
|
+
# Combine into a single regex
|
355
|
+
prompt_pattern = "".join(regex_parts)
|
356
|
+
self.term._prompt_re = re.compile(prompt_pattern + r"\s*$")
|
357
|
+
|
358
|
+
def terminate(self, timeout=10):
|
359
|
+
self.term.stop(timeout=timeout)
|
360
|
+
|
361
|
+
def send_ctrl_c(self, timeout=None):
|
362
|
+
self.term.send_ctrl_c(timeout)
|
326
363
|
|
327
364
|
cols = pyqtProperty(int, get_cols, set_cols)
|
328
365
|
rows = pyqtProperty(int, get_rows, set_rows)
|
@@ -336,7 +373,15 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
|
336
373
|
Start ``Backend`` process and render Pyte output as text.
|
337
374
|
"""
|
338
375
|
|
376
|
+
prompt = pyqtSignal(bool)
|
377
|
+
|
339
378
|
def __init__(self, parent, cols=125, rows=50, **kwargs):
|
379
|
+
# regexp to match prompt
|
380
|
+
self._prompt_re = None
|
381
|
+
# last prompt
|
382
|
+
self._prompt_str = None
|
383
|
+
# process pid
|
384
|
+
self.pid = None
|
340
385
|
# file descriptor to communicate with the subprocess
|
341
386
|
self.fd = None
|
342
387
|
self.backend = None
|
@@ -433,7 +478,7 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
|
433
478
|
self.update_term_size()
|
434
479
|
|
435
480
|
# Start the Bash process
|
436
|
-
self.fd = self.fork_shell()
|
481
|
+
self.pid, self.fd = self.fork_shell()
|
437
482
|
|
438
483
|
if self.fd:
|
439
484
|
# Create the ``Backend`` object
|
@@ -449,6 +494,62 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
|
449
494
|
self.appendHtml(f"<br><h2>{repr(self._cmd)} - Process exited.</h2>")
|
450
495
|
self.setReadOnly(True)
|
451
496
|
|
497
|
+
def send_ctrl_c(self, wait_prompt=True, timeout=None):
|
498
|
+
"""Send CTRL-C to the process
|
499
|
+
|
500
|
+
If wait_prompt=True (default), wait for a new prompt after CTRL-C
|
501
|
+
If no prompt is displayed after 'timeout' seconds, TimeoutError is raised
|
502
|
+
"""
|
503
|
+
os.kill(self.pid, signal.SIGINT)
|
504
|
+
if wait_prompt:
|
505
|
+
timeout_error = False
|
506
|
+
if timeout:
|
507
|
+
|
508
|
+
def set_timeout_error():
|
509
|
+
nonlocal timeout_error
|
510
|
+
timeout_error = True
|
511
|
+
|
512
|
+
timeout_timer = QTimer()
|
513
|
+
timeout_timer.singleShot(timeout * 1000, set_timeout_error)
|
514
|
+
while self._prompt_str is None:
|
515
|
+
QApplication.instance().process_events()
|
516
|
+
if timeout_error:
|
517
|
+
raise TimeoutError(
|
518
|
+
f"CTRL-C: could not get back to prompt after {timeout} seconds."
|
519
|
+
)
|
520
|
+
|
521
|
+
def _is_running(self):
|
522
|
+
if os.waitpid(self.pid, os.WNOHANG) == (0, 0):
|
523
|
+
return True
|
524
|
+
return False
|
525
|
+
|
526
|
+
def stop(self, kill=True, timeout=None):
|
527
|
+
"""Stop the running process
|
528
|
+
|
529
|
+
SIGTERM is the default signal for terminating processes.
|
530
|
+
|
531
|
+
If kill=True (default), SIGKILL will be sent if the process does not exit after timeout
|
532
|
+
"""
|
533
|
+
# try to exit gracefully
|
534
|
+
os.kill(self.pid, signal.SIGTERM)
|
535
|
+
|
536
|
+
# wait until process is truly dead
|
537
|
+
t0 = time.perf_counter()
|
538
|
+
while self._is_running():
|
539
|
+
time.sleep(1)
|
540
|
+
if timeout is not None and time.perf_counter() - t0 > timeout:
|
541
|
+
# still alive after 'timeout' seconds
|
542
|
+
if kill:
|
543
|
+
# send SIGKILL and make a last check in loop
|
544
|
+
os.kill(self.pid, signal.SIGKILL)
|
545
|
+
kill = False
|
546
|
+
else:
|
547
|
+
# still running after timeout...
|
548
|
+
raise TimeoutError(
|
549
|
+
f"Could not terminate process with pid: {self.pid} within timeout"
|
550
|
+
)
|
551
|
+
self.process_exited()
|
552
|
+
|
452
553
|
def data_ready(self, screen):
|
453
554
|
"""Handle new screen: redraw, set scroll bar max and slider, move cursor to its position
|
454
555
|
|
@@ -540,11 +641,13 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
|
540
641
|
elif code is not None:
|
541
642
|
self.write(code)
|
542
643
|
|
543
|
-
def push(self, text):
|
644
|
+
def push(self, text, hit_return=False):
|
544
645
|
"""
|
545
646
|
Write 'text' to terminal
|
546
647
|
"""
|
547
648
|
self.write(text.encode("utf-8"))
|
649
|
+
if hit_return:
|
650
|
+
self.write(b"\n")
|
548
651
|
|
549
652
|
def contextMenuEvent(self, event):
|
550
653
|
if self.fd is None:
|
@@ -650,6 +753,20 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
|
650
753
|
self.output[line_no] = line
|
651
754
|
# fill the text area with HTML contents in one go
|
652
755
|
self.appendHtml(f"<pre>{chr(10).join(self.output)}</pre>")
|
756
|
+
|
757
|
+
if self._prompt_re is not None:
|
758
|
+
text_buf = self.toPlainText()
|
759
|
+
prompt = self._prompt_re.search(text_buf)
|
760
|
+
if prompt is None:
|
761
|
+
if self._prompt_str:
|
762
|
+
self.prompt.emit(False)
|
763
|
+
self._prompt_str = None
|
764
|
+
else:
|
765
|
+
prompt_str = prompt.string.rstrip()
|
766
|
+
if prompt_str != self._prompt_str:
|
767
|
+
self._prompt_str = prompt_str
|
768
|
+
self.prompt.emit(True)
|
769
|
+
|
653
770
|
# did updates, all clean
|
654
771
|
screen.dirty.clear()
|
655
772
|
|
@@ -711,7 +828,7 @@ class _TerminalWidget(QtWidgets.QPlainTextEdit):
|
|
711
828
|
# We are in the parent process.
|
712
829
|
# Set file control
|
713
830
|
fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
|
714
|
-
return fd
|
831
|
+
return pid, fd
|
715
832
|
|
716
833
|
|
717
834
|
if __name__ == "__main__":
|
@@ -728,6 +845,24 @@ if __name__ == "__main__":
|
|
728
845
|
|
729
846
|
console = BECConsole(mainwin)
|
730
847
|
mainwin.setCentralWidget(console)
|
848
|
+
|
849
|
+
def check_prompt(at_prompt):
|
850
|
+
if at_prompt:
|
851
|
+
print("NEW PROMPT")
|
852
|
+
else:
|
853
|
+
print("EXECUTING SOMETHING...")
|
854
|
+
|
855
|
+
console.set_prompt_tokens(
|
856
|
+
(Token.OutPromptNum, "•"),
|
857
|
+
(Token.Prompt, ""), # will match arbitrary string,
|
858
|
+
(Token.Prompt, " ["),
|
859
|
+
(Token.PromptNum, "3"),
|
860
|
+
(Token.Prompt, "/"),
|
861
|
+
(Token.PromptNum, "1"),
|
862
|
+
(Token.Prompt, "] "),
|
863
|
+
(Token.Prompt, "❯❯"),
|
864
|
+
)
|
865
|
+
console.prompt.connect(check_prompt)
|
731
866
|
console.start()
|
732
867
|
|
733
868
|
# Show widget and launch Qt's event loop.
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
|
5
5
|
from bec_lib.logger import bec_logger
|
6
6
|
from qtpy.QtCore import QMimeData, Qt
|
7
7
|
from qtpy.QtGui import QDrag
|
8
|
-
from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget
|
8
|
+
from qtpy.QtWidgets import QApplication, QHBoxLayout, QLabel, QWidget
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from qtpy.QtGui import QMouseEvent
|
@@ -16,6 +16,9 @@ logger = bec_logger.logger
|
|
16
16
|
class DeviceItem(QWidget):
|
17
17
|
def __init__(self, device: str) -> None:
|
18
18
|
super().__init__()
|
19
|
+
|
20
|
+
self._drag_pos = None
|
21
|
+
|
19
22
|
self.device = device
|
20
23
|
layout = QHBoxLayout()
|
21
24
|
layout.setContentsMargins(10, 2, 10, 2)
|
@@ -32,12 +35,21 @@ class DeviceItem(QWidget):
|
|
32
35
|
)
|
33
36
|
|
34
37
|
def mousePressEvent(self, event: QMouseEvent) -> None:
|
38
|
+
super().mousePressEvent(event)
|
35
39
|
if event.button() == Qt.LeftButton:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
self._drag_pos = event.pos()
|
41
|
+
|
42
|
+
def mouseMoveEvent(self, event: QMouseEvent) -> None:
|
43
|
+
if not (event.buttons() and Qt.LeftButton):
|
44
|
+
return
|
45
|
+
if (event.pos() - self._drag_pos).manhattanLength() < QApplication.startDragDistance():
|
46
|
+
return
|
47
|
+
|
48
|
+
drag = QDrag(self)
|
49
|
+
mime_data = QMimeData()
|
50
|
+
mime_data.setText(self.device)
|
51
|
+
drag.setMimeData(mime_data)
|
52
|
+
drag.exec_(Qt.MoveAction)
|
41
53
|
|
42
54
|
def mouseDoubleClickEvent(self, event: QMouseEvent) -> None:
|
43
55
|
logger.debug("Double Clicked")
|
@@ -2,11 +2,11 @@
|
|
2
2
|
.gitlab-ci.yml,sha256=bAWGX_NR9rQZmv_bmyLXkEMRreWp0JzVNpsNTxk0NwE,8637
|
3
3
|
.pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
|
4
4
|
.readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
|
5
|
-
CHANGELOG.md,sha256
|
5
|
+
CHANGELOG.md,sha256=-XyIDJ1C204NhjKieHBciyZVtpEQnlqAZgusnhdxU-k,7859
|
6
6
|
LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
7
|
-
PKG-INFO,sha256=
|
7
|
+
PKG-INFO,sha256=0rkaKmhcyUiRQJnmMXE8GgO0BMm8JMEcVGpGzkTwdOc,1308
|
8
8
|
README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
|
9
|
-
pyproject.toml,sha256=
|
9
|
+
pyproject.toml,sha256=MH0RsHdGV1exWgR_0BWw67wwFgluGLni_d2Qdn3HlLk,2586
|
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
|
@@ -24,12 +24,12 @@ bec_widgets/assets/app_icons/alignment_1d.png,sha256=5VouaWieb4lVv3wUBNHaO5ovUW2
|
|
24
24
|
bec_widgets/assets/app_icons/bec_widgets_icon.png,sha256=K8dgGwIjalDh9PRHUsSQBqgdX7a00nM3igZdc20pkYM,1747017
|
25
25
|
bec_widgets/cli/__init__.py,sha256=d0Q6Fn44e7wFfLabDOBxpcJ1DPKWlFunGYDUBmO-4hA,22
|
26
26
|
bec_widgets/cli/auto_updates.py,sha256=DwzRChcFIWPH2kCYvp8H7dXvyYSKGYv6LwCmK2sDR2E,5676
|
27
|
-
bec_widgets/cli/client.py,sha256=
|
28
|
-
bec_widgets/cli/client_utils.py,sha256=
|
27
|
+
bec_widgets/cli/client.py,sha256=1XWSLn7CwqVkN9g3ELYTacjAsBaRPkGRboq9IvhDcpc,98538
|
28
|
+
bec_widgets/cli/client_utils.py,sha256=5qlMFOh-7J6CclEKMj0BD09cNA4XCp1d1Wj2Z5hPpnA,14106
|
29
29
|
bec_widgets/cli/generate_cli.py,sha256=YyYEPBpu5v9plCFQZHnyvEeFKoeHatpplPGil_D8DJM,6643
|
30
30
|
bec_widgets/cli/rpc_register.py,sha256=8s-YJxqYoKc2K7jRLvs0TjW6_OnhaRYCK00RIok_4qE,2252
|
31
31
|
bec_widgets/cli/rpc_wigdet_handler.py,sha256=C-HK8bXOGmbFb98ekMvfHYghU6mdjwMw6QVYBqVC_xk,1515
|
32
|
-
bec_widgets/cli/server.py,sha256=
|
32
|
+
bec_widgets/cli/server.py,sha256=rRrCcgy0VluiHycr1zL8EMf3siLN7gWxo6aMOZRzZQ8,9000
|
33
33
|
bec_widgets/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
34
|
bec_widgets/examples/general_app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
35
35
|
bec_widgets/examples/general_app/general_app.py,sha256=PoFCTuA_1yqrpgthASpYFgH7JDUZTcXAPZ5h0e3XZfc,3053
|
@@ -54,7 +54,7 @@ bec_widgets/qt_utils/toolbar.py,sha256=yR2WNPv7dD8jU12aHgUMAi5-FYyCKe2MNSsqMzsa5
|
|
54
54
|
bec_widgets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
bec_widgets/tests/utils.py,sha256=D1v3JLzzbnX3HXBQCjoFlNz5cYhjqrRkFcjx3yptMJA,6687
|
56
56
|
bec_widgets/utils/__init__.py,sha256=1930ji1Jj6dVuY81Wd2kYBhHYNV-2R0bN_L4o9zBj1U,533
|
57
|
-
bec_widgets/utils/bec_connector.py,sha256=
|
57
|
+
bec_widgets/utils/bec_connector.py,sha256=g40KPxhfuSHLlAa6dyO1PtC-9cQpN8ZGgn_IAEU_xD4,10070
|
58
58
|
bec_widgets/utils/bec_designer.py,sha256=Z3MeMju-KmTz8POtm23VQfp4rvtD2sF6eIOKQkl2F7w,4729
|
59
59
|
bec_widgets/utils/bec_dispatcher.py,sha256=OFmkx9vOz4pA4Sdc14QreyDZ870QYskJ4B5daVVeYg4,6325
|
60
60
|
bec_widgets/utils/bec_signal_proxy.py,sha256=PKJ7v8pKrAaqA9XNDMZZBlhVtEInX-ae6_0m2cQhiEw,2107
|
@@ -85,7 +85,7 @@ bec_widgets/widgets/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKV
|
|
85
85
|
bec_widgets/widgets/containers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
86
86
|
bec_widgets/widgets/containers/dock/__init__.py,sha256=B7foHt02gnhM7mFksa7GJVwT7n0j_JvYDCt6wc6XR5g,61
|
87
87
|
bec_widgets/widgets/containers/dock/dock.py,sha256=iQqPtjylG0LEmDSimogSD8Ks1WOr_JZMxLhyKjuhZmo,10375
|
88
|
-
bec_widgets/widgets/containers/dock/dock_area.py,sha256=
|
88
|
+
bec_widgets/widgets/containers/dock/dock_area.py,sha256=7IXfGiymWiHETxLHYbFoBsKd6L3_P48vyJO0fRShjlU,16705
|
89
89
|
bec_widgets/widgets/containers/dock/dock_area.pyproject,sha256=URW0UrDXCnkzk80rbQmUMgF6Uqay2TjHsq8Dq0g1j-c,37
|
90
90
|
bec_widgets/widgets/containers/dock/dock_area_plugin.py,sha256=fDVXKPZuHr85B2fLfAYf_Ic5d9mZQpnZrODTDquzZpM,1331
|
91
91
|
bec_widgets/widgets/containers/dock/register_dock_area.py,sha256=L7BL4qknCjtqsDP-RMQzk2qRPpcYuzXWlb7sJB_0DDM,475
|
@@ -106,6 +106,8 @@ bec_widgets/widgets/containers/figure/plots/multi_waveform/multi_waveform.py,sha
|
|
106
106
|
bec_widgets/widgets/containers/figure/plots/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
107
107
|
bec_widgets/widgets/containers/figure/plots/waveform/waveform.py,sha256=6j-3hg0tVtpCnDgbYObTYwiNI7ciuWgQ5L1TlAN0Kg8,57543
|
108
108
|
bec_widgets/widgets/containers/figure/plots/waveform/waveform_curve.py,sha256=9rOFHIxRjz0-G6f-mpw0FNmX846ZPwGn8yrJ3FpxlVc,8725
|
109
|
+
bec_widgets/widgets/containers/main_window/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
110
|
+
bec_widgets/widgets/containers/main_window/main_window.py,sha256=A2IAux-lqupMCVZOQu7AUt1AEaeIG3Eza85eN0bknlI,272
|
109
111
|
bec_widgets/widgets/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
110
112
|
bec_widgets/widgets/control/buttons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
111
113
|
bec_widgets/widgets/control/buttons/button_abort/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -195,7 +197,7 @@ bec_widgets/widgets/dap/lmfit_dialog/lmfit_dialog_vertical.ui,sha256=LQCQKQMcocd
|
|
195
197
|
bec_widgets/widgets/dap/lmfit_dialog/register_lm_fit_dialog.py,sha256=7tB1gsvv310_kVuKf2u4EdSR4F1posm7QCrWH5Kih-Q,480
|
196
198
|
bec_widgets/widgets/editors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
197
199
|
bec_widgets/widgets/editors/console/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
198
|
-
bec_widgets/widgets/editors/console/console.py,sha256=
|
200
|
+
bec_widgets/widgets/editors/console/console.py,sha256=RLzN-a2h27NLeQVjJiZRCtngRDPBmsGdouZ22bKbTUE,29327
|
199
201
|
bec_widgets/widgets/editors/console/console.pyproject,sha256=JcoDuZG03g1Bxkd3Aipo7jjLexujfbibIZqXHIgLSWc,26
|
200
202
|
bec_widgets/widgets/editors/console/console_plugin.py,sha256=EvFTruYDVHiS4pHIwZnuEvJhS9eQoktuB_k5mcPuEts,1357
|
201
203
|
bec_widgets/widgets/editors/console/register_console.py,sha256=zoF-i3R9sRGzb85sdoxVunebYOfOD53fkCELTPtrFRc,471
|
@@ -278,7 +280,7 @@ bec_widgets/widgets/services/device_browser/device_browser.ui,sha256=Dy_3oXArScP
|
|
278
280
|
bec_widgets/widgets/services/device_browser/device_browser_plugin.py,sha256=JS4uYQclT-h0jMTPZ_wZgi5MCert--gjmmBks_EmEoo,1299
|
279
281
|
bec_widgets/widgets/services/device_browser/register_device_browser.py,sha256=OrL6NwN47TMu-ib6vqtnQ3ogePMWVipG3IfEXyq2OqY,509
|
280
282
|
bec_widgets/widgets/services/device_browser/device_item/__init__.py,sha256=VGY-uNVCnpcY-q-gijteB2N8KxFNgYR-qQ209MVu1QI,36
|
281
|
-
bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=
|
283
|
+
bec_widgets/widgets/services/device_browser/device_item/device_item.py,sha256=u3CTgXJBqwNP3uxrIWYGacEuoOu1a_DxfXYZ3Q4sK_M,1901
|
282
284
|
bec_widgets/widgets/utility/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
283
285
|
bec_widgets/widgets/utility/spinner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
284
286
|
bec_widgets/widgets/utility/spinner/register_spinner_widget.py,sha256=96A13dEcyTgXfc9G0sTdlXYCDcVav8Z2P2eDC95bESQ,484
|
@@ -311,8 +313,8 @@ bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.py,sha256=Z
|
|
311
313
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button.pyproject,sha256=Lbi9zb6HNlIq14k6hlzR-oz6PIFShBuF7QxE6d87d64,34
|
312
314
|
bec_widgets/widgets/utility/visual/dark_mode_button/dark_mode_button_plugin.py,sha256=CzChz2SSETYsR8-36meqWnsXCT-FIy_J_xeU5coWDY8,1350
|
313
315
|
bec_widgets/widgets/utility/visual/dark_mode_button/register_dark_mode_button.py,sha256=rMpZ1CaoucwobgPj1FuKTnt07W82bV1GaSYdoqcdMb8,521
|
314
|
-
bec_widgets-1.
|
315
|
-
bec_widgets-1.
|
316
|
-
bec_widgets-1.
|
317
|
-
bec_widgets-1.
|
318
|
-
bec_widgets-1.
|
316
|
+
bec_widgets-1.7.0.dist-info/METADATA,sha256=0rkaKmhcyUiRQJnmMXE8GgO0BMm8JMEcVGpGzkTwdOc,1308
|
317
|
+
bec_widgets-1.7.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
318
|
+
bec_widgets-1.7.0.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
|
319
|
+
bec_widgets-1.7.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
|
320
|
+
bec_widgets-1.7.0.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|
File without changes
|