Chapter 2:
Use a model
Drawing the same shapes every time in the reset
handler doesn’t allow for any variety. You would normally use a model, a collection of information about what should be drawn. That way you can modify the model and have the reset
handler draw those modifications.
Step 1 - Creating the model
A list of dictionaries works well for the model. In this step we’ll define what our model will contain so that we can recreate the square and circle from before.
Define the model in the __init__
function of your Form:
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
self.model = [
{'type': 'square', 'x': 100, 'y': 100, 'width': 50, 'stroke': True, 'fill': False},
{'type': 'circle', 'x': 300, 'y': 100, 'radius': 50, 'stroke': True, 'fill': True},
]
self.canvas_size = 500
self.canvas_1.height = self.canvas_size
self.canvas_1.reset_context()
The type
field in the dictionary will tell us what type of shape to draw, while the other fields give information needed for that type of shape.
Step 2 - Using the model in the reset
handler
Now that we have a model, the reset
handler should draw based on that model. Change your reset
handler to use the following code:
def canvas_1_reset(self, **event_args):
# Adjust these coordinates if you want the drawing area to not be centered
self.canvas_offset = (self.canvas_1.get_width() - self.canvas_size)/2
self.canvas_1.translate(self.canvas_offset, 0)
# Restrict drawing to the section that we want visible
self.canvas_1.begin_path()
self.canvas_1.move_to(0, 0)
self.canvas_1.line_to(self.canvas_size, 0)
self.canvas_1.line_to(self.canvas_size,self.canvas_size)
self.canvas_1.line_to(0, self.canvas_size)
self.canvas_1.close_path()
self.canvas_1.clip()
for shape in self.model:
if shape['type'] == 'square':
self.canvas_1.begin_path()
self.canvas_1.move_to(shape['x'], shape['y'])
self.canvas_1.line_to(shape['x']+shape['width'],shape['y'])
self.canvas_1.line_to(shape['x']+shape['width'],shape['y']+shape['width'])
self.canvas_1.line_to(shape['x'], shape['y']+shape['width'])
self.canvas_1.close_path()
if shape['type'] == 'circle':
self.canvas_1.begin_path()
self.canvas_1.arc(shape['x'], shape['y'], shape['radius'])
self.canvas_1.close_path()
if shape['stroke']:
self.canvas_1.stroke()
if shape['fill']:
self.canvas_1.fill()
This loops over every shape in self.model
and executes the correct Canvas drawing functions for that shape.
Step 3 - Adding to the Canvas via the user interface
Now that we have a model and a reset
handler that draws from that model, we can add in a user interface that allows the user to add shapes to the Canvas. We’ll add the shapes with random coordinates.
Add a FlowPanel below the Canvas and add two Buttons to it, add_square
and add_circle
. Create the click
handler functions for both by using the Object Palette.

Adding Buttons to create squares and circles on the Canvas
Now in the click
handlers for those Buttons, add the code to add the shapes at random positions. First we’ll need to import random
at the top of the code:
from ._anvil_designer import CanvasPlaygroundTemplate
from anvil import *
import random
And then in the click
handlers themselves, we can add to the self.model
list.
def add_square_click(self, **event_args):
x = random.randint(0,self.canvas_size-50)
y = random.randint(0,self.canvas_size-50)
self.model.append({
'type': 'square',
'x': x,
'y': y,
'width': 50,
'stroke': True,
'fill': False
})
self.canvas_1.reset_context()
def add_circle_click(self, **event_args):
x = random.randint(0,self.canvas_size-50)
y = random.randint(0,self.canvas_size-50)
self.model.append({
'type': 'circle',
'x': x,
'y': y,
'radius': 50,
'stroke': True,
'fill': False
})
self.canvas_1.reset_context()
Remember that anytime the Canvas needs redrawn, we must call reset_context()
. This includes after modifying the model in any way.
Now run the app and click the Buttons to add shapes to the Canvas.

We’ve now learned how to draw shapes on the Canvas. Next, we’ll look at how to draw images.