Programmatic ASCII art generation for TUIs
Project description
ascii-art
Programmatic ASCII art generation for TUI applications. Build text banners, convert images, draw shapes, render tables and charts, animate sprites — all as plain text strings ready for terminal display.
Installation
pip install ascii-art
With optional integrations:
pip install ascii-art[rich] # Rich panels & text objects
pip install ascii-art[textual] # Textual Static widgets
pip install ascii-art[dev] # pytest, mypy, ruff
Quick Start
import ascii_art
# Figlet text banner
print(ascii_art.text_render("Hello!", font="slant"))
# Image to ASCII
print(ascii_art.image_render("photo.png", width=60))
# Draw shapes on a canvas
canvas = ascii_art.Canvas(40, 20)
ascii_art.circle(canvas, 20, 10, 8, char="*")
ascii_art.rectangle(canvas, 5, 3, 30, 14, char="#")
print(canvas.render())
# Border frame
print(ascii_art.frame("Important message", style=ascii_art.BorderStyle.ROUNDED, title="Notice"))
# Table
print(ascii_art.table(
[["Alice", "92"], ["Bob", "87"]],
headers=["Name", "Score"],
style=ascii_art.BorderStyle.DOUBLE,
))
# Sparkline chart
print(ascii_art.sparkline([1, 4, 2, 8, 5, 7, 3, 6]))
# Bar chart
print(ascii_art.bar_chart([30, 80, 45, 60], labels=["Q1", "Q2", "Q3", "Q4"], width=30))
# Scatter plot
print(ascii_art.scatter([(1, 2), (3, 7), (5, 4), (8, 9)], width=40, height=15))
Fluent Builder API
Every module provides a Builder class for method-chaining style configuration:
from ascii_art import TextBuilder, BorderBuilder, ShapesBuilder, ChartBuilder
# Text
output = TextBuilder().text("Welcome").font("big").width(100).render()
# Borders
box = BorderBuilder().text("Hello").style(BorderStyle.DOUBLE).title("Greeting").render()
# Shapes — compose multiple primitives
scene = (
ShapesBuilder()
.width(60).height(20).char("*")
.add_circle(30, 10, 8)
.add_rectangle(5, 3, 50, 14)
.add_line(0, 0, 59, 19)
.render()
)
# Charts
chart = ChartBuilder().chart_type("bar").data([10, 20, 30]).width(50).labels(["A", "B", "C"]).render()
Text Rendering
Renders text using pyfiglet fonts with alignment control.
from ascii_art import text_render, list_fonts, Alignment
# List available curated fonts
print(list_fonts())
# ['banner', 'big', 'block', 'bubble', 'digital', 'lean', 'slant', 'small', 'standard', ...]
# Centered text
print(text_render("Title", font="shadow", width=60, align=Alignment.CENTER))
Image Conversion
Converts images to ASCII art using luminance mapping with optional edge detection.
from ascii_art import image_render, DETAILED_CHAR_RAMP, BLOCK_CHAR_RAMP
# Basic conversion
print(image_render("landscape.jpg", width=100))
# Inverted with detailed character set
print(image_render("logo.png", width=60, charset=DETAILED_CHAR_RAMP, invert=True))
# Edge detection (Sobel filter)
print(image_render("diagram.png", width=80, edges=True))
# From a PIL Image object
from PIL import Image
img = Image.open("photo.jpg")
print(image_render(img, width=120))
Three built-in character ramps are available:
| Ramp | Characters | Use case |
|---|---|---|
DEFAULT_CHAR_RAMP |
.:-=+*#%@ |
General purpose |
DETAILED_CHAR_RAMP |
70 chars, fine gradation | High detail |
BLOCK_CHAR_RAMP |
░▒▓█ |
Block-style output |
Shape Drawing
Draw primitives on a Canvas — a 2D character grid. All drawing functions mutate the canvas in-place and return it for chaining.
from ascii_art import Canvas, line, rectangle, circle, ellipse, triangle, diamond, arrow
c = Canvas(60, 25)
rectangle(c, 0, 0, 59, 24, "#") # border
circle(c, 30, 12, 10, "o") # centered circle
line(c, 0, 0, 59, 24, ".") # diagonal
diamond(c, 30, 12, 5, "+") # diamond overlay
triangle(c, 10, 20, 30, 5, 50, 20) # triangle
arrow(c, 5, 12, 25, 12) # horizontal arrow
print(c.render())
Canvases support overlay for compositing:
bg = Canvas(40, 20)
fg = Canvas(10, 5)
circle(fg, 5, 2, 2, "*")
bg.overlay(fg, 15, 7) # paste fg onto bg at offset (15, 7)
Borders & Frames
from ascii_art import frame, divider, BorderStyle
# Six border styles: SINGLE, DOUBLE, ROUNDED, BOLD, ASCII, DASHED, SHADOW
print(frame("Content here", style=BorderStyle.ROUNDED, padding=2, title="Header"))
# Horizontal dividers
print(divider("Section", width=60, style=BorderStyle.DOUBLE))
# ═══════════════════════════ Section ══════════════════════════
Border Styles
SINGLE: ┌──┐ DOUBLE: ╔══╗ ROUNDED: ╭──╮
│ │ ║ ║ │ │
└──┘ ╚══╝ ╰──╯
BOLD: ┏━━┓ ASCII: +--+ DASHED: ┌╌╌┐
┃ ┃ | | ╎ ╎
┗━━┛ +--+ └╌╌┘
SHADOW: ┌──┐░
│ │░
└──┘░
░░░░
Tables
from ascii_art import table, Alignment, BorderStyle
data = [
["Alice", "Engineering", "92"],
["Bob", "Marketing", "87"],
["Carol", "Engineering", "95"],
]
print(table(
data,
headers=["Name", "Dept", "Score"],
style=BorderStyle.BOLD,
alignments=[Alignment.LEFT, Alignment.LEFT, Alignment.RIGHT],
))
Output:
┏━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
┃ Name ┃ Dept ┃ Score ┃
┣━━━━━━━╋━━━━━━━━━━━━━╋━━━━━━━┫
┃ Alice ┃ Engineering ┃ 92 ┃
┃ Bob ┃ Marketing ┃ 87 ┃
┃ Carol ┃ Engineering ┃ 95 ┃
┗━━━━━━━┻━━━━━━━━━━━━━┻━━━━━━━┛
Charts
from ascii_art import sparkline, bar_chart, scatter
# Sparkline — single-line Unicode block chart
print(sparkline([1, 4, 2, 8, 5, 7, 3, 6]))
# ▁▅▂█▄▆▃▅
# Horizontal bar chart with labels
print(bar_chart([30, 80, 45, 60], labels=["Q1", "Q2", "Q3", "Q4"], width=30))
# Scatter plot
print(scatter([(1, 1), (2, 4), (3, 2), (5, 8), (7, 3)], width=40, height=15))
Sprites & Animation
from ascii_art import Sprite, Animation, SPRITES
# Use a built-in sprite
heart = Sprite(SPRITES["heart"])
print(heart.render())
# Transforms (all return new Sprite)
flipped = heart.flip_h()
big = heart.scale(2)
rotated = heart.rotate(90)
# Animation from frames
frames = [heart, heart.flip_h(), heart.flip_v()]
anim = Animation(frames)
for frame in anim:
print(frame.render())
print("---")
Built-in sprites: heart, star, arrow_right, smiley.
Framework Integrations
Rich
from ascii_art.integrations.rich_panel import to_text, to_panel
from rich.console import Console
console = Console()
art = ascii_art.text_render("Hello", font="slant")
console.print(to_panel(art, title="ASCII Art"))
Textual
from ascii_art.integrations.textual_widget import to_static
widget = to_static(ascii_art.text_render("Dashboard"))
# Use in a Textual app's compose() method
Curses
from ascii_art.integrations.curses_win import write_to_window
# Inside a curses application
write_to_window(stdscr, ascii_art.frame("Status: OK"), y=2, x=5)
CLI
# Text banners
ascii-art text "Hello World" --font slant --width 100 --align center
# Image conversion
ascii-art image photo.png --width 80 --invert
ascii-art image diagram.png --edges
# Shape drawing
ascii-art shape circle --width 40 --height 20 --char "o"
ascii-art shape rectangle --width 30 --height 15
ascii-art shape diamond --char "+"
# Border framing (text argument or stdin)
ascii-art border "important text" --style rounded --title "Notice"
echo "piped text" | ascii-art border --style double
# Table from CSV (argument or stdin)
ascii-art table "Name,Score\nAlice,92\nBob,87" --headers --style bold
cat data.csv | ascii-art table --headers
# Charts
ascii-art chart "1,4,2,8,5,7" --type bar --width 30
ascii-art chart "1,4,2,8,5,7" --type sparkline
Development
# Create virtual environment and install dev dependencies
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Run tests
pytest
# Run a single test
pytest tests/test_shapes.py::test_rectangle -v
# Performance benchmarks
pytest -m perf
# Type checking (strict mode)
mypy src/
# Lint
ruff check src/ tests/
# Build distribution
python -m build
Project Structure
src/ascii_art/
├── __init__.py # Public API re-exports
├── __main__.py # CLI entry point
├── _types.py # Enums, type aliases, character ramps
├── _builder.py # BaseBuilder ABC (fluent API pattern)
├── _cache.py # @cached decorator
├── text.py # Figlet text rendering
├── image.py # Image-to-ASCII conversion
├── shapes.py # Canvas + drawing primitives
├── borders.py # Frame/divider rendering
├── tables.py # ASCII table rendering
├── charts.py # Sparkline, bar chart, scatter plot
├── sprites.py # Sprite transforms & animation
└── integrations/ # Rich, Textual, curses adapters
Requirements
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
File details
Details for the file tui_ascii_art-0.1.1.tar.gz.
File metadata
- Download URL: tui_ascii_art-0.1.1.tar.gz
- Upload date:
- Size: 30.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4379c57cf1e2bf7c88d8b1097535311b47ce187ed68788adab5c29252cedf748
|
|
| MD5 |
5ef45a428881d6f1b70aea6994b50ac9
|
|
| BLAKE2b-256 |
f2a8d23fe110e9b4e3fb1f1e756791d6790e06aa6b0e24a539ace11a701abebc
|
Provenance
The following attestation bundles were made for tui_ascii_art-0.1.1.tar.gz:
Publisher:
publish.yml on GCLNS/ascii-art
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tui_ascii_art-0.1.1.tar.gz -
Subject digest:
4379c57cf1e2bf7c88d8b1097535311b47ce187ed68788adab5c29252cedf748 - Sigstore transparency entry: 1033090148
- Sigstore integration time:
-
Permalink:
GCLNS/ascii-art@082a00e84e3a50fb88489dddd3067d7a0928e583 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/GCLNS
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@082a00e84e3a50fb88489dddd3067d7a0928e583 -
Trigger Event:
release
-
Statement type: