spacr.qt.widgets.setup_card

Render first-run questions in a rounded, pointer-responsive card.

The accent follows the corner nearest the pointer. All visual effects are decorative: if a blur, background, or accent cannot be painted, the card continues to display its questions and save their answers.

Classes

SetupCard

A rounded, translucent card whose border lights at the near corner.

Module Contents

class spacr.qt.widgets.setup_card.SetupCard(parent: PySide6.QtWidgets.QWidget | None = None, *, radius: int = 18, arc: int | None = None, lag: float | None = None, align: str = '', mode: str = '')[source]

Bases: PySide6.QtWidgets.QWidget

A rounded, translucent card whose border lights at the near corner.

Parameters:
  • radius – corner radius in pixels.

  • arc – how far along each edge the accent runs, in pixels.

Build the card and the accent that travels round its rim.

Parameters:
  • parent – parent widget.

  • radius – the card’s corner radius in pixels.

  • arc – how long the travelling accent is. None takes the preference.

  • lag – how hard the accent chases the pointer, 0 to 1. None takes the preference.

  • align – whether the accent is centred on the pointer or trails behind it. "" takes the preference.

  • mode – which look the card wears; "" for the default.

THE THREE THAT ARE A MATTER OF TASTE – arc, lag and align – default to the preference store rather than to constants, because how long the run is and how hard it chases are things to look at and decide about. A caller that wants a particular look for a particular card can still say so, which is why they are parameters at all and not read from preferences outright.

accent_alpha(along: float) → float[source]

Opacity at along (0 at one end, 1 at the peak), 0..1.

Both ends fall to zero, so neither has an edge to arrive or leave by. Where the peak sits is accent_peak().

Parameters:

along – position along the lit run, 0 at the tail and 1 at the head; clamped to that range.

accent_peak() → float[source]

Where along the run the accent is brightest, 0..1.

THE BRIGHT PART IS WHAT THE POINTER IS ON. With the run CENTRED on the pointer the peak belongs in the middle, or the brightest part sits to one side of the thing it is pointing at; with the run trailing from its head, it belongs near the front, where a wake is brightest.

accent_span(rect: PySide6.QtCore.QRectF) → float[source]

How much of the rim is lit, as a fraction of its length.

THE SAME FRACTION ON EVERY CARD, so a settings popup and the first-run window wear one rim rather than two takes on it. The arc preference is a length in pixels and is read as the length it should look on the REFERENCE surface; measured against each card’s own perimeter instead, the same 280 px covers a sixth of the setup window and two fifths of a small popup, and the short one reads as a thick bright band – “the rim is to thick and bright. make the rim and window look exactly like the setup spacr window.”

Parameters:

rect – the card’s drawing rectangle; not read, because the fraction is measured on the reference card size so every card’s rim looks alike.

accent_start(span: float) → float[source]

Where the lit run begins, as a fraction of the rim.

With centre alignment the MIDDLE of the run lands on position, which is what puts the light on the pointer rather than beside it; with head the run ends there and trails backwards.

Parameters:

span – length of the lit run as a fraction of the rim, as returned by accent_span().

alignment() → str[source]

'centre' or 'head' – where the run sits on the pointer.

animates() → bool[source]

Whether the rim changes when nothing else does.

A pulse and a moving spectrum have to repaint on a still card; a plain glow must NOT, or an arrived rim costs sixty composites a second over a live backdrop for no visible change. Under the spaceout dressing every mode oscillates, so every mode paints.

beat() → float[source]

A multiplier on the run’s brightness, 0..1.

One for every mode but beat, which breathes: the run brightens and dims on a steady cycle. It never reaches zero – a rim that vanishes reads as a fault rather than as a pulse.

circuit(clockwise: bool = True) → None[source]

Send the accent once round: clockwise, or anti- for Previous.

THE DIRECTION IS THE MESSAGE. It tells the user which way they went through the slides, which is worth more than the animation – so a circuit is never merged with another or shortened to catch up.

corner() → str[source]

The corner the accent is currently on.

ease() → float[source]

How far the accent closes the gap to the pointer each frame.

event(event)[source]

Handle the events Qt gives no named handler for.

Parameters:

event – the Qt event.

Returns:

True when handled here.

flow_towards(point: PySide6.QtCore.QPointF) → None[source]

Aim the accent at point. Ignored while a circuit is running.

A point at the exact centre names no direction and is ignored too, rather than being read as the top-left corner.

Parameters:

point – position in the card’s own widget coordinates; it may lie outside the card.

hideEvent(event)[source]

A card nobody is looking at does not need sixty frames a second.

Parameters:

event – the hide event; it is not inspected, only passed on to the base class before the animation timer stops.

ink_at(along: float, accent: PySide6.QtGui.QColor) → PySide6.QtGui.QColor[source]

The colour of the run at along (0 at the tail, 1 at the head).

Outside spaceout, glow and beat retain the blue rim; rainbow walks the hue along the run and turns it over time, so the light carries a spectrum that moves rather than a band that sits still.

UNDER spaceout THE RIM OSCILLATES IN EVERY MODE. The mark that follows the pointer is the one thing on a card that is already moving, and leaving it a fixed blue under a theme whose whole point is a moving spectrum is what the request is about. Its hue is the palette’s own drift plus a much faster cycle of its own (SPACEOUT_RIM_PERIOD), so it belongs to the same spectrum the rest of the window is travelling through instead of being a second unrelated rainbow.

The shipped Beat mode retains its blue hue while brightness pulses.

Parameters:
  • along – position along the lit run, 0 at the tail and 1 at the head.

  • accent – the theme’s accent colour; returned as a copy in the plain modes, and its saturation and value set the floor for the spectral ones.

mode() → str[source]

'glow', 'rainbow' or 'beat'.

mouseMoveEvent(event)[source]

Track the pointer so the rim can chase it.

Parameters:

event – the Qt mouse event.

nearest_corner(point: PySide6.QtCore.QPointF) → int[source]

Index into CORNERS of the corner nearest point.

Ties go to the earlier corner, which only happens at the exact centre and is therefore never seen.

Parameters:

point – position in the card’s own widget coordinates.

paintEvent(event)[source]

Draw the card and its animated rim.

Parameters:

event – the Qt paint event.

perimeter_position(point: PySide6.QtCore.QPointF)[source]

Where on the rim point is, as a fraction clockwise from 0.

THE RAY FROM THE CENTRE, not the nearest edge. Projecting a point onto whichever edge happens to be closest is discontinuous along the diagonals: the pointer crosses one and the target jumps from the middle of the top edge to the middle of the left, which is what was reported as the rim being unsynced with the pointer inside the card. The point where the ray from the centre through the pointer leaves the rectangle moves continuously, and is always the place on the rim that is actually in the pointer’s direction – which is what “flows towards the mouse” has to mean if the mouse can be anywhere.

Because it is a RAY it needs no clamping, and answers for a pointer outside the card as readily as for one inside it.

THE RIM IS TREATED AS A RECTANGLE, not as the rounded path it is drawn on. The corner arcs are a few pixels of a perimeter hundreds long, and paying for exact arc length here would buy an accuracy no eye can see on a value that is chasing a mouse anyway.

Parameters:

point – position in the card’s own widget coordinates; it may lie outside the card.

Returns:

the fraction, or None when the pointer is exactly at the centre and no direction can be read from it.

period() → float[source]

Seconds for one pulse, or one turn of the hue.

reread_the_preferences() → None[source]

Take the length, lag and alignment again, and redraw.

Called when the settings that own them change, so the card the user is looking at while they drag a slider is the one that answers.

showEvent(event)[source]

Start following as soon as there is something to follow.

Parameters:

event – the show event; it is not inspected, only passed on to the base class before following starts.

spaceout() → bool[source]

Return whether spaceout rendering is enabled for this process.

spaceout_hue(along: float) → float[source]

Return the animated spaceout hue at a normalised rim position.

Parameters:

along – Position along the rim in [0, 1].

Returns:

Hue-wheel position in [0, 1].

property position: float[source]

The accent’s place on the rim, 0..1 clockwise from the top-left.

property spinning: bool[source]

Whether a circuit is running.

Nested helpers

SetupCard.spaceout.read()

The current spaceout choice from the card’s dressing.

spacr/qt/widgets/setup_card.py:444

SetupCard.spaceout_hue.read()

The current spaceout hue from the card’s dressing.

spacr/qt/widgets/setup_card.py:738