vexy-lines-apy
Python bindings to the Vexy Lines MCP API and style engine.
Connect to the Vexy Lines app over TCP and drive it from Python: open documents, change fill parameters, render, export. Transfer or blend artistic styles between images without touching the GUI.
Requires the Vexy Lines app (macOS or Windows) for all MCP operations. If the app isn't running, the client launches it.
Install
pip install vexy-lines-apy
For SVG object manipulation (svg_parsed()), install the svg extra:
pip install "vexy-lines-apy[svg]"
or directly: pip install svglab
Quick start
from vexy_lines_api import MCPClient
with MCPClient() as vl:
vl.open_document("photo.lines")
info = vl.get_document_info()
print(f"{info.width_mm:.0f} x {info.height_mm:.0f} mm @ {info.resolution} dpi")
tree = vl.get_layer_tree() # LayerNode tree
vl.render() # render all layers, wait for completion
vl.export_svg("output.svg")
MCPClient() connects to localhost:47384. If the app isn't open, the client launches it and waits up to 30 seconds.
Export formats
with MCPClient() as vl:
vl.open_document("art.lines")
vl.render()
vl.export_svg("out.svg")
vl.export_pdf("out.pdf")
vl.export_png("out.png", dpi=150)
vl.export_jpeg("out.jpg")
vl.export_eps("out.eps")
# SVG as a string (useful for embedding or piping)
svg_text = vl.svg()
# SVG as a parsed svglab object (requires svglab)
svg_obj = vl.svg_parsed()
Edit fill parameters
with MCPClient() as vl:
vl.open_document("art.lines")
tree = vl.get_layer_tree()
# Find a fill node and change its colour
fill_id = tree.children[0].children[0].children[0].id
vl.set_fill_params(fill_id, color="#3a7bd5", opacity=0.9)
vl.render()
vl.export_png("result.png")
Edit image filters
Fills can carry an ordered source-image filter chain. The MCP API uses names
such as brightness, levels, invert, color, and gradient.
with MCPClient() as vl:
vl.open_document("art.lines")
fill_id = 42
vl.set_image_filters(fill_id, [
{"type": "brightness", "params": {"value": 25.0}},
{"type": "levels", "params": {"left": 10, "right": 240}},
])
filters = vl.get_image_filters(fill_id)
Style engine
Extract the complete fill structure from a .lines file and apply it to any source image. No GUI required.
from vexy_lines_api import MCPClient, extract_style, apply_style
style = extract_style("reference.lines") # parse fill tree from file
with MCPClient() as vl:
svg = apply_style(vl, style, "photo.jpg", dpi=72)
with open("result.svg", "w") as f:
f.write(svg)
Style interpolation
Blend two compatible styles at any mix ratio. Numeric fill parameters and colours interpolate linearly.
from vexy_lines_api import MCPClient, extract_style, interpolate_style, apply_style
painterly = extract_style("painterly.lines")
technical = extract_style("technical.lines")
mid = interpolate_style(painterly, technical, t=0.5) # halfway blend
with MCPClient() as vl:
svg = apply_style(vl, mid, "photo.jpg")
Two styles are compatible for interpolation when they share the same group/layer/fill structure with matching fill types and matching image-filter chains. Check with styles_compatible(a, b) before blending.
Explicit .lines interpolation
Generate an actual intermediate .lines file between two compatible documents without opening the app:
from vexy_lines_api import interpolate_lines
interpolate_lines("start.lines", "end.lines", "mid.lines", t=0.5)
The output keeps the XML structure and embedded images from start.lines, then rewrites matching interpolatable numeric XML attributes across the document, layers, fills, image filters, masks, and grid edges. Structural IDs, enum-like modes, flags, captions, and text stay intact. Native .lines colours are blended in their XML format, including #AARRGGBB. t=0 is the start document, t=1 is the end document.
Rendered timelines require the app:
from vexy_lines_api import render_interpolation_video, record_interpolation_screen
render_interpolation_video("start.lines", "end.lines", "blend.mp4", frames=120, fps=30)
record_interpolation_screen(
"start.lines",
"end.lines",
"screen-frames/",
frames=120,
fps=30,
zoom_steps=2,
video_path="screen-recording.mp4",
)
render_interpolation_video() renders generated .lines frames via MCP, exports SVG from the app, rasterizes PNG frames locally, resizes mismatched frame dimensions to the first frame, and assembles an MP4. record_interpolation_screen() opens the start document in the GUI, applies optional zoom keystrokes on macOS, steps through generated interpolation documents, and captures the Vexy Lines window. Pass video_path to assemble those screenshots into MP4. If temporary work is auto-cleaned, the result omits intermediate frame and .lines paths unless you pass keep_work=True or work_dir.
AI-assisted rename of layers & fills
Rename a document's layers and fills based on what each fill actually draws. The renamer renders each fill in isolation, builds an "inspection image" (the fill framed by a red box over the faint full artwork), asks a vision model to describe it in three words, and writes a renamed copy of the .lines that preserves every fill parameter, mask, and image.
from vexy_lines_api import rename_lines
plan = rename_lines("road-12.lines") # -> road-12-renamed.lines
for f in plan.fills:
print(f.object_id, f.old_caption, "->", f.new_caption, f"({f.description})")
The renamer uses any OpenAI-compatible /v1 endpoint, configured from the environment: VEXY_LINES_LLM_API_URL, VEXY_LINES_LLM_API_KEY, VEXY_LINES_LLM_MODEL_VISION (vision), and VEXY_LINES_VLM_MODEL (text). Install the extra with pip install "vexy-lines-apy[ai]".
Visibility is baked into a temporary copy of the
.lines(vexy_lines.set_visibility) rather than toggled live over MCP, becauseset_visibledoes not affect the export.
Full guide: AI Rename. Also available as vexy-lines-cli ai-rename and Lines ▸ AI Rename Layers & Fills… in the GUI.
Job folder (resumable exports)
Long-running exports (especially video) save every intermediate artifact to a persistent job folder alongside the output. If a job is interrupted, re-running the same command resumes where it left off.
from vexy_lines_api.export import ExportRequest, process_export
request = ExportRequest(
mode="video",
input_paths=["clip.mp4"],
style_path="look.lines",
end_style_path=None,
output_path="styled.mp4",
format="MP4",
size="1x",
)
process_export(request)
# Creates styled-vljob/ with all intermediates:
# src/src--styled--001.png, styled--001.lines, styled--001.svg, styled--001.png, ...
Use force=True to discard previous progress and start fresh. Use cleanup=True to delete the job folder after the final output is written.
Override the job folder location with the VEXY_LINES_JOB_FOLDER environment variable.
API reference
Document
| Method | Description |
|---|---|
new_document(width, height, dpi, source_image) |
Create a new document |
open_document(path) |
Open a .lines file |
save_document(path) |
Save (or Save As) |
export_document(path, format, dpi) |
Export to svg/pdf/png/jpg/eps |
get_document_info() |
Returns DocumentInfo |
Structure
| Method | Description |
|---|---|
get_layer_tree() |
Returns root LayerNode |
add_group(parent_id, caption) |
Add a group |
add_layer(group_id) |
Add a layer to a group |
add_fill(layer_id, fill_type, color, params) |
Add a fill to a layer |
delete_object(object_id) |
Delete any object |
Fill parameters
| Method | Description |
|---|---|
get_fill_params(fill_id) |
Get all params as a dict |
set_fill_params(fill_id, **params) |
Set params by keyword |
get_image_filters(fill_id) |
Get a fill's image-filter chain |
set_image_filters(fill_id, filters) |
Replace a fill's image-filter chain |
add_image_filter(fill_id, filter_type, params, index) |
Add one image filter |
remove_image_filter(fill_id, index) |
Remove one image filter |
Visual
| Method | Description |
|---|---|
set_source_image(image_path, group_id) |
Set source image for a group |
set_caption(object_id, caption) |
Rename an object |
set_visible(object_id, visible) |
Toggle visibility |
set_layer_mask(layer_id, paths, mode) |
Set SVG vector mask |
get_layer_mask(layer_id) |
Get layer mask data |
transform_layer(layer_id, ...) |
Translate, rotate, scale |
set_layer_warp(layer_id, ...) |
Perspective warp corners |
Control
| Method | Description |
|---|---|
render() |
Render all layers and wait |
render_all() |
Trigger render (no wait) |
wait_for_render(timeout) |
Poll until render completes |
get_render_status() |
Returns RenderStatus |
undo() / redo() |
Undo/redo last action |
get_selection() |
Get selected objects |
select_object(object_id) |
Select by ID |
Export shortcuts
| Method | Returns |
|---|---|
export_svg(path, dpi) |
Resolved Path |
export_pdf(path, dpi) |
Resolved Path |
export_png(path, dpi) |
Resolved Path |
export_jpeg(path, dpi) |
Resolved Path |
export_eps(path, dpi) |
Resolved Path |
svg() |
SVG content as str |
svg_parsed() |
svglab.Svg object (requires svglab) |
Style engine
| Function | Description |
|---|---|
extract_style(path) |
Parse a .lines file into a Style |
apply_style(client, style, source_image, dpi, save_lines_to) |
Apply style to an image, return SVG string. Optionally save the intermediate .lines file. |
interpolate_style(a, b, t) |
Blend two styles at ratio t in [0, 1], including matching image filters |
interpolate_lines(start, end, output, t) |
Write one intermediate .lines file between compatible documents, interpolating matching numeric XML attributes and native colours |
render_interpolation_video(start, end, output, frames, fps) |
Render a full .lines interpolation timeline to MP4 |
record_interpolation_screen(start, end, output, frames, fps, zoom_steps) |
Capture Vexy Lines window screenshots, optionally assembled to video |
styles_compatible(a, b) |
Check if two styles can be interpolated |
JobFolder(output_path, force) |
Persistent job folder for resumable exports |
Types
DocumentInfo, InterpolationVideoResult, JobFolder, LayerNode, NewDocumentResult, RenderStatus, ScreenRecordingResult, Style
Dependencies
vexy-lines-py:.linesfile parser and typesloguru: structured loggingtyping-extensions: backported type hints
Full documentation
Read the docs for the complete API reference, style engine guide, MCP protocol specification, and more examples.
License
MIT
Release files for vexy-lines-apy 1.0.48
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vexy_lines_apy-1.0.48.tar.gz | 13.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vexy_lines_apy-1.0.48-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 85.5 kB
Release files / vexy_lines_apy-1.0.48.tar.gz
| Download URL | vexy_lines_apy-1.0.48.tar.gz |
|---|---|
| Size | 13.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fe1cd5aa49d7a31c5adcaeef6e7a0dc427e6ab6a54531d148e50fe0c0d5b415c
|
|
BLAKE2b-256 checksum How to use checksums |
bc0007ae264ce0f5dcaf2139a9e13939d04bb0bd2bb9d1786fe7ac076f71a2b9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / vexy_lines_apy-1.0.48-py3-none-any.whl
| Download URL | vexy_lines_apy-1.0.48-py3-none-any.whl |
|---|---|
| Size | 72.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65eecc96d2487b34ab50499132640c1e2d0ac1027db18ed5b4d238579243134f
|
|
BLAKE2b-256 checksum How to use checksums |
1063581cfd77d4167d205eb4590968e193ce9916463e0dbde13e106830933e52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|