spacr.qt.widgets.setup_slides

Present first-run preferences as a short sequence of explained choices.

Each slide covers one preference group and writes through the existing setup model. The animated backdrop, translucent card, and pointer-responsive border are decorative; preference editing and persistence remain available when those effects cannot be rendered.

spacr.qt.setup_screen holds the model and is the only writer of a preference; this module is presentation alone. setup_dialog is the earlier grouped-form layout of the same questions.

Classes

SetupSlides

The setup screen: one question per slide, over a moving backdrop.

Functions

graphics_card(→ Tuple[bool, str])

(usable, name) for the machine's graphics card.

greeting_for(→ str)

"Hello" in code, falling back to English.

open_setup_if_needed(→ Optional[SetupSlides])

Show the setup slides when the recorded setup state requires them.

verdict_ink(→ str)

The colour a GPU verdict is drawn in on the theme in force.

Module Contents

class spacr.qt.widgets.setup_slides.SetupSlides(parent: PySide6.QtWidgets.QWidget | None = None)[source]

Bases: PySide6.QtWidgets.QDialog

The setup screen: one question per slide, over a moving backdrop.

Parameters:

parent – parent widget.

Build the first-run slide deck.

Frameless, like the rest of the shell: the card it draws has rounded corners, and a square window frame around them is the seam this avoids.

Parameters:

parent – parent widget, or None.

accept() → None[source]

Close the slides and record that they have been seen.

RECORDED, so first-run guidance does not greet a returning user as a new one. While an installer is running the user is asked first (_may_close()): keeping it running leaves the screen open, and stopping it closes the screen as before.

agreed_to_terms() → bool[source]

Whether the acceptance box is ticked on this screen.

animation_choice() → str[source]

Which backdrop the slide is showing, or "" with no row.

answers() → Dict[str, Any][source]

What the slides currently say.

mouseMoveEvent(event)[source]

Aim the rim at the pointer. Ignored while a circuit runs.

Parameters:

event – the mouse move event; its position, mapped into the card, is what the rim flows towards. It is then passed on to the base class.

next() → int[source]

Forward one slide, and one CLOCKWISE circuit of the rim.

LEAVING THE LANGUAGE SLIDE WAITS. “there should be a lag after the first next click to make time for Hello in the chosen language” – the greeting is the only proof the choice took, and it lives on the page being left, so without a pause it is on screen for one frame of a fade. The rim starts its circuit immediately, so the click is answered at once and only the page change is held.

The wait happens ONCE. A pause on every return to the first slide would be a delay the user has already sat through.

THE TERMS SLIDE IS THE ONE PAGE THIS WILL NOT LEAVE. Pressing Next there without the acceptance ticked stays on the slide and says why, rather than greying the button and leaving the reader to work out which control is holding them.

previous() → int[source]

Back one slide, and one ANTICLOCKWISE circuit.

THE DIRECTION IS THE MESSAGE: it tells the user which way they went, which is worth more than the animation.

static provider_status(code: str, command: str) → str[source]

ready / signed out / not installed for one provider.

THREE STATES, because they need three different things from the user. available was one boolean covering “the CLI is missing” and “the CLI is there and signed out”, so a mark could not say which, and both were drawn as a ghost – “GPT brings no text and no color just a rim”.

Parameters:
  • code – provider code as in PROVIDERS, e.g. "claude" or "gpt"; looked up in the AI provider registry.

  • command – the provider’s CLI name, e.g. "codex"; tried in the registry after code, and checked on PATH when neither resolves.

reject() → None[source]

Dismissed at any slide. STILL MARKED ANSWERED.

Every question has a working default, so a user who closes this has chosen them – and reopening on every launch until it is filled in would make dismissing it impossible.

THE TERMS ARE THE EXCEPTION, and they are not marked. A dismissal is a choice of defaults; it is not an acceptance of a licence, so nothing is recorded and open_setup_if_needed asks again.

While an installer is running the user is asked first, as accept() asks; keeping it running leaves the screen open.

resizeEvent(event)[source]

Re-lay the slide for the new size.

Parameters:

event – the Qt resize event.

retranslate() → None[source]

Redraw every caption on this screen in the current language.

Through the catalog walker rather than through a tr() at each call site: this screen builds its slides from tables, and a walker that remembers each widget’s English source can switch from Swedish to Korean without translating a translation.

showEvent(event)[source]

Schedule terms-gate evaluation after the window has been laid out.

Whether the end of the terms document is visible depends on its rendered viewport. The zero-delay callback runs on the next event-loop turn, after Qt has completed layout for the show event.

Parameters:

event – the show event, passed to the base class first.

slide() → int[source]

Which slide is showing, counting from zero.

terms_were_read() → bool[source]

Always True: the acceptance is not gated on scrolling.

THE SCROLL GATE IS GONE. Dragging a scroll bar to the bottom of a long document does not prove it was read, so scrolling is not a condition of acceptance.

WHAT IS NOT GONE IS THE ACCEPTANCE. The full text is still on the page and still scrollable for anyone who wants it, the checkbox is still explicit, and spacr.qt.terms.record_agreement() still records the version and the moment. Only the greying is removed.

Kept as a method rather than deleted because the slide, the Next button and the tests all ask this question, and one answer in one place is easier to be sure of than a gate removed from four.

spacr.qt.widgets.setup_slides.graphics_card() → Tuple[bool, str][source]

(usable, name) for the machine’s graphics card.

USABLE MEANS TORCH CAN REACH IT, which is the only sense that matters here: a card spaCR cannot run on is not a compatible card however well the driver reports it. Torch is asked first for that reason, and NVML second because it names the card even when torch was built without CUDA – which is the case worth telling apart, since the answer there is “install a CUDA build”, not “buy a card”.

Returns:

(True, 'NVIDIA GeForce RTX 3090') when segmentation can run on it; (False, name) when it cannot, with the best name available; (False, '') when nothing could be identified.

spacr.qt.widgets.setup_slides.greeting_for(code: str) → str[source]

“Hello” in code, falling back to English.

Parameters:

code – language code, a key of GREETINGS such as "sv" or "zh_CN"; unknown or empty codes give "Hello".

spacr.qt.widgets.setup_slides.open_setup_if_needed(parent=None) → SetupSlides | None[source]

Show the setup slides when the recorded setup state requires them.

The centralized spacr.qt.setup_screen.should_open() check prevents independent callers from opening duplicate dialogs during one launch, and spacr.qt.terms.needs_agreement() adds the one condition a default cannot satisfy: terms that have never been accepted, or that have been rewritten since they were.

spacr.qt.widgets.setup_slides.verdict_ink(ok: bool) → str[source]

The colour a GPU verdict is drawn in on the theme in force.

GPU_YES_INK and GPU_NO_INK are the dark theme’s green and red, and on a light page that green is under 2.5:1. A light theme draws the verdict in its own success and error instead, which its contrast rules hold readable.

Parameters:

ok – whether the verdict is good news.

Returns:

a hex colour.

Nested helpers

SetupSlides._what_this_machine_can_do._answer(prefix)

The first answer whose task starts with prefix.

spacr/qt/widgets/setup_slides.py:2073