Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions python/examples/drawing/render.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import tkinter as tk

from schema import Style, Box, Ellipse, Arrow, Drawing, UnknownText


# Map line style to dash patterns
dash_pattern = {
"solid": "",
"dashed": (4, 4), # 4 pixels drawn, 4 pixels space
"dotted": (1, 1), # 1 pixel drawn, 1 pixel space
}


def render_drawing(canvas: tk.Canvas, drawing: Drawing):

def draw_box(box: Box):
x1, y1 = box.x, box.y
x2, y2 = x1 + box.width, y1 + box.height
canvas.create_rectangle(
x1,
y1,
x2,
y2,
outline=getattr(box.style, "line_color", None) or "black",
fill=getattr(box.style, "fill_color", None) or "",
)
if box.text:
canvas.create_text((x1 + x2) / 2, (y1 + y2) / 2, text=box.text, fill="black")

def draw_ellipse(ellipse: Ellipse):
x1, y1 = ellipse.x, ellipse.y
x2, y2 = x1 + ellipse.width, y1 + ellipse.height
canvas.create_oval(
x1,
y1,
x2,
y2,
outline=getattr(ellipse.style, "line_color", None) or "black",
fill=getattr(ellipse.style, "fill_color", None) or "",
)
if ellipse.text:
canvas.create_text((x1 + x2) / 2, (y1 + y2) / 2, text=ellipse.text, fill="black")

def draw_arrow(arrow: Arrow):
canvas.create_line(
arrow.start_x,
arrow.start_y,
arrow.end_x,
arrow.end_y,
dash=dash_pattern.get(getattr(arrow.style, "line_style", None) or "solid", ""),
arrow=tk.LAST,
fill=getattr(arrow.style, "line_color", None) or "black",
)

for item in drawing.items:
match item:
case Box():
draw_box(item)
case Ellipse():
draw_ellipse(item)
case Arrow():
draw_arrow(item)
case UnknownText():
print(f"Unknown text: {item.text}")


if __name__ == "__main__":
example_drawing = Drawing(
type="Drawing",
items=[
Box(
type="Box",
x=50,
y=50,
width=100,
height=100,
text="Hello",
style=Style(type="Style"),
),
Ellipse(
type="Ellipse",
x=200,
y=50,
width=150,
height=100,
text="World",
style=Style(type="Style", fill_color="lightblue"),
),
Arrow(
type="Arrow",
start_x=50,
start_y=200,
end_x=150,
end_y=200,
style=Style(type="Style", line_style="dashed"),
),
],
)

window = tk.Tk()
window.title("Drawing")
canvas = tk.Canvas(window, width=800, height=600, bg="white", highlightthickness=0)
canvas.pack(padx=10, pady=10)
render_drawing(canvas, example_drawing)
window.mainloop()
83 changes: 83 additions & 0 deletions python/examples/drawing/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Schema for a drawing with boxes, ellipses, arrows, etc."""

from dataclasses import dataclass
from typing_extensions import Literal, Annotated, Doc, Optional


@dataclass
class Style:
"""Style settings for drawing elements."""

type: Literal["Style"]

corners: Annotated[
Optional[Literal["rounded", "sharp"]],
Doc("Corner style of the drawing elements."),
] = None
line_thickness: Annotated[Optional[int], Doc("Thickness of the lines.")] = None
line_color: Annotated[Optional[str], Doc("CSS-style color code for line color.")] = None
fill_color: Annotated[Optional[str], Doc("CSS-style color code for fill color.")] = None
line_style: Annotated[
Optional[Literal["solid", "dashed", "dotted"]],
Doc("Style of the line: 'solid', 'dashed', 'dotted'."),
] = None


@dataclass
class Box:
"""A rectangular box defined by a coordinate system with the origin at the top left."""

type: Literal["Box"]

x: Annotated[int, Doc("X-coordinate of the top left corner.")]
y: Annotated[int, Doc("Y-coordinate of the top left corner.")]
width: Annotated[int, Doc("Width of the box.")]
height: Annotated[int, Doc("Height of the box.")]
text: Annotated[Optional[str], Doc("Optional text centered in the box.")] = None
style: Annotated[Optional[Style], Doc("Optional style settings for the box.")] = None


@dataclass
class Ellipse:
"""An ellipse defined by its bounding box dimensions."""

type: Literal["Ellipse"]

x: Annotated[int, Doc("X-coordinate of the top left corner of the bounding box.")]
y: Annotated[int, Doc("Y-coordinate of the top left corner of the bounding box.")]
width: Annotated[int, Doc("Width of the bounding box.")]
height: Annotated[int, Doc("Height of the bounding box.")]
text: Annotated[Optional[str], Doc("Optional text centered in the box.")] = None
style: Annotated[Optional[Style], Doc("Optional style settings for the ellipse.")] = None


@dataclass
class Arrow:
"""A line with a directional arrow at the end, defined by start and end points."""

type: Literal["Arrow"]

start_x: Annotated[int, Doc("Starting X-coordinate.")]
start_y: Annotated[int, Doc("Starting Y-coordinate.")]
end_x: Annotated[int, Doc("Ending X-coordinate.")]
end_y: Annotated[int, Doc("Ending Y-coordinate.")]
style: Annotated[Optional[Style], Doc("Optional style settings for the arrow.")] = None
head_size: Annotated[Optional[int], Doc("Size of the arrowhead, if present.")] = None


@dataclass
class UnknownText:
"""Used for input that does not match any other specified type."""

type: Literal["Unknown"]

text: Annotated[str, Doc("The text that wasn't understood.")]


@dataclass
class Drawing:
"""A collection of graphical elements including boxes, ellipses, arrows, and unrecognized text."""

type: Literal["Drawing"]

items: Annotated[list[Box | Arrow | Ellipse | UnknownText], Doc("List of drawable elements.")]
2 changes: 1 addition & 1 deletion python/src/typechat/_internal/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async def process_requests(interactive_prompt: str, input_file_name: str | None,
else:
try:
# Use readline to enable input editing and history
import readline # type: ignore
import readline # type: ignore # noqa: F401
except ImportError:
pass
while True:
Expand Down