xnano.terminal
xnano.terminal
¶
xnano.terminal
A canvas on your screen, and the function that opens one.
Classes:
-
Terminal–A canvas that draws on your screen and reads your keyboard and mouse.
Functions:
Terminal
¶
Terminal(*, width: int | str | None, height: int | str | None, fps: float, bpm: float, is_realtime: bool, deps: object, state: object, title: str | None, gates: Mapping[str, object], appearance: Mapping[str, object], effects: Sequence[RenderableEffectSpecification], enclosing: Canvas[object, object] | None = None, layout: CanvasLayout = 'overlay', edges: EdgeBehavior = 'clip')
Bases: Canvas[DepsT, StateT]
A canvas that draws on your screen and reads your keyboard and mouse.
A canvas becomes a terminal when it is opened, with open or
Canvas.open: it is the same object, and
isinstance(canvas, xnano.Terminal) is how to ask whether a canvas is on the screen.
It has every member of Canvas. It runs by itself: opening it
starts a loop on a thread of its own, and everything you write, remove or draw appears the
moment it is declared, however slowly the rest of your program runs, because declaring
only changes buffers and the loop draws them. A program has one screen, so only one
terminal is open at a time.
Attributes:
-
is_closed(bool) –Whether the terminal has been closed, or its loop has ended.
-
is_running(bool) –Whether the loop is running and has not been asked to end.
Examples:
with xnano.open(width=20, height=2) as terminal:
terminal.write('hello')
assert isinstance(terminal, xnano.Terminal)
terminal.run(quit_on='q')
Create a canvas. Use new instead of calling this.
Parameters:
-
width(int | str | None) –The width in cells or as a percentage of the window, or
Nonefor 80. -
height(int | str | None) –The height in cells or as a percentage of the window, or
Nonefor 24. -
fps(float) –The most frames drawn per second.
-
bpm(float) –Beats per minute.
-
is_realtime(bool) –Whether the canvas runs on the wall clock, on a thread of its own.
-
deps(object) –The dependencies, or the unset marker when none were given.
-
state(object) –A dataclass instance the canvas keeps and validates, or the unset marker.
-
title(str | None) –The window title, or
None. -
gates(Mapping[str, object]) –The input gates that were given, by name.
-
appearance(Mapping[str, object]) –The appearance keywords of the canvas itself, by name.
-
effects(Sequence[RenderableEffectSpecification]) –Effects to apply to the canvas, as dictionaries with a
kindkey. -
enclosing(Canvas[object, object] | None, default:None) –The canvas whose
withblock this one was created in, which its sizes are measured in and which closes it, orNone. -
layout(CanvasLayout, default:'overlay') –How the canvas places what is written on it.
-
edges(EdgeBehavior, default:'clip') –What content written on the canvas does at the edges of its area.
Methods:
-
__setattr__–Set an attribute, or raise
AttributeErrorfor an unknown public name. -
bold–Make the text bold, or undo it with
False; the same as settingis_bold. -
dim–Dim the text, or undo it with
False; the same as settingis_dim. -
italic–Make the text italic, or undo it with
False; the same as settingis_italic. -
underline–Underline the text, or undo it with
False; the same as settingis_underlined. -
blink–Make the text blink, or undo it with
False; the same as settingis_blinking. -
reverse–Swap the foreground and background, or undo it with
False; the same as settingis_reversed. -
hidden–Hide the text, or undo it with
False; the same as settingis_hidden. -
crossed_out–Strike the text through, or undo it with
False; the same as settingis_crossed_out. -
hook–Call a function when something happens on this canvas or placement.
-
__new__–Refuse a direct call: a canvas is created with
xnano.new. -
open–Put this canvas on your screen, and return it as a
Terminal. -
close–Release everything the canvas holds, and give the screen back if it has one.
-
local–Convert a cell of the screen into a cell of this canvas.
-
move–Move a canvas that is written into another one by a number of cells along each axis.
-
up–Move a written canvas up by
countrows; seemove. -
down–Move a written canvas down by
countrows; seemove. -
left–Move a written canvas left by
countcolumns; seemove. -
right–Move a written canvas right by
countcolumns; seemove. -
new–Create a canvas inside this one and draw it here at once, at the top-left corner.
-
write–Put something on the canvas: text, a widget, a buffer, another canvas or an image.
-
clear–Erase everything written or drawn directly on the canvas's cells.
-
update–Change the canvas's own cells with operations, styles or effects.
-
__or__–Apply an operation, customization or style with
canvas | operation. -
__ior__–Apply an operation, customization or style in place with
canvas |= operation. -
inspect–Evaluate an expression against the canvas and return its value.
-
iter–Iterate over the canvas's frames, drawing between them; pass
count=orseconds=, -
step–Move time by
delta_seconds, one frame by default, and draw exactly one frame. -
deliver_events–Deliver pushed events to their hooks and draw a frame, without moving time.
-
stop–Freeze time: the same as
Time.stop. -
resume–Let time run on from where
stopfroze it. -
quit–Ask the canvas's loop to end.
-
run–Wait until the canvas's loop ends.
-
wait_for_key–Wait until a key is pressed, and return its name.
-
prompt–Prompt for a line of text, and return what was typed.
-
render–Draw the current frame and return it, without a screen.
-
push–Deliver an event to the canvas as if it had been typed, clicked or reported.
-
play–Play a note, a sound, a symbol name, a melody or a sequence of those, and return at once.
-
state_reference–A core reference for one field of the canvas's state.
Source code in xnano/canvas.py
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | |
region
property
¶
region: Region
The cells of the canvas as a Region: read and write
canvas.region[x, y].
The default resolution is one cell. canvas.region.half_blocks addresses two
pixels per cell and canvas.region.braille eight dots.
Examples:
color
property
¶
color: Colors
border
property
writable
¶
border: BorderAppearance
The border: its kind, a setting for each side, its color and its title.
Assign 'plain', 'rounded', 'double' or 'thick' to draw a border
on every side, True for 'plain' and False to remove it. It works on any
content, not only on text.
The border, padding and margin are drawn around the content, so the placement grows by the cells they take. A widget with no size of its own is shrunk to fit its parent, border included.
Examples:
padding
property
writable
¶
padding: PaddingAppearance
The space between the border and the content, edge by edge.
Assign a number of cells, a percentage such as '10%' or a dictionary such as
{'vertical': 1, 'horizontal': 2}, or set one edge with padding.left = 2.
margin
property
writable
¶
margin: MarginAppearance
The transparent space outside the border, edge by edge, set like padding.
output
instance-attribute
¶
The canvas's drawn frames and captured text.
is_closed
property
¶
is_closed: bool
Whether the canvas has been torn down: closed, or its loop has ended.
is_running
property
¶
is_running: bool
Whether the canvas is open and its loop has not been asked to end.
core_buffer
property
¶
core_buffer: Buffer
The core buffer the canvas draws, as large as the canvas.
Everything you write is a child of this buffer, and everything drawn directly on the canvas is stored in it. A canvas with a border, padding or margin draws them around a buffer of its own; this is the buffer inside them.
parent
property
¶
parent: ParentExpressionScope
Symbolic expressions for the parent container's width, height and size.
root
property
¶
root: RootExpressionScope
Symbolic expressions for the root canvas's width, height, size and time.
fps
property
¶
fps: float
The most frames drawn per second, and the rate iter aims for.
bpm
property
writable
¶
bpm: float
Beats per minute: the length of a beat for beat timers, notes and melodies.
It is 120 unless new(bpm=...) said otherwise, and it can change while things
are playing; the change applies from the next beat.
Raises:
-
ValueError–The tempo is not above 0 and at most 600000.
title
property
writable
¶
title: str | None
The window title last set on this canvas.
Assign a single line of printable text to set the title of the window a canvas on the
screen draws in, or None to clear it. A canvas without a screen keeps the value
and shows it when it is opened.
time
property
¶
time: Time
The canvas's Time: how long it has run, stopping, and formulas.
cursor
property
¶
cursor: Cursor
The canvas's Cursor.
A canvas has exactly one cursor. Set its position (a cell, or None to hide
it), shape, is_blinking and is_visible, move it with up, down,
left, right and move, or follow a placement. It can be changed from
any thread, and on the screen the cursor you had before is restored when the canvas
closes.
Examples:
import xnano
with xnano.new(width=10, height=2) as canvas:
canvas.cursor.position = (3, 0)
canvas.cursor.shape = 'bar'
assert canvas.cursor.position.x == 3
layout
property
¶
layout: CanvasLayout
How the canvas places what is written on it, chosen when the canvas is created.
'overlay' places every piece on its own over the whole canvas. 'column'
stacks the pieces from top to bottom in the order they are written, and 'row'
from left to right. Each piece then takes a slot: one with a size on the stacking
axis (height in a column, width in a row, in cells or as a percentage) gets
exactly that, and the others share what is left equally. Inside its slot a piece is
positioned as on any canvas, so y='center' and y='50%' are measured in the
slot. Removing a piece gives its slot back. A canvas created inside the with
block of a stacking canvas takes a slot only once it is written.
Examples:
edges
property
writable
¶
edges: EdgeBehavior
What content written on the canvas does at the edges of its area, unless given its own.
'clip' lets it move past the edge, where it is not drawn; 'stop' holds it
inside its area when it moves; 'wrap' continues text on the next rows of its
area. A piece written with edges=... keeps its own; see
Placement.edges.
Raises:
-
ValueError–The value is not
'clip','stop'or'wrap'.
audio
property
¶
audio: AudioDevice
The canvas's AudioDevice: notes, melodies, sounds and the microphone.
It needs the optional xnano-audio package, which is imported the first time this
is read.
Raises:
-
ImportError–xnano-audiois not installed. Install it withpip install "xnano[audio]".
fx
property
¶
The visual effects of the canvas itself, one method for each effect.
canvas.fx.fade_to(background='navy') starts a fade over the whole canvas at once,
and canvas.fx.clear() stops what was started here. A placement has its own
fx, and the sound effects of what the canvas plays are
canvas.audio.fx. See
RenderableEffects.
Examples:
import xnano
with xnano.new('hello', width=8, height=1) as canvas:
canvas.fx.dissolve(duration_milliseconds=300)
assert len(canvas.fx.active) == 1
input
property
¶
input: Input
The keys held down right now, for programs that poll instead of listening.
Read it anywhere, from any thread. A canvas opened with
keyboard='release_events' knows exactly when a key is let go.
state
class-attribute
instance-attribute
¶
The canvas's state: values that hooks and your code share, read and written by path.
canvas.state.get('player.score') reads a value and canvas.state.push('player.score',
3) writes one; see StateNamespace for the path rules. A
canvas created with new(state=Settings()) also gives the fields of the dataclass as
attributes with their declared types, so canvas.state.volume is an int for a field
declared volume: int; a write is checked against the field's type. A type checker
sees get and push only on a canvas without a state dataclass, but both exist on
every canvas.
The state can be used from any thread. A write updates every expression that reads the field and draws a frame.
__setattr__
¶
Set an attribute, or raise AttributeError for an unknown public name.
Source code in xnano/_attributes.py
bold
¶
bold(value: bool = True) -> Self
Make the text bold, or undo it with False; the same as setting is_bold.
Parameters:
-
value(bool, default:True) –Whether the text is bold.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Examples:
import xnano
with xnano.new(width=12, height=1) as canvas:
title = canvas.write('Ready').bold().underline()
assert title.is_bold and title.is_underlined
title.bold(False)
assert not title.is_bold
Source code in xnano/appearance.py
dim
¶
dim(value: bool = True) -> Self
Dim the text, or undo it with False; the same as setting is_dim.
Parameters:
-
value(bool, default:True) –Whether the text is dimmed.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
italic
¶
italic(value: bool = True) -> Self
Make the text italic, or undo it with False; the same as setting is_italic.
Parameters:
-
value(bool, default:True) –Whether the text is italic.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
underline
¶
underline(value: bool = True) -> Self
Underline the text, or undo it with False; the same as setting is_underlined.
Parameters:
-
value(bool, default:True) –Whether the text is underlined.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
blink
¶
blink(value: bool = True) -> Self
Make the text blink, or undo it with False; the same as setting is_blinking.
Parameters:
-
value(bool, default:True) –Whether the text blinks.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
reverse
¶
reverse(value: bool = True) -> Self
Swap the foreground and background, or undo it with False; the same as setting is_reversed.
Parameters:
-
value(bool, default:True) –Whether foreground and background are swapped.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
hidden
¶
hidden(value: bool = True) -> Self
Hide the text, or undo it with False; the same as setting is_hidden.
Parameters:
-
value(bool, default:True) –Whether the text is hidden.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
crossed_out
¶
crossed_out(value: bool = True) -> Self
Strike the text through, or undo it with False; the same as setting is_crossed_out.
Parameters:
-
value(bool, default:True) –Whether the text is struck through.
Returns:
-
Self–This canvas or placement, so calls can chain.
Raises:
-
TypeError–valueis notTrueorFalse.
Source code in xnano/appearance.py
hook
¶
hook(function: Hook[DepsT, StateT] | None = None, /, *, on: HookKind | None = None, when: When[DepsT, StateT] | None = None, every: float | None = None, after: float | None = None, keys: KeyName | Sequence[KeyName] | None = None, requires_deps: bool = False) -> HookHandle[DepsT, StateT] | Callable[[Hook[DepsT, StateT]], HookHandle[DepsT, StateT]]
Call a function when something happens on this canvas or placement.
on says what to listen to. A hook without on hears every input event. A mouse,
scroll or drag hook on a placement hears only the pointer inside the placement, and a
press inside holds the pointer for the hook until the button is released. A pointer hook
that matches an event stops it from reaching the parent; a hook that does not match lets
it through. Keys are not consumed: while nothing has focus, every key hook hears a key.
hook(function, ...) attaches one function, and @target.hook(...) decorates the
function that follows. A hook function may be a generator that receives a new context
at each bare yield: write context = yield. A drag is a loop that runs while
context.mouse.is_down.
every and after are seconds for on='tick' and beats for on='beat':
every=2 means 2 seconds for a tick hook and 2 beats for a beat hook. On a canvas
from new() a hook that is due several times in one step runs once for each
interval, up to 100,000 times in one step; the intervals that remain run in the steps
that follow. The function's return value is ignored, unless it is an operation, a
style or an effect, which is applied to this target.
Parameters:
-
function(Hook[DepsT, StateT] | None, default:None) –The function to call with a
Context, or a function with no parameters, which is called without it. -
on(HookKind | None, default:None) –What to listen to:
'frame','tick','beat','key','mouse','scroll','drag','paste','focus','resize','state'or'custom'. SeeHookKind. -
when(When[DepsT, StateT] | None, default:None) –A condition that must hold: an expression, a function of the context, or a string. See
When. A string tells xnano which input the hook needs, so the mouse, scroll and key-release gates it reads are turned on when the canvas opens. An expression or a function does not, so a condition that readsMouse,Scrollor key releases needs the gate set onnew()oropen()yourself:mouse=True,scroll=Trueorkeyboard='release_events'. A comparison with an input the event does not carry, such askeyboard.keyon a paste, does not hold;!=is one of them, sokeyboard.key != 'q'hears only key events.notandnot innegate the whole comparison, sonot keyboard.key == 'q'andkeyboard.key not in ['q']also hold for events that have no key. -
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 (
'tick') or beats ('beat'). -
keys(KeyName | Sequence[KeyName] | None, default:None) –The key or keys an
on='key'hook reacts to, such as'q'or'ctrl+s'. A key hook with keys hears presses and repeats; omit them and testkeyboard.kindto hear releases. -
requires_deps(bool, default:False) –Whether the hook needs the dependencies given to
new(deps=...).
Returns:
-
HookHandle[DepsT, StateT] | Callable[[Hook[DepsT, StateT]], HookHandle[DepsT, StateT]]–The hook, whose
removestops it; or, without a function, the decorator.
Raises:
-
ValueError–The combination of arguments does not make sense, a key name is unknown, or a
whenstring is not a condition. The message says what to write instead. -
TypeError–onis not text,whenis a plainbool, or the function is not callable with a context, or is anasyncfunction. -
TerminalError–requires_depsis true and the canvas was created withoutdeps, or the canvas is on a screen with an input gate this hook needs turned off.
Examples:
import xnano
from xnano_core import Event
with xnano.new(width=8, height=1) as canvas:
heard = []
@canvas.hook(on='key', keys=['q', 'escape'])
def leave(context: xnano.Context) -> None:
heard.append(context.keyboard.key)
@canvas.hook(on='tick', every=0.5)
def blink(context: xnano.Context) -> None:
heard.append('tick')
canvas.push(Event.key_press('q'))
canvas.step(delta_seconds=1.0)
assert heard == ['q', 'tick', 'tick']
Source code in xnano/_hooks.py
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 | |
__new__
¶
Refuse a direct call: a canvas is created with xnano.new.
Source code in xnano/canvas.py
open
¶
open(*, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, **settings: Unpack[OpenSettings]) -> Terminal[DepsT, StateT]
Put this canvas on your screen, and return it as a Terminal.
The same object is returned: after this, isinstance(canvas, xnano.Terminal) is
true. From then on it runs by itself on a thread of its own and on the wall clock, which
continues from the time the canvas had: elapsed time, the frame count, every and
after timers and a stopped clock carry over, and so do the cursor, the title,
everything written and every hook. Effects that were running start again from their
beginning, and keyboard focus and a captured pointer are released.
width and height are the size of the canvas: the ones given here, else the
ones given to new(), draw in a region of that size below the cursor; a canvas
created without a size, or with '100%' for both, fills the whole window. A
percentage such as '50%' is a share of the window when the canvas is opened. Any other keyword of new() except
realtime may be given and applies before the canvas is shown; a gate given here
wins over the one given to new(). title is the window title.
Parameters:
-
title(str | None, default:None) –The window title;
Nonekeeps the canvas's title. -
width(int | Percentage | str | None, default:None) –The width of the region to draw in: cells, or a percentage of the window.
-
height(int | Percentage | str | None, default:None) –The height of the region to draw in: cells, or a percentage of the window.
-
settings(Unpack[OpenSettings], default:{}) –Gates, appearance keywords,
fps,bpm,effects, anddepsandstatefor a canvas that was created without them; seeOpenSettings.
Returns:
-
Terminal[DepsT, StateT]–This canvas, now attached to the screen.
Raises:
-
TerminalAlreadyOpenError–Another canvas is already on the screen.
-
TerminalError–This canvas is already on the screen, was written into another canvas, has a hook that needs a gate given as
False, or there is no terminal to draw on; the canvas stays as it was. -
TypeError–realtimewas given,depsorstatethat the canvas already has, or an effect that is not a visual effect dictionary. -
ValueError–A
widthwas given without aheight, or a value is out of range.
Examples:
with xnano.new(width=40, height=5) as canvas:
canvas.write('hello', border='rounded')
canvas.step()
canvas.open(mouse=True) # now a xnano.Terminal, 40 by 5 below the prompt
canvas.run(quit_on='q')
Source code in xnano/canvas.py
close
¶
close(*, raise_failure: bool = True) -> None
Release everything the canvas holds, and give the screen back if it has one.
Closing twice is harmless. Leaving a with block closes the canvas, unless it is
part of another canvas or was created inside another canvas's with block: then it
stays usable and closes with that canvas. Closing a canvas closes the canvases created
inside its block that were not written anywhere. Closing a canvas that was written into
another one takes it off and leaves that one running.
Parameters:
-
raise_failure(bool, default:True) –Whether to raise the error a callback raised on the loop, which stopped the canvas. A
withblock passesFalsewhile another error is already propagating.
Raises:
-
TerminalError–A callback raised on the canvas's loop, or the loop would not stop.
Source code in xnano/canvas.py
1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 | |
local
¶
Convert a cell of the screen into a cell of this canvas.
Parameters:
-
point(Point) –A cell in screen coordinates, such as
context.mouse.position.
Examples:
import xnano
from xnano_core import Point
with xnano.new(width=10, height=3) as canvas:
assert canvas.local(Point(4, 1)) == Point(4, 1)
Source code in xnano/canvas.py
move
¶
Move a canvas that is written into another one by a number of cells along each axis.
This moves the placement the canvas is drawn in; see
Placement.move.
Parameters:
-
x(int, default:0) –Columns to move right; a negative number moves left.
-
y(int, default:0) –Rows to move down; a negative number moves up.
Returns:
-
Self–The canvas, so calls can chain.
Raises:
-
TerminalError–The canvas is not written into another canvas, so it sits at the origin and cannot move.
-
TypeError–An amount is not a whole number.
Examples:
import xnano
with xnano.new(width=20, height=3) as outer:
inner = outer.new('box', width=4, height=1)
inner.move(x=2, y=1)
assert (inner.x, inner.y) == (2, 1)
assert outer.render().splitlines()[1] == ' box'
Source code in xnano/canvas.py
up
¶
up(count: int = 1) -> Self
Move a written canvas up by count rows; see move.
Parameters:
-
count(int, default:1) –How many rows to move.
Raises:
-
TerminalError–The canvas is not written into another canvas.
-
TypeError–countis not a whole number. -
ValueError–countis negative.
Source code in xnano/canvas.py
down
¶
down(count: int = 1) -> Self
Move a written canvas down by count rows; see move.
Parameters:
-
count(int, default:1) –How many rows to move.
Raises:
-
TerminalError–The canvas is not written into another canvas.
-
TypeError–countis not a whole number. -
ValueError–countis negative.
Source code in xnano/canvas.py
left
¶
left(count: int = 1) -> Self
Move a written canvas left by count columns; see move.
Parameters:
-
count(int, default:1) –How many columns to move.
Raises:
-
TerminalError–The canvas is not written into another canvas.
-
TypeError–countis not a whole number. -
ValueError–countis negative.
Source code in xnano/canvas.py
right
¶
right(count: int = 1) -> Self
Move a written canvas right by count columns; see move.
Parameters:
-
count(int, default:1) –How many columns to move.
Raises:
-
TerminalError–The canvas is not written into another canvas.
-
TypeError–countis not a whole number. -
ValueError–countis negative.
Source code in xnano/canvas.py
new
¶
new(content: Content | None = None, /, *, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, effects: Sequence[RenderableEffectSpecification] = (), **settings: Unpack[CanvasSettings]) -> Canvas[DepsT, StateT]
Create a canvas inside this one and draw it here at once, at the top-left corner.
This is new followed by canvas.write(child): the child's size is
measured in this canvas ('50%' is half of it, and no size fills it), it runs on
this canvas's time and hears its input, and it closes with this canvas. Move it with
move and its relatives, or create it with
new and write it with x and y to place it elsewhere.
Parameters:
-
content(Content | None, default:None) –Something to write into the new canvas at once, such as text.
-
width(int | Percentage | str | None, default:None) –The width in cells, or a percentage of this canvas such as
'50%'. -
height(int | Percentage | str | None, default:None) –The height in cells, or a percentage of this canvas.
-
effects(Sequence[RenderableEffectSpecification], default:()) –Effects to apply to the new canvas, as dictionaries with a
kindkey. -
settings(Unpack[CanvasSettings], default:{}) –The input gates and the new canvas's own look; see
CanvasSettings.
Returns:
Raises:
-
TypeError–An appearance keyword was given with content, or a keyword is unknown.
-
ValueError–A size, gate or border kind is out of range.
-
TerminalError–This canvas is closed, or a hook of the new canvas needs a gate this canvas turned off.
Examples:
import xnano
with xnano.new(width=12, height=4) as outer:
panel = outer.new(height='50%', border='plain')
assert (panel.width, panel.height) == (12, 2)
assert outer.render().splitlines()[0] == '┌──────────┐'
Source code in xnano/canvas.py
2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 | |
write
¶
write(content: Content | Canvas[WrittenDepsT, WrittenStateT] | None = None, /, *, x: Axis | None = None, y: Axis | None = None, width: Axis | None = None, height: Axis | None = None, layer: int = 0, lock: bool = False, edges: EdgeBehavior | None = None, effects: Sequence[RenderableEffectSpecification] = (), style: Style | None = None, foreground: ColorValue | None = None, background: ColorValue | None = None, bold: bool = False, dim: bool = False, italic: bool = False, underline: bool = False, blink: bool = False, reverse: bool = False, hidden: bool = False, crossed_out: bool = False, title: str | None = None, border: BorderKind | bool | None = None, border_style: Style | None = None, padding: Spacing | None = None, margin: Spacing | None = None) -> Placement[DepsT, StateT]
Put something on the canvas: text, a widget, a buffer, another canvas or an image.
On a canvas that runs by itself it appears at once: this only declares it, and the
canvas's own loop draws it. Anything you leave out is taken from the content: x
and y default to the top-left corner, and width and height to the size of
the content, so canvas.write('hello') shows five cells of text where it is.
Change the text later by assigning the placement's
text.
Inside the with block of a canvas created in this one's block, what is written here
is placed and clipped within that canvas's area of this one: where it is written, or
the top-left corner while it is not. Positions such as y='center' are measured in
that area, and that canvas's edges applies there.
A canvas from new that is written here becomes part of this one: it
draws inside the placement, its own clock is set aside and it runs on this canvas's
time. It may not have been given state or deps, which belong to the outermost
canvas. Its audio is not carried over: a musical clock it started and a note it was
playing stop, and a formula that reads its audio.beat follows the outer canvas's
musical clock, which starts at the first beat.
An image is added as an Image (never a file path or a bare
TerminalImage; load one with
from_image). With neither width nor height it keeps
its own proportions at its own size, shrunk to fit the canvas. With one of them the
other follows the image's proportions, and with both the image fits inside that box. An
image sized by a percentage, or by the canvas because you gave no size, is converted
again when the canvas is resized, unless it is locked. An animated image plays on the
canvas's clock.
Parameters:
-
content(Content | Canvas[WrittenDepsT, WrittenStateT] | None, default:None) –Text (or a list of lines), a
Bufferor widget, a canvas fromnew(), an image, or anyRenderable;Nonefor an empty placement. -
x(Axis | None, default:None) –The column of the left edge: a number of cells, a percentage string, a word (
'left','center'or'right'), an expression, or a range ((0, 20)) that limits how far it may travel. SeeAxis. -
y(Axis | None, default:None) –The row of the top edge, in the same forms (
'top','center','bottom'). -
width(Axis | None, default:None) –The width, in the same forms; the content's own width when
None. -
height(Axis | None, default:None) –The height, in the same forms; the content's own height when
None. -
layer(int, default:0) –Content with a higher layer is drawn over content with a lower one.
-
lock(bool, default:False) –Keep the position and size the content has now, whatever the canvas's size does afterwards: percentages, words and formulas are read once.
-
edges(EdgeBehavior | None, default:None) –What the content does at the edges of its area:
'clip','stop'or'wrap'; seePlacement.edges. Left out, it follows what it is written on. -
effects(Sequence[RenderableEffectSpecification], default:()) –Effects to start on the placement, as dictionaries with a
kindkey, such as[{'kind': 'effect.fade_to', 'foreground': 'red'}]. -
style(Style | None, default:None) –A ready-made
Stylefor text. The parameters below are applied over it. -
foreground(ColorValue | None, default:None) –The text color.
-
background(ColorValue | None, default:None) –The color behind the text.
-
bold(bool, default:False) –Whether the text is bold.
-
dim(bool, default:False) –Whether the text is dimmed.
-
italic(bool, default:False) –Whether the text is italic.
-
underline(bool, default:False) –Whether the text is underlined.
-
blink(bool, default:False) –Whether the text blinks.
-
reverse(bool, default:False) –Whether foreground and background are swapped.
-
hidden(bool, default:False) –Whether the text is hidden.
-
crossed_out(bool, default:False) –Whether the text is struck through.
-
title(str | None, default:None) –A title on the border of a box around the text.
-
border(BorderKind | bool | None, default:None) –The kind of border for a box around the content, a
BorderKindsuch as'plain','rounded','double'or'thick';Truefor'plain'andFalsefor none. Giving atitlealone draws a plain one. -
border_style(Style | None, default:None) –The style of the box's border.
-
padding(Spacing | None, default:None) –Space between the border and the text: a number of cells, a float or a percentage of the text's size, or a dictionary with
top,right,bottom,left,verticalandhorizontal. Giving it draws the text in a box without a border unlessborderortitleis also given. -
margin(Spacing | None, default:None) –Space outside the box, in the same forms.
Returns:
Raises:
-
TypeError–The content is a file path, raw bytes, or an operation, style or effect (apply those with
canvas.update(...)instead). -
TerminalError–The content is a canvas that cannot be written here.
Examples:
import xnano
with xnano.new(width=20, height=4) as canvas:
box = canvas.write('hello', x='50%', y=1)
assert box.x == 10
assert (box.width, box.height) == (5, 1)
canvas.write(
'Ready',
x='center',
y='bottom',
foreground='green',
bold=True,
border='rounded',
title='Status',
)
Source code in xnano/canvas.py
2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 | |
clear
¶
update
¶
Change the canvas's own cells with operations, styles or effects.
canvas | operation does the same. To put content on the canvas, use
write instead.
Parameters:
-
operations(Operation | Style, default:()) –What to apply, in order: a core operation such as
FillorWrite, a style, or an xnano customization such as an effect.
Returns:
-
Self–The canvas, so calls chain.
Examples:
import xnano
from xnano_core import Fill
with xnano.new(width=4, height=1) as canvas:
canvas.update(Fill('.'))
assert canvas.render() == '....'
canvas | Fill('#')
assert canvas.render() == '####'
Source code in xnano/canvas.py
__or__
¶
Apply an operation, customization or style with canvas | operation.
Source code in xnano/canvas.py
__ior__
¶
Apply an operation, customization or style in place with canvas |= operation.
inspect
¶
inspect(expression: Expression[ValueT]) -> ValueT
Evaluate an expression against the canvas and return its value.
Parameters:
-
expression(Expression[ValueT]) –Any expression, such as
Width,TimeElapsed % 1orcanvas.audio.level.
Examples:
import xnano
from xnano_core import Width
with xnano.new(width=12, height=2) as canvas:
assert canvas.inspect(Width) == 12
Source code in xnano/canvas.py
iter
¶
Iterate over the canvas's frames, drawing between them; pass count= or seconds=,
or break out of the loop.
Each iteration yields a Frame, and whatever you change
before asking for the next one is drawn. On a canvas from new() each iteration
moves the clock by one frame of its fps, so the same loop produces the same
frames every run; without count or seconds it runs as fast as your code. A
canvas that runs by itself draws as you declare, so there this only paces your code
at its fps: if you are slow, frames are skipped rather than queued.
stop freezes the time iter() follows: the
iterations go on drawing and hooks still run, but the clock stands still until
resume. To move a stopped canvas one frame, call
step.
The iteration ends when count frames have been yielded, when the canvas's time has
run for seconds (whichever comes first), when the canvas is asked to
quit or closed, or when you break out of the loop.
Parameters:
-
count(int | None, default:None) –The most frames to yield.
-
seconds(float | None, default:None) –The most seconds of the canvas's time to run for.
Raises:
-
TerminalError–secondswithoutcounton a stopped canvas, which would never reach the end. -
TerminalClosedError–The canvas is closed.
Examples:
import xnano
with xnano.new(width=8, height=1, fps=10) as canvas:
frames = list(canvas.iter(count=3))
assert [frame.elapsed_seconds for frame in frames] == [0.0, 0.1, 0.2]
Source code in xnano/canvas.py
3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 | |
step
¶
Move time by delta_seconds, one frame by default, and draw exactly one frame.
On a canvas from new() this always works, and it moves time even while the
canvas is stopped, which leaves it stopped: it is how a stopped canvas is walked one
frame at a time. A canvas that runs by itself can be stepped only while it is
stopped. One frame is 1 / fps seconds; default steps and the frames of
iter share one count, so sixty of them at fps=60
are exactly one second. A large delta_seconds is one jump: timers that it crossed
all run, and so do fixed-step every hooks, at most 100000 of their steps in one
step; the steps that remain run in the steps that follow.
Parameters:
-
delta_seconds(float | None, default:None) –How far to move, in seconds, above zero; one frame when
None.
Returns:
Raises:
-
ValueError–delta_secondsis not above zero. -
TerminalError–The canvas runs by itself and is not stopped.
-
TerminalClosedError–The canvas was closed.
Examples:
import xnano
with xnano.new(width=8, height=1, fps=10) as canvas:
frame = canvas.step()
assert frame.elapsed_seconds == 0.1
canvas.stop()
canvas.step(delta_seconds=0.5)
assert canvas.time.elapsed == 0.6
assert canvas.time.is_stopped
Source code in xnano/canvas.py
deliver_events
¶
Deliver pushed events to their hooks and draw a frame, without moving time.
Events pushed with push reach their hooks at the
canvas's next step; this is that step on a canvas whose clock you move. A canvas that
runs by itself delivers them on its own.
Examples:
import xnano
from xnano_core import Event
with xnano.new(width=8, height=1) as canvas:
heard = []
canvas.hook(lambda context: heard.append(context.event.kind))
canvas.push(Event.custom('ping'))
canvas.deliver_events()
assert heard == ['custom']
assert canvas.time.elapsed == 0.0
Source code in xnano/canvas.py
stop
¶
Freeze time: the same as Time.stop.
The canvas keeps drawing and hooks still hear input, but animations, effects,
formulas, timers and the beat stand still until resume.
step moves a stopped canvas one frame at a time. To end a
loop, call quit.
Source code in xnano/canvas.py
quit
¶
Ask the canvas's loop to end.
Safe from any thread, including from a hook. A running iter(), run(),
wait_for_key() or prompt() ends. On a canvas from new() the canvas stays
open for writing and rendering; on a canvas that runs by itself (realtime=True, or
on the screen) the loop ends and the canvas closes.
Source code in xnano/canvas.py
run
¶
Wait until the canvas's loop ends.
A canvas that runs by itself draws and listens on its own, so this only keeps your
program alive until a quit key is pressed, or something calls
quit (a hook, another thread). Declare what you want
before calling it.
Parameters:
-
quit_on(KeyName | Sequence[KeyName] | None, default:'ctrl+c') –The key, or keys, that end the loop, such as
'q'or('q', 'escape').'ctrl+c'by default; giveNoneto leave ending to your own code.
Raises:
-
TerminalError–A callback raised on the canvas's loop, or the canvas has a clock that only moves when you move it, so waiting would never end.
Source code in xnano/canvas.py
wait_for_key
¶
Wait until a key is pressed, and return its name.
Your program waits here while the canvas goes on drawing and animating. It reads
like input(): nothing else runs on your thread until the key arrives.
Parameters:
-
keys(KeyName, default:()) –The keys to wait for, such as
'enter'or'q'. Give none to take any key.
Returns:
-
str–The name of the key that was pressed.
Raises:
-
TerminalError–The canvas ended, or has a manual clock, so no key could arrive.
Examples:
In a program: canvas.wait_for_key() after adding "Press any key to start".
Here a timer plays the part of the person pressing the key:
import xnano
from xnano.events import Event
with xnano.new(realtime=True, width=30, height=2) as canvas:
canvas.write('Press enter to start')
@canvas.hook(on='tick', after=0.05)
def press(context: xnano.Context) -> None:
canvas.push(Event.key_press('enter'))
assert canvas.wait_for_key('enter') == 'enter'
Source code in xnano/canvas.py
prompt
¶
Prompt for a line of text, and return what was typed.
The prompt and the answer appear on the canvas while your program waits, like
input(). Enter submits and Backspace deletes.
Parameters:
-
text(str) –What to ask, such as
'Your name: '. -
x(int, default:0) –The column of the prompt.
-
y(int, default:0) –The row of the prompt.
-
default(str, default:'') –Text already in the answer, which the user can edit.
Returns:
-
str–What was typed, without the prompt.
Raises:
-
TerminalError–The canvas ended, or has a manual clock, so no answer could arrive.
Examples:
In a program: name = canvas.prompt('Your name: '). Here a timer plays the part
of the person typing:
import xnano
from xnano.events import Event
with xnano.new(realtime=True, width=30, height=2) as canvas:
@canvas.hook(on='tick', after=0.05)
def type_answer(context: xnano.Context) -> None:
for letter in 'Ada':
canvas.push(Event.key_press(letter))
canvas.push(Event.key_press('enter'))
assert canvas.prompt('Your name: ') == 'Ada'
Source code in xnano/canvas.py
3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 | |
render
¶
Draw the current frame and return it, without a screen.
A canvas that was written into another one returns its own cells alone, at its own size, as they stand in the outer canvas's current frame.
Parameters:
-
format(Literal['text', 'ansi'], default:'text') –'text'for the characters alone,'ansi'for the characters with their colors and styles as escape codes.
Raises:
-
ValueError–formatis neither'text'nor'ansi'.
Examples:
import xnano
with xnano.new(width=6, height=2) as canvas:
canvas.write('hello', y=1)
assert canvas.render().splitlines() == ['', 'hello']
assert 'hello' in canvas.render(format='ansi')
Source code in xnano/canvas.py
push
¶
push(event: Event) -> None
Deliver an event to the canvas as if it had been typed, clicked or reported.
Hooks hear it the same way as input from the keyboard and mouse, at the canvas's
next step, whatever the input gates say. Build events with
Event; Event.custom(name, data=...) makes one of your own.
Parameters:
-
event(Event) –The event to deliver.
Raises:
Examples:
import xnano
from xnano_core import Event
with xnano.new(width=8, height=1) as canvas:
heard = []
canvas.hook(
lambda context: heard.append(context.event.custom_name),
when=lambda context: context.event.kind == 'custom',
)
canvas.push(Event.custom('volume_changed'))
canvas.deliver_events()
assert heard == ['volume_changed']
Source code in xnano/canvas.py
play
¶
play(playable: PlaySource, /, **options: Unpack[PlayOptions]) -> PlayHandle
Play a note, a sound, a symbol name, a melody or a sequence of those, and return at once.
This is audio.play; see it for what playable may
be and for every option. On a canvas from new() the sound is recorded instead of
played; read it with audio.render_sound.
Parameters:
-
playable(PlaySource) –What to play: a note, a sound, a symbol name, melody text such as
'C4 E4 G4:2', a MIDI number, a frequency, or a sequence of those. -
options(Unpack[PlayOptions], default:{}) –The settings of
PlayOptions, such asinstrument,volume,pan,beatsandat_beat.
Returns:
-
PlayHandle–A
PlayHandlethat tells when the sound is over.
Examples:
import xnano
with xnano.new(width=8, height=1, bpm=100) as canvas:
canvas.play('C4 E4 G4:2 rest C5+E5+G5', instrument='bell')
canvas.step(delta_seconds=1.0)
assert canvas.audio.render_sound().duration_milliseconds == 1000.0
Source code in xnano/canvas.py
state_reference
¶
state_reference(reader: Callable[[StateT], ValueT]) -> StateReference[ValueT]
A core reference for one field of the canvas's state.
The reference can be used as an axis, a range bound or a widget parameter.
Parameters:
-
reader(Callable[[StateT], ValueT]) –A function or lambda that reads the field, such as
lambda state: state.volume.
Raises:
-
TypeError–readeris not a function. -
TerminalError–The canvas was created without
state.
Source code in xnano/canvas.py
open
¶
open(canvas: Canvas[DepsT, StateT], /, *, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, **settings: Unpack[OpenSettings]) -> Terminal[DepsT, StateT]
open(content: Content | None = None, /, *, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, fps: float = 60.0, bpm: float = 120.0, effects: Sequence[RenderableEffectSpecification] = (), **settings: Unpack[CanvasSettings]) -> Terminal[None, None]
open(content: Content | None = None, /, *, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, fps: float = 60.0, bpm: float = 120.0, effects: Sequence[RenderableEffectSpecification] = (), state: StateT, **settings: Unpack[CanvasSettings]) -> Terminal[None, StateT]
open(content: Content | None = None, /, *, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, fps: float = 60.0, bpm: float = 120.0, effects: Sequence[RenderableEffectSpecification] = (), deps: DepsT, **settings: Unpack[CanvasSettings]) -> Terminal[DepsT, None]
open(content: Content | None = None, /, *, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, fps: float = 60.0, bpm: float = 120.0, effects: Sequence[RenderableEffectSpecification] = (), deps: DepsT, state: StateT, **settings: Unpack[CanvasSettings]) -> Terminal[DepsT, StateT]
open(canvas_or_content: Canvas[object, object] | Content | None = None, /, *, title: str | None = None, width: int | Percentage | str | None = None, height: int | Percentage | str | None = None, **settings: object) -> Terminal[DepsT, StateT]
Put a canvas on your screen and return it as a Terminal.
Given a canvas from new, this is Canvas.open:
the same object is attached to the screen and its time continues. Given content or
nothing, it creates the canvas with new() (the same keywords, except realtime)
and attaches that. From here on, anything you write, remove or draw appears at once, on a
loop of the canvas's own that your code never waits for. A with block closes it and
gives the screen back.
By default the canvas takes over the whole window, on the alternate screen so that your
shell's history comes back untouched; width='100%', height='100%' asks for the same.
Give height (and optionally width) to draw in a region of that size below the cursor
instead, in cells or as a percentage of the window such as '50%'; that region is erased
when the canvas closes, unless you pass clear=False. title is the window title.
The input gates say what the screen reports: mouse for buttons and drags, scroll
for the wheel, keyboard, paste, focus and interrupt (Ctrl-C), and
capture holds back what the program prints while the canvas is on the screen. See
GateSettings. mouse, scroll and paste are turned
on by themselves when a hook registered before opening needs them.
Parameters:
-
canvas_or_content(Canvas[object, object] | Content | None, default:None) –A canvas from
new(), or content to write into a new canvas. -
title(str | None, default:None) –The window title;
Nonekeeps the canvas's title. -
width(int | Percentage | str | None, default:None) –The width of the region to draw in, in cells or as a percentage of the window; the whole window when neither
widthnorheightis given. -
height(int | Percentage | str | None, default:None) –The height of the region to draw in, in cells or as a percentage of the window.
-
settings(object, default:{}) –The other keywords of
new()exceptrealtime: the gates, the canvas's look,fps,bpm,effects,depsandstate.
Returns:
-
Terminal[DepsT, StateT]–The open
Terminal, which is also the one module-level functions -
Terminal[DepsT, StateT]–such as
activefind.
Raises:
-
TerminalAlreadyOpenError–Another canvas is already on the screen.
-
TerminalError–There is no terminal to draw on.
-
TypeError–realtimewas given, an appearance keyword was given with content, or a keyword is unknown. -
ValueError–A
widthwas given without aheight, or a value is out of range.
Examples:
with xnano.open(height=3, mouse=True) as terminal:
terminal.write('hello')
terminal.run(quit_on='q')
Source code in xnano/terminal.py
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | |