A Python tool that generates beautiful, serializable dashboard images from JSON using Pillow. Dashboard images are easy to share across slack, emails or any communication software that manage.
Use image to make your dashboards accessible by everyone.
For complete docs, see docs/index.md.
Render an example dashboard to PNG using uv (no install required):
uv run -m quadre.cli examples/declarative_featured.json out/featured.pngValidate a JSON document (schema + friendly warnings):
quadre validate examples/declarative_featured.jsonUsing the installed CLI instead of uv:
quadre render examples/declarative_featured.json out.png
quadre validate examples/declarative_featured.jsonInstall and render from Python without touching disk or with custom outputs:
from quadre import render, build_image, to_bytes
# 1) Simple: write a file (default plugin)
render(doc, path="out.png")
# 2) Multiple outputs: file + anything else
render(doc, outputs=[
{"plugin": "file", "path": "out.webp", "format": "WEBP"},
# Example: if you installed a third-party plugin registered as "s3"
{"plugin": "s3", "bucket": "my-bucket", "key": "dashboards/out.png"},
])
# 3) In-memory usage
img = build_image(doc) # PIL.Image
data = to_bytes(doc, "PNG") # bytesPrefer composer code in Python? Use the typed builder to generate the same declarative JSON and keep a single rendering path:
from quadre.flex import Text, KPI, Row, Title, dref, make_doc
doc = make_doc(
Title("Awesome dashboard"),
Row().gap(12).add(
Text("Headline").font("heading").no_grow(),
KPI(title=dref("$.perf.title"), value=dref("$.perf.value")),
KPI(title="Orders", value="12,345"),
),
data={
"perf": {"title": "Revenue", "value": "4,567k€"}
},
canvas={"height": "auto"},
)
# Render (uses the same validator + adapter + flex engine)
from quadre import render
render(doc.to_dict(), path="out.png")Notes:
.grow(r)on children inside a Row maps toproperties.width_ratio(use.no_grow()to keep intrinsic width).- For Column children, use
.fill_remaining()to stretch or.height(px)to suggest a fixed basis. - You can mix builder-generated nodes with hand-written JSON in the same document if needed.
To add your own output destination, expose a function and register it:
from quadre import register_output_plugin
def my_sink(image, ctx, cfg):
# image: PIL.Image.Image
# ctx: OutputContext(path, format, doc, size)
# cfg: dict from your outputs spec
...
register_output_plugin("my_sink", my_sink)- Generate professional-looking dashboards from JSON data
- Customizable KPIs, tables, and metrics
- Support for percentage changes with visual indicators
- Clean, modern design with rounded cards and proper typography
- Cross-platform font support
- Python: 3.12+
- Recommended: uv for fast, isolated runs
From PyPI (CLI + library):
pip install quadreuv run -m quadre.cli render -hThis generates dashboard.png from your JSON data.
uv run -m quadre.cli render examples/declarative_featured.json out.png-i, --input: Input JSON file containing dashboard data (required)-o, --output: Output PNG file (default: dashboard.png)-h, --help: Show help message
# Generate dashboard with default output name
uv run -m quadre.cli render -h
# Generate dashboard with custom output name
quadre render -h
# Using long form arguments
quadre render examples/declarative_featured.json out.png
See examples/flex_e2e.json for a complete working example.
Older presets (horizontal, priority, grids, pyramid, featured, responsive, custom) remain available via the legacy or declarative paths. Flex is the default for new dashboards.
# Flex demos
uv run examples/flex_demo.py
uv run examples/flex_e2e.py
# Render your JSON with the default (Flex) path
uv run -m quadre.cli render your_data.json output.pngImages are written to out/.
The renderer now uses a flexbox-like engine under src/quadre/flex as the only path. It provides:
- Flex containers with
direction(row/column),gap,align_items,padding - Per-child
grow,shrink, andbasisproperties to distribute space - Main-axis
justify_content: start | center | end | space-between | space-around | space-evenly - Optional container background rendering with rounded corners
- Simple
TextWidgetandFixedBoxexamples
Auto-height is enabled by default: the image height grows to fit content (bounded). To force a fixed page height, set "canvas": { "height": "fixed" } or "canvas": { "height": 1280 }.
High-quality rendering via supersampling is available (optional). Enable with:
"canvas": {
"height": "auto",
"scale": 2.0, // render at 2x
"downscale": true // downscale to base size with Lanczos
}Run the demo:
python examples/flex_demo.pyThis generates out/flex_demo.png. The main CLI also uses Flex for the default path.
A JSON-driven example is also included:
python examples/flex_e2e.py # uses examples/flex_e2e.json
python examples/flex_e2e.py data.json out/flex_e2e.pngThis renders a header, a KPI row, and a simple platform list using the Flex engine.
See docs/declarative-layout.md for the JSON model, components, and examples.
quadre supports a Pydantic‑validated theme that defines the color palette, font sizes, and per‑widget defaults (e.g., FlexContainer gaps/padding and TableWidget styles). A theme is loaded for every render, then the layout can still override any property via properties.
- Default theme file:
src/quadre/theme/theme.json - Env override: set
quadre_THEME=/path/to/theme.json - CLI override:
quadre render --theme dark data.json out.pngor--theme /path/to/theme.json - Keys are case‑insensitive, but the bundled themes use lowercase (e.g.,
colors.foreground,fonts.h1).
Example theme (partial):
{
"colors": {
"background": "#ffffff",
"foreground": "#0f172a",
"border": "#d1d5db",
"muted": "#f1f5f9"
},
"fonts": { "h1": 42, "number": 40, "body": 24 },
"widgets": {
"TextWidget": { "fill": "foreground", "font": "body" },
"TableWidget": {
"min_row_height": 28,
"style": {
"background_color": "card_background",
"header_background": "muted",
"text_color": "foreground"
}
}
}
}Example CLI usage:
# Use bundled dark theme
quadre render --theme dark examples/declarative_featured.json out_dark.png
# Use a custom theme file
quadre render --theme /configs/my_theme.json examples/declarative_featured.json out.pngNotes:
- The layout can override any property per widget via
properties. For tables, you can passproperties.styleto override theme table styles locally. - A top‑level
themeobject in the document can still adjust colors/fonts quickly, but per‑widget defaults come from the validated theme file.
quadre automatically detects system fonts but you can customize font usage for better consistency across platforms.
The system automatically finds suitable fonts based on your platform:
- macOS: Helvetica, Arial, San Francisco
- Windows: Arial, Calibri, Segoe UI, Verdana
- Linux: DejaVu Sans, Liberation Sans, Ubuntu
Set a custom font using the quadre_FONT_PATH environment variable:
# macOS
export quadre_FONT_PATH="/System/Library/Fonts/Helvetica.ttc"
# Windows
set quadre_FONT_PATH=C:/Windows/Fonts/arial.ttf
# Linux
export quadre_FONT_PATH="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"Add to your shell configuration file (~/.zshrc, ~/.bashrc) to make it permanent.
# Run the automated installer
./scripts/install_fonts_macos.sh
# Or install manually with Homebrew
brew tap homebrew/cask-fonts
brew install --cask font-dejavu-sans# Ubuntu/Debian
sudo apt install fonts-dejavu fonts-liberation
# CentOS/RHEL/Fedora
sudo dnf install dejavu-sans-fonts liberation-sans-fonts
# Arch Linux
sudo pacman -S ttf-dejavu ttf-liberation- Download fonts from DejaVu Fonts
- Right-click
.ttffiles and select "Install"
Test font availability and configuration:
# Run font diagnostic tool
uv run utils/font_diagnostic.py
# This will show:
# - Available system fonts
# - Current quadre font configuration
# - Installation recommendations
# - Generate a font test imageFonts appear incorrect or distorted:
- Ensure the font file exists at the specified path
- Try a different font from your system
- Check file permissions
"Font not found" errors:
- Install recommended fonts for your platform
- Set
quadre_FONT_PATHto a valid font file - Use the diagnostic tool to find available fonts
Docker font issues:
- Fonts are pre-installed in the Docker image
- Custom fonts can be mounted as volumes
- Use the
quadre_FONT_PATHenvironment variable in containers
Default canvas: 1920×1280 PNG with:
- Header (title + date note)
- KPI row (evenly distributed)
- Platform table (auto columns, zebra rows)
Customize via src/quadre/components/config.py and Flex widgets in src/quadre/flex. The Flex renderer composes existing components (KPI cards, tables) with a simpler layout model.
The tool automatically detects and uses system fonts:
- Docker/Linux: DejaVu Sans fonts (installed in container)
- Local: DejaVu Sans or Liberation Sans fonts
- Fallback: PIL default font
The Docker setup uses:
- Base Image:
ghcr.io/astral-sh/uv:python3.13-bookworm-slim - Package Manager: UV for fast dependency resolution
- Fonts: DejaVu fonts pre-installed for consistent rendering
- Architecture: Multi-platform support (linux/amd64, linux/arm64)
The CLI provides clear error messages for:
- Missing input files
- Invalid JSON format
- File permission issues
- Image generation errors
MIT License - feel free to use and modify as needed.