[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Draw a smiley face with PIL in Python

[A smiley face drawn with PIL in Python]

This is undoubtedly one of the strangest posts I've made because it seems so unlikely. However, over the years I've used smiley faces for various examples dozens of times and because of that I've written smiley face programs in several programming languages. In this case, I needed a smiley face image for use with an upcoming example that fills shapes with an image. Yes, I could have used a stock image of a spaceship or something, but I like smiley faces, so there!

pil_draw_smiley

Anyway, the code is pretty straightforward; it just took a bunch of experimentation and tweaking to get the drawing parameters just so. Here's the pil_draw_smiley method that draws a smiley face on an image.

def pil_draw_smiley(dr, xmin, ymin, xmax, ymax): '''Draw a smiley face in the indicated area.''' wid = xmax - xmin hgt = ymax - ymin thick3 = round(max(wid, hgt) / 33) thick1 = round(max(wid, hgt) / 100) thick2 = round(max(wid, hgt) / 50) dr.ellipse((xmin, ymin, xmax, ymax), fill='yellow', outline='red', width=thick3) x1 = xmin + wid * 0.2 x2 = xmin + wid * 0.4 y1 = ymin + hgt * 0.2 y2 = ymin + hgt * 0.5 dr.ellipse((x1, y1, x2, y2), fill='white', outline='black', width=thick1) x1 = xmin + wid * 0.6 x2 = xmin + wid * 0.8 dr.ellipse((x1, y1, x2, y2), fill='white', outline='black', width=thick1) x1 = xmin + wid * 0.3 x2 = xmin + wid * 0.4 y1 = ymin + hgt * 0.25 y2 = ymin + hgt * 0.45 dr.ellipse((x1, y1, x2, y2), fill='black', outline='black', width=thick1) x1 = xmin + wid * 0.7 x2 = xmin + wid * 0.8 dr.ellipse((x1, y1, x2, y2), fill='black', outline='black', width=thick1) x1 = xmin + wid * 0.425 x2 = xmin + wid * 0.575 y1 = ymin + hgt * 0.45 y2 = ymin + hgt * 0.7 dr.ellipse((x1, y1, x2, y2), fill='lightblue', outline='black', width=thick1) x1 = xmin + wid * 0.15 x2 = xmin + wid * 0.85 y1 = ymin + hgt * 0.15 y2 = ymin + hgt * 0.85 bbox = (x1, y1, x2, y2) start_angle = 0 end_angle = 180 dr.arc(bbox, start_angle, end_angle, fill='black', width=thick2)

The dr parameter is an ImageDraw.Draw object associated with a PIL image, and it provides the drawing methods. The code simply does some calculations to figure out where things need to be drawn and the uses the dr object's methods to draw them.

The only thing to note is that the method adjusts everything to fit within the bounds given by xmin, ymin, xmax, and ymax. That lets the main program position and, if necessary, distort the smiley face.

Main Program

The main program uses the following fill_shapes to draw the smiley face three times.

def fill_shapes(self): # Get the canvas's dimensions. self.canvas.update() wid = self.canvas.winfo_width() hgt = self.canvas.winfo_height() # Make an image to draw on. image = Image.new('RGBA', (wid, hgt), 'plum') dr = ImageDraw.Draw(image) # Fill with a linear gradient. start_point = (0, 0) end_point = (0, hgt) start_color = (144, 238, 144, 255) # Light green. end_color = (0, 100, 0, 255) # Darkgreen. brush = LinearGradientBrush(start_point, end_point, start_color, end_color) bbox = (0, 0, wid, hgt) pil_fill_rectangle(image, brush, bbox) # Draw some smileys. margin = 10 bbox = margin, margin, margin + 200, margin + 200 pil_draw_smiley(dr, *bbox) bbox = margin, 2 * margin + 200, margin + 200, hgt - margin pil_draw_smiley(dr, *bbox) bbox = 2 * margin + 200, margin, wid - margin, hgt - margin pil_draw_smiley(dr, *bbox) # Display the result. self.photo_image = ImageTk.PhotoImage(image) self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo_image)

This code gets the current size of the program's Canvas widget and makes an image to fit. It then uses a linear gradient brush (see the post Make a LinearGradientBrush class for use with PIL in Python) to fill the image with color shading from light green at the top to dark green at the bottom.

The program then draws the 200 × 200 pixel smiley on the upper left. It calculates a bounding box to fill the canvas below that smiley and draws a short fat smiley there. Finally, it calculates a bounding box to fill the rest of the canvas and draws a tall skinny smiley there.

The code finishes by converting the image into a PhotoImage that tkinter can display. (Don't forget that you must save the PhotoImage somewhere safe where it won't be garbage collected so tkinter can redisplay it later as needed. If you forget to do that, no image appears and you'll be left scratching your head wondering why.)

Conclusion

As I mentioned, I'll use the smiley face in my next post to fill shapes with images. Meanwhile, download the example to experiment with it and to see additional details. Feel free to modify the smiley, perhaps adding vampire fangs, a hat, a bow tie, or whatever else catches your fancy. (I've done all of those in different versions of this program.)
© 2024 - 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.