From 6452b7cea46f18fa2f36ffa7d48148a7f32ea9f2 Mon Sep 17 00:00:00 2001 From: hitz_s Date: Thu, 24 Sep 2026 14:37:59 +0200 Subject: [PATCH] wip --- .../widgets/scheduler/schedule_logic.py | 10 +++-- .../widgets/scheduler/scheduler.py | 39 +++++++++++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/debye_bec/bec_widgets/widgets/scheduler/schedule_logic.py b/debye_bec/bec_widgets/widgets/scheduler/schedule_logic.py index 4073793..c229619 100644 --- a/debye_bec/bec_widgets/widgets/scheduler/schedule_logic.py +++ b/debye_bec/bec_widgets/widgets/scheduler/schedule_logic.py @@ -46,7 +46,7 @@ def index_of(items: list[ScheduleItem], item_id: str | None) -> int | None: def pick_next_runnable( - items: list[ScheduleItem], repeat_aborted_item + items: list[ScheduleItem], repeat_aborted_item, first_item: int = 0 ) -> ScheduleItem | Literal["stop"] | None: """ What the execution loop should do next: always re-derived from scratch @@ -57,9 +57,13 @@ def pick_next_runnable( earlier item failed/was aborted (execution stays parked there until the operator intervenes), or `None` if every item is COMPLETED. """ - logger.info(f"pick next runnable, repeat abort item is {repeat_aborted_item}") - for item in items: + logger.info( + f"pick next runnable, repeat abort item is {repeat_aborted_item} and first item is {first_item}" + ) + for idx, item in enumerate(items): logger.info(f"item: {item}") + if idx < first_item: + continue if item.status == ScheduleItemStatus.COMPLETED: continue if item.status == ScheduleItemStatus.ABORTED: diff --git a/debye_bec/bec_widgets/widgets/scheduler/scheduler.py b/debye_bec/bec_widgets/widgets/scheduler/scheduler.py index 76451fe..2bcf498 100644 --- a/debye_bec/bec_widgets/widgets/scheduler/scheduler.py +++ b/debye_bec/bec_widgets/widgets/scheduler/scheduler.py @@ -460,14 +460,16 @@ class Scheduler(BECWidget, QWidget): run_row = QHBoxLayout() self.run_btn = MyButton("Run / Continue", "success") - self.abort_btn = MyButton("Abort", "emergency") - self.reset_btn = MyButton("Reset", "warning") - for button in (self.run_btn, self.abort_btn, self.reset_btn): + self.run_from_current_btn = MyButton("Run / Continue from current selection", "success") + self.abort_btn = MyButton("Pause after current item", "emergency") + self.reset_btn = MyButton("Reset Schedule Status", "warning") + for button in (self.run_btn, self.run_from_current_btn, self.abort_btn, self.reset_btn): run_row.addWidget(button) run_row.addStretch() control_layout.addLayout(run_row) self.run_btn.clicked.connect(self.run_schedule) + self.run_from_current_btn.connect(self.run_schedule_from_selection) self.abort_btn.clicked.connect(self.abort_schedule) self.reset_btn.clicked.connect(self.reset_schedule) @@ -660,8 +662,10 @@ class Scheduler(BECWidget, QWidget): row = self._table_model.row_of(self._selected_item_id) if row is not None: self.table_view.selectRow(row) + self.run_from_current_btn.setEnabled(True) else: self._selected_item_id = None + self.run_from_current_btn.setEnabled(False) selection_model.blockSignals(False) @@ -722,6 +726,13 @@ class Scheduler(BECWidget, QWidget): self.copy_btn.setEnabled(selected_item is not None) self.paste_btn.setEnabled(self._clipboard_item is not None) + # Update execution control buttons + state = self._current_report is None # True if schedule is not running + self.run_btn.setEnabled(state) + self.run_from_current_btn.setEnabled(state) + self.abort_btn.setEnabled(not state) + self.reset_btn.setEnabled(state) + @SafeSlot() def _on_schedule_name_change(self): name = self.schedule_name_input.text() @@ -1103,7 +1114,7 @@ class Scheduler(BECWidget, QWidget): # RPC-exposed actions (USER_ACCESS) # ------------------------------------------------------------------ # @SafeSlot() - def run_schedule(self): + def run_schedule(self, first_item: int = 0): """Run the schedule, continuing from wherever it last left off.""" with self._lock: if self.schedule.is_running: @@ -1126,9 +1137,21 @@ class Scheduler(BECWidget, QWidget): self.submit_task( self._run_all, repeat_aborted_item, + first_item, on_complete=self._on_run_finished, on_failed=self._on_run_failed, ) + self._refresh_ui() + + @SafeSlot() + def run_schedule_from_selection(self): + """Run the schedule, starting from the current selected item.""" + if self._selected_item_id is None: + return + with self._lock: + idx = self._index_of_locked(self._selected_item_id) + if idx is not None: + self.run_schedule(idx) @SafeSlot() def abort_schedule(self): @@ -1314,9 +1337,9 @@ class Scheduler(BECWidget, QWidget): # the `schedule_changed` Qt signal so they are marshalled back onto # the GUI thread instead of touching widgets directly) # ------------------------------------------------------------------ # - def _pick_next_runnable_locked(self, repeat_aborted_item): + def _pick_next_runnable_locked(self, repeat_aborted_item, first_item): """Must be called while holding `self._lock`; see `schedule_logic.pick_next_runnable`.""" - return pick_next_runnable(self.schedule.items, repeat_aborted_item) + return pick_next_runnable(self.schedule.items, repeat_aborted_item, first_item) @SafeSlot() def _on_run_finished(self): @@ -1356,7 +1379,7 @@ class Scheduler(BECWidget, QWidget): super().cleanup() - def _run_all(self, repeat_aborted_item): + def _run_all(self, repeat_aborted_item, first_item): notify_schedule_state( self.client, self.schedule.notifications, self.schedule.schedule_name, "started" ) @@ -1369,7 +1392,7 @@ class Scheduler(BECWidget, QWidget): } while not self._abort_requested and not self._closing.is_set(): with self._lock: - next_item = self._pick_next_runnable_locked(repeat_aborted_item) + next_item = self._pick_next_runnable_locked(repeat_aborted_item, first_item) if next_item is None or next_item == "stop": logger.info("Next item is None or stop") break