Learning Goals
3 minBy the end of this lesson you can:
- Draw filled and outline rectangles using
screen.draw.filled_rectand aRectobject. - Draw filled and outline circles with
screen.draw.filled_circleandscreen.draw.circle. - Draw a straight line with
screen.draw.lineand combine multiple shapes into a small scene.
Warm-Up · Coordinate Check
5 minQuick recall from PZ-03. In a 500×400 window, what are the coordinates of:
- The top-right corner?
- The centre?
- A point 50 pixels in from the left and 30 pixels down from the top?
Show the answers
- Top-right: (500, 0)
- Centre: (250, 200)
- 50 in, 30 down: (50, 30)
New Concept · Three Drawing Tools
12 minThink of screen.draw as a box of coloured markers. Each function is a different marker tip — rectangle, circle, line. You tell it where to draw and what colour to use.
Rectangles — Rect((x, y), (w, h))
A Rect describes a rectangular area. Give it a top-left position and a size (width, height):
from pgzero.rect import Rect sky = Rect((0, 0), (600, 200)) # top-left corner, then width × height screen.draw.filled_rect(sky, "skyblue") # filled solid colour screen.draw.rect(sky, "navy") # outline only
Circles — centre pos + radius
screen.draw.filled_circle((300, 100), 50, "yellow") # solid circle screen.draw.circle((300, 100), 55, "orange") # ring/outline
The first argument is the centre position, the second is the radius in pixels.
Lines — start + end + colour
screen.draw.line((0, 200), (600, 200), "white") # horizontal line screen.draw.line((50, 0), (300, 400), "red") # diagonal line
Drawing order matters
Shapes are painted in the order you call them. Later shapes cover earlier ones — just like placing stickers on a page. Put backgrounds first, details last.
Worked Example · A Simple Flag Scene
12 minThe story
Aiman wants to draw a simple scene: a night sky, a round moon, a flagpole and a flag. Save as flag_scene.py. Build it in two stages.
Stage 1 — sky and moon
# flag_scene.py — Stage 1: sky and moon import pgzrun WIDTH = 600 HEIGHT = 400 TITLE = "Flag Scene" def draw(): screen.fill("midnightblue") # night sky background screen.draw.filled_circle((490, 70), 45, "ivory") # full moon screen.draw.circle((490, 70), 45, "wheat") # moon outline ring pgzrun.go()
Stage 2 — add the flagpole and flag
# flag_scene.py — Stage 2: add flagpole and flag import pgzrun WIDTH = 600 HEIGHT = 400 TITLE = "Flag Scene" def draw(): screen.fill("midnightblue") screen.draw.filled_circle((490, 70), 45, "ivory") screen.draw.circle((490, 70), 45, "wheat") screen.draw.line((100, 380), (100, 100), "tan") # flagpole screen.draw.filled_rect(Rect((100, 100), (160, 100)), "red") # flag body screen.draw.filled_rect(Rect((100, 100), (160, 34)), "white") # top stripe pgzrun.go()
What you'll see
Try It Yourself
13 minDraw a smiley face using only shapes: a filled circle for the head, two small filled circles for eyes, and a line for the mouth.
Hint
def draw(): screen.fill("black") screen.draw.filled_circle((300, 200), 80, "yellow") # head screen.draw.filled_circle((270, 180), 12, "black") # left eye screen.draw.filled_circle((330, 180), 12, "black") # right eye screen.draw.line((270, 230), (330, 230), "black") # mouth
Draw five Rects stacked vertically, each 600 pixels wide and 80 pixels tall. Give them five different colours. Start the first one at y = 0 and use a for loop with range(5) to position them automatically.
Hint
COLOURS = ["red", "orange", "yellow", "limegreen", "deepskyblue"] def draw(): screen.fill("white") for i in range(5): screen.draw.filled_rect(Rect((0, i * 80), (600, 80)), COLOURS[i])
Mini-Challenge 🔥 · Buggy Bullseye
8 minDaniel Tan wrote a bullseye (three concentric circles) but the circles are not centred and the largest ring is invisible. Find and fix the bugs.
# daniel_bullseye.py — buggy
import pgzrun
WIDTH = 500
HEIGHT = 400
def draw():
screen.fill("white")
screen.draw.filled_circle((250, 200), 100, "red") # outer
screen.draw.filled_circle((250, 200), 100, "white") # middle gap — should be radius 66
screen.draw.filled_circle(250, 200, 33, "red") # inner — wrong pos type
pgzrun.go()It works if…
three concentric red and white rings appear centred in the window
Show the fix
# daniel_bullseye.py — fixed import pgzrun WIDTH = 500 HEIGHT = 400 def draw(): screen.fill("white") screen.draw.filled_circle((250, 200), 100, "red") # outer ring screen.draw.filled_circle((250, 200), 66, "white") # bug 1: radius was 100, should be 66 screen.draw.filled_circle((250, 200), 33, "red") # bug 2: pos must be a tuple (250, 200) pgzrun.go()
Two bugs: the middle circle had the wrong radius (it was the same as the outer one, hiding it), and the inner circle was missing the tuple brackets around its position.
Recap
3 minPygame Zero's screen.draw object has three essential shape tools: filled_rect and rect for rectangles (using a Rect), filled_circle and circle for circles (centre position + radius), and line for straight lines (start + end). Shapes are drawn in order — later calls cover earlier ones. By combining these three tools you can build any simple scene.
Vocabulary Card
- Rect((x, y), (w, h))
- A Pygame Zero object describing a rectangular area by its top-left corner and its width and height.
- filled_rect / rect
- Draw a solid-colour rectangle or an outline-only rectangle at a given
Rect. - filled_circle / circle
- Draw a solid or outline circle given a centre position tuple and a radius.
- draw order
- Shapes drawn later appear on top of earlier ones — backgrounds go first.
Homework
4 minDraw a simple house scene using only rectangles and lines. The scene needs: a rectangle for the house body, two diagonal lines forming a roof triangle, a smaller rectangle for a door, and at least one rectangle for a window. Save as house.py and bring a screenshot.
Stretch. Add a sun (filled circle in the sky) and a ground line across the bottom.
Sample · house.py
# house.py — a simple house scene import pgzrun WIDTH = 600 HEIGHT = 400 TITLE = "House Scene" def draw(): screen.fill("skyblue") screen.draw.filled_circle((500, 80), 50, "yellow") # sun screen.draw.line((0, 340), (600, 340), "saddlebrown") # ground screen.draw.filled_rect(Rect((180, 200), (240, 140)), "beige") # house body screen.draw.line((180, 200), (300, 100), "firebrick") # roof left slope screen.draw.line((300, 100), (420, 200), "firebrick") # roof right slope screen.draw.filled_rect(Rect((270, 270), (60, 70)), "saddlebrown") # door screen.draw.filled_rect(Rect((200, 220), (50, 40)), "lightblue") # window pgzrun.go()
Your positions and colours will differ — what matters is using Rect for rectangles and two line calls for the roof slopes.