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_case
instead 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_style
expects a valid HTML color. The default is black.fill_style
expects a valid HTML color. The default is black.global_alpha
indicates the transparency. The default is 1.0.
Lines#
Line style#
Lines can be described by the following attributes:
line_width
line_cap
line_join
miter_limit
get_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.
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.
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: