xnano.components
xnano.components
¶
xnano.components
Components: functions and objects that draw or play themselves, and run again only when something they read changes.
Classes:
-
ComponentContext–What a component's hook receives besides the hook's own context.
-
ComponentHook–What
component.hook(...)returns: a hook on every placement of a component. -
RenderableComponent–A renderable function with its own parameters given:
gauge(ratio=0.7). -
PlayableComponent–A playable function with its own parameters given:
arpeggio(root='C4'). -
RenderableFunction–A function that draws itself: what
renderablereturns. -
PlayableFunction–A function that plays itself: what
playablereturns.
Functions:
-
renderable–Turn a function into a component that draws itself.
-
playable–Turn a function into a component that plays itself.
ComponentContext
dataclass
¶
ComponentContext(component: RenderableComponent[DepsT, StateT] | PlayableComponent[DepsT, StateT], placement: Placement[DepsT, StateT] | None, arguments: Mapping[str, object])
Bases: Generic[DepsT, StateT]
What a component's hook receives besides the hook's own context.
Attributes:
-
component(RenderableComponent[DepsT, StateT] | PlayableComponent[DepsT, StateT]) –The component the hook belongs to.
-
placement(Placement[DepsT, StateT] | None) –The placement the component is drawn in, or
Nonefor a playable component, which has no placement. -
arguments(Mapping[str, object]) –The component's own parameters, by name.
Examples:
import xnano
@xnano.renderable
def gauge(context: xnano.Context, ratio: float = 0.0) -> str:
return '#' * round(ratio * 10)
with xnano.new(width=12, height=1) as canvas:
seen = []
gauge.hook(
lambda context, component_context: seen.append(
component_context.arguments['ratio']
),
on='tick',
)
canvas.write(gauge(ratio=0.5))
canvas.step(delta_seconds=0.1)
assert seen == [0.5]
component
instance-attribute
¶
component: RenderableComponent[DepsT, StateT] | PlayableComponent[DepsT, StateT]
The component the hook belongs to.
ComponentHook
¶
What component.hook(...) returns: a hook on every placement of a component.
The hook is installed on the placements that exist now and on those created later;
remove takes it off all of them. It never starts a placement of its own.
Attributes:
-
handles(tuple[HookHandle[object, object], ...]) –The
HookHandleon each placement it is installed on right now. -
is_removed(bool) –Whether
removewas called.
Create the hook. Use component.hook(...) instead of calling this.
Parameters:
-
owner(_HookSource) –The component or component function the hook was added to.
-
function(Callable[..., object]) –The function to call with a context and a component context.
-
settings(_HookSettings) –The keywords of
hook.
Methods:
-
remove–Take the hook off every placement. Removing twice does nothing.
Source code in xnano/components.py
RenderableComponent
¶
RenderableComponent(function: _ComponentFunction[..., object, object], bound: BoundArguments)
Bases: _BoundComponent, Generic[DepsT, StateT]
A renderable function with its own parameters given: gauge(ratio=0.7).
Write it with canvas.write(component). It is evaluated once, and again only when it
read something that changes. See renderable.
Attributes:
Keep the function and its arguments. Call the decorated function instead.
Methods:
-
__xnano_render__–Draw the component with the parameters it was given.
-
hook–Call a function when something happens to a placement of this component.
Source code in xnano/components.py
__xnano_render__
¶
__xnano_render__(context: Context[DepsT, StateT]) -> Renderable
hook
¶
hook(function: Callable[..., object] | None = None, /, *, on: HookKind | None = None, when: When[object, object] | None = None, every: float | None = None, after: float | None = None, keys: KeyName | Sequence[KeyName] | None = None, requires_deps: bool = False) -> ComponentHook | Callable[[Callable[..., object]], ComponentHook]
Call a function when something happens to a placement of this component.
The function receives the hook's Context and a
ComponentContext. The arguments are those of
hook on a placement, and a mouse, scroll or drag hook hears only the pointer
inside the placement. Keys are not consumed: while nothing has focus, every key hook
hears a key. On a component function the hook is installed on every
placement of that function, those that exist now and those created later; on a
component made by calling it, only on its placements. A playable component has no
placement: its hook is installed on the canvas when the component is played.
Parameters:
-
function(Callable[..., object] | None, default:None) –The function to call, or
Noneto use this as a decorator. -
on(HookKind | None, default:None) –What to listen to; see
HookKind. -
when(When[object, object] | None, default:None) –A condition that must hold; see
When. -
every(float | None, default:None) –Repeat every this many seconds (
'tick') or beats ('beat'). -
after(float | None, default:None) –Run once after this many seconds or beats.
-
keys(KeyName | Sequence[KeyName] | None, default:None) –The key or keys an
on='key'hook reacts to. -
requires_deps(bool, default:False) –Whether the hook needs the dependencies given to
new(deps=...). Checked each time the hook is installed.
Returns:
-
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–A
ComponentHook, whoseremovetakes the -
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–hook off every placement; or, without a function, the decorator.
Raises:
-
ValueError–The combination of arguments does not make sense; see
hook. -
TerminalError–requires_depsis true and the canvas was created withoutdeps.
Examples:
import xnano
from xnano_core import Event
@xnano.renderable
def label(context: xnano.Context, text: str = 'x') -> str:
return text
with xnano.new(width=10, height=1) as canvas:
pressed = []
@label.hook(on='key', keys='a')
def heard(
context: xnano.Context,
component_context: xnano.components.ComponentContext,
) -> None:
pressed.append(component_context.arguments['text'])
canvas.write(label(text='hi'))
canvas.push(Event.key_press('a'))
canvas.deliver_events()
assert pressed == ['hi']
Source code in xnano/components.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
PlayableComponent
¶
PlayableComponent(function: _ComponentFunction[..., object, object], bound: BoundArguments)
Bases: _BoundComponent, Generic[DepsT, StateT]
A playable function with its own parameters given: arpeggio(root='C4').
Play it with canvas.play(component). See playable.
Attributes:
Keep the function and its arguments. Call the decorated function instead.
Methods:
-
__xnano_play__–Return what to play, with the parameters the component was given.
-
hook–Call a function when something happens to a placement of this component.
Source code in xnano/components.py
__xnano_play__
¶
hook
¶
hook(function: Callable[..., object] | None = None, /, *, on: HookKind | None = None, when: When[object, object] | None = None, every: float | None = None, after: float | None = None, keys: KeyName | Sequence[KeyName] | None = None, requires_deps: bool = False) -> ComponentHook | Callable[[Callable[..., object]], ComponentHook]
Call a function when something happens to a placement of this component.
The function receives the hook's Context and a
ComponentContext. The arguments are those of
hook on a placement, and a mouse, scroll or drag hook hears only the pointer
inside the placement. Keys are not consumed: while nothing has focus, every key hook
hears a key. On a component function the hook is installed on every
placement of that function, those that exist now and those created later; on a
component made by calling it, only on its placements. A playable component has no
placement: its hook is installed on the canvas when the component is played.
Parameters:
-
function(Callable[..., object] | None, default:None) –The function to call, or
Noneto use this as a decorator. -
on(HookKind | None, default:None) –What to listen to; see
HookKind. -
when(When[object, object] | None, default:None) –A condition that must hold; see
When. -
every(float | None, default:None) –Repeat every this many seconds (
'tick') or beats ('beat'). -
after(float | None, default:None) –Run once after this many seconds or beats.
-
keys(KeyName | Sequence[KeyName] | None, default:None) –The key or keys an
on='key'hook reacts to. -
requires_deps(bool, default:False) –Whether the hook needs the dependencies given to
new(deps=...). Checked each time the hook is installed.
Returns:
-
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–A
ComponentHook, whoseremovetakes the -
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–hook off every placement; or, without a function, the decorator.
Raises:
-
ValueError–The combination of arguments does not make sense; see
hook. -
TerminalError–requires_depsis true and the canvas was created withoutdeps.
Examples:
import xnano
from xnano_core import Event
@xnano.renderable
def label(context: xnano.Context, text: str = 'x') -> str:
return text
with xnano.new(width=10, height=1) as canvas:
pressed = []
@label.hook(on='key', keys='a')
def heard(
context: xnano.Context,
component_context: xnano.components.ComponentContext,
) -> None:
pressed.append(component_context.arguments['text'])
canvas.write(label(text='hi'))
canvas.push(Event.key_press('a'))
canvas.deliver_events()
assert pressed == ['hi']
Source code in xnano/components.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
RenderableFunction
¶
RenderableFunction(function: Callable[Concatenate[Context[DepsT, StateT], ParametersT], object])
Bases: _ComponentFunction[ParametersT, DepsT, StateT]
A function that draws itself: what renderable returns.
The function is the component. canvas.write(function) draws it with its default
parameters, and canvas.write(function(ratio=0.7)) gives it its own parameters
first. Calling it does not run it; it makes a
RenderableComponent.
Attributes:
Keep the function. Use @xnano.renderable or @xnano.playable instead.
Methods:
-
__call__–Give the component its own parameters; the context is passed for you.
-
__xnano_render__–Draw the component with its default parameters.
-
hook–Call a function when something happens to a placement of this component.
Source code in xnano/components.py
arguments
property
¶
The parameters the component is written with: none, so the defaults apply.
__call__
¶
__call__(*arguments: args, **keywords: kwargs) -> RenderableComponent[DepsT, StateT]
Give the component its own parameters; the context is passed for you.
Source code in xnano/components.py
__xnano_render__
¶
__xnano_render__(context: Context[DepsT, StateT]) -> Renderable
hook
¶
hook(function: Callable[..., object] | None = None, /, *, on: HookKind | None = None, when: When[object, object] | None = None, every: float | None = None, after: float | None = None, keys: KeyName | Sequence[KeyName] | None = None, requires_deps: bool = False) -> ComponentHook | Callable[[Callable[..., object]], ComponentHook]
Call a function when something happens to a placement of this component.
The function receives the hook's Context and a
ComponentContext. The arguments are those of
hook on a placement, and a mouse, scroll or drag hook hears only the pointer
inside the placement. Keys are not consumed: while nothing has focus, every key hook
hears a key. On a component function the hook is installed on every
placement of that function, those that exist now and those created later; on a
component made by calling it, only on its placements. A playable component has no
placement: its hook is installed on the canvas when the component is played.
Parameters:
-
function(Callable[..., object] | None, default:None) –The function to call, or
Noneto use this as a decorator. -
on(HookKind | None, default:None) –What to listen to; see
HookKind. -
when(When[object, object] | None, default:None) –A condition that must hold; see
When. -
every(float | None, default:None) –Repeat every this many seconds (
'tick') or beats ('beat'). -
after(float | None, default:None) –Run once after this many seconds or beats.
-
keys(KeyName | Sequence[KeyName] | None, default:None) –The key or keys an
on='key'hook reacts to. -
requires_deps(bool, default:False) –Whether the hook needs the dependencies given to
new(deps=...). Checked each time the hook is installed.
Returns:
-
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–A
ComponentHook, whoseremovetakes the -
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–hook off every placement; or, without a function, the decorator.
Raises:
-
ValueError–The combination of arguments does not make sense; see
hook. -
TerminalError–requires_depsis true and the canvas was created withoutdeps.
Examples:
import xnano
from xnano_core import Event
@xnano.renderable
def label(context: xnano.Context, text: str = 'x') -> str:
return text
with xnano.new(width=10, height=1) as canvas:
pressed = []
@label.hook(on='key', keys='a')
def heard(
context: xnano.Context,
component_context: xnano.components.ComponentContext,
) -> None:
pressed.append(component_context.arguments['text'])
canvas.write(label(text='hi'))
canvas.push(Event.key_press('a'))
canvas.deliver_events()
assert pressed == ['hi']
Source code in xnano/components.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
PlayableFunction
¶
PlayableFunction(function: Callable[Concatenate[Context[DepsT, StateT], ParametersT], object])
Bases: _ComponentFunction[ParametersT, DepsT, StateT]
A function that plays itself: what playable returns.
canvas.play(function) plays it with its default parameters, and
canvas.play(function(root='C4')) gives it its own parameters first.
Attributes:
Keep the function. Use @xnano.renderable or @xnano.playable instead.
Methods:
-
__call__–Give the component its own parameters; the context is passed for you.
-
__xnano_play__–Return what to play with the default parameters.
-
hook–Call a function when something happens to a placement of this component.
Source code in xnano/components.py
arguments
property
¶
The parameters the component is written with: none, so the defaults apply.
__call__
¶
__call__(*arguments: args, **keywords: kwargs) -> PlayableComponent[DepsT, StateT]
Give the component its own parameters; the context is passed for you.
Source code in xnano/components.py
__xnano_play__
¶
hook
¶
hook(function: Callable[..., object] | None = None, /, *, on: HookKind | None = None, when: When[object, object] | None = None, every: float | None = None, after: float | None = None, keys: KeyName | Sequence[KeyName] | None = None, requires_deps: bool = False) -> ComponentHook | Callable[[Callable[..., object]], ComponentHook]
Call a function when something happens to a placement of this component.
The function receives the hook's Context and a
ComponentContext. The arguments are those of
hook on a placement, and a mouse, scroll or drag hook hears only the pointer
inside the placement. Keys are not consumed: while nothing has focus, every key hook
hears a key. On a component function the hook is installed on every
placement of that function, those that exist now and those created later; on a
component made by calling it, only on its placements. A playable component has no
placement: its hook is installed on the canvas when the component is played.
Parameters:
-
function(Callable[..., object] | None, default:None) –The function to call, or
Noneto use this as a decorator. -
on(HookKind | None, default:None) –What to listen to; see
HookKind. -
when(When[object, object] | None, default:None) –A condition that must hold; see
When. -
every(float | None, default:None) –Repeat every this many seconds (
'tick') or beats ('beat'). -
after(float | None, default:None) –Run once after this many seconds or beats.
-
keys(KeyName | Sequence[KeyName] | None, default:None) –The key or keys an
on='key'hook reacts to. -
requires_deps(bool, default:False) –Whether the hook needs the dependencies given to
new(deps=...). Checked each time the hook is installed.
Returns:
-
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–A
ComponentHook, whoseremovetakes the -
ComponentHook | Callable[[Callable[..., object]], ComponentHook]–hook off every placement; or, without a function, the decorator.
Raises:
-
ValueError–The combination of arguments does not make sense; see
hook. -
TerminalError–requires_depsis true and the canvas was created withoutdeps.
Examples:
import xnano
from xnano_core import Event
@xnano.renderable
def label(context: xnano.Context, text: str = 'x') -> str:
return text
with xnano.new(width=10, height=1) as canvas:
pressed = []
@label.hook(on='key', keys='a')
def heard(
context: xnano.Context,
component_context: xnano.components.ComponentContext,
) -> None:
pressed.append(component_context.arguments['text'])
canvas.write(label(text='hi'))
canvas.push(Event.key_press('a'))
canvas.deliver_events()
assert pressed == ['hi']
Source code in xnano/components.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
renderable
¶
renderable(function: Callable[[], Renderable]) -> RenderableFunction[[], None, None]
renderable(function: Callable[Concatenate[Context[DepsT, StateT], ParametersT], Renderable]) -> RenderableFunction[ParametersT, DepsT, StateT]
renderable(function: Callable[Concatenate[Context[DepsT, StateT], ParametersT], Renderable] | Callable[[], Renderable]) -> RenderableFunction[ParametersT, DepsT, StateT] | RenderableFunction[[], None, None]
Turn a function into a component that draws itself.
The function takes the Context first, then its own parameters,
and returns anything canvas.write accepts; a function with no parameters at all is
called without the context. The decorated object is the component:
canvas.write(gauge) draws it with its defaults, canvas.write(gauge(ratio=0.7)) gives
it parameters, and gauge.hook(...) adds behavior to its placements.
A component is evaluated once, and again only when it read something that changes: the
time or the event (every frame), the size of its buffer, the parent or the canvas (when
the screen is resized), a state path (when it changes), or an input such as
context.keyboard (when it arrives). What it reads is recorded as it runs. Reads of
context.deps and of mutable fields on self are not tracked: when they change, call
placement.refresh().
Parameters:
-
function(Callable[Concatenate[Context[DepsT, StateT], ParametersT], Renderable] | Callable[[], Renderable]) –The function
(context, **own_parameters) -> content.
Raises:
-
TypeError–The decorated object is a class or is not callable, or the function's first parameter is keyword-only or variadic, so it cannot take the context.
Examples:
import xnano
@xnano.renderable
def gauge(context: xnano.Context, ratio: float = 0.5) -> str:
return '#' * round(ratio * 10)
with xnano.new(width=12, height=1) as canvas:
canvas.write(gauge(ratio=0.3))
assert canvas.render().strip() == '###'
Source code in xnano/components.py
playable
¶
playable(function: Callable[[], Playable]) -> PlayableFunction[[], None, None]
playable(function: Callable[Concatenate[Context[DepsT, StateT], ParametersT], Playable]) -> PlayableFunction[ParametersT, DepsT, StateT]
playable(function: Callable[Concatenate[Context[DepsT, StateT], ParametersT], Playable] | Callable[[], Playable]) -> PlayableFunction[ParametersT, DepsT, StateT] | PlayableFunction[[], None, None]
Turn a function into a component that plays itself.
The function takes the Context first, then its own parameters,
and returns anything canvas.play accepts; a function with no parameters at all is
called without the context. It is evaluated once when played, and again
on a beat only when it read something that changes (the time or the beat, a state path or
an input), playing the new result each time. canvas.play returns a
PlayHandle whose remove() stops it.
Parameters:
-
function(Callable[Concatenate[Context[DepsT, StateT], ParametersT], Playable] | Callable[[], Playable]) –The function
(context, **own_parameters) -> playable.
Raises:
-
TypeError–The decorated object is a class or is not callable, or the function's first parameter is keyword-only or variadic, so it cannot take the context.
Examples:
import xnano
@xnano.playable
def chord(context: xnano.Context, root: str = 'C4') -> xnano.Note:
return xnano.note(root, beats=0.5)
with xnano.new(width=8, height=1) as canvas:
canvas.play(chord(root='E4'))
canvas.step(delta_seconds=0.5)
assert canvas.audio.render_sound().peak > 0.0