Repository navigation
Focus Test: default the Method to "Z axis steps" when the machine has a Z axis #550
Description
Split out from @atkaper's suggestion on #539 — the comment there asks for the "Z axis steps" method to become the default when the machine definition indicates it has a Z axis. Same idea, tracked separately so #539 stays about the Focus Test itself.
Would it be an idea to make the Method "Z- axis steps" the default if the machine definition indicates it supports a z-axis?
Most of this already exists
available_focus_modes() already does the capability filtering, and already orders the result by preference:
modes = [FocusMode.RAMP]
if machine is None or step_registry.get("CommandStep") is None:
return modes
if not _driver_uses_gcode(machine):
return modes
modes.insert(0, FocusMode.MANUAL)
if machine.has_z_axis:
modes.insert(0, FocusMode.Z_AXIS)
return modes
So Z_AXIS is already first whenever it is available. has_z_axis is the right indicator too — it is Machine.axes.get(Axis.Z) is not None, i.e. a 2-axis laser simply omits Z.
The gap is only the dialog's initial selection
FocusTestDialog.__init__ seeds itself from the dataclass default instead of from that ordered list:
d = FocusTestParams() # mode defaults to FocusMode.MANUAL
...
if d.mode in modes:
self.mode_row.set_selected(modes.index(d.mode))FocusTestParams.mode defaults to FocusMode.MANUAL, and MANUAL is in modes on every path that also reaches Z_AXIS, so the condition is always true and the dialog always opens on Manual — even on a Z-axis machine where Z_AXIS is offered and listed first. That is the whole of @atkaper's ask.
It also works by accident in the one case it handles correctly: with no machine, modes is [RAMP], MANUAL is not in it, nothing is selected, and the ComboRow falls back to index 0, which is RAMP.
Suggested shape
Default to the first available mode rather than to the dataclass value:
self.mode_row.set_selected(0)since modes is already preference-ordered. That falls out correctly in all three cases:
| machine | modes |
dialog opens on |
|---|---|---|
none, or no CommandStep, or non-G-code driver |
[RAMP] |
Ramp (unchanged) |
| G-code machine, no Z | [MANUAL, RAMP] |
Manual (unchanged) |
| G-code machine, has Z | [Z_AXIS, MANUAL, RAMP] |
Z axis steps (new) |
Two notes
Leave the dataclass default alone. FocusTestParams() has no machine reference — it cannot — and it doubles as the source for every other field in that dialog (start_offset, step, count, …). Changing mode there would change the default for headless and scripted creation too, where the safe answer is still Manual. The capability decision belongs in the dialog, next to the modes list that already knows the answer.
Worth considering but out of scope here: the method could be greyed out with an explanatory row rather than merely absent when there is no Z axis, so a user on a 2-axis machine learns why it is not offered. available_focus_modes() already returns without it, and the command rejects a Z_AXIS params object with ValueError, so the current behaviour is safe — it is just not self-explanatory.
UI tests for the dialog live in builtin_addons/rayforge-addon-laser/tests/ui_gtk/test_calibration_test_dialogs.py; the backend behaviour of available_focus_modes() is already covered in tests/doceditor/test_calibration_tests.py.
Activity
knipknap commentedon Oct 12, 2026
Thanks for the very clear write-up, and for splitting this out so #539 can stay focused on the Focus Test itself — that was the right call.
I agree with the suggestion. When a machine has a Z axis, there is no good reason the Focus Test should open on the manual method; the z-axis method is the more accurate and more convenient one, and it is exactly what a Z-axis machine is capable of. On machines without Z, nothing should change, and neither should the behaviour when no machine is set up at all.
Your analysis matches what I see in the pull request: the list of offered methods is already sorted so the best-capable one comes first, and the dialog simply does not use that ordering for its initial selection. Picking the first entry of that list instead of a fixed default gives exactly the behaviour you describe: Ramp for machines that can't do better, Manual for G-code machines without Z, and Z axis steps as soon as a Z axis is present. I'd also keep the general default untouched so anything created headless or by scripts keeps the safe Manual fallback — as you say, that decision belongs in the dialog.
I'll fold this into #539 before it merges, since that is where the dialog is being introduced. The greying-out idea with an explanatory hint for 2-axis machines is a nice touch; I'll consider it as a follow-up rather than holding up the test generators for it.
docs: mention the Focus Test's Z axis default in the changelog
switch87 commentedon Oct 13, 2026
This is in #539 as 73ca5b7: the dialog now starts on the first entry of the preference-ordered method list, so it opens on Z axis steps on a G-code machine with a Z axis, Manual without Z, and Ramp without a machine.
FocusTestParamskeeps Manual as its default. A UI test covers the Z axis case, and #539 now closes this issue.docs: mention the Focus Test's Z axis default in the changelog
docs: mention the Focus Test's Z axis default in the changelog
switch87 commentedon Oct 17, 2026
Reminder: this one stays parked on #539 (the Focus Test PR itself). #539 is rebased on current main with Lint, UI and Stress green, so this picks up as soon as it merges.
Nothing else is missing for the change:
available_focus_modes()already returns[Z_AXIS, MANUAL, RAMP]on a G-code machine with a Z axis, so the fix stays insideFocusTestDialog.__init__— seed from that ordered list instead of fromFocusTestParams().mode— plus a test.