wip
This commit is contained in:
@@ -26,6 +26,11 @@ can reopen the dialog pre-filled instead of asking the user to start over.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from bec_lib.logger import bec_logger
|
||||
from bec_widgets.widgets.control.device_input.device_combobox.device_combobox import (
|
||||
BECDeviceFilter,
|
||||
DeviceComboBox,
|
||||
)
|
||||
from bec_widgets.widgets.control.scan_control.scan_control import ScanControl, ScanParameterConfig
|
||||
from qtpy.QtWidgets import (
|
||||
QCheckBox,
|
||||
@@ -42,7 +47,9 @@ from qtpy.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from .scan_form import list_movable_device_names
|
||||
from ..scan_control_xas.scan_control_xas import ScanControlXAS
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
_DSPIN_RANGE = (-1e12, 1e12)
|
||||
|
||||
@@ -68,6 +75,8 @@ class ScheduleItemDialog(QDialog):
|
||||
self._build_custom_tab()
|
||||
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
buttons.button(QDialogButtonBox.Ok).setText("Add")
|
||||
buttons.setStyleSheet("QPushButton {qproperty-icon: none;}")
|
||||
buttons.accepted.connect(self._on_accept)
|
||||
buttons.rejected.connect(self.reject)
|
||||
layout.addWidget(buttons)
|
||||
@@ -84,17 +93,17 @@ class ScheduleItemDialog(QDialog):
|
||||
# client=None resolves to the same process-wide BEC client
|
||||
# (bec_dispatcher.client) our own widget uses - no second Redis
|
||||
# connection is opened.
|
||||
self.scan_control = ScanControl(parent=tab, client=self._client)
|
||||
self.scan_control = ScanControlXAS(parent=tab, client=self._client)
|
||||
self.scan_control.button_run_scan.hide()
|
||||
layout.addWidget(self.scan_control)
|
||||
|
||||
hint = QLabel(
|
||||
"Configure the scan above, then confirm with OK below - it will be "
|
||||
"added to the schedule, not started immediately."
|
||||
)
|
||||
hint.setWordWrap(True)
|
||||
hint.setStyleSheet("color: gray;")
|
||||
layout.addWidget(hint)
|
||||
# hint = QLabel(
|
||||
# "Configure the scan above, then confirm with OK below - it will be "
|
||||
# "added to the schedule, not started immediately."
|
||||
# )
|
||||
# hint.setWordWrap(True)
|
||||
# hint.setStyleSheet("color: gray;")
|
||||
# layout.addWidget(hint)
|
||||
|
||||
self.tabs.addTab(tab, "Scan")
|
||||
|
||||
@@ -127,6 +136,7 @@ class ScheduleItemDialog(QDialog):
|
||||
name=scan_name, args=args, kwargs=kwargs
|
||||
)
|
||||
self.scan_control.current_scan = scan_name
|
||||
self.scan_control.restore_scan_parameters(scan_name)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# Move tab
|
||||
@@ -134,18 +144,23 @@ class ScheduleItemDialog(QDialog):
|
||||
def _build_move_tab(self):
|
||||
tab = QWidget()
|
||||
form = QFormLayout(tab)
|
||||
form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.FieldsStayAtSizeHint)
|
||||
|
||||
# self.move_device_combo = QComboBox()
|
||||
# self.move_device_combo.addItems(list_movable_device_names(self._dev))
|
||||
# self.move_device_combo.setMaxVisibleItems(12)
|
||||
|
||||
self.move_device_combo = DeviceComboBox(self, device_filter=[BECDeviceFilter.POSITIONER])
|
||||
|
||||
self.move_device_combo = QComboBox()
|
||||
self.move_device_combo.addItems(list_movable_device_names(self._dev))
|
||||
form.addRow("Device", self.move_device_combo)
|
||||
|
||||
self.move_value_spin = QDoubleSpinBox()
|
||||
self.move_value_spin.setDecimals(6)
|
||||
self.move_value_spin.setRange(*_DSPIN_RANGE)
|
||||
form.addRow("Target value", self.move_value_spin)
|
||||
form.addRow("Value", self.move_value_spin)
|
||||
|
||||
self.move_relative_check = QCheckBox("Relative move")
|
||||
form.addRow("", self.move_relative_check)
|
||||
self.move_relative_check = QCheckBox("")
|
||||
form.addRow("Relative move", self.move_relative_check)
|
||||
|
||||
self.tabs.addTab(tab, "Move")
|
||||
|
||||
@@ -209,6 +224,8 @@ class ScheduleItemDialog(QDialog):
|
||||
# showing the raw command text as-is.
|
||||
self.custom_edit.setText(state.get("text", initial["command"]))
|
||||
self.tabs.setCurrentIndex(2)
|
||||
else:
|
||||
logger.warning(f"Unknown kind: {kind}")
|
||||
|
||||
def _on_accept(self):
|
||||
try:
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
"""
|
||||
Small device-listing helper for the "Move" tab of `item_dialog.py`.
|
||||
|
||||
Scan parameter introspection/forms used to live in this module too, but
|
||||
that's now handled by embedding BEC's own `bec_widgets` `ScanControl`
|
||||
widget directly (see `item_dialog.py`) instead of a plugin-owned
|
||||
reimplementation - one less UI to keep in sync with BEC's scan
|
||||
capabilities.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def list_movable_device_names(dev) -> list[str]:
|
||||
"""Device names that expose `.move(...)` (i.e. positioner-like devices)."""
|
||||
return sorted(name for name, device in dev.items() if hasattr(device, "move"))
|
||||
@@ -18,6 +18,7 @@ from typing import Literal, Optional
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from bec_lib.logger import bec_logger
|
||||
from bec_lib.messages import VariableMessage
|
||||
from bec_qthemes import material_icon
|
||||
from bec_qthemes._theme import AccentColors
|
||||
from bec_widgets.utils.bec_connector import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
@@ -71,6 +72,15 @@ _STATUS_ICON = {
|
||||
ScheduleItemStatus.ABORTED: "\u23f9", # stop
|
||||
}
|
||||
|
||||
ICON_SIZE = 20
|
||||
_ICON_MAP = {
|
||||
ScheduleItemStatus.PENDING: ("hourglass", "#919090"),
|
||||
ScheduleItemStatus.RUNNING: ("cycle", "#2980b9"),
|
||||
ScheduleItemStatus.COMPLETED: ("check", "#27ae60"),
|
||||
ScheduleItemStatus.FAILED: ("warning", "#e74c3c"),
|
||||
ScheduleItemStatus.ABORTED: ("cancel", "#e74c3c"),
|
||||
}
|
||||
|
||||
|
||||
class ScheduleWidgetConfig(ConnectionConfig):
|
||||
"""
|
||||
@@ -239,16 +249,15 @@ class Scheduler(BECWidget, QWidget):
|
||||
edit_row.addWidget(button)
|
||||
edit_row.addStretch()
|
||||
schedule_layout.addLayout(edit_row)
|
||||
# layout.addLayout(edit_row)
|
||||
|
||||
# layout.addWidget(QLabel(f"Schedule: {self.schedule_name}"))
|
||||
schedule_layout.addWidget(QLabel(f"Schedule: {self.schedule_name}"))
|
||||
|
||||
self.list_widget = MyListWidget()
|
||||
self.list_widget.currentRowChanged.connect(self._on_selection_changed)
|
||||
self.list_widget.deletePressed.connect(self._on_delete_clicked)
|
||||
self.list_widget.emptySpaceClicked.connect(self._on_empty_space_clicked)
|
||||
# layout.ada(scheduldWidget(self.list_widget)
|
||||
self.list_widget.itemDoubleClicked.connect(self._on_edit_clicked)
|
||||
|
||||
schedule_layout.addWidget(self.list_widget)
|
||||
|
||||
self.add_btn.clicked.connect(self._on_add_clicked)
|
||||
@@ -270,7 +279,6 @@ class Scheduler(BECWidget, QWidget):
|
||||
run_row.addWidget(button)
|
||||
run_row.addStretch()
|
||||
control_layout.addLayout(run_row)
|
||||
# layout.addLayout(run_row)
|
||||
|
||||
self.run_btn.clicked.connect(self.run_schedule)
|
||||
self.abort_btn.clicked.connect(self.abort_schedule)
|
||||
@@ -288,7 +296,6 @@ class Scheduler(BECWidget, QWidget):
|
||||
settings_row.addWidget(self.guard_btn)
|
||||
settings_row.addStretch()
|
||||
settings_layout.addLayout(settings_row)
|
||||
# layout.addLayout(settings_row)
|
||||
|
||||
self.notifications_btn.clicked.connect(self._on_notifications_clicked)
|
||||
self.guard_btn.clicked.connect(self._on_guard_clicked)
|
||||
@@ -437,11 +444,26 @@ class Scheduler(BECWidget, QWidget):
|
||||
self.list_widget.clear()
|
||||
selected_row = None
|
||||
for row, item in enumerate(items):
|
||||
text = f"{_STATUS_ICON.get(item.status, '?')} {item.command}"
|
||||
|
||||
# text = f"{_STATUS_ICON.get(item.status, '?')} {item.command}"
|
||||
|
||||
text = f"{item.command}"
|
||||
|
||||
icon_name, color = _ICON_MAP[item.status]
|
||||
icon = material_icon(
|
||||
icon_name, size=(ICON_SIZE, ICON_SIZE), color=color, convert_to_pixmap=True
|
||||
)
|
||||
# if item.status == ScheduleItemStatus.RUNNING:
|
||||
# self._spin_anim.start()
|
||||
# else:
|
||||
# self._spin_anim.stop()
|
||||
# self._label.setPixmap(icon)
|
||||
|
||||
if item.error:
|
||||
text += f" ({item.error.strip().splitlines()[-1]})"
|
||||
list_item = QListWidgetItem(text, self.list_widget)
|
||||
list_item.setData(_ITEM_ID_ROLE, item.item_id)
|
||||
list_item.setIcon(icon)
|
||||
if item.item_id == self._selected_item_id:
|
||||
selected_row = row
|
||||
if selected_row is not None:
|
||||
@@ -450,32 +472,32 @@ class Scheduler(BECWidget, QWidget):
|
||||
self._selected_item_id = None
|
||||
self.list_widget.blockSignals(False)
|
||||
|
||||
idx = None
|
||||
selected_item = None
|
||||
if self._selected_item_id is not None:
|
||||
for i, item in enumerate(items):
|
||||
if item.item_id == self._selected_item_id:
|
||||
idx, selected_item = i, item
|
||||
break
|
||||
# idx = None
|
||||
# selected_item = None
|
||||
# if self._selected_item_id is not None:
|
||||
# for i, item in enumerate(items):
|
||||
# if item.item_id == self._selected_item_id:
|
||||
# idx, selected_item = i, item
|
||||
# break
|
||||
|
||||
# Editable = the selected item hasn't started yet. Thanks to the
|
||||
# protected-prefix invariant, everything after the first PENDING
|
||||
# item is guaranteed PENDING too, so a plain index/status check is
|
||||
# enough here - see module docstring.
|
||||
can_edit_selected = (
|
||||
selected_item is not None and selected_item.status == ScheduleItemStatus.PENDING
|
||||
)
|
||||
# # Editable = the selected item hasn't started yet. Thanks to the
|
||||
# # protected-prefix invariant, everything after the first PENDING
|
||||
# # item is guaranteed PENDING too, so a plain index/status check is
|
||||
# # enough here - see module docstring.
|
||||
# can_edit_selected = (
|
||||
# selected_item is not None and selected_item.status == ScheduleItemStatus.PENDING
|
||||
# )
|
||||
|
||||
self.run_btn.setEnabled(not is_running)
|
||||
# Adding is always safe: new items are clamped into the PENDING
|
||||
# suffix regardless of what's selected (see add_item()).
|
||||
self.add_btn.setEnabled(True)
|
||||
self.edit_btn.setEnabled(can_edit_selected)
|
||||
self.delete_btn.setEnabled(can_edit_selected)
|
||||
self.move_up_btn.setEnabled(can_edit_selected and idx not in (None, 0))
|
||||
self.move_down_btn.setEnabled(
|
||||
can_edit_selected and idx is not None and idx < len(items) - 1
|
||||
)
|
||||
# self.run_btn.setEnabled(not is_running)
|
||||
# # Adding is always safe: new items are clamped into the PENDING
|
||||
# # suffix regardless of what's selected (see add_item()).
|
||||
# self.add_btn.setEnabled(True)
|
||||
# self.edit_btn.setEnabled(can_edit_selected)
|
||||
# self.delete_btn.setEnabled(can_edit_selected)
|
||||
# self.move_up_btn.setEnabled(can_edit_selected and idx not in (None, 0))
|
||||
# self.move_down_btn.setEnabled(
|
||||
# can_edit_selected and idx is not None and idx < len(items) - 1
|
||||
# )
|
||||
|
||||
if self._guard.enabled:
|
||||
state = "OK" if self._guard.is_clear() else "PAUSED - waiting to resume"
|
||||
@@ -489,11 +511,39 @@ class Scheduler(BECWidget, QWidget):
|
||||
else:
|
||||
self.guard_status_label.hide()
|
||||
|
||||
def _update_buttons(self):
|
||||
with self._lock:
|
||||
items = list(self.schedule.items)
|
||||
is_running = self.schedule.is_running
|
||||
|
||||
idx = None
|
||||
selected_item = None
|
||||
|
||||
if self._selected_item_id is not None:
|
||||
for i, item in enumerate(items):
|
||||
if item.item_id == self._selected_item_id:
|
||||
idx, selected_item = i, item
|
||||
break
|
||||
|
||||
can_edit_selected = (
|
||||
selected_item is not None and selected_item.status == ScheduleItemStatus.PENDING
|
||||
)
|
||||
|
||||
self.run_btn.setEnabled(not is_running)
|
||||
self.add_btn.setEnabled(True)
|
||||
self.edit_btn.setEnabled(can_edit_selected)
|
||||
self.delete_btn.setEnabled(can_edit_selected)
|
||||
self.move_up_btn.setEnabled(can_edit_selected and idx not in (None, 0))
|
||||
self.move_down_btn.setEnabled(
|
||||
can_edit_selected and idx is not None and idx < len(items) - 1
|
||||
)
|
||||
|
||||
@SafeSlot()
|
||||
def _on_selection_changed(self, row: int):
|
||||
logger.info("On selection changed")
|
||||
item = self.list_widget.item(row) if row >= 0 else None
|
||||
self._selected_item_id = item.data(_ITEM_ID_ROLE) if item is not None else None
|
||||
self._refresh_ui()
|
||||
self._update_buttons()
|
||||
|
||||
@SafeSlot()
|
||||
def _on_empty_space_clicked(self):
|
||||
@@ -567,9 +617,10 @@ class Scheduler(BECWidget, QWidget):
|
||||
)
|
||||
self._selected_item_id = new_id
|
||||
self._refresh_ui()
|
||||
self._update_buttons()
|
||||
|
||||
@SafeSlot()
|
||||
def _on_edit_clicked(self):
|
||||
def _on_edit_clicked(self, row=None):
|
||||
if self._selected_item_id is None:
|
||||
return
|
||||
with self._lock:
|
||||
@@ -583,6 +634,8 @@ class Scheduler(BECWidget, QWidget):
|
||||
if initial is None:
|
||||
return
|
||||
|
||||
logger.info(f"Initial: {initial}")
|
||||
|
||||
dialog = ScheduleItemDialog(
|
||||
self.scans, self.dev, parent=self, initial=initial, client=self.client
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user