Node¶
A handle to one node in a Window's tree. Returned by
window.create, window.root, and the tree methods
below; never constructed directly. Two handles compare equal, and hash
equal, when they name the same node, so nodes can key a dict.
set, get, and focus¶
set(**props) sets any number of properties at once, atomically: every
value is checked first, and a bad name or value raises ValueError without
changing anything. Optional properties take None to clear them.
thumb.set(role="slider", label="Volume", value=0.4, value_min=0.0,
value_max=1.0, value_step=0.1, focusable=True, cursor="pointer")
| Property | Value |
|---|---|
role |
"button", "checkbox", "radio", "switch", "slider", "progressbar", "link", "textbox", "tab", "tablist", "tabpanel", "menu", "menuitem", "dialog", "alert", "list", "listitem", "tree", "treeitem", "heading", "img", "group", "none", "separator" (0.5.6; AccessKit has no separator role, so it is a splitter, which AT-SPI and UIA expose as a separator) |
label |
str or None — the name assistive technology reads |
value |
str, number, or None |
value_min, value_max, value_step |
number or None |
checked, selected, expanded |
bool or None |
disabled |
bool |
pressed |
True, False, "mixed", or None (0.5.6) — a toggle button's state; where both are set, it wins over checked |
invalid, busy |
bool (0.5.6) — the value is not acceptable; the node is updating |
description |
str or None (0.5.6) — a longer description than label |
describedby, controls |
a Node, a list of Nodes, or None (0.5.6) — nodes that describe this one, and nodes it controls. They must belong to the same window. get returns the list of nodes still alive; one that is hidden or removed is left out of what assistive technology sees |
current |
"page", "step", "location", "date", "time", True, False, or None (0.5.6) — the current item in a set |
value_now, value_text |
number or None; str or None (0.5.6) — value_now is the number of value (setting one sets the other), value_text is how the value is said ("40 percent") |
level |
positive int or None — a heading's level |
live |
"off", "polite", "assertive", or None — how changes are announced |
a11y_hidden |
bool — hidden from assistive technology |
focusable |
bool — focusable by Tab, click, and focus(); a click on a descendant focuses the nearest focusable ancestor |
tab_index |
int — positive values come first in Tab order, ascending; then 0 in tree order; negative leaves the node out of Tab order but focusable by click and focus() |
cursor |
the pointer shape over the node, inherited by descendants: "default", "pointer", "text", "grab", "grabbing", "move", "not_allowed", "wait", "progress", "crosshair", "help", "col_resize", "row_resize", "ew_resize", "ns_resize", "nesw_resize", "nwse_resize", "copy", "cell", "context_menu", "zoom_in", "zoom_out", "all_scroll", or None |
hit_testable |
bool — whether the node can be the target of pointer events |
shader |
a Shader or None (0.5.1) — WGSL that paints the node, or transforms its content |
window_region |
"drag", "none", or None (0.5.0) — "drag" makes the node a title bar that moves the window; "none" keeps a node inside one from dragging. See Title bar and borders |
The actions assistive technology is offered follow from role and state:
focusable nodes offer focus, button-like roles offer activation, a slider
or a value range offers increment/decrement/set-value, and expanded offers
expand/collapse. Requests arrive as the a11y_action event.
get(name) reads any property back exactly as set — an animating one
at its current, mid-animation value — plus the read-only kind, focused,
layer_placement, and layout_* values; see
Nodes and Properties.
focus() moves keyboard focus to the node, firing unfocus and focus;
the scroll views around it scroll to show it (0.4.2).
scroll_into_view() (0.4.2) scrolls every scroll view around the node,
innermost first, by the least that shows it; a node longer than a view is
aligned to its start. An assistive technology's scroll_into_view request
does the same, after any a11y_action listener.
Cursors¶
A node's cursor is the pointer shape over it, inherited by its descendants: one of the
named shapes in the table above, None, or (0.5.4) a tre.CursorImage, a shape drawn
from pixels:
import tre
rgba = bytearray(32 * 32 * 4) # straight-alpha RGBA8, drawn however you like
... # (decode a PNG with your imaging library)
reticle = tre.CursorImage(bytes(rgba), 32, 32, hotspot=(16, 16))
canvas.set(cursor=reticle)
CursorImage(rgba, width, height, hotspot=(0, 0)) takes width * height * 4 bytes, each
side 1 to 256 pixels, and a hotspot, the pixel that is the pointer's position. The engine
decodes no image format. The same image made twice is one cursor, and a cursor lives as
long as the process. The OS cursor is made when App.run() runs its loop (image.ready
says), so the default shape shows for the first moments. The image is in device pixels, not
scaled for a HiDPI display: give a larger one on a 2x screen. node.get("cursor") returns
the CursorImage that was set. It works on Windows, macOS, X11 and Wayland, and was checked
by making the cursor on Wayland and X11 (KDE); the pixels themselves on screen were not
inspected.
animate¶
animate(property, to, duration_ms=0, easing=None, on_complete=None)
Eases one property from its current value to to and returns at once:
node.animate("opacity", 0.0, 300, easing=(0.3, 0.0, 0.8, 0.15),
on_complete=lambda: node.remove())
The animatable properties are fill, stroke_color, stroke_width,
opacity, corner_radius, shadows, translate_x, translate_y,
scale, rotation_deg, a scroll view's scroll_offset, and a path's
data, trim_start, and trim_end. Any other name raises ValueError;
a to of the wrong shape raises ValueError, or TypeError for a
non-number to on a numeric property. easing is "linear" or a cubic bezier
(x1, y1, x2, y2). on_complete is called with no arguments exactly once,
in the frame the value arrives — or in window.advance(ms) in a test — and
never for an animation replaced or stopped first.
get_target(name) is where a running animation is heading — get(name)
when nothing animates it — and stop_animation(name) stops it where it
is. See Paint, Paths, and Animation and the
Animation guide.
redraw¶
redraw() runs a canvas's draw callback now; see
Painter. Raises ValueError on any other kind.
Events¶
on(event, handler) registers a listener — click, pointer_down,
pointer_enter, change, focus, and the rest — and off(event)
removes it; see Events and Listeners for every event, bubbling,
and the Event fields. A handler takes no arguments or one, the Event. An
exception raised inside a handler is caught, logged, and non-fatal.
capture_pointer() routes every later pointer event to this node until
the button is released or release_pointer() is called — for drags.
Tree structure¶
| Method | Does |
|---|---|
add_child(child) |
Appends child, moving it if it's attached elsewhere |
insert_child(index, child) |
Attaches child so it ends up at index — afterwards children()[index] == child — moving it if it's attached anywhere. index counts the children once child has left its old place; past the end raises IndexError |
children() |
This node's children, in order |
parent() |
Its parent, or None for the root or a detached node |
remove() |
Detaches this node from its parent; it stays alive and can be attached again |
destroy() |
Frees this node and its whole subtree now, with their listeners |
Attaching raises ValueError if child is this node or one of its
ancestors, or belongs to a different Window. Moving a node keeps
everything about it — the same Node, its listeners, its focus, and any
running animation — which is what a keyed list reconciler needs:
for index, key in enumerate(new_order):
list_box.insert_child(index, rows[key])
Lifetime¶
- A node attached to a window lives while it's attached.
- A node made by
window.create, or detached byremove(), lives while anyNodehandle to it — or to anything in its subtree — exists. When the last one goes, the subtree is freed with its listeners, so a forgottendestroy()never leaks. destroy()frees now. Using a handle to a freed node raisesValueError.
Switching screens is old.remove() then window.root.add_child(new):
- Focus leaves with a removed or destroyed subtree. When focus is inside
it, the focused node gets
unfocusfirst — bubbling through the tree as it was — and nothing is focused afterwards. - A detached subtree keeps everything else: scroll offsets, a text input's text, caret, and selection, and running animations, which keep advancing on the window's clock. Attach it again and it's as you left it.