Canvas Drawing¶
The Canvas view provides a drawing surface backed by Core Graphics. You can render shapes, lines, text, and images using declarative drawing commands from the nib.draw module.
Creating a canvas¶
import nib
canvas = nib.Canvas(width=400, height=300, background_color="#1a1a1a")
The canvas also accepts all standard view modifiers (padding, opacity, corner_radius, etc.) as keyword arguments.
Drawing commands¶
All drawing commands live in the nib.draw module. Pass a list of commands to canvas.draw():
canvas.draw([
nib.draw.Rect(x=10, y=10, width=100, height=50, fill="#3498db"),
nib.draw.Circle(cx=200, cy=100, radius=40, fill="#e74c3c"),
nib.draw.Line(x1=10, y1=200, x2=390, y2=200, stroke="#2ecc71"),
nib.draw.Text("Hello Canvas!", x=10, y=280, fill="#ffffff"),
])
Rect¶
Draws a rectangle, optionally with rounded corners:
nib.draw.Rect(
x=10, y=10,
width=100, height=80,
corner_radius=8,
fill="#3498db",
stroke="#2980b9",
stroke_width=2,
opacity=0.9,
)
Circle¶
Draws a circle from a center point and radius:
nib.draw.Circle(cx=200, cy=100, radius=40, fill="#e74c3c")
Ellipse¶
Draws an ellipse with independent horizontal and vertical radii:
nib.draw.Ellipse(cx=150, cy=100, rx=80, ry=40, fill="#9b59b6")
Line¶
Draws a straight line between two points:
nib.draw.Line(
x1=10, y1=10,
x2=200, y2=200,
stroke="#000000",
stroke_width=2,
line_cap="round", # "butt", "round", or "square"
)
Text¶
Draws text at a position:
nib.draw.Text(
"Hello World",
x=10, y=50,
fill="#ffffff",
font=nib.Font.system(20, weight=nib.FontWeight.BOLD),
alignment=nib.HorizontalAlignment.CENTER,
)
Arc¶
Draws an arc segment:
import math
nib.draw.Arc(
cx=100, cy=100,
radius=50,
start_angle=0,
end_angle=math.pi,
fill="#f39c12",
stroke="#e67e22",
)
Path¶
Draws a path from a list of points:
nib.draw.Path(
points=[(10, 10), (100, 50), (50, 100)],
closed=True,
fill="#2ecc71",
stroke="#27ae60",
stroke_width=2,
)
Polygon¶
A convenience wrapper around Path with closed=True:
nib.draw.Polygon(
points=[(100, 10), (190, 80), (160, 170), (40, 170), (10, 80)],
fill="#1abc9c",
)
BezierPath¶
Draws complex curves using typed path elements:
from nib.draw import BezierPath, MoveTo, CubicTo, QuadraticTo, LineTo, Close
nib.draw.BezierPath(
elements=[
MoveTo(50, 150),
CubicTo(cp1x=50, cp1y=50, cp2x=150, cp2y=50, x=150, y=150),
Close(),
],
fill="#e74c3c",
stroke="#c0392b",
)
Available path elements:
| Element | Parameters | Description |
|---|---|---|
MoveTo(x, y) |
x, y | Move to a point without drawing |
LineTo(x, y) |
x, y | Draw a straight line to a point |
CubicTo(...) |
cp1x, cp1y, cp2x, cp2y, x, y | Cubic bezier curve |
QuadraticTo(...) |
cp1x, cp1y, x, y, w | Quadratic bezier curve |
Close() |
none | Close the path back to the start |
ArcTo(...) |
x, y, radius, rotation, large_arc, clockwise | Arc segment |
Oval(...) |
x, y, width, height | Ellipse inscribed in rectangle |
PathRect(...) |
x, y, width, height, border_radius | Rectangle sub-path |
SubPath(...) |
x, y, elements | Nested sub-path at offset |
Image¶
Draws an image from raw bytes:
with open("photo.jpg", "rb") as f:
nib.draw.Image(
data=f.read(),
x=10, y=10,
width=200,
height=150,
)
Points¶
Draws a set of points with different modes:
nib.draw.Points(
points=[(10, 10), (50, 50), (100, 30), (150, 80)],
point_mode=nib.draw.PointMode.POINTS, # POINTS, LINES, or POLYGON
stroke="#e74c3c",
stroke_width=5,
stroke_cap="round",
)
Fill¶
Fills the entire canvas with a color or gradient:
nib.draw.Fill(fill="#1a1a1a")
Colors¶
All color parameters accept hex strings or nib.Color objects:
# Hex strings
nib.draw.Rect(x=0, y=0, width=100, height=100, fill="#FF5733")
# nib.Color constants
nib.draw.Circle(cx=50, cy=50, radius=30, fill=nib.Color.RED)
Gradients¶
Fill parameters on Rect, Circle, Ellipse, BezierPath, and Fill accept gradient objects instead of solid colors:
LinearGradient¶
nib.draw.Rect(
x=0, y=0, width=200, height=100,
fill=nib.draw.LinearGradient(
start=(0, 0),
end=(200, 100),
colors=["#FF0000", "#0000FF"],
stops=[0.0, 1.0], # optional
),
)
RadialGradient¶
nib.draw.Circle(
cx=100, cy=100, radius=80,
fill=nib.draw.RadialGradient(
center=(100, 100),
radius=80,
colors=[nib.Color.YELLOW, nib.Color.RED],
),
)
SweepGradient¶
nib.draw.Circle(
cx=100, cy=100, radius=80,
fill=nib.draw.SweepGradient(
center=(100, 100),
colors=[nib.Color.RED, nib.Color.GREEN, nib.Color.BLUE, nib.Color.RED],
),
)
Drawing methods¶
The Canvas provides three methods to manage drawing commands:
canvas.draw(commands)¶
Replaces all current commands and triggers a re-render:
canvas.draw([
nib.draw.Rect(x=0, y=0, width=100, height=100, fill="#3498db"),
])
canvas.append(command)¶
Adds a single command to the existing list:
canvas.append(nib.draw.Circle(cx=50, cy=50, radius=20, fill="#e74c3c"))
canvas.clear()¶
Removes all commands and updates the display:
canvas.clear()
Gesture handling¶
Enable gestures to respond to mouse and trackpad input on the canvas. Gestures are enabled automatically when you provide any gesture callback:
canvas = nib.Canvas(
width=400, height=300,
on_pan_start=handle_start,
on_pan_update=handle_update,
on_pan_end=handle_end,
on_hover=handle_hover,
)
You can also set gesture callbacks after creation:
canvas = nib.Canvas(width=400, height=300)
canvas.on_pan_start = handle_start
canvas.on_pan_update = handle_update
PanEvent¶
All gesture callbacks receive a PanEvent dataclass with the cursor coordinates:
from nib import PanEvent
def handle_update(e: PanEvent):
print(f"x={e.x}, y={e.y}")
Related¶
- Canvas, Draw module
- Path & Shape:
nib.PathandShapeview - Primitives
- Paint & Gradients
- Path Elements
- Image & Text
- Drawing App: Canvas example app