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 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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 1.5.3
3
+ Version: 1.7.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
bec_widgets/cli/client.py CHANGED
@@ -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
@@ -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.auto_updates.msg_queue.put(msg)
184
-
185
- def show(self) -> None:
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
- Show the figure.
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
- while not self.gui_is_alive():
195
- print("Waiting for GUI to start...")
196
- time.sleep(1)
197
- logger.success(f"GUI started with id: {self._gui_id}")
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
- if self.auto_updates is not None:
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
- sig = inspect.signature(method_obj)
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, QMainWindow
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 = BECFigure
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
- win = QMainWindow()
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
- server = _start_server(args.id, gui_class, args.config)
232
+ RPCRegister().add_rpc(win)
232
233
 
233
234
  gui = server.gui
234
235
  win.setCentralWidget(gui)
235
236
  win.resize(800, 600)
236
- win.show()
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
- drag = QDrag(self)
37
- mime_data = QMimeData()
38
- mime_data.setText(self.device)
39
- drag.setMimeData(mime_data)
40
- drag.exec_(Qt.MoveAction)
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")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 1.5.3
3
+ Version: 1.7.0
4
4
  Summary: BEC Widgets
5
5
  Project-URL: Bug Tracker, https://gitlab.psi.ch/bec/bec_widgets/issues
6
6
  Project-URL: Homepage, https://gitlab.psi.ch/bec/bec_widgets
@@ -2,11 +2,11 @@
2
2
  .gitlab-ci.yml,sha256=bAWGX_NR9rQZmv_bmyLXkEMRreWp0JzVNpsNTxk0NwE,8637
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=jYMKaIxITS78BZWfSsSTmO5pwgeMKQrlAv6LLvoxzFk,6871
5
+ CHANGELOG.md,sha256=-XyIDJ1C204NhjKieHBciyZVtpEQnlqAZgusnhdxU-k,7859
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=mR0DSHPSJ3kTo2xORIaMWAvuxqm_mNIGWsHSKAAN5TY,1308
7
+ PKG-INFO,sha256=0rkaKmhcyUiRQJnmMXE8GgO0BMm8JMEcVGpGzkTwdOc,1308
8
8
  README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
9
- pyproject.toml,sha256=T0PWJVVsbqLBCg4RiK0PqhUMk26vOCU_N7gNYUizbFU,2586
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=YEgfLPF6ZmRd9KU_M7NhlAGeL6jPm2hKRRZ9E2IMqj0,95013
28
- bec_widgets/cli/client_utils.py,sha256=EdDfo3uuYAWtZiDGGu3_GPnl94FSLkNG2N_4I9FNfMc,11809
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=Drnfqf1u_BavJ06a_KE80XLxUXyRa38qnKAgc8CoUqE,8783
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=I9M_4g-_-WaMmhyXzChFCGXAElelx0mG6E1g0KvtEQs,10059
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=jbTzKPhqHiVJvRBXcdvj5nI5VV8YS_-FrXj7rkLrdtc,16317
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=V_xT5q_yBvstGMBXnocD10MFXmum8l_82HfPD2tj_FE,24581
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=pnsLmxcsULrt2E-Afbnbx3_6dAx4MDjMT2r479eM2GE,1549
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.5.3.dist-info/METADATA,sha256=mR0DSHPSJ3kTo2xORIaMWAvuxqm_mNIGWsHSKAAN5TY,1308
315
- bec_widgets-1.5.3.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
316
- bec_widgets-1.5.3.dist-info/entry_points.txt,sha256=dItMzmwA1wizJ1Itx15qnfJ0ZzKVYFLVJ1voxT7K7D4,214
317
- bec_widgets-1.5.3.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
318
- bec_widgets-1.5.3.dist-info/RECORD,,
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_widgets"
7
- version = "1.5.3"
7
+ version = "1.7.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [