spacr.qt.widgets.ambient¶
Ambient animated backdrop — soft motion behind every module screen.
The sequencing screen has its own backdrop (spacr.qt.widgets.dna_rain,
the ATGC cascade). This is the one for everything else: a slow, diffuse
animation that sits behind the settings form and the console, takes no focus
and no mouse events, and can be switched off entirely in Preferences.
Seven themes, chosen so each reads as a different kind of movement rather than as a re-skin of the same one:
blobs(default)Big and small colour blobs drifting over the page, each pulsing in size on its own period. They overlap and blend, so the result reads as soft colour fields rather than as a bag of circles.
auroraThree overlapping curtains of vertical rays, folding along their own length. The folds are travelling waves — several superposed frequencies running lengthwise along the arc — with brightness surges on a separate schedule, a sharp lower edge, a diffuse top, and the real thing’s vertical colour order: green through the body, red high up, a violet fringe underneath.
rippleConcentric rings expanding out of three fixed sources and fading as they grow, like rain on water. Soft-edged, so it never reads as line work.
driftA slow starfield in three parallax layers: small, dim, slow ones behind; bigger, brighter, faster ones in front. The one crisp theme. It travels up, down, or every which way — see
DRIFT_DIRECTIONS.bokehOut-of-focus points of light, the way a fluorescence field looks off the focal plane: an aperture image is a disc with a bright rim, not a Gaussian smudge, and the ones further out of focus are larger and flatter.
cellsCells drifting through the field, turning as they go — a soft body, a slightly brighter membrane where the edge is seen nearly edge-on, and a distinctly brighter nucleus set off centre.
resonanceSand on a vibrating plate, gathering along the lines that do not move. The only theme with an input other than the clock: while the music bed is playing it is driven by the bed’s own precomputed envelope and spectrum (
spacr.qt.resonance), and in silence — which is almost everybody, because sound is off on a fresh install — it breathes on its own. SeeResonanceEnginefor why that input is read inResonanceEngine.advance()and nowhere else.
There is an eighth engine, fractal, and it is not one of the seven: it is
not in AMBIENT_THEMES, no menu lists it, no preference can hold it,
and the only way to see it is to start the application with the spaceout
command instead of spacr. See SPACEOUT_THEME and
FractalEngine.
The random direction of drift produces Brownian-style motion without
duplicating the starfield as a separate theme. Themes dominated by many
antialiased lines or per-pixel noise are omitted because their raster cost is
too high for an always-running backdrop.
Palettes¶
Every theme declares the palettes it offers (palettes_for()), because a
palette that works as a 400 px blob does not necessarily work as a 2 px star.
PALETTE_SETS holds the colours themselves; spacr uses the three
brand hues from the module-maturity legend, and okabe is the Okabe–Ito set
for red–green colour deficiency (see its note).
Both dark and light¶
spaCR ships seven themes, and a blob set tuned only for a near-black page turns to mud on a white one. So the composition mode follows the background:
dark page ->
CompositionMode_Plus. Overlapping blobs add up and glow, which is what makes two circles read as one colour field. Additive over a light page just clips to white and the whole effect vanishes.light page ->
CompositionMode_Multiply, with the palette colour mixed toward white first. Multiply is the exact dual: overlaps get darker and still blend hue-wise, so the same geometry reads the same way.SourceOverwould let later blobs cover earlier ones, producing discrete discs instead of a blended field.
AmbientWidget.set_background_color() re-derives all of that, so a live
theme switch is one call.
Cost¶
This paints behind every module screen, on machines that are simultaneously running Cellpose on a GPU and a 40-plate pipeline, so cost is a correctness requirement rather than a nicety. Two things get it there:
The timer stops whenever the widget is not on screen — hidden, on another tab, or in a minimised window. Zero frames, zero CPU. These screens stay open for hours, so this is the whole ball game.
The soft themes are painted into a small reusable QImage and scaled up, never at full resolution. The buffer’s long edge is whatever the theme declares (
_BufferedEngine.base_edge) times the user’s resolution setting, so the diffuse fields shade ~37 000 pixels instead of ~2 000 000 and the aurora, which has real structure in it, shades ~520 000. On the synchronous path the one allocation happens on resize and never per frame; the shading thread copies the finished buffer once a frame so the GUI thread can blit it while the next one is being drawn, which measures 0.003 ms forblobsand 0.035 for the aurora’s 2 MiB — 2 % of the shading pass it makes safe.The shading happens on its own thread (
_FrameProducer), so the GUI thread’s whole share of a frame is onedrawImage. That is the next section, and it is the one that matters while a pipeline is running.
While a run is going¶
The animation can lag while a job is running, and the cause is not the obvious
one. Measurements on a real X server at 1920x1080 with a real ConsolePanel
under the real stylesheet and a real Qt event loop, blobs at the shipped
24 fps cap, best of five interleaved rounds:
condition |
delivered |
GUI paint |
|---|---|---|
idle |
25.0 fps |
2.04 ms |
a numpy thread (1024² matmul + FFT), flat out |
24.5 fps |
2.10 ms |
200 console lines a second, nothing else |
24.9 fps |
1.27 ms |
one pure-Python thread |
24.5 fps |
17.21 ms |
a worker doing Python work and printing |
17.3 fps |
17.16 ms |
So CPU saturation is not a cause: numpy releases the interpreter lock and a core burning flat out costs this module nothing. A signal flood is not a cause on its own: 200 lines a second are free, and it only bites in the thousands, where the console’s own per-line work saturates the GUI thread and nothing in this module can help. What is left is the interpreter lock: identical drawing work, eleven times slower, because the shading pass is Python and numpy and something else is holding the lock.
Translucent overlays can also trigger repaints outside the animation timer. The console sits over this widget, so each new line can expose it and request a full frame even while the animation timer is stopped. Expensive shading must therefore remain off the GUI thread even when the frame rate is capped.
The frame is split at the seam where the cost occurs. Per theme, milliseconds, idle against one Python thread, min of nine interleaved rounds:
theme |
shading (moved) |
soften + blit (stays) |
|---|---|---|
blobs |
0.240 -> 0.572 |
0.651 -> 1.097 |
aurora |
1.396 -> 7.176 |
0.797 -> 1.043 |
ripple |
0.367 -> 0.589 |
0.644 -> 1.196 |
bokeh |
0.663 -> 3.533 |
0.679 -> 1.008 |
cells |
0.538 -> 26.179 |
0.663 -> 0.909 |
drift |
0.528 -> 1.084 |
(no buffer) |
resonance |
1.072 -> see below |
0.842 -> 1.115 |
resonance is the one row whose contended figure is a range rather than a
number, and the shape of its shading is the reason. The others are one long
pass of QPainter calls; the plate is dozens of small NumPy calls, and
each one gives the lock back and then queues for it again, so what the cell
would measure is how often the shading thread was descheduled rather than
how much work the theme does. Four nine-round repeats of the protocol above
gave medians of 10, 174, 286 and 407 ms on the same machine. Two things make
that liveable and both are already here: the cost is paid on the producer
thread, so a late frame is a repeated frame
(AmbientWidget.repeated_frames) and never a slow interface; and
while a run is going the process holds
spacr.qt.gil_priority.BUSY_INTERVAL, where the same measurement is 6
to 24 ms. Its row was taken later than the rest and on a busier machine —
blobs read 0.262 and 0.749 -> 1.002 in the same run — so read it against
those rather than against the table.
The shading pass is sensitive to interpreter-lock contention, whereas the Qt
blit remains inexpensive. _BufferedEngine.shade() therefore runs in
_FrameProducer, while _BufferedEngine.blit() stays on the GUI
thread. If a shaded frame is not ready, the widget repeats the previous frame
instead of blocking the interface.
blobs, the default, goes 17.3 fps to 24.7 on the same worker. What this
does not address is a genuinely chatty run: at 200 lines a second both
land at about 4 fps, because by then the GUI thread is inside ConsolePanel
and not in here at all. Two levers finish the job and neither is in this file —
sys.setswitchinterval(0.001) in the Qt bootstrap (measured independently:
32 % of the frame rate to 99 %, for about 6 % of the worker’s throughput) and
coalescing PipelineWorker.line_ready.
Three constraints shape the implementation. Animation periods are sampled
from continuous ranges rather than replayed from a precomputed loop, avoiding
visible jumps and large frame caches. Shading runs outside the GUI thread; if
a frame is late, the previous frame is repeated and counted by
AmbientWidget.repeated_frames. The animation clock remains on the GUI
thread, and rendered frames remain deterministic functions of
(seed, clock, size).
drift keeps the synchronous path, and its row above is the reason: it is
the one engine with no buffer, it degrades the least of the seven (2.1x against
48.7x for cells), and threading it would mean publishing a full-resolution
frame — 7.91 MiB a slot against 126.6 KiB for blobs — to buy the smallest
improvement on the list.
Performance depends on hardware, display size, theme, and concurrent work.
The settings have predictable relative costs: detail is approximately
quadratic, density is approximately linear, and blur is inexpensive for the
six buffered themes. drift has no buffer, so its blur is a second wider
pass per dot and is capped by DRIFT_HALO_MAX_PX. The
WORK_BUDGET limits combinations of density and detail that would make
the backdrop compete with analysis work. Hidden widgets stop rendering
entirely.
Classes¶
The live backdrop: paints an |
|
Every user control that shapes the animation, in one value. |
Functions¶
|
Human label for an entry of |
|
One-line description of an animation choice, for a tooltip. |
|
The palette |
|
Resolve the ambient theme and palette for the current launch mode. |
|
Human label for a starfield direction, for a menu. |
|
One-line description of a starfield direction, for a tooltip. |
|
Put a live ambient backdrop behind |
|
True for anything the Animation preference may hold — including |
|
True when |
|
True when |
|
True when |
|
The |
|
Human label for |
|
One-line description of |
|
The palette names |
|
The animation controls, from the user's preferences. |
|
Human label for |
|
One-line description of |
|
How many ambient frames this process has painted. For tests. |
Module Contents¶
- class spacr.qt.widgets.ambient.AmbientWidget(parent: PySide6.QtWidgets.QWidget | None = None, *, theme: str = DEFAULT_THEME, palette: str = DEFAULT_PALETTE, background: PySide6.QtGui.QColor | str | None = None, backdrop=None, fps: int = DEFAULT_FPS, seed: int | None = None, blur: float | None = None, speed: float | None = None, size: float | None = None, resolution: float | None = None, density: float | None = None, direction: str | None = None, corner_radius: int = 0)[source]¶
Bases:
PySide6.QtWidgets.QWidgetThe live backdrop: paints an
AmbientEngineat a capped rate.Screen content sits in front of it, so it never takes focus, is transparent to mouse events, and lowers itself to the bottom of the sibling stacking order. It is fully opaque — it paints the page colour (or the theme wallpaper) itself and the animation on top — so the widget it covers has nothing to repaint underneath.
- Parameters:
parent – parent widget;
follow_parent()sizes it to that.theme – one of
AMBIENT_THEMES.palette – one of
palettes_for()for that theme. A palette that exists but is not offered by the theme is downgraded to the theme’s default (stale preferences must not break a screen); an unknown name raises.background – the flat colour under the animation; defaults to the current theme’s page colour.
backdrop – an image to paint under the animation instead of the flat colour — a path, a
QPixmap/QImage, orNone. Give it the Space/Cell wallpaper and the animation composites over the picture rather than replacing it.fps – frame-rate cap.
seed – RNG seed, for a reproducible animation.
blur – how much the finished picture is softened;
Nonereads Preferences.speed – motion multiplier;
Nonereads Preferences.size – element-size multiplier;
Nonereads Preferences.resolution – how much detail is shaded, as a multiplier on the theme’s own buffer;
Nonereads Preferences.density – how many elements are drawn, as a multiplier on the theme’s own count;
Nonereads Preferences.direction – which way the starfield travels, one of
DRIFT_DIRECTIONS;Nonereads Preferences. Meaningless to the other themes, and kept anyway so switching away and back does not lose it.corner_radius – round the backdrop’s own corners by this many px;
0leaves it square. CLIPPED, not masked – a mask region gives stair-stepped corners against the card’s anti-aliased rim – and applied before the base fill so the flat page colour is rounded with the animation rather than showing at the corners behind it.
Build the widget and start its engine.
- Parameters:
parent – parent widget.
- advance_frame(dt: float) None[source]¶
Step the animation by
dtseconds and schedule a repaint.Called directly by the tests and by the tutorial recorder, so no caller ever waits on a real clock. Re-shades before it returns, so the next paint shows the frame that was asked for rather than whatever the shading thread last finished — the timer does not come through here for exactly that reason (
_on_tick()).- Parameters:
dt – seconds to step; the engine scales the step by its speed, and a step that is not positive leaves the clock where it is.
- background_color() PySide6.QtGui.QColor[source]¶
The colour painted behind the animation.
A copy, so a caller cannot recolour this widget in place.
- Returns:
the background colour.
- changeEvent(event) None[source]¶
Follow a live theme switch when nobody else is going to.
A host that passed its own
backgroundowns that colour and is expected to re-set it (that is whatapp_screendoes, because it also has to re-resolve the wallpaper). A host that did not gets this for free instead of a stale dark rectangle on a white page.- Parameters:
event – the change event; it goes to the base class first, and only an
ApplicationPaletteChangeis acted on.
- direction() str[source]¶
Which way the starfield travels. Meaningless to the others, and kept anyway, so switching themes and back does not lose it.
- eventFilter(obj, event)[source]¶
Follow the parent’s size; pause when the window is minimised.
- Parameters:
obj – the object the event is for — the parent (whose resize this widget follows) or the watched top-level window.
event – the event; resize, window-state, hide, show, move and screen-change types are acted on, and every event is still passed on to the base-class filter.
- focusInEvent(event) None[source]¶
Reject even programmatic focus; this widget is decorative only.
- Parameters:
event – the focus event; it is ignored and focus is cleared.
- fps() int[source]¶
The cap on repaints per second.
A CAP, NOT A RATE. This is a backdrop and must not take frames from whatever the user is doing in front of it.
- Returns:
the frame cap.
- frames_shaded() int[source]¶
Frames the shading thread has finished for this backdrop.
Below
frames_paintedunder load, by design: the difference isrepeated_frames.
- hideEvent(event)[source]¶
The whole performance story: a screen the user is not looking at costs nothing. Qt sends this to the children of a hidden parent too, so switching tabs stops the animation on the tab you left.
- Parameters:
event – the hide event; passed on to the base class before the animation stops.
- paintEvent(event)[source]¶
Put the page down, then the newest frame the shading thread has.
This method never waits for anything. That is the requirement the whole change exists to meet — “a frame that is not ready is a repeated frame, never a blocked GUI thread” — and it is met the only way it can be:
_FrameProducer.latest()refuses the slot rather than blocking on it, the widget keeps a reference to the frame it is already showing, and a paint with nothing new blits that one again and counts it inrepeated_frames.With a shading thread the engine lock is never waited on here, and with no shading thread it cannot be contended — the one path that takes it is the case where nothing has been published yet (a widget shown at zero size and resized in the same tick), and it takes it without blocking, settling for the flat page if the thread is mid-frame.
Repainting is not only the timer’s doing: every console line the window paints over a translucent surface exposes this widget and Qt asks it for a whole frame. Measured on a real X server with the real stylesheet, one ambient repaint per console line — 0.99 of them, with the animation timer stopped. That is the fps cap being bypassed entirely, and it is why the cost of a repaint matters more than the cap suggests: shading it cost 1.7 ms idle and 22 ms under a Python worker, and blitting an already-shaded frame costs 0.24-0.32 ms at every load measured.
- Parameters:
event – the paint event; a new frame is taken from the shading thread only when its
rect()covers the whole widget, otherwise the current frame is repainted.
- palette_name() str[source]¶
The ambient palette’s name.
Not
palette()—QWidgetalready owns that name and it returns aQPalette.
- set_animating(on: bool) None[source]¶
Pause or resume without destroying anything.
A paused widget keeps its last frame on screen and its engine in memory; its timer stops ticking. This is the “off” switch for the Preferences toggle when the user wants the colours but not the motion — turning the feature off entirely is the install site’s job, not this widget’s.
- Parameters:
on – truthy to animate, falsy to pause.
- set_backdrop(source) None[source]¶
Paint
sourceunder the animation instead of the flat colour.The animation composites (adds on dark, multiplies on light), so a wallpaper handed in here shows through it rather than being replaced.
Nonegoes back to the flat fill.- Parameters:
source – an image path,
QPixmaporQImage, orNone; anything that does not load as a non-null pixmap also gives the flat fill.
- set_background_color(color: PySide6.QtGui.QColor | str) None[source]¶
Set the flat fill under the animation.
This is also what tells the engine whether it is painting on a dark or a light page, which picks additive versus multiply compositing — so it must be called on a live theme switch, or a dark-tuned frame ends up on a white page.
- Parameters:
color – a
QColoror any stringQColoraccepts; an invalid colour keeps the current one, and alpha is forced opaque. The widget then stops following the application theme.
- set_blur(value: float) None[source]¶
Set the softening. Clamped to
BLUR_RANGE.- Parameters:
value – the blur amount, converted with
float.
- set_density(value: float) None[source]¶
Set the element-count multiplier. Clamped to
DENSITY_RANGE.- Parameters:
value – the density multiplier, converted with
float.
- set_direction(name: str) None[source]¶
Set the starfield direction. An unknown name is ignored.
- Parameters:
name – one of
DRIFT_DIRECTIONS.
- set_fps(fps: int) None[source]¶
Cap the frame rate. Caps the shading thread with it, so a lowered cap actually reduces the work rather than just how much of it is shown.
- Parameters:
fps – frames per second, converted with
intand clamped toMIN_FPStoMAX_FPS.
- set_palette(name: str) None[source]¶
Switch colour set, live, keeping the motion exactly where it is.
Raises
ValueErrorif the current theme does not offername— an explicit request for a palette is not something to silently substitute. The one exception is thespaceoutdressing, where the request is replaced rather than refused, for the reason given indressed().- Parameters:
name – a palette the current theme offers.
- set_resolution(value: float) None[source]¶
Set the detail multiplier. Clamped to
RESOLUTION_RANGE.- Parameters:
value – the resolution multiplier, converted with
float.
- set_size_scale(value: float) None[source]¶
Set the element-size multiplier. Clamped to
SIZE_RANGE.- Parameters:
value – the size multiplier, converted with
float.
- set_speed(value: float) None[source]¶
Set the motion multiplier. Clamped to
SPEED_RANGE.Takes effect on the next step, so nothing already on screen moves.
- Parameters:
value – the speed multiplier, converted with
float.
- set_theme(name: str) None[source]¶
Switch animation, live. Raises
ValueErroron an unknown name.The clock carries over and the old engine is dropped — there is no second timer and no second engine, so a user flipping through the menu cannot leave anything ticking behind them. If the current palette is not one this theme offers, it downgrades to the theme’s default (see
palettes_for()for why the lists differ).Under the
spaceoutdressing every request lands on the fractal, whoever asked and for whatever — includingspacr.qt.preferences.apply_ambient_preferences(), which calls this with the stored animation on every settings save. Seedressed().- Parameters:
name – a paintable theme name.
- set_time(seconds: float) None[source]¶
Jump the animation clock and repaint.
- Parameters:
seconds – the new clock value, in animation seconds (the clock
advance_frame()steps, already scaled by speed).
- shading_thread_alive() bool[source]¶
Whether a shading thread is running for this backdrop.
The CPU guarantee used to be a claim about a timer and is now also a claim about a thread, so it needs something to assert on: a backdrop behind a screen nobody is looking at must not be keeping a core warm.
- showEvent(event)[source]¶
Start animating, and follow the window this widget belongs to.
- Parameters:
event – the Qt show event.
- size_scale() float[source]¶
The element-size multiplier; 1.0 is the shipped animation.
Not
size()—QWidgetalready owns that name and it returns aQSize.
- start() None[source]¶
Start the animation timer and shading worker if not already running.
The timer and worker share one lifetime. Hiding the widget, switching tabs, minimizing the window, or disabling ambient animation stops both. A widget that is never shown uses the synchronous rendering path.
- stop() None[source]¶
Stop ticking and retire the shading thread. Costs exactly nothing while stopped — no timer, no thread, and no frame held in memory.
Dropping the published frame matters because these screens stay built: a dozen module screens the user has visited would otherwise each keep a slot warm behind a tab nobody is on, which is 2 MiB apiece for the aurora. The next
start()shades a replacement before it starts the thread, so there is nothing to show for it.
- property engine: AmbientEngine[source]¶
The live engine. Replaced wholesale by
set_theme().
- class spacr.qt.widgets.ambient.Motion[source]¶
Bases:
NamedTupleEvery user control that shapes the animation, in one value.
A named tuple rather than five arguments because the set grows: it went from three to five in one change, and every install site that had unpacked a plain tuple would have broken.
- Parameters:
blur – softness of the painted shapes; the widget clamps it to
BLUR_RANGE.speed – animation-clock multiplier, clamped to
SPEED_RANGE.size – element-size multiplier, clamped to
SIZE_RANGE.resolution – detail (render buffer) multiplier, clamped to
RESOLUTION_RANGE.density – element-count multiplier, clamped to
DENSITY_RANGE.direction – starfield drift direction, one of
DRIFT_DIRECTIONS; an unknown name falls back to the default.
- spacr.qt.widgets.ambient.animation_label(name: str) str[source]¶
Human label for an entry of
ANIMATION_CHOICES.“None” rather than “Off”: the row is called Animation and this is one of the animations it can be set to, the way a font size can be set to zero.
- Parameters:
name –
NO_ANIMATIONor a paintable theme name; any other name raisesValueError.
- spacr.qt.widgets.ambient.animation_note(name: str) str[source]¶
One-line description of an animation choice, for a tooltip.
The note for “None” states the cost, because that is the only reason a reader picks it — and the claim is asserted rather than advertised: see
tests/qt/test_ambient_none.py, which counts painted frames over a real second instead of trusting this sentence.- Parameters:
name –
NO_ANIMATIONor a paintable theme name; any other name raisesValueError.
- spacr.qt.widgets.ambient.default_palette_for(theme: str) str[source]¶
The palette
themefalls back to —DEFAULT_PALETTEwhen it is on offer, otherwise the first one listed.- Parameters:
theme – a paintable theme name; an unknown one raises
ValueError.
- spacr.qt.widgets.ambient.dressed(theme: str, palette: str) Tuple[str, str][source]¶
Resolve the ambient theme and palette for the current launch mode.
Standard launches preserve the requested pair. Spaceout launches return
SPACEOUT_THEMEandSPACEOUT_PALETTE.- Parameters:
theme – the requested theme name; returned as given unless the spaceout dressing is on. It is not validated here.
palette – the requested palette name, treated the same way.
- spacr.qt.widgets.ambient.drift_direction_label(name: str) str[source]¶
Human label for a starfield direction, for a menu.
- Parameters:
name – one of
DRIFT_DIRECTIONS; any other value raisesValueError.
- spacr.qt.widgets.ambient.drift_direction_note(name: str) str[source]¶
One-line description of a starfield direction, for a tooltip.
- Parameters:
name – one of
DRIFT_DIRECTIONS; any other value raisesValueError.
- spacr.qt.widgets.ambient.install_ambient(host: PySide6.QtWidgets.QWidget, layout=None, *, theme: str = DEFAULT_THEME, palette: str = DEFAULT_PALETTE, backdrop=None, corner_radius: int = 0, **kwargs) AmbientWidget[source]¶
Put a live ambient backdrop behind
host.The widget becomes a child of
host, tracks its geometry, and is lowered to the bottom of the sibling stacking order so every screen widget paints in front of it. It takes no focus and no mouse events, and it does not tick untilhostis actually on screen.Note that a backdrop is only as visible as its siblings are transparent: under dark and light every container is an opaque page colour, and an animation behind them reaches the eye through nothing but the few pixels of layout spacing. The caller is responsible for clearing those surfaces first — see
AppScreen._clear_page_surfaces.- Parameters:
host – the screen the animation sits behind.
layout – accepted for signature compatibility with
spacr.qt.widgets.dna_rain.install_dna_rain(), which appends its settings bar to it. The ambient backdrop has no on-screen controls — it is configured in Preferences — so nothing is added here, and the two installers stay interchangeable at a call site.theme – one of
AMBIENT_THEMES.palette – one of
palettes_for()for that theme.backdrop – wallpaper to composite over; see
AmbientWidget.set_backdrop().corner_radius – Radius in pixels used to clip the backdrop. The default of zero leaves square corners. For a frameless dialog containing a rounded card, use the card’s radius so the backdrop does not extend beyond its corners.
kwargs – forwarded to
AmbientWidget(background,fps,seed,blur,speed,size,resolution,density,direction). Everything frombluron defaults to the user’s preferences, so a caller that does not care about them should not pass them.
- Returns:
the widget, already shown and lowered.
- spacr.qt.widgets.ambient.is_animation_choice(name) bool[source]¶
True for anything the Animation preference may hold — including
NO_ANIMATION, whichis_valid_theme()rejects because it cannot be painted.- Parameters:
name – the value to test, typically a stored preference.
- spacr.qt.widgets.ambient.is_valid_drift_direction(name) bool[source]¶
True when
nameis one ofDRIFT_DIRECTIONS. Never raises.- Parameters:
name – the value to test.
- spacr.qt.widgets.ambient.is_valid_palette(theme, palette) bool[source]¶
True when
paletteis offered bytheme. Never raises.- Parameters:
theme – the theme name; an unknown theme offers nothing.
palette – the palette name to look for among
theme’s palettes.
- spacr.qt.widgets.ambient.is_valid_theme(name) bool[source]¶
True when
nameis one ofAMBIENT_THEMES.The predicate exists so a caller validating stored preferences does not have to catch
ValueErrorfrom the strict accessors below.- Parameters:
name – the value to test, typically a stored preference.
- spacr.qt.widgets.ambient.palette_colors(theme: str, palette: str) Tuple[str, ...][source]¶
The
#rrggbbcolours behindpalette, fortheme.- Parameters:
theme – a paintable theme name.
palette – a palette
themeoffers; an unknown theme, or a palette the theme does not offer, raisesValueError.
- spacr.qt.widgets.ambient.palette_label(theme: str, palette: str) str[source]¶
Human label for
paletteas offered bytheme.- Parameters:
theme – a paintable theme name.
palette – a palette
themeoffers; an unknown theme, or a palette the theme does not offer, raisesValueError.
- spacr.qt.widgets.ambient.palette_note(theme: str, palette: str) str[source]¶
One-line description of
palette, for a tooltip.- Parameters:
theme – a paintable theme name.
palette – a palette
themeoffers; an unknown theme, or a palette the theme does not offer, raisesValueError.
- spacr.qt.widgets.ambient.palettes_for(theme: str) Tuple[str, ...][source]¶
The palette names
themeoffers, in menu order.Never empty. Raises
ValueErroron an unknown theme rather than returning(), because an empty tuple reads as “this theme has no palettes” and would quietly leave a settings menu blank.- Parameters:
theme – a paintable theme name.
- spacr.qt.widgets.ambient.preferred_motion() Motion[source]¶
The animation controls, from the user’s preferences.
Read here rather than passed in by every install site, for the same reason
_theme_background()is: the two callers that build ambient widgets are a module screen and Home, and neither of them has any business knowing what the animation’s knobs are called. Falls back to the shipped defaults if preferences cannot be read at all.
- spacr.qt.widgets.ambient.theme_label(name: str) str[source]¶
Human label for
name, for a menu. Raises on an unknown theme.- Parameters:
name – a paintable theme name; any other raises
ValueError.
- spacr.qt.widgets.ambient.theme_note(name: str) str[source]¶
One-line description of
name, for a tooltip.- Parameters:
name – a paintable theme name; any other raises
ValueError.