Release 1.4.2: load-only dialog when nothing is selected
tests / fedora:latest (push) Successful in 1m15s
tests / ubuntu-latest · py3.11 (push) Successful in 1m32s
tests / ubuntu-latest · py3.13 (push) Successful in 1m1s
tests / archlinux:latest (push) Successful in 53s
tests / debian:12 (push) Successful in 51s
tests / ubuntu:24.04 (push) Successful in 1m8s
tests / NixOS (FHS wrapper from docs/NIXOS.md) (push) Skipped
Build PCM package / build (push) Successful in 10s

With neither mode derivable (nothing selected, no marker rectangles,
no config) the dialog now opens anyway instead of failing with an
error figure: both radios disabled with their reasons, OK/Save
disabled, Load config… live - a saved config can bootstrap the run.

README: note that macOS/NixOS field tests last ran with plugin v1.3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HjSRLWQ8ywBBYyakvr3YTx
This commit is contained in:
janik
2026-08-31 11:42:51 +07:00
co-authored by Claude Fable 5
parent 19327c23b1
commit 634df87fe0
9 changed files with 144 additions and 41 deletions
+47 -19
View File
@@ -26,7 +26,11 @@ Two run modes share the dialog, chosen by a radio at the top:
"Load config…" swaps the whole setup for another config file: ask()
then returns a LoadRequest instead of a Selection and main re-derives
everything from that file and reopens the dialog.
everything from that file and reopens the dialog. The dialog even
opens with NO runnable mode at all (nothing selected, no marker
rectangles, no config) - load-only: both radios unchecked and
disabled with their reasons, OK/Save disabled - so a saved config can
bootstrap the run.
Row identity is POSITIONAL: the tables never sort or reorder, so
main.py zips Selection.pdn_rows with its own parallel terminal list.
@@ -205,16 +209,28 @@ class _Dialog(QDialog):
self.mode_pdn = QRadioButton("PDN")
self.mode_classic.setEnabled(self._classic_ok)
self.mode_pdn.setEnabled(self._pdn_ok)
start_pdn = self._pdn_ok and (not self._classic_ok
or start_mode == "pdn")
(self.mode_pdn if start_pdn else self.mode_classic).setChecked(True)
reason = None
# with NEITHER mode derivable (nothing selected, no marker
# rectangles, no config terminals) the dialog still opens
# LOAD-ONLY: no radio checked, OK/Save disabled below - so
# "Load config…" can pull in a saved setup instead of the
# launch dying with an error figure
self._no_mode = not (self._classic_ok or self._pdn_ok)
if not self._no_mode:
start_pdn = self._pdn_ok and (not self._classic_ok
or start_mode == "pdn")
(self.mode_pdn if start_pdn
else self.mode_classic).setChecked(True)
reasons = []
if not self._classic_ok and classic_reason:
reason = f"Classic unavailable: {classic_reason}"
reasons.append(f"Classic unavailable: {classic_reason}")
self.mode_classic.setToolTip(classic_reason)
elif not self._pdn_ok and pdn_reason:
reason = f"PDN unavailable: {pdn_reason}"
if not self._pdn_ok and pdn_reason:
reasons.append(f"PDN unavailable: {pdn_reason}")
self.mode_pdn.setToolTip(pdn_reason)
if self._no_mode and load_dir is not None:
reasons.append("Load config… below can still set up a run "
"from a saved config file.")
reason = "\n".join(reasons) if reasons else None
# --- shared form #1 --------------------------------------------
form1 = QFormLayout()
@@ -387,14 +403,23 @@ class _Dialog(QDialog):
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttons.accepted.connect(self._try_accept)
buttons.rejected.connect(self.reject)
self.ok_button = buttons.button(QDialogButtonBox.Ok)
self.load_button = None
self.save_button = None
if load_dir is not None:
load_btn = buttons.addButton("Load config…",
QDialogButtonBox.ActionRole)
load_btn.clicked.connect(self._load_config)
self.load_button = buttons.addButton(
"Load config…", QDialogButtonBox.ActionRole)
self.load_button.clicked.connect(self._load_config)
if save_callback is not None:
save_btn = buttons.addButton("Save config…",
QDialogButtonBox.ActionRole)
save_btn.clicked.connect(self._save_config)
self.save_button = buttons.addButton(
"Save config…", QDialogButtonBox.ActionRole)
self.save_button.clicked.connect(self._save_config)
if self._no_mode:
# nothing to run or to save until a load re-derives; Load
# config… and Cancel stay live
self.ok_button.setEnabled(False)
if self.save_button is not None:
self.save_button.setEnabled(False)
content = QWidget()
lay = QVBoxLayout(content)
@@ -405,11 +430,12 @@ class _Dialog(QDialog):
mode_row.addWidget(self.mode_pdn)
mode_row.addStretch(1)
lay.addLayout(mode_row)
self.reason_label = None
if reason is not None:
rl = QLabel(reason)
rl.setWordWrap(True)
rl.setStyleSheet("color: gray; font-size: 10px;")
lay.addWidget(rl)
self.reason_label = QLabel(reason)
self.reason_label.setWordWrap(True)
self.reason_label.setStyleSheet("color: gray; font-size: 10px;")
lay.addWidget(self.reason_label)
lay.addLayout(form1)
if self.classic_section is not None:
lay.addWidget(self.classic_section)
@@ -997,7 +1023,9 @@ def ask(candidates: dict[str, list[str]], layer_order: list[str],
config file); pdn: the PDN terminal setup (editable tables);
pdn_candidates: net candidates for PDN mode (classic uses
`candidates`); classic_reason / pdn_reason: why a mode is
unavailable (its radio is disabled with the reason shown);
unavailable (its radio is disabled with the reason shown; with
BOTH unavailable the dialog opens load-only - OK and Save disabled
until Load config… returns a LoadRequest);
save_callback(selection, target_path) -> saved name string enables
the "Save config…" button in both modes (the file name is asked
per save, seeded with save_target); load_dir (the board directory)