bec-widgets 0.112.1__py3-none-any.whl → 0.114.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.
Files changed (33) hide show
  1. CHANGELOG.md +52 -48
  2. PKG-INFO +1 -1
  3. bec_widgets/applications/__init__.py +0 -0
  4. bec_widgets/applications/alignment/__init__.py +0 -0
  5. bec_widgets/applications/alignment/alignment_1d/__init__.py +0 -0
  6. bec_widgets/applications/alignment/alignment_1d/alignment_1d.py +265 -0
  7. bec_widgets/applications/alignment/alignment_1d/alignment_1d.ui +887 -0
  8. bec_widgets/assets/app_icons/alignment_1d.png +0 -0
  9. bec_widgets/qt_utils/toolbar.py +3 -1
  10. bec_widgets/utils/bec_signal_proxy.py +54 -0
  11. bec_widgets/utils/linear_region_selector.py +15 -3
  12. bec_widgets/utils/plot_indicator_items.py +247 -0
  13. bec_widgets/widgets/bec_status_box/bec_status_box.py +5 -9
  14. bec_widgets/widgets/bec_status_box/status_item.py +18 -9
  15. bec_widgets/widgets/dap_combo_box/dap_combo_box.py +2 -0
  16. bec_widgets/widgets/figure/plots/plot_base.py +5 -0
  17. bec_widgets/widgets/figure/plots/waveform/waveform.py +28 -15
  18. bec_widgets/widgets/lmfit_dialog/lmfit_dialog.py +151 -16
  19. bec_widgets/widgets/lmfit_dialog/lmfit_dialog_vertical.ui +47 -14
  20. bec_widgets/widgets/positioner_box/positioner_box.py +4 -1
  21. bec_widgets/widgets/scan_control/scan_control.py +42 -1
  22. bec_widgets/widgets/stop_button/stop_button.py +14 -2
  23. {bec_widgets-0.112.1.dist-info → bec_widgets-0.114.0.dist-info}/METADATA +1 -1
  24. {bec_widgets-0.112.1.dist-info → bec_widgets-0.114.0.dist-info}/RECORD +28 -25
  25. pyproject.toml +1 -1
  26. bec_widgets/assets/status_icons/error.svg +0 -3
  27. bec_widgets/assets/status_icons/not_connected.svg +0 -3
  28. bec_widgets/assets/status_icons/refresh.svg +0 -3
  29. bec_widgets/assets/status_icons/running.svg +0 -3
  30. bec_widgets/assets/status_icons/warning.svg +0 -3
  31. {bec_widgets-0.112.1.dist-info → bec_widgets-0.114.0.dist-info}/WHEEL +0 -0
  32. {bec_widgets-0.112.1.dist-info → bec_widgets-0.114.0.dist-info}/entry_points.txt +0 -0
  33. {bec_widgets-0.112.1.dist-info → bec_widgets-0.114.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,12 @@
1
1
  import os
2
2
 
3
- from bec_lib.endpoints import MessageEndpoints
4
3
  from bec_lib.logger import bec_logger
5
4
  from qtpy.QtCore import Property, Signal, Slot
6
- from qtpy.QtWidgets import QTreeWidgetItem, QVBoxLayout, QWidget
5
+ from qtpy.QtWidgets import QPushButton, QTreeWidgetItem, QVBoxLayout, QWidget
7
6
 
8
7
  from bec_widgets.utils import UILoader
9
8
  from bec_widgets.utils.bec_widget import BECWidget
9
+ from bec_widgets.utils.colors import get_accent_colors
10
10
 
11
11
  logger = bec_logger.logger
12
12
 
@@ -15,7 +15,10 @@ class LMFitDialog(BECWidget, QWidget):
15
15
  """Dialog for displaying the fit summary and params for LMFit DAP processes"""
16
16
 
17
17
  ICON_NAME = "monitoring"
18
+ # Signal to emit the currently selected fit curve_id
18
19
  selected_fit = Signal(str)
20
+ # Signal to emit a move action in form of a tuple (param_name, value)
21
+ move_action = Signal(tuple)
19
22
 
20
23
  def __init__(
21
24
  self,
@@ -49,9 +52,54 @@ class LMFitDialog(BECWidget, QWidget):
49
52
  self.summary_data = {}
50
53
  self._fit_curve_id = None
51
54
  self._deci_precision = 3
55
+ self._always_show_latest = False
52
56
  self.ui.curve_list.currentItemChanged.connect(self.display_fit_details)
53
57
  self.layout.setContentsMargins(0, 0, 0, 0)
54
58
  self.setLayout(self.layout)
59
+ self._active_actions = []
60
+ self._enable_actions = True
61
+ self._move_buttons = []
62
+ self._accent_colors = get_accent_colors()
63
+ self.action_buttons = {}
64
+
65
+ @property
66
+ def enable_actions(self) -> bool:
67
+ """Property to enable the move to buttons."""
68
+ return self._enable_actions
69
+
70
+ @enable_actions.setter
71
+ def enable_actions(self, enable: bool):
72
+ self._enable_actions = enable
73
+ for button in self.action_buttons.values():
74
+ button.setEnabled(enable)
75
+
76
+ @Property(list)
77
+ def active_action_list(self) -> list[str]:
78
+ """Property to list the names of the fit parameters for which actions should be enabled."""
79
+ return self._active_actions
80
+
81
+ @active_action_list.setter
82
+ def active_action_list(self, actions: list[str]):
83
+ self._active_actions = actions
84
+
85
+ # This slot needed?
86
+ @Slot(bool)
87
+ def set_actions_enabled(self, enable: bool) -> bool:
88
+ """Slot to enable the move to buttons.
89
+
90
+ Args:
91
+ enable (bool): Whether to enable the action buttons.
92
+ """
93
+ self.enable_actions = enable
94
+
95
+ @Property(bool)
96
+ def always_show_latest(self):
97
+ """Property to indicate if always the latest DAP update is displayed."""
98
+ return self._always_show_latest
99
+
100
+ @always_show_latest.setter
101
+ def always_show_latest(self, show: bool):
102
+ self._always_show_latest = show
55
103
 
56
104
  @Property(bool)
57
105
  def hide_curve_selection(self):
@@ -67,8 +115,36 @@ class LMFitDialog(BECWidget, QWidget):
67
115
  """
68
116
  self.ui.group_curve_selection.setVisible(not show)
69
117
 
118
+ @Property(bool)
119
+ def hide_summary(self) -> bool:
120
+ """Property for showing the summary."""
121
+ return not self.ui.group_summary.isVisible()
122
+
123
+ @hide_summary.setter
124
+ def hide_summary(self, show: bool):
125
+ """Setter for showing the summary.
126
+
127
+ Args:
128
+ show (bool): Whether to show the summary.
129
+ """
130
+ self.ui.group_summary.setVisible(not show)
131
+
132
+ @Property(bool)
133
+ def hide_parameters(self) -> bool:
134
+ """Property for showing the parameters."""
135
+ return not self.ui.group_parameters.isVisible()
136
+
137
+ @hide_parameters.setter
138
+ def hide_parameters(self, show: bool):
139
+ """Setter for showing the parameters.
140
+
141
+ Args:
142
+ show (bool): Whether to show the parameters.
143
+ """
144
+ self.ui.group_parameters.setVisible(not show)
145
+
70
146
  @property
71
- def fit_curve_id(self):
147
+ def fit_curve_id(self) -> str:
72
148
  """Property for the currently displayed fit curve_id."""
73
149
  return self._fit_curve_id
74
150
 
@@ -112,19 +188,34 @@ class LMFitDialog(BECWidget, QWidget):
112
188
  curve_id = metadata.get("curve_id", "")
113
189
  self.summary_data.update({curve_id: data})
114
190
  self.refresh_curve_list()
115
- if self.fit_curve_id is None:
191
+ if self.fit_curve_id is None or self.always_show_latest is True:
116
192
  self.fit_curve_id = curve_id
117
193
  if curve_id != self.fit_curve_id:
118
194
  return
119
195
  if data is None:
120
196
  return
121
197
  self.ui.summary_tree.clear()
198
+ chi_squared = data.get("chisqr", 0.0)
199
+ if isinstance(chi_squared, float) or isinstance(chi_squared, int):
200
+ chi_squared = f"{chi_squared:.{self._deci_precision}f}"
201
+ else:
202
+ chi_squared = "None"
203
+ reduced_chi_squared = data.get("redchi", 0.0)
204
+ if isinstance(reduced_chi_squared, float) or isinstance(reduced_chi_squared, int):
205
+ reduced_chi_squared = f"{reduced_chi_squared:.{self._deci_precision}f}"
206
+ else:
207
+ reduced_chi_squared = "None"
208
+ r_squared = data.get("rsquared", 0.0)
209
+ if isinstance(r_squared, float) or isinstance(r_squared, int):
210
+ r_squared = f"{r_squared:.{self._deci_precision}f}"
211
+ else:
212
+ r_squared = "None"
122
213
  properties = [
123
214
  ("Model", data.get("model", "")),
124
215
  ("Method", data.get("method", "")),
125
- ("Chi-Squared", f"{data.get('chisqr', 0.0):.{self._deci_precision}f}"),
126
- ("Reduced Chi-Squared", f"{data.get('redchi', 0.0):.{self._deci_precision}f}"),
127
- ("R-Squared", f"{data.get('rsquared', 0.0):.{self._deci_precision}f}"),
216
+ ("Chi-Squared", chi_squared),
217
+ ("Reduced Chi-Squared", reduced_chi_squared),
218
+ ("R-Squared", r_squared),
128
219
  ("Message", data.get("message", "")),
129
220
  ]
130
221
  for prop, val in properties:
@@ -149,18 +240,62 @@ class LMFitDialog(BECWidget, QWidget):
149
240
  Args:
150
241
  params (list): List of LMFit parameters for the fit curve.
151
242
  """
243
+ self._move_buttons = []
152
244
  self.ui.param_tree.clear()
153
245
  for param in params:
154
- param_name, param_value, param_std = (
155
- param[0],
156
- f"{param[1]:.{self._deci_precision}f}",
157
- f"{param[7]:.{self._deci_precision}f}",
158
- )
159
- QTreeWidgetItem(self.ui.param_tree, [param_name, param_value, param_std])
246
+ param_name = param[0]
247
+ param_value = param[1]
248
+ if isinstance(param_value, float) or isinstance(param_value, int):
249
+ param_value = f"{param_value:.{self._deci_precision}f}"
250
+ else:
251
+ param_value = "None"
252
+ param_std = param[7]
253
+ if isinstance(param_std, float) or isinstance(param_std, int):
254
+ param_std = f"{param_std:.{self._deci_precision}f}"
255
+ else:
256
+ param_std = "None"
257
+
258
+ tree_item = QTreeWidgetItem(self.ui.param_tree, [param_name, param_value, param_std])
259
+ if param_name in self.active_action_list: # pylint: disable=unsupported-membership-test
260
+ # Create a push button to move the motor to a specific position
261
+ widget = QWidget()
262
+ button = QPushButton(f"Move to {param_name}")
263
+ button.clicked.connect(self._create_move_action(param_name, param[1]))
264
+ if self.enable_actions is True:
265
+ button.setEnabled(True)
266
+ else:
267
+ button.setEnabled(False)
268
+ button.setStyleSheet(
269
+ f"""
270
+ QPushButton:enabled {{ background-color: {self._accent_colors.success.name()};color: white; }}
271
+ QPushButton:disabled {{ background-color: grey;color: white; }}
272
+ """
273
+ )
274
+ self.action_buttons[param_name] = button
275
+ layout = QVBoxLayout()
276
+ layout.addWidget(self.action_buttons[param_name])
277
+ layout.setContentsMargins(0, 0, 0, 0)
278
+ widget.setLayout(layout)
279
+ self.ui.param_tree.setItemWidget(tree_item, 3, widget)
280
+
281
+ def _create_move_action(self, param_name: str, param_value: float) -> callable:
282
+ """Create a move action for the given parameter name and value.
283
+
284
+ Args:
285
+ param_name (str): The name of the parameter.
286
+ param_value (float): The value of the parameter.
287
+ Returns:
288
+ callable: The move action with the given parameter name and value.
289
+ """
290
+
291
+ def move_action():
292
+ self.move_action.emit((param_name, param_value))
293
+
294
+ return move_action
160
295
 
161
296
  def populate_curve_list(self):
162
297
  """Populate the curve list with the available fit curves."""
163
- for curve_name in self.summary_data.keys():
298
+ for curve_name in self.summary_data:
164
299
  self.ui.curve_list.addItem(curve_name)
165
300
 
166
301
  def refresh_curve_list(self):
@@ -183,10 +318,10 @@ class LMFitDialog(BECWidget, QWidget):
183
318
  self.update_summary_tree(data, {"curve_id": curve_name})
184
319
 
185
320
 
186
- if __name__ == "__main__":
321
+ if __name__ == "__main__": # pragma: no cover
187
322
  import sys
188
323
 
189
- from qtpy.QtWidgets import QApplication
324
+ from qtpy.QtWidgets import QApplication # pylint: disable=ungrouped-imports
190
325
 
191
326
  app = QApplication(sys.argv)
192
327
  dialog = LMFitDialog()
@@ -6,12 +6,12 @@
6
6
  <rect>
7
7
  <x>0</x>
8
8
  <y>0</y>
9
- <width>274</width>
10
- <height>568</height>
9
+ <width>303</width>
10
+ <height>457</height>
11
11
  </rect>
12
12
  </property>
13
13
  <property name="sizePolicy">
14
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
14
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
15
15
  <horstretch>0</horstretch>
16
16
  <verstretch>0</verstretch>
17
17
  </sizepolicy>
@@ -19,15 +19,40 @@
19
19
  <property name="windowTitle">
20
20
  <string>Form</string>
21
21
  </property>
22
- <layout class="QVBoxLayout" name="verticalLayout_4" stretch="0,0,0">
22
+ <layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,2,2">
23
+ <property name="leftMargin">
24
+ <number>0</number>
25
+ </property>
26
+ <property name="topMargin">
27
+ <number>0</number>
28
+ </property>
29
+ <property name="rightMargin">
30
+ <number>0</number>
31
+ </property>
32
+ <property name="bottomMargin">
33
+ <number>0</number>
34
+ </property>
23
35
  <item>
24
36
  <widget class="QGroupBox" name="group_curve_selection">
37
+ <property name="sizePolicy">
38
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
39
+ <horstretch>0</horstretch>
40
+ <verstretch>0</verstretch>
41
+ </sizepolicy>
42
+ </property>
25
43
  <property name="title">
26
44
  <string>Select Curve</string>
27
45
  </property>
28
46
  <layout class="QVBoxLayout" name="verticalLayout" stretch="3">
29
47
  <item>
30
- <widget class="QListWidget" name="curve_list"/>
48
+ <widget class="QListWidget" name="curve_list">
49
+ <property name="sizePolicy">
50
+ <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
51
+ <horstretch>0</horstretch>
52
+ <verstretch>0</verstretch>
53
+ </sizepolicy>
54
+ </property>
55
+ </widget>
31
56
  </item>
32
57
  </layout>
33
58
  </widget>
@@ -35,15 +60,15 @@
35
60
  <item>
36
61
  <widget class="QGroupBox" name="group_summary">
37
62
  <property name="sizePolicy">
38
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
63
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
39
64
  <horstretch>0</horstretch>
40
65
  <verstretch>0</verstretch>
41
66
  </sizepolicy>
42
67
  </property>
43
68
  <property name="minimumSize">
44
69
  <size>
45
- <width>250</width>
46
- <height>200</height>
70
+ <width>0</width>
71
+ <height>0</height>
47
72
  </size>
48
73
  </property>
49
74
  <property name="title">
@@ -53,7 +78,7 @@
53
78
  <item>
54
79
  <widget class="QTreeWidget" name="summary_tree">
55
80
  <property name="sizePolicy">
56
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
81
+ <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
57
82
  <horstretch>0</horstretch>
58
83
  <verstretch>0</verstretch>
59
84
  </sizepolicy>
@@ -88,15 +113,15 @@
88
113
  <item>
89
114
  <widget class="QGroupBox" name="group_parameters">
90
115
  <property name="sizePolicy">
91
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
116
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
92
117
  <horstretch>0</horstretch>
93
118
  <verstretch>0</verstretch>
94
119
  </sizepolicy>
95
120
  </property>
96
121
  <property name="minimumSize">
97
122
  <size>
98
- <width>250</width>
99
- <height>200</height>
123
+ <width>0</width>
124
+ <height>0</height>
100
125
  </size>
101
126
  </property>
102
127
  <property name="title">
@@ -106,7 +131,7 @@
106
131
  <item>
107
132
  <widget class="QTreeWidget" name="param_tree">
108
133
  <property name="sizePolicy">
109
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
134
+ <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
110
135
  <horstretch>0</horstretch>
111
136
  <verstretch>0</verstretch>
112
137
  </sizepolicy>
@@ -117,8 +142,11 @@
117
142
  <height>0</height>
118
143
  </size>
119
144
  </property>
145
+ <property name="columnCount">
146
+ <number>4</number>
147
+ </property>
120
148
  <attribute name="headerDefaultSectionSize">
121
- <number>80</number>
149
+ <number>70</number>
122
150
  </attribute>
123
151
  <column>
124
152
  <property name="text">
@@ -135,6 +163,11 @@
135
163
  <string>Std</string>
136
164
  </property>
137
165
  </column>
166
+ <column>
167
+ <property name="text">
168
+ <string>Action</string>
169
+ </property>
170
+ </column>
138
171
  </widget>
139
172
  </item>
140
173
  </layout>
@@ -33,6 +33,8 @@ class PositionerBox(BECWidget, QWidget):
33
33
  ICON_NAME = "switch_right"
34
34
  USER_ACCESS = ["set_positioner"]
35
35
  device_changed = Signal(str, str)
36
+ # Signal emitted to inform listeners about a position update
37
+ position_update = Signal(float)
36
38
 
37
39
  def __init__(self, parent=None, device: Positioner = None, **kwargs):
38
40
  """Initialize the PositionerBox widget.
@@ -245,6 +247,7 @@ class PositionerBox(BECWidget, QWidget):
245
247
 
246
248
  if readback_val is not None:
247
249
  self.ui.readback.setText(f"{readback_val:.{precision}f}")
250
+ self.position_update.emit(readback_val)
248
251
 
249
252
  if setpoint_val is not None:
250
253
  self.ui.setpoint.setText(f"{setpoint_val:.{precision}f}")
@@ -318,7 +321,7 @@ class PositionerBox(BECWidget, QWidget):
318
321
  if __name__ == "__main__": # pragma: no cover
319
322
  import sys
320
323
 
321
- from qtpy.QtWidgets import QApplication
324
+ from qtpy.QtWidgets import QApplication # pylint: disable=ungrouped-imports
322
325
 
323
326
  app = QApplication(sys.argv)
324
327
  set_theme("dark")
@@ -42,6 +42,7 @@ class ScanControl(BECWidget, QWidget):
42
42
 
43
43
  scan_started = Signal()
44
44
  scan_selected = Signal(str)
45
+ scan_axis = Signal(str, float, float)
45
46
 
46
47
  def __init__(
47
48
  self,
@@ -243,6 +244,44 @@ class ScanControl(BECWidget, QWidget):
243
244
  """
244
245
  self.current_scan = scan_name
245
246
 
247
+ @Property(bool)
248
+ def hide_arg_box(self):
249
+ """Property to hide the argument box."""
250
+ if self.arg_box is None:
251
+ return True
252
+ return not self.arg_box.isVisible()
253
+
254
+ @hide_arg_box.setter
255
+ def hide_arg_box(self, hide: bool):
256
+ """Setter for the hide_arg_box property.
257
+
258
+ Args:
259
+ hide(bool): Hide or show the argument box.
260
+ """
261
+ if self.arg_box is not None:
262
+ self.arg_box.setVisible(not hide)
263
+
264
+ @Property(bool)
265
+ def hide_kwarg_boxes(self):
266
+ """Property to hide the keyword argument boxes."""
267
+ if len(self.kwarg_boxes) == 0:
268
+ return True
269
+
270
+ for box in self.kwarg_boxes:
271
+ if box is not None:
272
+ return not box.isVisible()
273
+
274
+ @hide_kwarg_boxes.setter
275
+ def hide_kwarg_boxes(self, hide: bool):
276
+ """Setter for the hide_kwarg_boxes property.
277
+
278
+ Args:
279
+ hide(bool): Hide or show the keyword argument boxes.
280
+ """
281
+ if len(self.kwarg_boxes) > 0:
282
+ for box in self.kwarg_boxes:
283
+ box.setVisible(not hide)
284
+
246
285
  @Property(bool)
247
286
  def hide_scan_remember_toggle(self):
248
287
  """Property to hide the scan remember toggle."""
@@ -465,10 +504,12 @@ class ScanControl(BECWidget, QWidget):
465
504
  @SafeSlot(popup_error=True)
466
505
  def run_scan(self):
467
506
  """Starts the selected scan with the given parameters."""
468
- self.scan_started.emit()
469
507
  args, kwargs = self.get_scan_parameters()
508
+ for device, start, stop in zip(*[iter(args)] * 3):
509
+ self.scan_axis.emit(device.name, start, stop)
470
510
  scan_function = getattr(self.scans, self.comboBox_scan_selection.currentText())
471
511
  if callable(scan_function):
512
+ self.scan_started.emit()
472
513
  scan_function(*args, **kwargs)
473
514
 
474
515
  def cleanup(self):
@@ -1,6 +1,6 @@
1
1
  from bec_qthemes import material_icon
2
2
  from qtpy.QtCore import Qt
3
- from qtpy.QtWidgets import QHBoxLayout, QPushButton, QToolButton, QWidget
3
+ from qtpy.QtWidgets import QHBoxLayout, QPushButton, QSizePolicy, QToolButton, QWidget
4
4
 
5
5
  from bec_widgets.qt_utils.error_popups import SafeSlot
6
6
  from bec_widgets.utils.bec_widget import BECWidget
@@ -28,9 +28,10 @@ class StopButton(BECWidget, QWidget):
28
28
  self.button.setToolTip("Stop the scan queue")
29
29
  else:
30
30
  self.button = QPushButton()
31
+ self.button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
31
32
  self.button.setText("Stop")
32
33
  self.button.setStyleSheet(
33
- "background-color: #cc181e; color: white; font-weight: bold; font-size: 12px;"
34
+ f"background-color: #cc181e; color: white; font-weight: bold; font-size: 12px;"
34
35
  )
35
36
  self.button.clicked.connect(self.stop_scan)
36
37
 
@@ -47,3 +48,14 @@ class StopButton(BECWidget, QWidget):
47
48
  scan_id(str|None): The scan id to stop. If None, the current scan will be stopped.
48
49
  """
49
50
  self.queue.request_scan_halt()
51
+
52
+
53
+ if __name__ == "__main__": # pragma: no cover
54
+ import sys
55
+
56
+ from qtpy.QtWidgets import QApplication
57
+
58
+ app = QApplication(sys.argv)
59
+ w = StopButton()
60
+ w.show()
61
+ sys.exit(app.exec_())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bec_widgets
3
- Version: 0.112.1
3
+ Version: 0.114.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