wip
CI for debye_bec / test (push) Successful in 1m5s
CI for debye_bec / test (pull_request) Successful in 1m6s

This commit is contained in:
x01da
2026-09-03 08:24:29 +02:00
parent 1bf4bd98d2
commit 96e2f45c50
4 changed files with 187 additions and 65 deletions
@@ -41,15 +41,19 @@ from qtpy.QtWidgets import (
QDialogButtonBox,
QDoubleSpinBox,
QFormLayout,
QGroupBox,
QHBoxLayout,
QLabel,
QLineEdit,
QMessageBox,
QPushButton,
QTabWidget,
QVBoxLayout,
QWidget,
)
from ..scan_control_xas.scan_control_xas import ScanControlXAS
from .qt_widgets import MyButton
logger = bec_logger.logger
@@ -204,10 +208,119 @@ class ScheduleItemDialog(QDialog):
)
)
self.custom_edit = QLineEdit()
# TODO Change to a different placeholder text
self.custom_edit.setPlaceholderText("scans.xas_simple_scan(12000, 14000, 2, 10)")
layout.addWidget(self.custom_edit)
ic_form = self._create_ionization_chamber_form()
if ic_form is not None:
layout.addWidget(ic_form)
reffoil_form = self._create_reffoil_form()
if reffoil_form is not None:
layout.addWidget(reffoil_form)
layout.addStretch(1)
self.tabs.addTab(tab, "Custom")
self.tabs.addTab(tab, "Other")
def _create_ionization_chamber_form(self):
if all(key in self._dev for key in ("ic0", "ic1", "ic2")):
ic_group = QGroupBox("Ionization chamber filling")
layout = QVBoxLayout(ic_group)
form = QFormLayout()
layout.addLayout(form)
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.FieldsStayAtSizeHint)
self.ic_selector = QComboBox()
self.ic_selector.addItems(["IC0", "IC1", "IC2"])
gases = ["He", "N2", "Ar", "Kr"]
self.gas1 = QComboBox()
self.gas2 = QComboBox()
self.gas1.addItems(gases)
self.gas2.addItems(gases)
self.conc1 = QDoubleSpinBox()
self.conc2 = QDoubleSpinBox()
for conc in [self.conc1, self.conc2]:
conc.setDecimals(0)
conc.setSuffix(" %")
conc.setMinimum(0)
conc.setMaximum(100)
conc.setSingleStep(1)
self.pressure = QDoubleSpinBox()
self.pressure.setDecimals(3)
self.pressure.setSuffix(" bar abs")
self.pressure.setMinimum(1)
self.pressure.setMaximum(3)
self.pressure.setSingleStep(0.1)
form.addRow("Ionization chamber", self.ic_selector)
form.addRow("Gas 1", self.gas1)
form.addRow("Concentration 1", self.conc1)
form.addRow("Gas 2", self.gas2)
form.addRow("Concentration 2", self.conc2)
form.addRow("Pressure", self.pressure)
button_layout = QHBoxLayout()
generate_cmd = MyButton("Generate command", "default")
button_layout.addWidget(generate_cmd)
button_layout.addStretch(1)
layout.addLayout(button_layout)
self.conc1.valueChanged.connect(self._equalize_ic_conc)
self.conc2.valueChanged.connect(self._equalize_ic_conc)
generate_cmd.clicked.connect(self._generate_ic_command)
return ic_group
return None
def _equalize_ic_conc(self, new_val):
if self.conc1.value() == new_val: # conc1 was changed
self.conc2.setValue(100 - new_val)
else:
self.conc1.setValue(100 - new_val)
def _generate_ic_command(self):
match self.ic_selector.currentText():
case "IC0":
ic = "ic0"
case "IC1":
ic = "ic1"
case "IC2":
ic = "ic2"
cmd = (
f"dev.{ic}.fill("
+ f"gas1='{self.gas1.currentText()}', conc1={self.conc1.value()}, "
+ f"gas2='{self.gas2.currentText()}', conc2={self.conc2.value()}, "
+ f"pressure={self.pressure.value()}, wait=True)"
)
self.custom_edit.setText(cmd)
def _create_reffoil_form(self):
if "reffoilchanger" in self._dev:
reffoil_group = QGroupBox("Reference foil changer")
layout = QVBoxLayout(reffoil_group)
form = QFormLayout()
layout.addLayout(form)
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.FieldsStayAtSizeHint)
self.reffoil_selector = QComboBox()
available_foils = self._dev.reffoilchanger.get_all_foils()
self.reffoil_selector.addItems(available_foils)
form.addRow("Reference foil", self.reffoil_selector)
button_layout = QHBoxLayout()
generate_cmd = MyButton("Generate command", "default")
button_layout.addWidget(generate_cmd)
button_layout.addStretch(1)
layout.addLayout(button_layout)
generate_cmd.clicked.connect(self._generate_reffoil_command)
return reffoil_group
return None
def _generate_reffoil_command(self):
cmd = f"dev.reffoilchanger.insert(ref='{self.reffoil_selector.currentText()}', wait=True)"
self.custom_edit.setText(cmd)
def _collect_custom_result(self) -> dict:
text = self.custom_edit.text().strip()
@@ -129,7 +129,7 @@ def notify_item_finished(
text = text + f"\nScan number: {scan_number}"
text = text + f"\n\nCommand: {command}"
if error:
text += f"\n{error.strip().splitlines()[-1]}"
text += f"\n\n{error.strip().splitlines()[-1]}"
try:
send_notification(client, settings, text)
@@ -0,0 +1,61 @@
from bec_widgets.utils.colors import get_accent_colors
from qtpy.QtCore import Qt, Signal
# pylint: disable=E0611
from qtpy.QtGui import QKeySequence
from qtpy.QtWidgets import QListWidget, QPushButton
class MyListWidget(QListWidget):
deletePressed = Signal()
emptySpaceClicked = Signal()
copyPressed = Signal()
pastePressed = Signal()
def keyPressEvent(self, event):
if event.key() == Qt.Key_Delete and self.currentItem() is not None:
self.deletePressed.emit()
event.accept()
return
if event.matches(QKeySequence.StandardKey.Copy) and self.currentItem() is not None:
self.copyPressed.emit()
event.accept()
return
if event.matches(QKeySequence.StandardKey.Paste):
self.pastePressed.emit()
event.accept()
return
super().keyPressEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.itemAt(event.position().toPoint()) is None:
self.clearSelection()
self.setCurrentRow(-1)
self.emptySpaceClicked.emit()
return
super().mousePressEvent(event)
class MyButton(QPushButton):
def __init__(self, text="", color="default", parent=None):
self.color = color
super().__init__(text, parent)
self.apply_theme()
def apply_theme(self):
if self.isEnabled():
colors = get_accent_colors()
color = getattr(colors, self.color).name()
self.setStyleSheet(f"QPushButton {{ background-color: {color}; color: white; }}")
else:
self.setStyleSheet(
"QPushButton {{background-color: rgb(120, 120, 120); color: white;}}"
)
def setEnabled(self, enable: bool = True):
super().setEnabled(enable)
self.apply_theme()
@@ -22,13 +22,11 @@ from bec_lib.messages import VariableMessage
from bec_qthemes import material_icon
from bec_widgets.utils.bec_connector import ConnectionConfig
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.colors import get_accent_colors
from bec_widgets.utils.error_popups import SafeSlot
from pydantic import ValidationError
# pylint: disable=E0611
from qtpy.QtCore import Qt, QTimer, Signal
from qtpy.QtGui import QKeySequence
from qtpy.QtWidgets import (
QApplication,
QDialog,
@@ -37,11 +35,9 @@ from qtpy.QtWidgets import (
QHBoxLayout,
QLabel,
QLineEdit,
QListWidget,
QListWidgetItem,
QMessageBox,
QPlainTextEdit,
QPushButton,
QVBoxLayout,
QWidget,
)
@@ -58,6 +54,7 @@ from .notifications import (
notify_item_finished,
notify_schedule_state,
)
from .qt_widgets import MyButton, MyListWidget
from .schedule_item import Schedule, ScheduleItem
from .schedule_logic import index_of, pick_next_runnable, protected_prefix_length
@@ -97,60 +94,6 @@ class ScheduleWidgetConfig(ConnectionConfig):
schedule_name: str = "default_schedule"
class MyListWidget(QListWidget):
deletePressed = Signal()
emptySpaceClicked = Signal()
copyPressed = Signal()
pastePressed = Signal()
def keyPressEvent(self, event):
if event.key() == Qt.Key_Delete and self.currentItem() is not None:
self.deletePressed.emit()
event.accept()
return
if event.matches(QKeySequence.StandardKey.Copy) and self.currentItem() is not None:
self.copyPressed.emit()
event.accept()
return
if event.matches(QKeySequence.StandardKey.Paste):
self.pastePressed.emit()
event.accept()
return
super().keyPressEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.itemAt(event.position().toPoint()) is None:
self.clearSelection()
self.setCurrentRow(-1)
self.emptySpaceClicked.emit()
return
super().mousePressEvent(event)
class MyButton(QPushButton):
def __init__(self, text="", color="default", parent=None):
self.color = color
super().__init__(text, parent)
def apply_theme(self):
if self.isEnabled():
colors = get_accent_colors()
color = getattr(colors, self.color).name()
self.setStyleSheet(f"QPushButton {{ background-color: {color}; color: white; }}")
else:
self.setStyleSheet(
"QPushButton {{background-color: rgb(120, 120, 120); color: white;}}"
)
def setEnabled(self, enable: bool = True):
super().setEnabled(enable)
self.apply_theme()
class Scheduler(BECWidget, QWidget):
"""Schedule, persist and execute a sequence of BEC scan/device commands."""
@@ -1192,7 +1135,7 @@ class Scheduler(BECWidget, QWidget):
def _run_all(self, repeat_aborted_item):
notify_schedule_state(
self.client, self.schedule.notifications, self.schedule_name, "started"
self.client, self.schedule.notifications, self.schedule.schedule_name, "started"
)
namespace = {"scans": self.scans, "dev": self.dev}
while not self._abort_requested and not self._closing.is_set():
@@ -1207,15 +1150,18 @@ class Scheduler(BECWidget, QWidget):
self._execute_item(next_item, namespace)
if self._abort_requested:
notify_schedule_state(
self.client, self.schedule.notifications, self.schedule_name, "aborted"
self.client, self.schedule.notifications, self.schedule.schedule_name, "aborted"
)
elif self._closing.is_set():
notify_schedule_state(
self.client, self.schedule.notifications, self.schedule_name, "widget_closed"
self.client,
self.schedule.notifications,
self.schedule.schedule_name,
"widget_closed",
)
else:
notify_schedule_state(
self.client, self.schedule.notifications, self.schedule_name, "finished"
self.client, self.schedule.notifications, self.schedule.schedule_name, "finished"
)
with self._lock:
logger.info("in _run_all, set is_running to false")
@@ -1252,7 +1198,9 @@ class Scheduler(BECWidget, QWidget):
item.request_id = getattr(request, "requestID", None)
self._persist_locked()
report.wait()
# RPC commands may not return a status, thus have no wait()
if item.kind != "custom":
report.wait()
if self._closing.is_set():
# The widget was closed mid-item; exit without changing status in Redis.