spacr.qt.tutorial.engine
Tutorial rendering engine — narration, cursor overlay, capture, mux.
The pipeline is intentionally linear and easy to reason about:
Synthesize each Step’s narration through Piper → WAV, know duration
Spin up the MainWindow (on real DISPLAY or under Xvfb) at 1920x1080
Walk through the Steps, capturing frames at 30 fps into a scratch dir. Each step gets ceil(narration_s * 30) + hold frames budget. A synthesized cursor (little arrow drawn onto each frame) animates to each step’s target widget before the step’s action fires.
Concatenate all step WAVs → one audio track
ffmpeg -framerate 30 -i frames/%06d.png -i audio.wav … out.mp4
Emit matching .srt sidecar
The engine has no Qt-specific business logic — that lives in per-app scripts.py functions that return a list of Steps.
Module Contents
- class spacr.qt.tutorial.engine.Step[source]
A single narrated beat of a tutorial.
- Fields:
narration: what the narrator says (also becomes the subtitle) action: optional callable that mutates the UI. Runs AFTER
the cursor animation completes but BEFORE the narration finishes playing.
- target: optional (widget, point-in-widget) the cursor
animates to before the action fires. Point is relative to widget. Pass a QWidget with point omitted to target its center.
- hold_ms: extra silence at the end of the step, in ms.
Useful to let a UI change settle before the next step begins.
- highlight: optional widget to draw a soft highlight ring
around while this step runs.
- class spacr.qt.tutorial.engine.Narrator(voice_model: pathlib.Path | None = None, length_scale: float = 1.0)[source]
Synthesize step narration WAVs using Piper.
Uses the Piper CLI (already installed via pip install piper-tts). Voice model defaults to ~/.spacr/piper/en_US-lessac-medium.onnx but any Piper .onnx can be passed via voice_model=.
- class spacr.qt.tutorial.engine.Recorder(window, frames_dir: pathlib.Path, fps: int = FRAME_RATE, size: Tuple[int, int] = VIDEO_SIZE)[source]
Grab the MainWindow’s rendered pixmap N times a second, compositing a synthetic cursor onto each frame.
- Parameters:
window – source Qt window to grab.
frames_dir – destination folder for numbered PNG frames.
fps – capture frame rate.
size – fixed output frame size
(width, height)in px.
- class spacr.qt.tutorial.engine.RenderResult[source]
Output paths and metadata for a completed tutorial render.
- Variables:
mp4 – absolute path to the produced MP4.
srt – absolute path to the produced SRT sidecar.
frames – total number of frames captured.
duration_s – total narration duration in seconds.
- class spacr.qt.tutorial.engine.Director(window, steps: List[Step], out_dir: pathlib.Path, narrator: Narrator | None = None, fps: int = FRAME_RATE)[source]
Orchestrates narration, capture, and mux into a final MP4 + SRT.
- Parameters:
- render(name: str) RenderResult[source]
Run the full narrate → capture → mux pipeline for this director.
- Parameters:
name – base filename for the produced
<name>.mp4and.srt.- Returns:
RenderResultwith paths and duration metadata.
- spacr.qt.tutorial.engine.render_tutorial(app_key: str, out_dir: pathlib.Path | None = None, voice_model: pathlib.Path | None = None, length_scale: float = 1.0) RenderResult[source]
Boot MainWindow, run the per-app tutorial script, render MP4.
Returns a RenderResult describing where the MP4 and SRT ended up.