ipycanvas#
provides the Web-Canvas-API. However, there are some differences:
The Canvas widget exposes the CanvasRenderingContext2D API directly
The entire API is written in
snake_caseinstead ofcamelCase, so for examplecanvas.fillStyle = 'red'written in Python becomescanvas.fill_style = 'red'in JavaScript.
Installation#
$ pipenv install ipycanvas
Installing ipycanvas…
…
Creating canvas elements#
Before we can start creating canvas elements, first a note about the canvas grid. The origin of a grid is in the upper left corner at the coordinate (0,0). All elements are placed relative to this origin.
There are four methods of drawing rectangles:
fill_rect(x, y, width, height=None)draws a filled rectanglestroke_rect(x, y, width, height=None)draws a rectangular outlinefill_rects(x, y, width, height=None)draws filled rectanglesstroke_rects(x, y, width, height=None)draws rectangular outlines
With height=None, the same value is used as with width.
For *_rects, x, y, width and height are integers, lists of integers or numpy arrays.
[1]:
from ipycanvas import Canvas
canvas = Canvas(size=(120, 100))
canvas.fill_style = 'lime'
canvas.stroke_style = 'green'
canvas.fill_rect(10, 20, 100, 50)
canvas.stroke_rect(10, 20, 100, 50)
canvas
[2]:
from ipycanvas import MultiCanvas
# Create a multi-layer canvas with 2 layers
multi_canvas = MultiCanvas(2, size=(165, 115))
multi_canvas[0] #  Access first layer (background)
multi_canvas[0].fill_style = 'lime'
multi_canvas[0].stroke_style = 'green'
multi_canvas[0].fill_rect(10, 20, 100, 50)
multi_canvas[0].stroke_rect(10, 20, 100, 50)
multi_canvas[1] #  Access last layer
multi_canvas[1].fill_style = 'red'
multi_canvas[1].stroke_style = 'brown'
multi_canvas[1].fill_rect(55, 45, 100, 50)
multi_canvas[1].stroke_rect(55, 45, 100, 50)
multi_canvas
[3]:
import numpy as np
from ipycanvas import Canvas
n_particles = 75_000
x = np.array(np.random.rayleigh(350, n_particles), dtype=np.int32)
y = np.array(np.random.rayleigh(150, n_particles), dtype=np.int32)
size = np.random.randint(1, 3, n_particles)
canvas = Canvas(size=(1000, 500))
canvas.fill_style = 'green'
canvas.fill_rects(x, y, size)
canvas
Since Canvas is an ipywidget, it can
appear several times in a notebook
change the attributes
Link changed attributes to other widget attributes
Delete canvas#
[4]:
from ipycanvas import Canvas
canvas = Canvas(size=(120, 100))
# Perform some drawings…
canvas.clear()
[5]:
from ipycanvas import Canvas
canvas = Canvas(size=(165, 115))
canvas.fill_style = 'lime'
canvas.stroke_style = 'brown'
canvas.fill_rect(10, 20, 100, 50)
canvas.clear_rect(52, 42, 100, 50)
canvas.stroke_rect(55, 45, 100, 50)
canvas
Shapes#
The available drawing commands are:
move_to(x, y):line_to(x, y):arc(x, y, radius, start_angle, end_angle, anticlockwise=False):arc_to(x1, y1, x2, y2, radius):quadratic_curve_to(cp1x, cp1y, x, y):bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y):rect(x, y, width, height):
Draw circles#
There are four different ways to draw circles:
fill_arc(x, y, radius, start_angle, end_angle, anticlockwise=False)stroke_arc(x, y, radius, start_angle, end_angle, anticlockwise=False)fill_arcs(x, y, radius, start_angle, end_angle, anticlockwise=False)stroke_arcs(x, y, radius, start_angle, end_angle, anticlockwise=False)
With *_arcs, x, y and radius are NumPy arrays, lists or scalar values.
[6]:
from math import pi
from ipycanvas import Canvas
canvas = Canvas(size=(200, 200))
canvas.fill_style = 'red'
canvas.stroke_style = 'green'
canvas.fill_arc(60, 60, 50, 0, pi)
canvas.stroke_arc(60, 60, 40, 0, 2 * pi)
canvas
Drawing paths#
A path is a list of points connected by line segments that can be different shapes, straight or curved, closed or open, different widths and colors. The following functions are available:
begin_path ()
close_path () adds a straight line to the path leading to the beginning of the current path
stroke () draws the shape along the contour
fill (rule) draws the shape using a fill within the path
begin_path()creates a new pathclose_path()adds a straight line to the path leading to the beginning of the current pathstroke()draws the shape along the contourfill(rule)draws the shape using a fill within the path
[7]:
from ipycanvas import Canvas
canvas = Canvas(size=(100, 100))
# Draw simple triangle shape
canvas.begin_path()
canvas.move_to(75, 50)
canvas.line_to(100, 75)
canvas.line_to(100, 25)
canvas.fill()
canvas
Examples#
arc#
[8]:
from math import pi
from ipycanvas import Canvas
canvas = Canvas(size=(200, 200))
# Draw smiley face
canvas.begin_path()
canvas.arc(75, 75, 50, 0, pi * 2, True) # Outer circle
canvas.move_to(110, 75)
canvas.arc(75, 75, 35, 0, pi, False) # Mouth (clockwise)
canvas.move_to(65, 65)
canvas.arc(60, 65, 5, 0, pi * 2, True) # Left eye
canvas.move_to(95, 65)
canvas.arc(90, 65, 5, 0, pi * 2, True) # Right eye
canvas.stroke()
canvas
bezier_curve_to#
[9]:
from ipycanvas import Canvas
canvas = Canvas(size=(200, 200))
# Cubic curves example
canvas.begin_path()
canvas.move_to(75, 40)
canvas.bezier_curve_to(75, 37, 70, 25, 50, 25)
canvas.bezier_curve_to(20, 25, 20, 62.5, 20, 62.5)
canvas.bezier_curve_to(20, 80, 40, 102, 75, 120)
canvas.bezier_curve_to(110, 102, 130, 80, 130, 62.5)
canvas.bezier_curve_to(130, 62.5, 130, 25, 100, 25)
canvas.bezier_curve_to(85, 25, 75, 37, 75, 40)
canvas.fill()
canvas
Styles and colors#
Colors#
Canvas has two color attributes, one for strokes and one for areas; the transparency can also be changed.
stroke_styleexpects a valid HTML color. The default is black.fill_styleexpects a valid HTML color. The default is black.global_alphaindicates the transparency. The default is 1.0.
Lines#
Line style#
Lines can be described by the following attributes:
line_widthline_capline_joinmiter_limitget_line_dash()set_line_dash(segments)line_dash_offset
Line width#
[10]:
from ipycanvas import Canvas
canvas = Canvas(size=(400, 280))
canvas.scale(2)
for i in range(10):
    width = 1 + i
    x = 5 + i * 20
    canvas.line_width = width
    canvas.fill_text(str(width), x - 5, 15)
    canvas.begin_path()
    canvas.move_to(x, 20)
    canvas.line_to(x, 140)
    canvas.stroke()
canvas
Line end#
[11]:
from ipycanvas import Canvas
canvas = Canvas(size=(320, 360))
# Possible line_cap values
line_caps = ['butt', 'round', 'square']
canvas.scale(2)
# Draw guides
canvas.stroke_style = '#09f'
canvas.begin_path()
canvas.move_to(10, 30)
canvas.line_to(140, 30)
canvas.move_to(10, 140)
canvas.line_to(140, 140)
canvas.stroke()
# Draw lines
canvas.stroke_style = 'black'
canvas.font = '15px serif'
for i in range(len(line_caps)):
    line_cap = line_caps[i]
    x = 25 + i * 50
    canvas.fill_text(line_cap, x - 15, 15)
    canvas.line_width = 15
    canvas.line_cap = line_cap
    canvas.begin_path()
    canvas.move_to(x, 30)
    canvas.line_to(x, 140)
    canvas.stroke()
canvas
Line connection#
defines the appearance of the corners where lines meet.
[12]:
from ipycanvas import Canvas
canvas = Canvas(size=(320, 360))
# Possible line_join values
line_joins = ['round', 'bevel', 'miter']
min_y = 40
max_y = 80
spacing = 45
canvas.line_width = 10
canvas.scale(2)
for i in range(len(line_joins)):
    line_join = line_joins[i]
    y1 = min_y + i * spacing
    y2 = max_y + i * spacing
    canvas.line_join = line_join
    canvas.fill_text(line_join, 60, y1 - 10)
    canvas.begin_path()
    canvas.move_to(-5, y1)
    canvas.line_to(35, y2)
    canvas.line_to(75, y1)
    canvas.line_to(115, y2)
    canvas.line_to(155, y1)
    canvas.stroke()
canvas
Line pattern#
[13]:
from ipycanvas import Canvas
canvas = Canvas(size=(400, 280))
canvas.scale(2)
line_dashes = [
    [5, 10],
    [10, 5],
    [5, 10, 20],
    [10, 20],
    [20, 10],
    [20, 20]
]
canvas.line_width = 2
for i in range(len(line_dashes)):
    x = 5 + i * 20
    canvas.set_line_dash(line_dashes[i])
    canvas.begin_path()
    canvas.move_to(x, 0)
    canvas.line_to(x, 140)
    canvas.stroke()
canvas
text#
There are two methods of designing text:
fill_text(text, x, y, max_width=None)stroke_text(text, x, y, max_width=None)
[14]:
from ipycanvas import Canvas
canvas = Canvas(size=(400, 50))
canvas.font = '32px serif'
canvas.fill_text('Drawing from Python is cool!', 10, 32)
canvas
[15]:
from ipycanvas import Canvas
canvas = Canvas(size=(400, 50))
canvas.font = '32px serif'
canvas.stroke_text('Hello There!', 10, 32)
canvas
font indicates the current text style. The default value is “12px sans-serif”. text_align specifies the text alignment. Possible values are “start”, “end”, “left”, “right” or “center”. text_baseline indicates the alignment with the baseline. Possible values are “top”, “hanging”, “middle”, “alphabetic”, “ideographic” and “bottom”. The default value is “alphabetic”. direction indicates the text direction. Possible values are “ltr”, “rtl”, “inherit”. The default value is “inherit”.
Of course, fill_style and stroke_style can also be used to color the text.
fontindicates the current text style. The default value is"12px sans-serif".text_alignspecifies the text alignment. Possible values are"start","end","left","right"or"center".text_baselineindicates the alignment with the baseline. Possible values are"top","hanging","middle","alphabetic","ideographic"and"bottom". The default value is"alphabetic".directionindicates the text direction. Possible values are"ltr","rtl","inherit". The default value is"inherit".
Of course, fill_style and stroke_style can also be used to color the text.
Images#
… from a Numpy array#
With put_image_data(image_data, dx, dy) an image can be displayed, where image_data specifies a Numpy array and dx and dy the upper left corner of the image.
[16]:
import numpy as np
from ipycanvas import Canvas
x = np.linspace(-1, 1, 600)
y = np.linspace(-1, 1, 600)
x_grid, y_grid = np.meshgrid(x, y)
blue_channel = np.array(np.sin(x_grid**2 + y_grid**2) * 255, dtype=np.int32)
red_channel = np.zeros_like(blue_channel) + 200
green_channel = np.zeros_like(blue_channel) + 50
image_data = np.stack((red_channel, blue_channel, green_channel), axis=2)
canvas = Canvas(size=(image_data.shape[0], image_data.shape[1]))
canvas.put_image_data(image_data, 0, 0)
canvas
Status#
The status can be specified with two values:
save()saves the status of the canvas element.restore()restores the last saved status of the canvas element. This method can be called as often as required.
Transformations#
translate(x, y)moves the canvas elementrotate(angle)rotates the canvas element clockwisescale(x, y=None)scales the canvas element
See also: