ipycanvas

stellt die Web-Canvas-API zur Verfügung. Es gibt jedoch einige Unterschiede:

  • Das Canvas-Widget macht die CanvasRenderingContext2D-API direkt verfügbar

  • Die gesamte API ist in snake_case anstatt in camelCase geschrieben, sodass beispielsweise das in Python geschriebene canvas.fillStyle = 'red' in JavaScript zu canvas.fill_style = 'red' wird.

Installation

$ pipenv install ipycanvas
Installing ipycanvas…
…

Erstellen von Canvas-Elementen

Bevor wir mit dem Erstellen von Canvas-Elementen beginnen können, zunächst ein Hinweis zum Canvas-Raster. Der Ursprung eines Gitters befindet sich in der oberen linken Ecke bei der Koordinate (0,0). Alle Elemente werden relativ zu diesem Ursprung platziert.

Es gibt vier Methoden zum Zeichnen von Rechtecken:

  • fill_rect(x, y, width, height=None) zeichnet ein gefülltes Rechteck

  • stroke_rect(x, y, width, height=None) zeichnet einen rechteckigen Umriss

  • fill_rects(x, y, width, height=None) zeichnet gefüllte Rechtecke

  • stroke_rects(x, y, width, height=None) zeichnet rechteckige Umrisse

Mit height=None wird derselbe Wert verwendet wie bei width.

Bei *_rects sind x, y, width und height ganze Zahlen, Listen von ganzen Zahlen oder 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

Da Canvas ein ipywidget ist, kann es

  • mehrfach in einem Notebook vorkommen

  • die Attribute ändern

  • geänderte Attribute auf andere Widget-Attribute verlinken

Canvas löschen

[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

Formen

Die verfügbaren Zeichenkommandos sind:

  • 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):

Kreise zeichnen

Es gibt vier verschiedene Arten, Kreise zu zeichnen:

  • 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)

Bei *_arcs sind x, y und radius NumPy-Arrays, Listen oder skalare Werte.

[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

Zeichenpfade

Ein Pfad ist eine Liste von Punkten, die durch Liniensegmente verbunden sind, die unterschiedliche Formen, gerade oder gekrümmt, geschlossen oder offen, unterschiedlich breit und farbig sein können. Dabei stehen die folgenden Funktionen zur Verfügung:

  • begin_path() erstellt einen neuen Pfad

  • close_path() fügt dem Pfad eine gerade Linie hinzu, die zum Anfang des aktuellen Pfads führt

  • stroke() zeichnet die Form entlang der Kontur

  • fill(rule) zeichnet die Form durch eine Füllung innerhalb des Pfades

[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

Beispiele

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

Stile und Farben

Farben

Canvas hat zwei Farbattribute, eines für Striche und eines für Flächen; zudem kann die Transparenz geändert werden.

  • stroke_style erwartet eine gültige HTML-Farbe. Der Standardwert ist schwarz.

  • fill_style erwartet eine gültige HTML-Farbe. Der Standardwert ist schwarz.

  • global_alpha gibt die Transparenz an. Der Standardwert ist 1.0.

Linien

Linienart

Linien lassen sich durch folgende Attribute beschreiben:

  • line_width

  • line_cap

  • line_join

  • miter_limit

  • get_line_dash()

  • set_line_dash(segments)

  • line_dash_offset

Linienbreite

[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

Linienende

[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

Linienverbindung

legt das Erscheinungsbild der Ecken fest, an denen sich Linien treffen.

[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

Strichmuster

[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

Es gibt zwei Methoden zur Gestaltung von 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 gibt den aktuellen Textstil an. Der Standardwert ist "12px sans-serif".

  • text_align gibt die Textausrichtung an. Mögliche Werte sind "start", "end", "left", "right" oder "center" .

  • text_baseline gibt die Ausrichtung an der Grundlinie an. Mögliche Werte sind "top", "hanging", "middle", "alphabetic", "ideographic" und "bottom". Der Standardwert ist "alphabetic".

  • direction gibt die Textrichtung an. Mögliche Werte sind "ltr", "rtl", "inherit". Der Standardwert ist "inherit".

Selbstverständlich kann auch fill_style und stroke_style zum EInfürben der Text verwendet werden.

Bilder

aus einem Numpy-Array

Mit put_image_data(image_data, dx, dy) lässt sich ein Bild darstellen wobei image_data ein Numyp-Array angibt und dx und dy das obere linke Eck des Bildes.

[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

Der Status kann mit zwei Werten angegeben werden:

  • save() speichert den Status des Canvas-Elements

  • restore() stellt den zuletzt gespeicherten Status des Canvas-Elements wieder her wobei diese Methode beliebig oft aufgerufen werden kann.

Transformationen

  • translate(x, y) verschiebt das Canvas-Element

  • rotate(angle) rotiert das Canvas-Element im Uhrzeigersinn

  • scale(x, y=None) skaliert das Canvas-Element

Siehe auch