Getting Started¶
Install¶
pip install xnano # the terminal, widgets, effects and events
pip install "xnano[audio]" # adds notes, melodies and sounds
pip install "xnano[image]" # adds images and animations
Build values from the inside out¶
write, draw, note and box each
make a small value. A value needs no screen and no sound device, and the text and pixel ones
render on their own:
import xnano
card = xnano.write('Hello, world!', border='rounded', padding={'horizontal': 2})
heart = xnano.draw(
"""
.RR.RR.
RRRRRRR
.RRRRR.
..RRR..
...R...
""",
'R=#e03040',
)
print(card.render())
print(heart.render(format='ansi'))
A Pixel is one cell or dot: xnano.Pixel('diamond', foreground='red').
A character the palette does not name, such as ., is transparent. Pixel art keeps its
color only in the ansi format, so print it with render(format='ansi').
Put it on a canvas¶
new makes a Canvas, a virtual terminal with a size, a clock,
hooks, a cursor, sound and state, and no screen. Its time moves only when you move it, so the
same program draws the same frames every run:
import xnano
with xnano.new(width=40, height=6) as canvas:
canvas.write(xnano.write('Hello, world!', border='rounded'), x='center')
for frame in canvas.iter(count=3):
pass
print(canvas.render())
canvas.step() moves one frame, stop()
freezes time and resume() lets it run on. Positions take numbers,
percentages ('50%'), words ('left', 'center', 'right', 'top', 'bottom') or formulas.
Put it on your screen¶
open is new on your screen: it returns the same canvas as a
Terminal. canvas.open() puts a canvas you already built there, with its time
carried over.
import xnano
with xnano.open(width=40, height=5) as terminal:
terminal.write('Hello, world!', x='center', y='center', border='rounded')
terminal.run(quit_on='q')
Make it move¶
A formula of time is evaluated again on every frame, so there is no loop to write:
import xnano
from xnano.expressions import ParentWidth, TimeElapsed, Width
with xnano.open(width=40, height=3) as terminal:
ball = terminal.write('●', y=1)
ball.x = (ParentWidth - Width) * (TimeElapsed % 2 / 2)
terminal.run(quit_on='q')
React to the keyboard, the mouse and the clock¶
One hook reacts to everything, and on= says what: 'key',
'mouse', 'scroll', 'drag', 'paste', 'tick', 'beat', 'frame', 'resize' or 'state'.
import xnano
with xnano.open(width=30, height=3) as terminal:
terminal.write('press space', y=1)
elapsed = terminal.write(' 0.0s')
@terminal.hook(on='key', keys='space')
def toggle(context: xnano.Context) -> None:
if context.time.is_stopped:
context.canvas.resume()
else:
context.canvas.stop()
@terminal.hook(on='tick', every=1.0)
def tick(context: xnano.Context) -> None:
elapsed.text = f'{context.time.elapsed:5.1f}s'
terminal.run(quit_on='q')
when= narrows a hook with a string such as "mouse.position in buffer.region", an expression
or a function. A hook on 'drag' is a generator: context = yield waits for the next movement.
context.keyboard, context.mouse, context.scroll and context.clipboard are the inputs, and
context.root.state.get(path) and .push(path, value) read and write state that
on='state' hooks follow.
Components, art and sound¶
@xnano.renderable turns a function of (context, **parameters) into a
component that is drawn again only when it read something that changes, such as the clock.
@xnano.playable does the same for sound. xnano.draw pixel art can hold other
art and a camera, xnano.play('C4 E4 G4:2') plays a melody written as text, and
canvas.color, canvas.border, canvas.padding and canvas.margin change how any canvas,
placement or region looks. The
examples show each of them, ending with a
playable world.
Where everything lives¶
The top-level xnano holds the main API. Everything else is in a module named for what it
holds: xnano.widgets, xnano.expressions,
xnano.effects, xnano.styles,
xnano.events, xnano.art and so on, with the optional
xnano.audio and xnano.image.