Skip to main content

Gridquill

Build editable diagrams in Python using reactive coordinates, nested canvases, elements, signals, captions and connectors. Export to .excalidraw for Excalidraw or .excalidraw.md for Obsidian. Python 3.10+; no runtime dependencies.

Install from this checkout

python -m pip install -e .

The package is prepared for PyPI but has not been published.

Draw a diagram

import gridquill as gq

gq.set_color_scheme(
    element_color=0x1971C2, element_fill=0xA5D8FF,
    signal_color=0xF08C00, signal_fill=0xFFEC99,
)

with gq.canvas("Example") as diagram:
    with gq.canvas("Acquisition", column=2, row=1,
                   background=0xEDF2FF, margin_left=20, margin_right=20,
                   margin_top=20, margin_bottom=20):
        adc = gq.element("ADC", column=0, top=0, length=4)
        fpga = gq.element("FPGA", column=4, top=0, length=4)
        samples = gq.signal("Samples", row=adc.port(2), components=[adc])
        samples.components.append(fpga)
        gq.connector(fpga, samples, inout="in")
        gq.caption("Acquisition", row=-1, column=0)

diagram.save("example.excalidraw.md")

Coordinates and ownership

Literal coordinates are local to their canvas. On save, coordinate references resolve to positions in the drawing, including all parent offsets. A captured reference such as fpga.column follows later moves and assignments. Sizes and lengths are distances; coordinate formulas return drawing coordinates.

fpga.column = lambda: adc.column + 5
diagram.resolve()  # resolve without writing a file

Geometry fields and span entries accept numbers, reactive arithmetic or zero-argument functions. Each save evaluates them in passes. Pending dependencies read as NaN; stalled resolution raises ReactiveResolutionError naming the unresolved fields. Functions must be pure. float(value) reads a snapshot of the last resolved value. Failed resolution leaves an existing output file untouched.

Creating a canvas opens it. Subsequent objects attach automatically; pop() returns to the previous canvas. with canvas(...) restores the previous scope on exit. Use canvas(activate=False) for explicit ownership and append().

Objects

  • element(label, column=..., top=..., bottom=... / length=...): vertical box. Alternatively use span=[top, bottom]. element.port(index) returns a reactive row, with 0.5 grid-unit spacing; negative indices count from the bottom.
  • signal(label, row=..., left=..., right=... / length=...): horizontal box. Alternatively use span=[left, right] or a mutable components list whose outermost columns are connected. Endpoint formulas are prepared at save time.
  • caption(label, row=..., column=..., fill=..., length=...): automatically sized label box; omit length to fit the text on each save.
  • connector(element_or_column, signal_or_row, position="auto", inout="out"): chevron marker. With a signal, the default chooses left or right when its span lies entirely on that side of the element's column, including a signal ending at the element. The side and arrow direction follow layout changes on every save. A signal crossing both sides, or a numeric row, defaults to left. Set position="left" or "right" to override. Flow is in, out or inout.
  • chevron(...): individual direction stencil.
  • canvas(...): group with an optional background, border style and per-side margins in pixels. Bounds follow its members automatically.

Default grid size is 80 pixels. set_color_scheme() changes global defaults; explicit object colors take precedence. Assign None to inherit again. get_color_scheme() returns a copy; reset_color_scheme() restores defaults.

draw_diagram() provides the lower-level renderer. To normalize existing drawings, run gridquill-normalize drawing.excalidraw.md (edits the input file).

Drawing order

Set z_index to control overlaps. Higher values appear in front, and each box and its label stay together:

with gq.canvas() as diagram:
    block = gq.element("FPGA", column=2, span=[0, 4])
    bus = gq.signal("Bus", row=2, span=[0, 4], z_index=1)

bus.z_index = 2  # editable after creation; applied on the next save
diagram.save("layers.excalidraw")

Signals, elements, chevrons, captions, connectors and canvases accept z_index (default 0). Negative and fractional values are allowed, as are reactive formulas. A canvas adds its z_index to its descendants' values, so a connector or a nested canvas can shift all its members together. Layers compare across canvas boundaries. Canvas backgrounds remain behind all drawing objects.

Equal values preserve the existing order: signals (including captions), then elements, then chevrons, in traversal order within each kind. Existing diagrams therefore keep their drawing order. For draw_diagram(), set z_index in each horizontal or vertical item dictionary; connection arrows remain behind boxes.

Examples and development

python -m pip install -e ".[dev]"
python -m unittest discover -s tests
python examples/canvas_scope_example.py
python examples/recreate_udt_overview.py --output overview.excalidraw.md
python -m build
python -m twine check dist/*

Source lives in src/gridquill; tests and runnable examples live in tests and examples. The firmware-specific CSV layout exporter remains in the firmware repository because it depends on the separate fmake toolchain.

Publishing

Add author details and actual project URLs to pyproject.toml. Set __version__ in src/gridquill/__init__.py for a release. Build and validate the distributions using the commands above. When ready, upload explicitly with python -m twine upload dist/* using your PyPI account. No publishing credentials or upload automation are included.

License

MIT. See LICENSE.

Metadata

Release files for gridquill 0.1.1

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

Source distribution (sdist)

Source distribution for gridquill 0.1.1
File Size Uploaded Provenance Actions
gridquill-0.1.1.tar.gz 43.1 kB Without provenance Details for gridquill-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for gridquill 0.1.1
File Interpreter ABI Platform Actions
gridquill-0.1.1-py3-none-any.whl Python 3 none any Details for gridquill-0.1.1-py3-none-any.whl

Total release size: 67.9 kB

Release files / gridquill-0.1.1.tar.gz

Download URL gridquill-0.1.1.tar.gz
Size 43.1 kB
Tags Source
SHA-256 checksum
How to use checksums
715ed0599f856432d580d572953e74df67192f8d270bc01ddd244887f3e93082
BLAKE2b-256 checksum
How to use checksums
707bed030630bd7327f6ab20617878392a4ed10682744a2a037e83e4cd0acd52
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / gridquill-0.1.1-py3-none-any.whl

Download URL gridquill-0.1.1-py3-none-any.whl
Size 24.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a4524307f7ebb88896584a8dc558c5f2ec49da03e1cf0082db0c94343d380d3e
BLAKE2b-256 checksum
How to use checksums
7a3b38ffa2de2e209fc6015f5229ad8947afd34d374ba4014b43db9ae7943473
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Security for gridquill 0.1.1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

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