Day 19 of the Anvil Advent Calendar

Build a web app every day until Christmas, with nothing but Python!

Decorate a Virtual Cookie

Last year, I built an app that uses the Canvas API to make mess-free, no-bake virtual Christmas cookies. This year, I spruced up the app so you can now take a photo of your deliciously real cookies and decorate them virtually. The FileLoader component allows you to upload a photo, or on a device with a camera, you can take your own!

Check out my app and decorate your cookies by following the link below. And keep reading to see how I built the app.

https://cookie-photo-decorator.anvil.app/

Preparing the Image

Images taken or uploaded by the user can be any size, which doesn’t work well for the app. We want to make sure the decorations actually get added on top of the cookie! The first step is then to resize the image before it’s displayed on the Canvas. This is done in a server module using Pillow:

@anvil.server.callable
def resize_img(file):
  #convert the file to a Pillow Image
  img = Image.open(io.BytesIO(file.get_bytes()))
  img = img.resize((300, 300))
  #convert to an Anvil media object
  bs = io.BytesIO()
  img.save(bs, format="PNG")
  return anvil.BlobMedia("image/png", bs.getvalue())

Making Lots of Circles

I made a simple cookie decorator using the Canvas component by just building circles and triangles of different sizes and colors. By adding more elements, the app could easily be extended to add more options or be more complicated.

Because my app makes extensive use of circles, I first defined a function to draw a circle. The function takes in the desired x and y coordinates of the circle and its radius as arguments:

def draw_circle(self, x, y, radius):
    c = self.canvas_1
    c.begin_path()
    c.arc(x, y, radius, 0, 2 * math.pi)
    c.close_path()

This is called when making the sprinkles and snowman.

Adding Random Sprinkles

In the form_show event, self.c_width and self.c_height are set as the width and height as the Canvas component. The x and y coordinates for the sprinkles are chosen randomly based on the size of the canvas each time the sprinkles_button is clicked:

import random

...

  def sprinkles_button_click(self, **event_args):
    """This method is called when the button is clicked"""
    #randomly choose from red, green, blue, and yellow
    color = random.choice(('#e62325', '#34bc6e', '#79a6f6', '#ffb000'))    
    self.canvas_1.fill_style = color
    
    #randomly pick coordinates over the cookie
    x_list = list(range(int(self.c_width/2-150), int(self.c_width/2+150)))
    x_list = random.sample(x_list, 15)
    
    y_list = list(range(int(self.c_height/2-250), int(self.c_height/2+50)))
    y_list = random.sample(y_list, 15)
    
    for x, y in zip(x_list, y_list):
      self.draw_circle(x, y, 6)
      self.canvas_1.fill()

Christmas Tree Triangles

For the Christmas tree, I just made three overlapping green triangles that get progressively smaller as they move up the y-axis. The following code draws one triangle:

c = self.canvas_1

c.fill_style = '#00884b'

c.begin_path()
c.move_to(self.c_width/2-80, self.c_height/2-20)     
c.line_to(self.c_width/2+80, self.c_height/2-20)
c.line_to(self.c_width/2,self.c_height/2-120)
c.close_path()
c.fill()

You can check out the entire source code by cloning the app:


Give the Gift of Python

Share this post: