We will add a new tool to our drawing program: an eraser! When you press the 'E' key, everything you have drawn will disappear. We will use the pygame
library for this.
Missing Image
We will add a new tool to our drawing program: an eraser! When you press the 'E' key, everything you have drawn will disappear. We will use the pygame
library for this.
Missing Image
Let's check if your program is working. We do this by running a special test. Open your terminal and type this command:
pytest module-1.2/blueprint-PY-1.2-BP4/quest-60
What you will see:
test_drawing_on_mouse_motion
test_erase_on_e_key_press
test_no_drawing_if_mouse_not_down
test_app_quits_on_quit_event
These tests will show a red X because your code is not finished yet.
Open the main.py
file. You need to add code to make your program draw lines when you move your mouse and clear the screen when you press a key.
Your program will run in a loop, checking for things like mouse clicks, mouse movement, and key presses.
Here's how you can check for different events:
import pygame
# Inside your game loop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# This means the user wants to close the window
pass
elif event.type == pygame.MOUSEBUTTONDOWN:
# This happens when you click the mouse button down
print("Mouse button pressed!")
elif event.type == pygame.MOUSEBUTTONUP:
# This happens when you let go of the mouse button
print("Mouse button released!")
elif event.type == pygame.MOUSEMOTION:
# This happens when you move the mouse
print(f"Mouse moved to {event.pos}")
elif event.type == pygame.KEYDOWN:
# This happens when you press a key down
if event.key == pygame.K_a:
print("You pressed the 'a' key!")
To draw a line, you need a starting point and an ending point, a color, and a thickness:
import pygame
# ... setup code ...
line_color = (0, 0, 0) # Black
start_point = (10, 10)
end_point = (50, 50)
line_thickness = 2
pygame.draw.line(my_screen, line_color, start_point, end_point, line_thickness)
To clear the screen, you can fill it with a color:
# Inside your game loop, when you want to clear:
my_screen.fill((255, 255, 255)) # Fills with white
Remember to update the display after drawing:
import pygame
# ... drawing code ...
pygame.display.flip()
Use these examples to help you make your drawing program.
Now that you've written your code, let's check it again. Type this command in your terminal:
pytest module-1.2/blueprint-PY-1.2-BP4/quest-60
test_drawing_on_mouse_motion
test_erase_on_e_key_press
test_no_drawing_if_mouse_not_down
test_app_quits_on_quit_event
This document provides a reference for key concepts and functions used in the micro-quests for Blueprint PY-1.2-BP4.
Before using Pygame functions, the library must be initialized. A display window (screen) is then created.
import pygame
# Initialize Pygame
pygame.init()
# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
# Create the screen surface
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# Set the window title
pygame.display.set_caption("My Pygame Window")
After drawing, you must update the display to make your changes visible. It's common to fill the background first to clear the previous frame.
# Inside the main game loop, after drawing commands:
screen.fill((255, 255, 255)) # Fill with white
# ... drawing commands ...
pygame.display.flip() # Make the drawn content visible
pygame.draw
)The pygame.draw
module provides functions for drawing simple shapes onto a Surface
(like your screen). The first argument is always the surface
to draw on.
pygame.draw.rect(surface, color, (x, y, width, height))
or pygame.draw.rect(surface, color, rect_object)
color
is an RGB tuple (e.g., (255, 0, 0)
for red).(x, y, width, height)
is a tuple defining the top-left corner, width, and height.pygame.draw.circle(surface, color, (center_x, center_y), radius)
(center_x, center_y)
is a tuple for the circle's center point.radius
is the circle's radius in pixels.pygame.draw.lines(surface, color, closed, list_of_points, thickness)
closed
is True
or False
. If True
, it connects the last point to the first.list_of_points
is a list of coordinate tuples, e.g., [(10, 20), (50, 60), (80, 30)]
.thickness
is the line width in pixels.pygame.draw.polygon(surface, color, list_of_points)
list_of_points
is a list of coordinate tuples defining the vertices of the polygon. The points are automatically connected, and the shape is filled.# Example drawing calls:
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# Draw a red rectangle
pygame.draw.rect(screen, RED, (100, 100, 50, 80))
# Draw a green circle
pygame.draw.circle(screen, GREEN, (400, 300), 25)
# Draw blue lines
points = [(50, 50), (150, 50), (100, 150)]
pygame.draw.lines(screen, BLUE, False, points, 2)
pygame.Rect
ObjectThe pygame.Rect
object is a fundamental Pygame class for storing and manipulating rectangular areas. It holds integer coordinates for the top-left corner, width, and height. It also provides convenient attributes for other corners, the center, and dimensions, which are calculated automatically.
A Rect
object can be created using pygame.Rect(left, top, width, height)
.
# Example Rect creation and attributes:
my_rect = pygame.Rect(100, 150, 200, 80)
print(f"x: {my_rect.x}")
print(f"y: {my_rect.y}")
print(f"width: {my_rect.width}")
print(f"height: {my_rect.height}")
print(f"center: {my_rect.center}")
print(f"topleft: {my_rect.topleft}")
print(f"bottomright: {my_rect.bottomright}")
# You can also draw a Rect object directly
# pygame.draw.rect(screen, BLUE, my_rect)
Most real-time applications, including games, run on a continuous loop that processes three main phases:
# Basic structure inside the main game loop:
running = True
while running:
# 1. Handle Input
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Handle other events (key presses, mouse clicks)
# 2. Update State
# Check key states for continuous movement
# Update player position, check collisions, update scores, etc.
# 3. Render
screen.fill(BACKGROUND_COLOR) # Clear previous frame
# Draw game objects (player, enemies, score, etc.)
pygame.display.flip() # Show the newly drawn frame
Game state refers to all the variables that describe the current condition of the game (e.g., player position, score, whether an object is active, a list of items).
Python lists are useful for keeping track of a sequence of game states, such as the history of positions for a drawing trail.
# Initialize an empty list
trail_points = []
# Add a position to the list
current_position = (100, 100)
trail_points.append(current_position)
# Add another position
new_position = (110, 105)
trail_points.append(new_position)
print(trail_points) # Output: [(100, 100), (110, 105)]
# You can then use this list with drawing functions like pygame.draw.lines().
# if len(trail_points) > 1:
# pygame.draw.lines(screen, BLUE, False, trail_points, 2)
# To clear a list:
# trail_points.clear()
# or
# trail_points = []
A boolean variable (True
or False
) can act as a "flag" to control program behavior. Toggling a boolean means flipping its value (True
becomes False
, False
becomes True
).
# Global flag
is_active = True
def toggle_active():
global is_active # Use global to modify the variable outside the function
is_active = not is_active # Toggles the value
print(f"Is active: {is_active}")
# Example usage:
# toggle_active() # is_active becomes False
# toggle_active() # is_active becomes True
You can use an if
statement to make code run only when a flag is True
.
# Inside a function or loop:
if is_active:
# Code here only runs if is_active is True
print("Action performed because active.")
else:
# Code here runs if is_active is False
print("Action skipped because inactive.")
You can use an index variable and the modulo operator (%
) to cycle through items in a list, looping back to the beginning when you reach the end.
items = ['apple', 'banana', 'cherry']
current_index = 0
def get_next_item():
global current_index
# Calculate the next index, wrapping around using modulo
current_index = (current_index + 1) % len(items)
# Return the item at the new index
return items[current_index]
# Example usage:
# print(get_next_item()) # Prints 'banana', index becomes 1
# print(get_next_item()) # Prints 'cherry', index becomes 2
# print(get_next_item()) # Prints 'apple', index becomes 0 (wraps around)
pygame.event.get()
)The pygame.event.get()
function retrieves events from Pygame's event queue. This is typically done once per frame inside the main game loop. You then iterate through the list of events to check their type
.
# Inside the main game loop:
for event in pygame.event.get():
# Check event.type here
pass
QUIT
The pygame.QUIT
event is generated when the user clicks the close button on the window. Checking for this event is essential for allowing the user to exit the application gracefully.
# Inside the event loop:
if event.type == pygame.QUIT:
running = False # Set a flag to exit the main game loop
KEYDOWN
The pygame.KEYDOWN
event is generated once when a key is pressed down. The event.key
attribute contains the constant representing the specific key that was pressed (e.g., pygame.K_LEFT
, pygame.K_SPACE
, pygame.K_c
).
# Inside the event loop:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
# Code to handle left arrow key press
pass
elif event.key == pygame.K_SPACE:
# Code to handle spacebar press
pass
# Add more elif checks for other keys
pygame.key.get_pressed()
)The pygame.key.get_pressed()
function returns a sequence (like a list or tuple) containing the state of every keyboard key. Each item in the sequence is True
if the corresponding key is currently held down, and False
otherwise. This is useful for continuous actions like movement, as you can check the key's state every frame.
# Inside the main game loop, but OUTSIDE the event loop:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
# Code to move left continuously while the key is held
pass
if keys[pygame.K_RIGHT]:
# Code to move right continuously while the key is held
pass
# Use separate 'if' statements for each direction to allow diagonal movement
You need a Screen
object to represent the drawing window and a Turtle
object (often called a "stylus" or "pen") to do the drawing.
import turtle
# Get the screen object
screen = turtle.Screen()
screen.title("My Turtle Window")
# Create a turtle object
stylus = turtle.Turtle()
The turtle
object has methods to control its movement, drawing state, and appearance.
stylus.forward(distance)
: Move forward by distance
pixels.stylus.backward(distance)
: Move backward.stylus.left(degrees)
: Turn left by degrees
.stylus.right(degrees)
: Turn right by degrees
.\stylus.goto(x, y)
: Move directly to the specified coordinates.stylus.penup()
: Lift the pen up (stop drawing when moving).stylus.pendown()
: Put the pen down (start drawing when moving).stylus.pencolor("color_name")
: Set the color of the line drawn. Can use color names (like "red", "blue") or hex codes.stylus.shape("shape_name")
: Change the appearance of the turtle cursor (e.g., "arrow", "turtle", "circle", "square", "triangle", "classic").stylus.clear()
: Erase the turtle's drawings from the screen, but the turtle's position and state remain unchanged.stylus.reset()
: Erase drawings and reset the turtle to its initial state (center, facing right, pen down).# Example Turtle commands:
stylus.pencolor("green")
stylus.forward(100)
stylus.left(90)
stylus.penup()
stylus.forward(50)
stylus.pendown()
stylus.pencolor("purple")
stylus.circle(30)
stylus.clear() # Erase everything drawn so far
Turtle Graphics uses a simple event binding system. You define functions (handlers) and tell the screen which event should trigger which function.
# Define a function to be called on an event
def handle_click(x, y):
print(f"Clicked at ({x}, {y})")
# Tell the screen to start listening for events
screen.listen()
# Bind the left mouse click to the handler function
screen.onclick(handle_click)
# Bind a key press to a handler function
# The second argument is the key name (e.g., "space", "Up", "Down", "Left", "Right", "c")
def handle_space_press():
print("Space was pressed!")
screen.onkey(handle_space_press, "space")
# Bind mouse drag to a handler function
# The handler receives the x, y coordinates where the mouse is dragged
def handle_drag(x, y):
print(f"Dragging at ({x}, {y})")
# stylus.goto(x, y) # Example: move the turtle to the drag position
turtle.ondrag(handle_drag)