simvx.core.scripted_demo¶
Scripted demo playback: automated input sequences with narration and assertions.
Drive a game/UI scene with pre-recorded steps: move cursor, click, type text, press keys, wait, assert state, and show narration overlays. Useful for creating self-playing demos, tutorials, and integration tests.
Usage: from simvx.core.scripted_demo import DemoRunner, MoveTo, Click, Narrate, Assert
steps = [
Narrate("Welcome to the demo!", duration=2.0),
MoveTo(200, 150, duration=0.5),
Click(200, 150),
Assert(lambda g: g.board[0][0] == "X", "Cell should be X"),
]
runner = DemoRunner(steps, speed=2.0)
game.add_child(runner)
Module Contents¶
Classes¶
Smoothly move the virtual cursor to a screen position. |
|
Move to position then click (press + release). |
|
Type a string character by character. |
|
Press a key, hold it, then release it. |
|
Press a key and leave it held until a matching :class: |
|
Release a key held by an earlier :class: |
|
Pause playback, either for demo seconds or for a count of fixed updates. |
|
Run a check function against the game node (parent of DemoRunner). |
|
Execute an action against the game node (parent of DemoRunner). |
|
Scroll the mouse wheel at a position. |
|
Display narration text at the bottom of the screen. |
|
Plays a scripted sequence of demo steps, injecting input and drawing overlays. |
Data¶
API¶
- simvx.core.scripted_demo.log¶
‘getLogger(…)’
- simvx.core.scripted_demo.__all__¶
[‘MoveTo’, ‘Click’, ‘TypeText’, ‘PressKey’, ‘KeyDown’, ‘KeyUp’, ‘Wait’, ‘Assert’, ‘Do’, ‘Scroll’, ‘N…
- class simvx.core.scripted_demo.MoveTo[source]¶
Smoothly move the virtual cursor to a screen position.
- x: float¶
None
- y: float¶
None
- duration: float¶
0.5
- class simvx.core.scripted_demo.Click[source]¶
Move to position then click (press + release).
- x: float¶
None
- y: float¶
None
- button: simvx.core.input.enums.MouseButton¶
None
- class simvx.core.scripted_demo.TypeText[source]¶
Type a string character by character.
- text: str¶
None
- delay_per_char: float¶
0.05
- class simvx.core.scripted_demo.PressKey[source]¶
Press a key, hold it, then release it.
hold_durationis in real seconds and is never scaled by playback speed: the runner converts it to whole fixed updates (hold_durationdivided by the fixed timestep, at least one), so the key is observably held for the same number of ticks at every speed. Reach for :class:KeyDown,- Class:
Waitand :class:KeyUpwhen the tick count is the thing under test, or when other steps have to run while the key is down.
- key: int¶
None
- hold_duration: float¶
0.1
- class simvx.core.scripted_demo.KeyDown[source]¶
Press a key and leave it held until a matching :class:
KeyUp.Completes in one fixed update. Steps placed between the two edges decide how long the game sees the key held, so a hold is expressed in ticks rather than in speed-scaled seconds.
- key: int¶
None
- class simvx.core.scripted_demo.KeyUp[source]¶
Release a key held by an earlier :class:
KeyDown. Completes in one fixed update.- key: int¶
None
- class simvx.core.scripted_demo.Wait[source]¶
Pause playback, either for demo seconds or for a count of fixed updates.
durationis demo time: it is scaled by playback speed, so it says how long a viewer should watch and collapses to a single tick at the fast preset.framesis a tick count and is never scaled, which is what a test needs when the game has to be given a known number of updates (while a key is held, for instance). Exactly one of the two is set.- duration: float¶
0.0
- frames: int¶
0
- class simvx.core.scripted_demo.Assert[source]¶
Run a check function against the game node (parent of DemoRunner).
- check_fn: Any¶
None
- message: str = <Multiline-String>¶
- actual_fn: Any¶
None
- class simvx.core.scripted_demo.Do[source]¶
Execute an action against the game node (parent of DemoRunner).
Like Assert but semantically different: never fails on return value. In test_mode, exceptions propagate; in interactive mode, they’re logged.
- action: Any¶
None
- message: str = <Multiline-String>¶
- class simvx.core.scripted_demo.Scroll[source]¶
Scroll the mouse wheel at a position.
- x: float¶
None
- y: float¶
None
- dy: float¶
None
- dx: float¶
0.0
- class simvx.core.scripted_demo.Narrate[source]¶
Display narration text at the bottom of the screen.
- text: str¶
None
- duration: float¶
2.0
- class simvx.core.scripted_demo.DemoRunner(steps: list, test_mode: bool = False, on_complete: collections.abc.Callable | None = None, speed: float | None = None, speed_mode: int = 0, delay_between_steps: float = 0.15, halt_on_failure: bool = True, **kwargs)[source]¶
Bases:
simvx.core.node.NodePlays a scripted sequence of demo steps, injecting input and drawing overlays.
Add as a child of the game/scene root. In test_mode, hotkeys are disabled and assertions raise on failure.
Args: steps: List of step dataclasses to execute in order. test_mode: If True, skip hotkeys and raise on assertion failure. halt_on_failure: In interactive mode, freeze playback at a failed check or step (RIGHT skips, Escape ends) instead of running every later step against derailed state. Pass False to record failures and keep playing (the playtest harness’s reporting mode). on_complete: Optional callback invoked when all steps finish. speed: Explicit speed override. If set, takes precedence over speed_mode. speed_mode: Speed preset index into :data:
_SPEED_PRESETS(0 = 0.25x, 1 = 0.5x, 2 = 50x). Default 0, so a demo opens at quarter speed unless the caller says otherwise. delay_between_steps: Natural pause (seconds) between steps. Default 0.15.Speed is cosmetic: it scales cursor glide, typing rate, narration and the pause between steps, which is what somebody watching a demo wants to change. It never scales how many fixed updates an input-bearing step occupies, so a key held across
Wait(frames=3)is held for three ticks at every speed.Initialization
- dynamic¶
True
- update_mode¶
‘Property(…)’
- classmethod register_step_handler(step_type: type, handler: collections.abc.Callable)[source]¶
Register a handler for a custom step type.
Handler signature: (runner: DemoRunner, step, dt: float) -> None. The handler must call runner._advance() when the step is complete.
- step_failed(message: str) None[source]¶
Record that a driver step could not do its job.
A step that cannot find its target (a menu label, a popup item, a widget) must say so: silently advancing lets every later step run against derailed state, typing into whatever happens to hold focus. Test mode raises like a failed :class:
Assert; visual mode records the failure, shows it, and halts playback at the failed step.
- on_enter_tree() None[source]¶
Subscribe to window resizes so the overlay re-lays out after playback ends.
- classmethod run_headless(scene: simvx.core.node.Node, steps: list, *, speed: float = 50.0, screen_size: tuple[int, int] = (800, 600), max_frames: int = 20000, delay_between_steps: float = 0.0) bool[source]¶
Run a demo headlessly and return True if all steps pass.
Creates a DemoRunner in test_mode, adds it to scene, and advances frames via SceneRunner until completion or max_frames is reached.
- classmethod run_visual(scene: simvx.core.node.Node, steps: list, *, speed: float | None = None, speed_mode: int = 0, title: str = 'Demo', width: int = 800, height: int = 600, backend: str | None = None)[source]¶
Run a demo visually with the Vulkan App.
Lazily imports
simvx.graphics.Appto keep core free of graphics deps.
- strict_errors: ClassVar[bool]¶
True
- dev_checks: ClassVar[bool]¶
None
- script_error_raised¶
‘Signal(…)’
- visible¶
‘Property(…)’
- __properties__: ClassVar[dict[str, simvx.core.descriptors.Property]]¶
None
- classmethod __init_subclass__(**kwargs)¶
- property name: str¶
- property visible_in_tree: bool¶
- reset_error() None¶
- add_child(node: simvx.core.node.T) simvx.core.node.T¶
- remove_child(node: simvx.core.node.Node) None¶
- reparent(new_parent: simvx.core.node.Node)¶
- node_at(path, default=_NO_DEFAULT)¶
- find(target, *, direct: bool = False)¶
- find_all(target, *, direct: bool = False)¶
- expect(target, *, direct: bool = False)¶
- ancestor(target)¶
- walk(*, include_self: bool = True) collections.abc.Iterator[simvx.core.node.Node]¶
- property path: str¶
- property is_scene_root: bool¶
- add_to_group(group: str)¶
- remove_from_group(group: str)¶
- is_in_group(group: str) bool¶
- on_ready() None¶
- on_update(dt: float) None¶
- on_picked(event: simvx.core.events.InputEvent) None¶
- on_unhandled_input(event: simvx.core.events.TreeInputEvent) None¶
- start_coroutine(gen: simvx.core.descriptors.Coroutine) simvx.core.descriptors.CoroutineHandle¶
- stop_coroutine(gen_or_handle)¶
- queue_redraw() None¶
- property render_dirty: bool¶
- clear_children()¶
- destroy()¶
- property destroying: bool¶
- call_deferred(method: collections.abc.Callable[..., Any], *args: Any) None¶
- property app¶
- property tree: simvx.core.scene_tree.SceneTree¶
- property physics¶
- property physics_2d¶
- __getitem__(key: str)¶
- classmethod get_properties() dict[str, simvx.core.descriptors.Property]¶
- __repr__()¶