Skip to main content

openpyxl-toolkit

PyPI Python versions CI License

openpyxl formatting without the boilerplate.

Purpose

It is our opinion that formatting and styling worksheets with openpyxl is clunky and non-intuitive. We wanted to build something to make the process less painful and the end result more readable.

For example, openpyxl stores a cell's style as an immutable object. This means changing one attribute results in building an entirely new Font object and assigning it, which silently drops the size, the color, and anything else that was set. This toolkit allows you to change attributes one by one without having to copy previously set attributes.

>>> from openpyxl import Workbook
>>> from openpyxl_toolkit import WorksheetToolkit

>>> workbook = Workbook()
>>> sheet = workbook.active
>>> sheet["A1"] = "Region"

>>> toolkit = WorksheetToolkit(sheet)
>>> toolkit.set_font(cells="A1", size=14, color="#1d3557")
>>> toolkit.set_font(cells="A1", bold=True) # size and color are not discarded

Install

pip install openpyxl-toolkit

Python 3.10 or newer, and openpyxl 3.1 or newer.

Choosing cells

Three ways, on every method that formats cells.

>>> toolkit.set_fill(cells="A1:C3", fill_type="solid", start_color="#f4f6f8")
>>> toolkit.set_fill(cells="A:C", fill_type="solid", start_color="#f4f6f8")
>>> toolkit.set_fill(rows=[1, 2], columns=[1, 2], intersections_only=True,
...                  fill_type="solid", start_color="#ffff00")

cells takes a block ("A1:C3"), whole columns ("B:D"), whole rows ("2:5") or one cell ("C3"). Case does not matter, a reversed range such as "C3:A1" is normalized, and an unbounded side is filled in from the used range, so "B:B" means column B as far as the sheet goes rather than all 1,048,576 rows.

rows and columns take lists.

Given both rows and columns, intersections_only decides what they mean:

Cells formatted
intersections_only=True (default) where the rows and the columns cross — a block
intersections_only=False every cell in those rows and every cell in those columns — a cross

Giving both cells and rows/columns raises.

Column letters and numbers

rows and columns take numbers, so there is a pair for converting either way.

>>> from openpyxl_toolkit import column_letter, column_index

>>> column_letter(3)    # 'C'
>>> column_index("C")   # 3
>>> toolkit.set_column_width(width=18, columns=[column_index("D")])

openpyxl has its own pair of methods in openpyxl.utils, but they run off the end of the grid. get_column_letter(16385) returns "XFE", and a workbook using that column will not open. These stop at XFD, column 16,384.

Methods

Method What it sets
set_font name, size, bold, italic, underline, strike, color
set_fill fill_type, start_color, end_color
set_alignment horizontal, vertical, text_rotation, wrap_text, shrink_to_fit, indent, reading_order
set_number_format how a value is displayed: currency, percent, dates, decimal places
set_border style and color, on any of six sides
set_outside_border one border around the edge of a block, leaving the inside alone
set_column_width / set_row_height an explicit size; 0 hides the column or row
set_column_best_fit a width that fits the widest cell
merge_cells / unmerge_cells a merged range; unmerging one that is not merged does nothing
freeze_panes rows above and columns left of a cell stay visible
set_zoom_scale 10 to 400

Each returns the toolkit, so calls chain. Every parameter is keyword-only, apart from freeze_panes. Anything not named is left as is. None is not the same as leaving it out: set_font(color=None) clears the color, while omitting color keeps whatever was there.

Fitting columns

set_column_best_fit measures every character at its own width in the font being used, so a column of i's does not come out as wide as a column of W's. Excel's own formula turns the total into a width:

width = (pixels of text + 5 padding pixels) / max digit width
>>> toolkit.set_column_best_fit(ignore_rows=[1], padding=1.5, min_width=9)

Built-in metrics cover Aptos, Arial, Calibri, Cambria, Courier New, Futura, Garamond, Georgia, Inter, Open Sans, Palatino, Roboto, Segoe UI, Tahoma, Times New Roman and Verdana. Helvetica, Book Antiqua, Selawik, Arimo, Carlito, Tinos, Cousine, Caladea and the Liberation faces are recognized too, each sharing the table of the face it matches advance for advance.

Any other face is measured with Verdana's character widths unless measure is given. Verdana is the widest of them, so a face with no metrics errs wide: a column that is too narrow hides what it holds, while one that is too wide only looks untidy.

Three items to note:

  • Text is assumed to be on one line. Wrapped text is not accounted for.
  • Only dates and times are rendered as Excel displays them. Other number formats, including the ones set_number_format writes, are measured as the value is stored, so a currency column can come out narrower than it needs to be. min_width is the answer.
  • Formula cells are skipped by default. Set those column widths directly, or pass ignore_formulas=False to override this behavior.

A merged title in row 1 will size column A to the whole title, because a merged range stores its value in the top-left cell. Use ignore_rows in these cases.

Type hints

The package ships py.typed. Alignment names, border styles, fill patterns and underline styles are Literal types, so an editor completes them and a checker catches a typo before the code runs:

error: Argument "horizontal" to "set_alignment" has incompatible type "Literal['centre']"

openpyxl rejects the same typo, but only once the call runs; the Literal types move it into the editor. Elsewhere the annotations really are stricter than openpyxl is at runtime, which matters more than it sounds: openpyxl accepts bold="no" and stores True, and name=42 and stores "42".

License

MIT.

Release files for openpyxl-toolkit 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openpyxl-toolkit 0.2.0
File Size Uploaded
openpyxl_toolkit-0.2.0.tar.gz 61.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openpyxl-toolkit 0.2.0
File Interpreter ABI Platform
openpyxl_toolkit-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 96.9 kB

Release files / openpyxl_toolkit-0.2.0.tar.gz

Download URL openpyxl_toolkit-0.2.0.tar.gz
Size 61.5 kB
Tags Source
SHA-256 checksum
How to use checksums
fa59a6772cf6be96a0d05e9473fd402f6aea0d00b0b3f4cca3ca3780acf639bb
BLAKE2b-256 checksum
How to use checksums
58cef820d591bc38934e1d8b6de6ca8487a3e133051b320e875fa2e869493041
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / openpyxl_toolkit-0.2.0-py3-none-any.whl

Download URL openpyxl_toolkit-0.2.0-py3-none-any.whl
Size 35.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d99a21691d66f73bd0c0baec11947fb588669cb20cc240d600aa714cfa2fdcf4
BLAKE2b-256 checksum
How to use checksums
886b5a1b8f09b1810e19ed00e24fc263f5ca6657334c4cf669767eff61a3c8a8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release history Release notifications | RSS feed

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page