bec-widgets 0.102.0__py3-none-any.whl → 0.104.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.
.gitlab-ci.yml CHANGED
@@ -5,8 +5,12 @@ image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:3.11
5
5
  #commands to run in the Docker container before starting each job.
6
6
  variables:
7
7
  DOCKER_TLS_CERTDIR: ""
8
- BEC_CORE_BRANCH: "main"
9
- OPHYD_DEVICES_BRANCH: "main"
8
+ BEC_CORE_BRANCH:
9
+ description: bec branch
10
+ value: main
11
+ OPHYD_DEVICES_BRANCH:
12
+ description: ophyd_devices branch
13
+ value: main
10
14
  CHILD_PIPELINE_BRANCH: $CI_DEFAULT_BRANCH
11
15
 
12
16
  workflow:
CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.104.0 (2024-09-04)
4
+
5
+ ### Documentation
6
+
7
+ * docs(scan_control): docs extended ([`730e25f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/730e25fd3a8be156603005982bfd2a2c2b16dff1))
8
+
9
+ ### Feature
10
+
11
+ * feat(scan_control): scan control remember the previously set parameters and shares kwarg settings across scans ([`d28f9b0`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d28f9b04c45385179353cc247221ec821dcaa29b))
12
+
13
+ ### Fix
14
+
15
+ * fix(scan_control): SafeSlot applied to run_scan to avoid faulty scan requests ([`9047916`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/90479167fb5cae393c884e71a80fcfdb48a76427))
16
+
17
+ * fix(scan_control): scan parameters can be loaded from the last executed scan from redis ([`ec3bc8b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ec3bc8b5194c680b847d3306c41eef4638ccfcc7))
18
+
19
+ * fix(toggle): state can be determined with the widget initialisation ([`2cd9c7f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/2cd9c7f5854f158468e53b5b29ec31b1ff1e00e6))
20
+
21
+ ### Refactor
22
+
23
+ * refactor(scan_control): scan control layout adjusted ([`85dcbda`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/85dcbdaa88fe77aeea7012bfc16f10c4f873f75e))
24
+
25
+ * refactor(scan_control): basic pydantic config added ([`fe8dc55`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/fe8dc55eb102c51c34bf9606690914da53b5ac02))
26
+
27
+ ### Test
28
+
29
+ * test(scan_control): tests extended for getting kwargs between scan switching and getting parameters from redis ([`b07e677`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/b07e67715c9284e9bf36056ba4ba8068f60cbaf3))
30
+
31
+ * test(conftest): only run cleanup checks if test passed ([`26920f8`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/26920f8482bdb35ece46df37232af50ab9cab463))
32
+
33
+ ## v0.103.0 (2024-09-04)
34
+
35
+ ### Ci
36
+
37
+ * ci: prefill variables for manual pipeline start ([`158c19e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/158c19eda771562a325fd59405f9fd4cb9a17ed6))
38
+
39
+ ### Feature
40
+
41
+ * feat(vscode): open vscode on a free port ([`52da835`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/52da835803f2453096a8b7df23bee5fdf93ae2bb))
42
+
43
+ * feat(website): added method to wait until the webpage is loaded ([`9be19d4`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9be19d4abebad08c5fc6bea936dd97475fe8f628))
44
+
45
+ ### Fix
46
+
47
+ * fix(theme): fixed segfault for webengineview for auto updates ([`9866075`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9866075100577948755b563dc7b7dc4cdc60d040))
48
+
49
+ ### Test
50
+
51
+ * test(webview): fixed tests after refactoring ([`d5eb30c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/d5eb30cd7df4cb0dc3275dd362768afc211eaf2d))
52
+
53
+ * test(vscode): popen call does not have to be the only one ([`39f98ec`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/39f98ec223ba8b59e478ac788c08c59ffe886b4e))
54
+
3
55
  ## v0.102.0 (2024-09-04)
4
56
 
5
57
  ### Documentation
@@ -103,57 +155,3 @@
103
155
  ### Refactor
104
156
 
105
157
  * refactor(stop_button): stop button changed to QWidget and adapted for toolbar ([`097946f`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/097946fd688b8faf770e7cc0e689ea668206bc7a))
106
-
107
- * refactor: added hide option for device selection button ([`cdd1752`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/cdd175207e922904b2efbb2d9ecf7c556c617f2e))
108
-
109
- ## v0.99.9 (2024-08-28)
110
-
111
- ### Fix
112
-
113
- * fix: fixed build process and excluded docs and tests from tarballs and wheels ([`719254c`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/719254cf0a48e1fc4bd541edba239570778bcfea))
114
-
115
- ## v0.99.8 (2024-08-28)
116
-
117
- ### Fix
118
-
119
- * fix(website): fixed designer integration for website widget ([`5f37e86`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/5f37e862c95ac7173b6918ad39bcaef938dad698))
120
-
121
- ### Refactor
122
-
123
- * refactor(website): changed inheritance of website widget to simple qwidget; closes #325 ([`9925bbd`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/9925bbdb48b55eacbbce9fd6a1555a21b84221f9))
124
-
125
- ## v0.99.7 (2024-08-28)
126
-
127
- ### Fix
128
-
129
- * fix(toolbar): material icons can accept color as kwarg ([`ffc871e`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/ffc871ebbd3b68abc3e151bb8f5849e6c50e775e))
130
-
131
- ## v0.99.6 (2024-08-28)
132
-
133
- ### Documentation
134
-
135
- * docs: various bugs fixed ([`c31e9a3`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c31e9a3aff3ee8e984674dee0965ee7f1b6e2b8f))
136
-
137
- ### Fix
138
-
139
- * fix(toolbar): use of native qt separators ([`09c6c93`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/09c6c93c397ce4a21c293f6c79106c74b2db65ca))
140
-
141
- ## v0.99.5 (2024-08-28)
142
-
143
- ### Documentation
144
-
145
- * docs(index): index page is centered ([`02239de`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/02239de0a36fcd6cbf97990b0dec1ddf7ecf6ba6))
146
-
147
- ### Fix
148
-
149
- * fix(dock_area): dark button added ([`e6f204b`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/e6f204b6aa295747a68769f43af2e549149b401a))
150
-
151
- ## v0.99.4 (2024-08-28)
152
-
153
- ### Documentation
154
-
155
- * docs(buttons): added missing buttons docs ([`4e5520a`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/4e5520aee2115d2fc0cebb3865433478a5ec8253))
156
-
157
- ### Fix
158
-
159
- * fix(theme): apply theme to all pyqtgraph widgets on manual updates ([`c550186`](https://gitlab.psi.ch/bec/bec_widgets/-/commit/c5501860e8e07a53f4bce144d44ed39eda6290ef))
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.102.0
3
+ Version: 0.104.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
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import darkdetect
3
4
  from qtpy.QtCore import Slot
4
5
  from qtpy.QtWidgets import QApplication, QWidget
5
6
 
@@ -40,12 +41,17 @@ class BECWidget(BECConnector):
40
41
  """
41
42
  if not isinstance(self, QWidget):
42
43
  raise RuntimeError(f"{repr(self)} is not a subclass of QWidget")
43
- super().__init__(client, config, gui_id)
44
+ super().__init__(client=client, config=config, gui_id=gui_id)
44
45
 
45
46
  # Set the theme to auto if it is not set yet
46
47
  app = QApplication.instance()
47
48
  if not hasattr(app, "theme"):
48
- set_theme("auto")
49
+ # DO NOT SET THE THEME TO AUTO! Otherwise, the qwebengineview will segfault
50
+ # Instead, we will set the theme to the system setting on startup
51
+ if darkdetect.isDark():
52
+ set_theme("dark")
53
+ else:
54
+ set_theme("light")
49
55
 
50
56
  if theme_update:
51
57
  self._connect_to_theme_change()
@@ -1,21 +1,39 @@
1
+ from collections import defaultdict
2
+ from typing import Optional
3
+
1
4
  from bec_lib.endpoints import MessageEndpoints
5
+ from pydantic import BaseModel, Field
2
6
  from qtpy.QtCore import Property, Signal, Slot
3
7
  from qtpy.QtWidgets import (
4
8
  QApplication,
5
9
  QComboBox,
6
- QGridLayout,
7
10
  QGroupBox,
8
11
  QHBoxLayout,
12
+ QLabel,
9
13
  QPushButton,
10
14
  QSizePolicy,
11
15
  QVBoxLayout,
12
16
  QWidget,
13
17
  )
14
18
 
19
+ from bec_widgets.qt_utils.error_popups import SafeSlot
20
+ from bec_widgets.utils import ConnectionConfig
15
21
  from bec_widgets.utils.bec_widget import BECWidget
16
- from bec_widgets.utils.colors import apply_theme
17
22
  from bec_widgets.widgets.scan_control.scan_group_box import ScanGroupBox
18
23
  from bec_widgets.widgets.stop_button.stop_button import StopButton
24
+ from bec_widgets.widgets.toggle.toggle import ToggleSwitch
25
+
26
+
27
+ class ScanParameterConfig(BaseModel):
28
+ name: str
29
+ args: Optional[list] = Field(None)
30
+ kwargs: Optional[dict] = Field(None)
31
+
32
+
33
+ class ScanControlConfig(ConnectionConfig):
34
+ default_scan: Optional[str] = Field(None)
35
+ allowed_scans: Optional[list] = Field(None)
36
+ scans: Optional[dict[str, ScanParameterConfig]] = defaultdict(dict)
19
37
 
20
38
 
21
39
  class ScanControl(BECWidget, QWidget):
@@ -26,9 +44,20 @@ class ScanControl(BECWidget, QWidget):
26
44
  scan_selected = Signal(str)
27
45
 
28
46
  def __init__(
29
- self, parent=None, client=None, gui_id: str | None = None, allowed_scans: list | None = None
47
+ self,
48
+ parent=None,
49
+ client=None,
50
+ config: ScanControlConfig | dict | None = None,
51
+ gui_id: str | None = None,
52
+ allowed_scans: list | None = None,
53
+ default_scan: str | None = None,
30
54
  ):
31
- super().__init__(client=client, gui_id=gui_id)
55
+
56
+ if config is None:
57
+ config = ScanControlConfig(
58
+ widget_class=self.__class__.__name__, allowed_scans=allowed_scans
59
+ )
60
+ super().__init__(client=client, gui_id=gui_id, config=config)
32
61
  QWidget.__init__(self, parent=parent)
33
62
 
34
63
  # Client from BEC + shortcuts to device manager and scans
@@ -36,12 +65,16 @@ class ScanControl(BECWidget, QWidget):
36
65
 
37
66
  # Main layout
38
67
  self.layout = QVBoxLayout(self)
68
+ self.layout.setContentsMargins(5, 5, 5, 5)
39
69
  self.arg_box = None
40
70
  self.kwarg_boxes = []
41
71
  self.expert_mode = False # TODO implement in the future versions
72
+ self.previous_scan = None
73
+ self.last_scan_found = None
42
74
 
43
- # Scan list - allowed scans for the GUI
44
- self.allowed_scans = allowed_scans
75
+ # Widget Default Parameters
76
+ self.config.default_scan = default_scan
77
+ self.config.allowed_scans = allowed_scans
45
78
 
46
79
  # Create and set main layout
47
80
  self._init_UI()
@@ -56,7 +89,12 @@ class ScanControl(BECWidget, QWidget):
56
89
  self.scan_selection_group.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
57
90
  self.layout.addWidget(self.scan_selection_group)
58
91
 
92
+ # Default scan from config
93
+ if self.config.default_scan is not None:
94
+ self.comboBox_scan_selection.setCurrentText(self.config.default_scan)
95
+
59
96
  # Connect signals
97
+ self.comboBox_scan_selection.view().pressed.connect(self.save_current_scan_parameters)
60
98
  self.comboBox_scan_selection.currentIndexChanged.connect(self.on_scan_selection_changed)
61
99
  self.button_run_scan.clicked.connect(self.run_scan)
62
100
 
@@ -89,18 +127,35 @@ class ScanControl(BECWidget, QWidget):
89
127
  """
90
128
 
91
129
  scan_selection_group = QGroupBox("Scan Selection", self)
92
- self.scan_selection_layout = QGridLayout(scan_selection_group)
130
+ self.scan_selection_layout = QVBoxLayout(scan_selection_group)
93
131
  self.comboBox_scan_selection = QComboBox(scan_selection_group)
94
132
 
95
- # Run button
133
+ # Buttons
134
+ self.button_layout = QHBoxLayout()
135
+ ## Run button
96
136
  self.button_run_scan = QPushButton("Start", scan_selection_group)
97
137
  self.button_run_scan.setStyleSheet("background-color: #559900; color: white")
98
- # Stop button
138
+ ## Stop button
99
139
  self.button_stop_scan = StopButton(parent=scan_selection_group)
100
-
101
- self.scan_selection_layout.addWidget(self.comboBox_scan_selection, 0, 0, 1, 2)
102
- self.scan_selection_layout.addWidget(self.button_run_scan, 1, 0)
103
- self.scan_selection_layout.addWidget(self.button_stop_scan, 1, 1)
140
+ ## Add buttons to layout
141
+ self.button_layout.addWidget(self.button_run_scan)
142
+ self.button_layout.addWidget(self.button_stop_scan)
143
+
144
+ # Label to reload the last scan parameters
145
+ self.toggle_layout = QHBoxLayout()
146
+ ## Label
147
+ self.last_scan_label = QLabel("Restore last scan parameters", scan_selection_group)
148
+ ## Switch toggle button
149
+ self.toggle = ToggleSwitch(parent=scan_selection_group, checked=False)
150
+ self.toggle.enabled.connect(self.request_last_executed_scan_parameters)
151
+ ## Add label and switch to layout
152
+ self.toggle_layout.addWidget(self.last_scan_label)
153
+ self.toggle_layout.addWidget(self.toggle)
154
+
155
+ # Add widgets to layout
156
+ self.scan_selection_layout.addWidget(self.comboBox_scan_selection)
157
+ self.scan_selection_layout.addLayout(self.button_layout)
158
+ self.scan_selection_layout.addLayout(self.toggle_layout)
104
159
 
105
160
  return scan_selection_group
106
161
 
@@ -109,7 +164,7 @@ class ScanControl(BECWidget, QWidget):
109
164
  self.available_scans = self.client.connector.get(
110
165
  MessageEndpoints.available_scans()
111
166
  ).resource
112
- if self.allowed_scans is None:
167
+ if self.config.allowed_scans is None:
113
168
  supported_scans = ["ScanBase", "SyncFlyScanBase", "AsyncFlyScanBase"]
114
169
  allowed_scans = [
115
170
  scan_name
@@ -118,13 +173,50 @@ class ScanControl(BECWidget, QWidget):
118
173
  ]
119
174
 
120
175
  else:
121
- allowed_scans = self.allowed_scans
176
+ allowed_scans = self.config.allowed_scans
122
177
  self.comboBox_scan_selection.addItems(allowed_scans)
123
178
 
124
179
  def on_scan_selection_changed(self, index: int):
125
180
  """Callback for scan selection combo box"""
126
181
  selected_scan_name = self.comboBox_scan_selection.currentText()
127
182
  self.scan_selected.emit(selected_scan_name)
183
+ self.request_last_executed_scan_parameters()
184
+ self.restore_scan_parameters(selected_scan_name)
185
+
186
+ @Slot()
187
+ def request_last_executed_scan_parameters(self):
188
+ """
189
+ Requests the last executed scan parameters from BEC and restores them to the scan control widget.
190
+ """
191
+ enabled = self.toggle.checked
192
+ current_scan = self.comboBox_scan_selection.currentText()
193
+ if enabled:
194
+ history = self.client.connector.lrange(MessageEndpoints.scan_queue_history(), 0, -1)
195
+
196
+ for scan in history:
197
+ scan_name = scan.content["info"]["request_blocks"][-1]["msg"].content["scan_type"]
198
+ if scan_name == current_scan:
199
+ args_dict = scan.content["info"]["request_blocks"][-1]["msg"].content[
200
+ "parameter"
201
+ ]["args"]
202
+ args_list = []
203
+ for key, value in args_dict.items():
204
+ args_list.append(key)
205
+ args_list.extend(value)
206
+ if len(args_list) > 1 and self.arg_box is not None:
207
+ self.arg_box.set_parameters(args_list)
208
+ kwargs = scan.content["info"]["request_blocks"][-1]["msg"].content["parameter"][
209
+ "kwargs"
210
+ ]
211
+ if kwargs and self.kwarg_boxes:
212
+ for box in self.kwarg_boxes:
213
+ box.set_parameters(kwargs)
214
+ self.last_scan_found = True
215
+ break
216
+ else:
217
+ self.last_scan_found = False
218
+ else:
219
+ self.last_scan_found = False
128
220
 
129
221
  @Property(str)
130
222
  def current_scan(self):
@@ -151,6 +243,29 @@ class ScanControl(BECWidget, QWidget):
151
243
  """
152
244
  self.current_scan = scan_name
153
245
 
246
+ @Property(bool)
247
+ def hide_scan_remember_toggle(self):
248
+ """Property to hide the scan remember toggle."""
249
+ return not self.toggle.isVisible()
250
+
251
+ @hide_scan_remember_toggle.setter
252
+ def hide_scan_remember_toggle(self, hide: bool):
253
+ """Setter for the hide_scan_remember_toggle property.
254
+
255
+ Args:
256
+ hide(bool): Hide or show the scan remember toggle.
257
+ """
258
+ self.show_scan_remember_toggle(not hide)
259
+
260
+ @Slot(bool)
261
+ def show_scan_remember_toggle(self, show: bool):
262
+ """Shows or hides the scan control buttons."""
263
+ self.toggle.setVisible(show)
264
+ self.last_scan_label.setVisible(show)
265
+
266
+ show_group = show or self.button_run_scan.isVisible()
267
+ self.scan_selection_group.setVisible(show_group)
268
+
154
269
  @Property(bool)
155
270
  def hide_scan_control_buttons(self):
156
271
  """Property to hide the scan control buttons."""
@@ -294,17 +409,64 @@ class ScanControl(BECWidget, QWidget):
294
409
  box.deleteLater()
295
410
  self.kwarg_boxes = []
296
411
 
297
- @Slot()
298
- def run_scan(self):
299
- """Starts the selected scan with the given parameters."""
300
- self.scan_started.emit()
412
+ def get_scan_parameters(self, bec_object: bool = True):
413
+ """
414
+ Returns the scan parameters for the selected scan.
415
+
416
+ Args:
417
+ bec_object(bool): If True, returns the BEC object for the scan parameters such as device objects.
418
+ """
301
419
  args = []
302
420
  kwargs = {}
303
421
  if self.arg_box is not None:
304
- args = self.arg_box.get_parameters()
422
+ args = self.arg_box.get_parameters(bec_object)
305
423
  for box in self.kwarg_boxes:
306
- box_kwargs = box.get_parameters()
424
+ box_kwargs = box.get_parameters(bec_object)
307
425
  kwargs.update(box_kwargs)
426
+ return args, kwargs
427
+
428
+ def restore_scan_parameters(self, scan_name: str):
429
+ """
430
+ Restores the scan parameters for the given scan name
431
+
432
+ Args:
433
+ scan_name(str): Name of the scan to restore the parameters for.
434
+ """
435
+ if self.last_scan_found is True:
436
+ return
437
+ scan_params = self.config.scans.get(scan_name, None)
438
+ if scan_params is None and self.previous_scan is None:
439
+ return
440
+
441
+ if scan_params is None and self.previous_scan is not None:
442
+ previous_scan_params = self.config.scans.get(self.previous_scan, None)
443
+ self._restore_kwargs(previous_scan_params.kwargs)
444
+ return
445
+
446
+ if scan_params.args is not None and self.arg_box is not None:
447
+ self.arg_box.set_parameters(scan_params.args)
448
+
449
+ self._restore_kwargs(scan_params.kwargs)
450
+
451
+ def _restore_kwargs(self, scan_kwargs: dict):
452
+ """Restores the kwargs for the given scan parameters."""
453
+ if scan_kwargs is not None and self.kwarg_boxes is not None:
454
+ for box in self.kwarg_boxes:
455
+ box.set_parameters(scan_kwargs)
456
+
457
+ def save_current_scan_parameters(self):
458
+ """Saves the current scan parameters to the scan control config for further use."""
459
+ scan_name = self.comboBox_scan_selection.currentText()
460
+ self.previous_scan = scan_name
461
+ args, kwargs = self.get_scan_parameters(False)
462
+ scan_params = ScanParameterConfig(name=scan_name, args=args, kwargs=kwargs)
463
+ self.config.scans[scan_name] = scan_params
464
+
465
+ @SafeSlot(popup_error=True)
466
+ def run_scan(self):
467
+ """Starts the selected scan with the given parameters."""
468
+ self.scan_started.emit()
469
+ args, kwargs = self.get_scan_parameters()
308
470
  scan_function = getattr(self.scans, self.comboBox_scan_selection.currentText())
309
471
  if callable(scan_function):
310
472
  scan_function(*args, **kwargs)
@@ -230,32 +230,35 @@ class ScanGroupBox(QGroupBox):
230
230
  widget.deleteLater()
231
231
  self.widgets = self.widgets[: -len(self.inputs)]
232
232
 
233
- def get_parameters(self):
233
+ def get_parameters(self, device_object: bool = True):
234
234
  """
235
235
  Returns the parameters from the widgets in the scan control layout formated to run scan from BEC.
236
236
  """
237
237
  if self.box_type == "args":
238
- return self._get_arg_parameterts()
238
+ return self._get_arg_parameterts(device_object=device_object)
239
239
  elif self.box_type == "kwargs":
240
- return self._get_kwarg_parameters()
240
+ return self._get_kwarg_parameters(device_object=device_object)
241
241
 
242
- def _get_arg_parameterts(self):
242
+ def _get_arg_parameterts(self, device_object: bool = True):
243
243
  args = []
244
244
  for i in range(1, self.layout.rowCount()):
245
245
  for j in range(self.layout.columnCount()):
246
- widget = self.layout.itemAtPosition(i, j).widget()
247
- if isinstance(widget, DeviceLineEdit):
248
- value = widget.get_device()
249
- else:
250
- value = WidgetIO.get_value(widget)
251
- args.append(value)
246
+ try: # In case that the bundle size changes
247
+ widget = self.layout.itemAtPosition(i, j).widget()
248
+ if isinstance(widget, DeviceLineEdit) and device_object:
249
+ value = widget.get_device()
250
+ else:
251
+ value = WidgetIO.get_value(widget)
252
+ args.append(value)
253
+ except AttributeError:
254
+ continue
252
255
  return args
253
256
 
254
- def _get_kwarg_parameters(self):
257
+ def _get_kwarg_parameters(self, device_object: bool = True):
255
258
  kwargs = {}
256
259
  for i in range(self.layout.columnCount()):
257
260
  widget = self.layout.itemAtPosition(1, i).widget()
258
- if isinstance(widget, DeviceLineEdit):
261
+ if isinstance(widget, DeviceLineEdit) and device_object:
259
262
  value = widget.get_device()
260
263
  else:
261
264
  value = WidgetIO.get_value(widget)
@@ -273,3 +276,22 @@ class ScanGroupBox(QGroupBox):
273
276
  if isinstance(widget, DeviceLineEdit):
274
277
  widget_rows += 1
275
278
  return widget_rows
279
+
280
+ def set_parameters(self, parameters: list | dict):
281
+ if self.box_type == "args":
282
+ self._set_arg_parameters(parameters)
283
+ elif self.box_type == "kwargs":
284
+ self._set_kwarg_parameters(parameters)
285
+
286
+ def _set_arg_parameters(self, parameters: list):
287
+ while len(parameters) != len(self.widgets):
288
+ self.add_widget_bundle()
289
+ for i, parameter in enumerate(parameters):
290
+ WidgetIO.set_value(self.widgets[i], parameter)
291
+
292
+ def _set_kwarg_parameters(self, parameters: dict):
293
+ for widget in self.widgets:
294
+ for key, value in parameters.items():
295
+ if widget.arg_name == key:
296
+ WidgetIO.set_value(widget, value)
297
+ break
@@ -13,7 +13,7 @@ class ToggleSwitch(QWidget):
13
13
  enabled = Signal(bool)
14
14
  ICON_NAME = "toggle_on"
15
15
 
16
- def __init__(self, parent=None):
16
+ def __init__(self, parent=None, checked=True):
17
17
  super().__init__(parent)
18
18
  self.setFixedSize(40, 21)
19
19
 
@@ -23,14 +23,14 @@ class ToggleSwitch(QWidget):
23
23
  self._inactive_track_color = QColor(200, 200, 200)
24
24
  self._inactive_thumb_color = QColor(255, 255, 255)
25
25
 
26
- self._checked = False
26
+ self._checked = checked
27
27
  self._track_color = self.inactive_track_color
28
28
  self._thumb_color = self.inactive_thumb_color
29
29
 
30
30
  self._animation = QPropertyAnimation(self, b"thumb_pos")
31
31
  self._animation.setDuration(200)
32
32
  self._animation.setEasingCurve(QEasingCurve.Type.OutBack)
33
- self.setProperty("checked", True)
33
+ self.setProperty("checked", checked)
34
34
 
35
35
  @Property(bool)
36
36
  def checked(self):
@@ -2,12 +2,27 @@ import os
2
2
  import select
3
3
  import shlex
4
4
  import signal
5
+ import socket
5
6
  import subprocess
6
7
  import sys
7
8
 
8
9
  from bec_widgets.widgets.website.website import WebsiteWidget
9
10
 
10
11
 
12
+ def get_free_port():
13
+ """
14
+ Get a free port on the local machine.
15
+
16
+ Returns:
17
+ int: The free port number
18
+ """
19
+ sock = socket.socket()
20
+ sock.bind(("", 0))
21
+ port = sock.getsockname()[1]
22
+ sock.close()
23
+ return port
24
+
25
+
11
26
  class VSCodeEditor(WebsiteWidget):
12
27
  """
13
28
  A widget to display the VSCode editor.
@@ -15,7 +30,6 @@ class VSCodeEditor(WebsiteWidget):
15
30
 
16
31
  token = "bec"
17
32
  host = "127.0.0.1"
18
- port = 7000
19
33
 
20
34
  USER_ACCESS = []
21
35
  ICON_NAME = "developer_mode_tv"
@@ -23,6 +37,7 @@ class VSCodeEditor(WebsiteWidget):
23
37
  def __init__(self, parent=None, config=None, client=None, gui_id=None):
24
38
 
25
39
  self.process = None
40
+ self.port = get_free_port()
26
41
  self._url = f"http://{self.host}:{self.port}?tkn={self.token}"
27
42
  super().__init__(parent=parent, config=config, client=client, gui_id=gui_id)
28
43
  self.start_server()
@@ -49,6 +64,7 @@ class VSCodeEditor(WebsiteWidget):
49
64
  if output and f"available at {self._url}" in output:
50
65
  break
51
66
  self.set_url(self._url)
67
+ self.wait_until_loaded()
52
68
 
53
69
  def cleanup_vscode(self):
54
70
  """
@@ -1,5 +1,5 @@
1
1
  from qtpy.QtCore import Property, QUrl, Slot, qInstallMessageHandler
2
- from qtpy.QtWebEngineWidgets import QWebEngineView
2
+ from qtpy.QtWebEngineWidgets import QWebEngineSettings, QWebEngineView
3
3
  from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
4
4
 
5
5
  from bec_widgets.utils.bec_widget import BECWidget
@@ -32,6 +32,19 @@ class WebsiteWidget(BECWidget, QWidget):
32
32
  self.setLayout(layout)
33
33
  self.set_url(url)
34
34
 
35
+ self._loaded = False
36
+ self.website.loadFinished.connect(self._on_load_finished)
37
+
38
+ def wait_until_loaded(self):
39
+ while not self._loaded:
40
+ QApplication.processEvents()
41
+
42
+ def _on_load_finished(self):
43
+ """
44
+ Callback when the website has finished loading
45
+ """
46
+ self._loaded = True
47
+
35
48
  @Property(str)
36
49
  def url(self) -> str:
37
50
  """
@@ -64,6 +77,7 @@ class WebsiteWidget(BECWidget, QWidget):
64
77
  return
65
78
  if not isinstance(url, str):
66
79
  return
80
+ self._loaded = False
67
81
  self.website.setUrl(QUrl(url))
68
82
 
69
83
  def get_url(self) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.102.0
3
+ Version: 0.104.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
@@ -1,12 +1,12 @@
1
1
  .gitignore,sha256=cMQ1MLmnoR88aMCCJwUyfoTnufzl4-ckmHtlFUqHcT4,3253
2
- .gitlab-ci.yml,sha256=HE_stq5wl-0wE5ZetfEmaaDbectk20jX7Z2cxwsBegg,8348
2
+ .gitlab-ci.yml,sha256=Dc1iDjsc72UxdUtihx4uSZU0lrTQeR8hZwGx1MQBfKE,8432
3
3
  .pylintrc,sha256=eeY8YwSI74oFfq6IYIbCqnx3Vk8ZncKaatv96n_Y8Rs,18544
4
4
  .readthedocs.yaml,sha256=aSOc277LqXcsTI6lgvm_JY80lMlr69GbPKgivua2cS0,603
5
- CHANGELOG.md,sha256=kLNb6ReaOEE1uDs95RSaQ1aNG1zvaVd1PHFIFigfL6s,6700
5
+ CHANGELOG.md,sha256=56BXszrhvVmZ-ko-vL6XvL-n404_pI3WPFat9QBsGKw,7391
6
6
  LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
7
- PKG-INFO,sha256=Y_CcBGqNdoop95Gj0XARDh0Vg5-Ol70bj0qb8Gjg5Ok,1334
7
+ PKG-INFO,sha256=5uieac1rLNLfxfbqXF9q2AyKsrEM34ZR7Mp_zjJOcLg,1334
8
8
  README.md,sha256=Od69x-RS85Hph0-WwWACwal4yUd67XkEn4APEfHhHFw,2649
9
- pyproject.toml,sha256=TkhUO4NlPY__hCeNKJNhoE-JXSkqoli1Ici3Rlwn-dc,2544
9
+ pyproject.toml,sha256=2KA4GlcKlp8NhsbCE5skdf7xye1F8SaNHRy11G7Ikfs,2544
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
@@ -52,7 +52,7 @@ bec_widgets/utils/bec_connector.py,sha256=SivHKXVyNVqeu3kCXYEPpbleTVw8g1cW0FKq1Q
52
52
  bec_widgets/utils/bec_designer.py,sha256=Z3MeMju-KmTz8POtm23VQfp4rvtD2sF6eIOKQkl2F7w,4729
53
53
  bec_widgets/utils/bec_dispatcher.py,sha256=NkObWO_gRO9Uobz-fy0gVTZqQsbFRaKj6fbjYZoErFI,6400
54
54
  bec_widgets/utils/bec_table.py,sha256=nA2b8ukSeUfquFMAxGrUVOqdrzMoDYD6O_4EYbOG2zk,717
55
- bec_widgets/utils/bec_widget.py,sha256=ANFx-Ll5mjOjmILniTH5jKBITDijUtfFLb1eoiXoFws,2881
55
+ bec_widgets/utils/bec_widget.py,sha256=7KmIdWCa7skN1jTsQQkhOpu2PZmKMr98AMB1f_MZfv4,3179
56
56
  bec_widgets/utils/colors.py,sha256=N3GbVBpExfC1m_dnYFDoua-iRLM90E5kTKVOIkZVmDM,12372
57
57
  bec_widgets/utils/container_utils.py,sha256=0wr3ZfuMiAFKCrQHVjxjw-Vuk8wsHdridqcjy2eY840,1531
58
58
  bec_widgets/utils/crosshair.py,sha256=8lik9k69WI2WMj5FGLbrKtny9duxqXUJAhpX8tHoyp0,11543
@@ -199,10 +199,10 @@ bec_widgets/widgets/ring_progress_bar/ring_progress_bar.pyproject,sha256=ZNYDnKD
199
199
  bec_widgets/widgets/ring_progress_bar/ring_progress_bar_plugin.py,sha256=-rw9ZSThgAH0Ubbr3X-L5x-ZqMXUGnauyFb4OmfUvD4,1394
200
200
  bec_widgets/widgets/scan_control/__init__.py,sha256=IOfHl15vxb_uC6KN62-PeUzbBha_vQyqkkXbJ2HU674,38
201
201
  bec_widgets/widgets/scan_control/register_scan_control.py,sha256=xUX2yR0-MaIMg9_y9qe50yDDphzsh2x1b5PMrF90yPM,475
202
- bec_widgets/widgets/scan_control/scan_control.py,sha256=pQkc9mPfGxOCJaJPuPUudSStuBk9lqUZovbGdY1cx7M,11587
202
+ bec_widgets/widgets/scan_control/scan_control.py,sha256=PVy_gqivO_Nut6ZxdOhgXfRyRPcQ92wSGOGY8duxXlE,18074
203
203
  bec_widgets/widgets/scan_control/scan_control.pyproject,sha256=eTgVDFKToIH8_BbJjM2RvbOLr7HnYoidX0SAHx640DM,30
204
204
  bec_widgets/widgets/scan_control/scan_control_plugin.py,sha256=7GaqmaRgbwIPjVoXcyKVDl8UpfqC2weViup-YFfzUsM,1270
205
- bec_widgets/widgets/scan_control/scan_group_box.py,sha256=BpX9ZphqOhdEbQnGzNeNlmuZsMS__U97CkBMical2FY,9300
205
+ bec_widgets/widgets/scan_control/scan_group_box.py,sha256=ZGF5alvw4vK---6-zsVUzW-6dt7-V68EbzrGp5EL_RE,10377
206
206
  bec_widgets/widgets/spinner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
207
207
  bec_widgets/widgets/spinner/register_spinner_widget.py,sha256=_zCPjLh4M7NTSHP1Atdn6yu33zJ3LJkcBy3KOJ5eSVY,476
208
208
  bec_widgets/widgets/spinner/spinner.py,sha256=5LoRGuRvQmY1hUCt06P3DJjXqOVkFCszbNAXRmPzqlo,2628
@@ -220,14 +220,14 @@ bec_widgets/widgets/text_box/text_box.pyproject,sha256=XohO1BIe2hrpU-z_KHKRgjcUk
220
220
  bec_widgets/widgets/text_box/text_box_plugin.py,sha256=rjRxqqovggpiI3gfQrXU-uc7TEjugIameQBrFD6s9hY,1302
221
221
  bec_widgets/widgets/toggle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
222
222
  bec_widgets/widgets/toggle/register_toggle_switch.py,sha256=8pVtkeEeiDOjV4OPoZq1I30F9JDzl4nQE7e7xoWyWBs,472
223
- bec_widgets/widgets/toggle/toggle.py,sha256=NNWjl2xfEFwlmvI7M68QkezS-KsbqBhFKrvY-tVY_q0,4479
223
+ bec_widgets/widgets/toggle/toggle.py,sha256=cLKWXH5vLG6VcycN_EgwdFsMPS65EbqoS0dfKE1S5kk,4498
224
224
  bec_widgets/widgets/toggle/toggle_switch.pyproject,sha256=Msa-AS5H5XlqW65r8GlX2AxD8FnFnDyDgGnbKcXqKOw,24
225
225
  bec_widgets/widgets/toggle/toggle_switch_plugin.py,sha256=1snX-mKw_K3oPqUApGRPDnNRnLMZjqnmQOzuIJ9MOT4,1344
226
226
  bec_widgets/widgets/vscode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
227
227
  bec_widgets/widgets/vscode/register_vs_code_editor.py,sha256=JATKBkTEuReeQ2Jj402xasjgVRMFI8uUOAAwmnFOWRA,473
228
228
  bec_widgets/widgets/vscode/vs_code_editor.pyproject,sha256=bxx0jZlSfBo-Em7p15W1QIJ9lFr9jqTqGynUQG01ocU,24
229
229
  bec_widgets/widgets/vscode/vs_code_editor_plugin.py,sha256=exFR6HTVdLLPfn2U6BMDugPoxZaebcHTnHWMrX2n_d4,1338
230
- bec_widgets/widgets/vscode/vscode.py,sha256=KReXQ5y-Bs-F1Tc0-_1f6c0dC5_MlgkWPGhUgbOORO0,2267
230
+ bec_widgets/widgets/vscode/vscode.py,sha256=3HDWYJZPi4lNr7gW4IabTYyivMdhZGhq5ETGlJihqlc,2579
231
231
  bec_widgets/widgets/waveform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
232
232
  bec_widgets/widgets/waveform/bec_waveform_widget.pyproject,sha256=GLD8GN9dXx9wNbtnevrxqqcwk7vKV-Uv8QYSycdaoaI,33
233
233
  bec_widgets/widgets/waveform/bec_waveform_widget_plugin.py,sha256=qSQTeCzIUn8rgDkjIM47Rr3-fqg1uk8rDf_lCoY9gZw,1402
@@ -241,11 +241,11 @@ bec_widgets/widgets/waveform/waveform_popups/dap_summary_dialog/__init__.py,sha2
241
241
  bec_widgets/widgets/waveform/waveform_popups/dap_summary_dialog/dap_summary_dialog.py,sha256=XOHppMcONwfhAYpqIc51VOrpunWmPSn50sT3I0MjW2c,1173
242
242
  bec_widgets/widgets/website/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
243
243
  bec_widgets/widgets/website/register_website_widget.py,sha256=LIQJpV9uqcBiPR9cEAiDjaUS_l7JroYVdsotnLpD9H0,476
244
- bec_widgets/widgets/website/website.py,sha256=kDlqjwtx0yft1ZNRhTdHnYh_5KZHfqT_ZGPOKT4C-yI,2619
244
+ bec_widgets/widgets/website/website.py,sha256=42pncCc_zI2eqeMArIurVmPUukRo5bTxa2h1Skah-io,3012
245
245
  bec_widgets/widgets/website/website_widget.pyproject,sha256=scOiV3cV1_BjbzpPzy2N8rIJL5P2qIZz8ObTJ-Uvdtg,25
246
246
  bec_widgets/widgets/website/website_widget_plugin.py,sha256=pz38_C2cZ0yvPPS02wdIPcmhFo_yiwUhflsASocAPQQ,1341
247
- bec_widgets-0.102.0.dist-info/METADATA,sha256=Y_CcBGqNdoop95Gj0XARDh0Vg5-Ol70bj0qb8Gjg5Ok,1334
248
- bec_widgets-0.102.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
249
- bec_widgets-0.102.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
250
- bec_widgets-0.102.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
251
- bec_widgets-0.102.0.dist-info/RECORD,,
247
+ bec_widgets-0.104.0.dist-info/METADATA,sha256=5uieac1rLNLfxfbqXF9q2AyKsrEM34ZR7Mp_zjJOcLg,1334
248
+ bec_widgets-0.104.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
249
+ bec_widgets-0.104.0.dist-info/entry_points.txt,sha256=3otEkCdDB9LZJuBLzG4pFLK5Di0CVybN_12IsZrQ-58,166
250
+ bec_widgets-0.104.0.dist-info/licenses/LICENSE,sha256=YRKe85CBRyP7UpEAWwU8_qSIyuy5-l_9C-HKg5Qm8MQ,1511
251
+ bec_widgets-0.104.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 = "0.102.0"
7
+ version = "0.104.0"
8
8
  description = "BEC Widgets"
9
9
  requires-python = ">=3.10"
10
10
  classifiers = [