✨ feat: collapsible flash sub-steps UI + erase_all checkbox
Rename erase_before_program → erase_all (semantics fix): - erase_all: true → -erase_all -erase_only (entire flash chip) - erase_all: false → -erase_only (sectors matching BIN size) Both options always erase; the checkbox controls scope, not yes/no. UI additions: - SubStepFrame widget: collapsible Erase/Program/Verify sub-steps with status dots (○/◉/●) and colored labels - Automatically expands on Step 2 start, collapses on completion - Phase and progress driven by real-time program_flash output parsing - Checkbox label: '整片Flash擦除 (Erase entire flash)' subprocess_utils.py phase detection: - Parses 'Performing Erase/Program/Verify Operation' for phase transitions - Emits 'phase' callbacks (erase/program/verify/done) - Progress % tracked within current phase
This commit is contained in:
+34
-8
@@ -52,6 +52,7 @@ from gui.widgets import (
|
||||
StatusDisplay,
|
||||
FileSelector,
|
||||
LogDisplay,
|
||||
SubStepFrame,
|
||||
)
|
||||
|
||||
|
||||
@@ -187,7 +188,7 @@ class MainWindow(ctk.CTk):
|
||||
self._config.bootloader_elf_path = files["bootloader_elf_path"]
|
||||
self._config.bootloader_bin_path = files["bootloader_bin_path"]
|
||||
self._config.fsbl_elf_path = files["fsbl_elf_path"]
|
||||
self._config.erase_before_program = self._erase_cb_var.get()
|
||||
self._config.erase_all = self._erase_cb_var.get()
|
||||
self._config.firmware_bin_path = files["firmware_bin_path"]
|
||||
|
||||
def _save_config(self) -> None:
|
||||
@@ -338,6 +339,16 @@ class MainWindow(ctk.CTk):
|
||||
pady=(0, 2),
|
||||
)
|
||||
|
||||
# Step 2: add collapsible sub-step frame
|
||||
if i == 1:
|
||||
self._sub_step_frame = SubStepFrame(panel)
|
||||
self._sub_step_frame.grid(
|
||||
row=i * 2 + 2, column=0,
|
||||
sticky="nsew",
|
||||
padx=PADDING_SMALL + 16,
|
||||
pady=(0, 2),
|
||||
)
|
||||
|
||||
# ── Progress indicator ──
|
||||
self._progress = ProgressIndicator(panel)
|
||||
self._progress.grid(
|
||||
@@ -468,10 +479,10 @@ class MainWindow(ctk.CTk):
|
||||
flash_frame.grid(row=6, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||
flash_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
self._erase_cb_var = ctk.BooleanVar(value=self._config.erase_before_program)
|
||||
self._erase_cb_var = ctk.BooleanVar(value=self._config.erase_all)
|
||||
self._erase_cb = ctk.CTkCheckBox(
|
||||
flash_frame,
|
||||
text="Erase flash before programming",
|
||||
text="整片Flash擦除 (Erase entire flash)",
|
||||
variable=self._erase_cb_var,
|
||||
font=FONT_SMALL,
|
||||
)
|
||||
@@ -748,20 +759,25 @@ class MainWindow(ctk.CTk):
|
||||
return False
|
||||
|
||||
def _flash_callback(self, status: str, message: str) -> None:
|
||||
"""Callback for flash programming progress — tracks phases."""
|
||||
"""Callback for flash programming — drives sub-step UI."""
|
||||
if status == "phase":
|
||||
# Track current flash phase for UI
|
||||
self._flash_phase = message # 'erase', 'program', 'verify', 'done'
|
||||
self._flash_phase = message
|
||||
phase_names = {"erase": "Erasing", "program": "Programming", "verify": "Verifying", "done": "Done"}
|
||||
label = phase_names.get(message, message)
|
||||
self._log_message(f" ▸ {label}")
|
||||
if hasattr(self, '_sub_step_frame'):
|
||||
self._sub_step_frame.set_phase(message)
|
||||
return
|
||||
if status == "progress":
|
||||
pct = message.rstrip('%')
|
||||
# Show progress within current phase
|
||||
try:
|
||||
pct = int(message.rstrip('%'))
|
||||
except ValueError:
|
||||
pct = -1
|
||||
phase_label = {"erase": "Erase", "program": "Program", "verify": "Verify"}.get(
|
||||
self._flash_phase, "Flash")
|
||||
self._log_message(f" [{phase_label}] {pct}%")
|
||||
if hasattr(self, '_sub_step_frame'):
|
||||
self._sub_step_frame.set_phase(self._flash_phase, pct)
|
||||
return
|
||||
self._log_message(f" [{status}] {message}")
|
||||
|
||||
@@ -1018,6 +1034,12 @@ class MainWindow(ctk.CTk):
|
||||
step_idx = i - start_index
|
||||
self._set_step_status(i, "running")
|
||||
|
||||
# Step 2: expand sub-step frame and reset
|
||||
if i == 1 and hasattr(self, '_sub_step_frame'):
|
||||
self._sub_step_frame.reset()
|
||||
self._sub_step_frame.set_expanded(True)
|
||||
self._flash_phase = ""
|
||||
|
||||
# Log step timeout estimate
|
||||
timeout_key, default_timeout = step_timeout_keys[i] if i < len(step_timeout_keys) else ("", 30)
|
||||
step_timeout = self._config.step_timeouts.get(timeout_key, default_timeout) if self._config else default_timeout
|
||||
@@ -1032,6 +1054,10 @@ class MainWindow(ctk.CTk):
|
||||
self._set_step_status(i, "complete" if success else "error")
|
||||
elapsed = time.time() - t_step_start
|
||||
self._log_message(f" ⏱ Step {i+1} completed in {elapsed:.0f}s")
|
||||
|
||||
# Step 2: mark all sub-steps done
|
||||
if i == 1 and hasattr(self, '_sub_step_frame'):
|
||||
self._sub_step_frame.set_phase("done")
|
||||
except Exception as e:
|
||||
results.append(False)
|
||||
self._set_step_status(i, "error")
|
||||
|
||||
Reference in New Issue
Block a user