Skip to main content

Composable framework for building styled Excel reports with openpyxl and matplotlib.

Project description

xllayout

Composable framework to build styled Excel reports with openpyxl and matplotlib.

Philosophy

xllayout is split into three layers:

  1. Theme: centralized visual tokens (fonts, fills, borders, number formats, spacing).
  2. Components: reusable blocks (TitleBlock, KpiCard, TableBlock, MatplotlibChart, etc.).
  3. Layouts: sheet composition using absolute canvas coordinates or Grid12.

This lets you place components at any coordinate without relying on cursor-based writing.

Structure

xllayout/
  pyproject.toml
  xllayout/
    __init__.py
    core/
      canvas.py
      cell_style.py
      grid.py
      style_engine.py
      theme.py
      formats.py
      utils.py
    components/
      title.py
      text.py
      section.py
      kpi.py
      table.py
      chart.py
    layouts/
      summary.py
    writer/
      workbook.py
  examples/
    build_summary.py

Install

Editable (recommended for development):

pip install -e ".[charts]"

Standard install:

pip install ".[charts]"
  • Base dependency: openpyxl
  • Optional extra charts: matplotlib, Pillow

Quick API

from xllayout.core.theme import LightTheme
from xllayout.layouts.summary import ExecutiveSummaryData
from xllayout.core.style_engine import StyleRegistry, StyleRule, StyleSelector
from xllayout.core.cell_style import CellStyleSpec
from xllayout.writer.workbook import ReportWriter

style_registry = StyleRegistry()
style_registry.add_rule(
    StyleRule(
        name="table_headers_center",
        selector=StyleSelector(role="table_header"),
        style=CellStyleSpec(align_horizontal="center", wrap_text=True),
        priority=10,
    )
)

writer = ReportWriter(
    theme=LightTheme(),
    detect_collisions=True,
    style_registry=style_registry,
)
wb = writer.new_workbook()
writer.build_summary_sheet(wb, data)  # data: ExecutiveSummaryData
wb.save("report.xlsx")

Table Cell Styling (v1.1.0)

TableBlock now supports per-column text/cell styling:

from xllayout.components.table import TableBlock
from xllayout.core.cell_style import CellStyleSpec

table = TableBlock(
    headers=["id", "descripcion", "valor"],
    rows=[[1, "Texto largo que debe envolver en varias lineas", 1200]],
    col_widths=[10, 34, 14],
    number_formats_by_col={3: "#,##0"},
    body_style_by_col={
        1: CellStyleSpec(align_horizontal="center", bold=True),
        2: CellStyleSpec(wrap_text=True, align_vertical="top"),
        3: CellStyleSpec(align_horizontal="right", font_color="#1B4D8A"),
    },
    header_style=CellStyleSpec(
        bold=True,
        align_horizontal="center",
        fill_color="#E2EAF3",
        border_style="thin",
        border_color="#D0D7DE",
    ),
    header_row_height=24,
    default_row_height=32,
)

Supported style attributes (CellStyleSpec):

  • font: font_name, font_size, bold, italic, underline, font_color
  • cell: fill_color, border_style, border_color
  • alignment: align_horizontal, align_vertical, wrap_text, shrink_to_fit, text_rotation
  • format: number_format

Global Style Engine (v1.2.0)

You can apply style rules to any component region using roles like:

  • title, title_subtitle
  • section_header
  • kpi_card, kpi_label, kpi_value
  • table_header, table_body, table_empty
  • description, chart_area

Rules are evaluated by priority, then merged into the existing cell style.

from xllayout.core.style_engine import StyleRegistry, StyleRule, StyleSelector
from xllayout.core.cell_style import CellStyleSpec

registry = StyleRegistry()
registry.set_role_style("table_body", CellStyleSpec(font_size=9))
registry.add_rule(
    StyleRule(
        name="program_column_wrap",
        selector=StyleSelector(role="table_body", column_name="program_name"),
        style=CellStyleSpec(wrap_text=True, align_vertical="top"),
        priority=20,
    )
)
registry.add_rule(
    StyleRule(
        name="critical_rows",
        selector=StyleSelector(role="table_body", col=8),
        style=CellStyleSpec(font_color="#9C1C1C", bold=True),
        priority=30,
    )
)

writer = ReportWriter(theme=LightTheme(), style_registry=registry)

Canvas vs Grid12

Absolute placement:

from xllayout.core.canvas import SheetCanvas

canvas = SheetCanvas(ws, theme, detect_collisions=True)
canvas.put(component, row=5, col=3)

Grid placement:

from xllayout.core.grid import Grid12

grid = Grid12(canvas)  # 12 tracks
grid.put(component, y=4, x=1, w=6, h=5)

If detect_collisions=True, overlapping components raise ValueError.

Build a New Layout

  1. Create a typed input dataclass under layouts/.
  2. In build(ws), initialize SheetCanvas and optionally Grid12.
  3. Place components with canvas.put(...) or grid.put(...).
  4. Expose a facade method in ReportWriter.

Run the example

python examples/build_summary.py

Output:

examples/output/summary_report_example.xlsx

Versioning and publishing

  • Uses SemVer (1.0.0, 1.0.1, 1.1.0, 1.2.0, ...).
  • Recommended for teams: publish to a private PyPI-compatible registry.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xllayout-1.2.0.tar.gz (17.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xllayout-1.2.0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file xllayout-1.2.0.tar.gz.

File metadata

  • Download URL: xllayout-1.2.0.tar.gz
  • Upload date:
  • Size: 17.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for xllayout-1.2.0.tar.gz
Algorithm Hash digest
SHA256 db63352440d6720746c689376e636df195f7a8bd2fd0a1e1fbc335adb7a40b78
MD5 7cef6cca719087cc096e2bf539d839d0
BLAKE2b-256 3ee1180c308924a305dbe63f6e1f1f3f5ffb3c825097f24be0df1644f356e427

See more details on using hashes here.

File details

Details for the file xllayout-1.2.0-py3-none-any.whl.

File metadata

  • Download URL: xllayout-1.2.0-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for xllayout-1.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1384f465679ac827291ba743ff84afd42d7eb5359b138d748432b8fd0a9ee7ce
MD5 49437511505902b3c0a160c21714d1c5
BLAKE2b-256 bb58f18dfb26adb4c734c17afbce646d9f5aa28165bf67e83583fbb53a54d4d0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page