A Python TUI library with differential rendering - port of @mariozechner/pi-tui
Project description
Terminal UIs that don't flicker. Native scrollback. 60fps.
from pypitui import TUI, Text, Input, ProcessTerminal
terminal = ProcessTerminal()
tui = TUI(terminal)
tui.add_child(Text("Hello, World!"))
inp = Input(placeholder="Type here...")
inp.on_submit = lambda v: print(f"You typed: {v}")
tui.add_child(inp)
tui.set_focus(inp)
tui.run() # 60fps, no flicker, scrollback enabled
Why PyPiTUI?
| Library | Rendering | Scrollback | 60fps | Size |
|---|---|---|---|---|
| curses | Full redraw | ❌ | ❌ | Built-in |
| Textual | Full redraw | ❌ | ⚠️ | ~50MB |
| Rich (Live) | Full redraw | ❌ | ⚠️ | ~10MB |
| PyPiTUI | Differential | ✓ | ✓ | ~100KB |
Only changed lines redraw. No alternate screen buffer—your content flows into normal terminal scrollback.
Install
pip install pypitui
# pip install pypitui[rich] # Optional: markdown, tables
Requires Python 3.12+.
Quick Start
from pypitui import (
TUI, Container, Text, Input, SelectList, SelectItem,
BorderedBox, ProcessTerminal, Key, matches_key
)
class App:
def __init__(self):
self.terminal = ProcessTerminal()
self.tui = TUI(self.terminal)
self.root = Container()
self.tui.add_child(self.root)
self.show_form()
def show_form(self):
"""Compose a form from multiple components."""
self.root.children.clear()
# Container composes children vertically
self.root.add_child(Text("User Registration"))
self.root.add_child(Text("─" * 30))
# Input with validation
name_input = Input(placeholder="Enter username", max_length=20)
self.root.add_child(name_input)
# Another input
email_input = Input(placeholder="Enter email")
self.root.add_child(email_input)
# Bordered box containing a list
box = BorderedBox(title="Select Role")
roles = SelectList([
SelectItem("admin", "Administrator"),
SelectItem("user", "Standard User"),
], max_visible=3)
box.add_child(roles)
self.root.add_child(box)
self.tui.set_focus(name_input)
def run(self):
self.running = True
self.tui.start()
try:
while self.running:
data = self.terminal.read_sequence(timeout=0.05)
if data and matches_key(data, Key.ctrl("c")):
break
self.tui.handle_input(data)
self.tui.request_render()
self.tui.render_frame()
finally:
self.tui.stop()
App().run()
Components
Text— Multi-line text with wrappingInput— Text input with cursor, validationSelectList— Interactive selection with filteringBorderedBox— Panel with borders and titleContainer— Groups components verticallyOverlayOptions— Floating dialogs and modals
Critical Pattern: Reuse the TUI
Wrong: Creating new TUI instances breaks differential rendering.
# ❌ DON'T
def switch_screen():
return TUI(terminal) # Loses state!
Right: Clear containers, not the TUI.
# ✅ DO
class App:
def __init__(self):
self.tui = TUI(terminal) # Create once
self.root = Container()
self.tui.add_child(self.root)
def switch_screen(self):
self.root.children.clear() # Clear container
self.root.add_child(Text("New Screen"))
Rich Integration
Optional Rich support for markdown and tables:
from pypitui.rich_components import Markdown, RichTable, RichText
tui.add_child(Markdown("# Hello\n\n**Bold** text"))
Development
git clone https://github.com/jeremysball/pypitui.git
cd pypitui && uv sync --extra dev
# Install pre-commit hooks (runs ruff, mypy, pytest)
git config core.hooksPath .githooks
uv run python examples/demo.py
API Reference
| Import | Purpose |
|---|---|
TUI |
Main controller |
Container, Text, Input, SelectList, BorderedBox |
Components |
OverlayOptions, OverlayMargin |
Overlay positioning |
Key, matches_key, parse_key |
Keyboard handling |
ProcessTerminal, MockTerminal |
Terminal I/O |
Full docs: LLMS.md
License
MIT — see LICENSE.
Inspired by pi-tui
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pypitui-0.2.1.tar.gz.
File metadata
- Download URL: pypitui-0.2.1.tar.gz
- Upload date:
- Size: 35.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea8387c07046dcc301f72e34d9edffc13e3b7869a572d6f9b8c1be1fdac24d20
|
|
| MD5 |
b528d457506494d6d68de86c824e6adc
|
|
| BLAKE2b-256 |
6727e2ec0f049a6b3d806f7f50fd582712e6a80bba40277a16ce61503e43f35a
|
Provenance
The following attestation bundles were made for pypitui-0.2.1.tar.gz:
Publisher:
release.yml on jeremysball/pypitui
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pypitui-0.2.1.tar.gz -
Subject digest:
ea8387c07046dcc301f72e34d9edffc13e3b7869a572d6f9b8c1be1fdac24d20 - Sigstore transparency entry: 1004324048
- Sigstore integration time:
-
Permalink:
jeremysball/pypitui@5d3c8db91f34fb45dab9b08c89a94844c47f538f -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/jeremysball
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5d3c8db91f34fb45dab9b08c89a94844c47f538f -
Trigger Event:
push
-
Statement type:
File details
Details for the file pypitui-0.2.1-py3-none-any.whl.
File metadata
- Download URL: pypitui-0.2.1-py3-none-any.whl
- Upload date:
- Size: 62.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87f1887e3bdbf9011f8b04dceb126a8ecc0d03f51cf9ea999cf9e222b0f87ae8
|
|
| MD5 |
0347d2949f1f9de3626986536447d61b
|
|
| BLAKE2b-256 |
84200060195486ae4e69069280b9070176226e7027393fd16e064c52ae304f7b
|
Provenance
The following attestation bundles were made for pypitui-0.2.1-py3-none-any.whl:
Publisher:
release.yml on jeremysball/pypitui
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pypitui-0.2.1-py3-none-any.whl -
Subject digest:
87f1887e3bdbf9011f8b04dceb126a8ecc0d03f51cf9ea999cf9e222b0f87ae8 - Sigstore transparency entry: 1004324052
- Sigstore integration time:
-
Permalink:
jeremysball/pypitui@5d3c8db91f34fb45dab9b08c89a94844c47f538f -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/jeremysball
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@5d3c8db91f34fb45dab9b08c89a94844c47f538f -
Trigger Event:
push
-
Statement type: