How it works
Python Rust (pyzed._core, PyO3) Iced 0.14App.view() ──► Widget tree ──────► render.rs: Widget → iced::Element ──► layout + wgpu/tiny-skiaApp.update() ◄── messages ◄────── app.rs: Message, Command → Task ◄──── events (mouse, keyboard)Pieces
Section titled “Pieces”flowchart TB
subgraph PY["Python (your code)"]
A["App / Window"] --> W["view() → pz.* / ui.*"]
U["update()"]
L["asyncio loop + threads"]
end
subgraph RS["Rust (native extension, PyO3)"]
B["build Iced elements"] --> I["Iced 0.14"]
I --> G["wgpu / tiny-skia"]
end
W -->|"_core.Widget"| B
I -->|"events → messages"| U
L -->|"pz.post / results"| U
U --> W
| File | Responsibility |
|---|---|
src/widget.rs |
The (immutable) Widget class and the functions that build it (text, row…). |
src/render.rs |
Turns the Widget tree into an iced::Element every frame. |
src/app.rs |
The runtime: Message, Command, dispatch to Python, keyboard, post, run and screenshot. |
src/style.rs |
Conversion of colors, sizes, padding and fonts from Python values. |
src/highlighter.rs |
Syntax highlighting with syntect (two-face grammars) and theme colors. |
src/fonts.rs |
Bundled fonts (IBM Plex, Lucide) and load_font. |
python/pyzed/ |
The App class, themes, icons, CLI and the pyzed.ui components. |
Performance
Section titled “Performance”- Widgets are Rust data. Building the tree from Python creates small, immutable objects; no live Iced objects exist in Python.
- The heavy lifting is in Rust. Layout, text measurement, highlighting, scrolling and drawing happen in Rust; Python only runs when there is a message.
- The GIL is released while Iced waits for events, so Python threads keep running.
- Native state for the editor. Each editor’s buffer (text, cursor, history) lives in Rust
and is identified by
id, so typing doesn’t rebuild the text in Python.
Messages
Section titled “Messages”Every interactive widget keeps a reference to the Python value you gave it. When Iced produces
an event, app.rs turns it into a call:
- If it is a callable, it calls it (with the control’s value, if any).
- If it returns a
Command, it translates it into an IcedTask. - If it returns any other value (or the message was not callable), it calls
app.update(value). - It asks for
view()again and Iced draws the difference.
Contributing
Section titled “Contributing”maturin develop --releasepytest testscargo fmt --check && cargo clippy -- -D warningsThe documentation lives in docs/. See
editing the documentation.