Fix sweep/scan Stop button and state cleanup

_sweeping/_scanning flags were never reset when workers finished,
leaving the UI stuck in "Stopping..." forever. Both _do_sweep and
_do_scan now use try/finally to always clear state and reset button
styles. Firmware sweep checks the flag after the blocking serial
call returns and discards results if Stop was pressed mid-execution.
This commit is contained in:
Ryan Malloy 2026-02-14 16:50:08 -07:00
parent 3cd6424168
commit 2ee2f47275

View File

@ -434,6 +434,7 @@ class SignalScreen(Container):
if device is None: if device is None:
return return
try:
# Check if user forced software mode. # Check if user forced software mode.
force_software = self.query_one("#sweep-software-mode", Checkbox).value force_software = self.query_one("#sweep-software-mode", Checkbox).value
@ -447,10 +448,14 @@ class SignalScreen(Container):
exc_info=True, exc_info=True,
) )
self.app.call_from_thread( self.app.call_from_thread(
self._set_sweep_status, "Firmware sweep failed -- falling back..." self._set_sweep_status,
"Firmware sweep failed -- falling back...",
) )
self._do_sweep_software(device) self._do_sweep_software(device)
finally:
self._sweeping = False
self.app.call_from_thread(self._reset_sweep_buttons)
def _do_sweep_firmware(self, device: DeviceLike) -> None: def _do_sweep_firmware(self, device: DeviceLike) -> None:
"""Firmware-accelerated sweep via azscanwxp (runs in worker thread).""" """Firmware-accelerated sweep via azscanwxp (runs in worker thread)."""
@ -494,6 +499,11 @@ class SignalScreen(Container):
num_xponders=iterations, num_xponders=iterations,
) )
# Check if Stop was pressed while firmware was executing.
if not self._sweeping or shutdown.is_set():
self.app.call_from_thread(self._set_sweep_status, "Sweep stopped")
return
if not results: if not results:
self.app.call_from_thread( self.app.call_from_thread(
self._set_sweep_status, "Firmware sweep returned no data" self._set_sweep_status, "Firmware sweep returned no data"
@ -600,6 +610,11 @@ class SignalScreen(Container):
self.query_one("#sweep-progress", ProgressBar).update(progress=pct) self.query_one("#sweep-progress", ProgressBar).update(progress=pct)
self.query_one("#sweep-status-text", Static).update(status_text) self.query_one("#sweep-status-text", Static).update(status_text)
def _reset_sweep_buttons(self) -> None:
"""Restore sweep button styles to idle state (main thread)."""
self.query_one("#btn-start-sweep", Button).variant = "primary"
self.query_one("#btn-stop-sweep", Button).variant = "default"
# -- Sweep button handlers -- # -- Sweep button handlers --
def _handle_sweep_start(self) -> None: def _handle_sweep_start(self) -> None:
@ -616,12 +631,16 @@ class SignalScreen(Container):
self.query_one("#sweep-progress", ProgressBar).update(progress=0) self.query_one("#sweep-progress", ProgressBar).update(progress=0)
self._set_sweep_status("Starting sweep...") self._set_sweep_status("Starting sweep...")
self.query_one("#btn-start-sweep", Button).variant = "default"
self.query_one("#btn-stop-sweep", Button).variant = "warning"
self._sweeping = True self._sweeping = True
self._do_sweep() self._do_sweep()
def _handle_sweep_stop(self) -> None: def _handle_sweep_stop(self) -> None:
self._sweeping = False self._sweeping = False
self._set_sweep_status("Stopping...") self._set_sweep_status("Stopping...")
self._reset_sweep_buttons()
def _export_sweep_csv(self) -> None: def _export_sweep_csv(self) -> None:
if not self._sweep_data: if not self._sweep_data:
@ -647,12 +666,21 @@ class SignalScreen(Container):
@work(thread=True) @work(thread=True)
def _do_scan(self) -> None: def _do_scan(self) -> None:
"""Execute the AZ/EL grid scan in a background thread.""" """Execute the AZ/EL grid scan in a background thread."""
worker = get_current_worker()
shutdown = self.app.shutdown_event
device = self._device device = self._device
if device is None: if device is None:
return return
try:
self._do_scan_inner(device)
finally:
self._scanning = False
self.app.call_from_thread(self._reset_scan_buttons)
def _do_scan_inner(self, device: DeviceLike) -> None:
"""Inner scan logic (called from _do_scan worker thread)."""
worker = get_current_worker()
shutdown = self.app.shutdown_event
az_start = self._read_float("scan-az-start", 160.0) az_start = self._read_float("scan-az-start", 160.0)
az_end = self._read_float("scan-az-end", 220.0) az_end = self._read_float("scan-az-end", 220.0)
az_step = self._read_float("scan-az-step", 1.5) az_step = self._read_float("scan-az-step", 1.5)
@ -815,6 +843,11 @@ class SignalScreen(Container):
self.query_one("#scan-progress", ProgressBar).update(progress=pct) self.query_one("#scan-progress", ProgressBar).update(progress=pct)
self.query_one("#scan-status-text", Static).update(status_text) self.query_one("#scan-status-text", Static).update(status_text)
def _reset_scan_buttons(self) -> None:
"""Restore scan button styles to idle state (main thread)."""
self.query_one("#btn-start-scan", Button).variant = "primary"
self.query_one("#btn-stop-scan", Button).variant = "default"
# -- Scan button handlers -- # -- Scan button handlers --
def _handle_scan_start(self) -> None: def _handle_scan_start(self) -> None:
@ -831,12 +864,16 @@ class SignalScreen(Container):
self.query_one("#scan-progress", ProgressBar).update(progress=0) self.query_one("#scan-progress", ProgressBar).update(progress=0)
self._set_scan_status("Starting scan...") self._set_scan_status("Starting scan...")
self.query_one("#btn-start-scan", Button).variant = "default"
self.query_one("#btn-stop-scan", Button).variant = "warning"
self._scanning = True self._scanning = True
self._do_scan() self._do_scan()
def _handle_scan_stop(self) -> None: def _handle_scan_stop(self) -> None:
self._scanning = False self._scanning = False
self._set_scan_status("Stopping...") self._set_scan_status("Stopping...")
self._reset_scan_buttons()
def _export_scan_csv(self) -> None: def _export_scan_csv(self) -> None:
if not self._scan_data: if not self._scan_data: