PY-1.2-BP2-MQ40

Change the Background Color

We will make a Pygame window that starts with a blue background. When you press the 'R' key on your keyboard, the background should change to red and stay red.

Here is what the code will do:

See What's Not Working

Run this command to see what parts of your code are not working yet.

pytest module-1.2/blueprint-2/quest-40

Here's what we expect to see:

  • test_color_switch_logic

Write Your Code

Now it's your turn to write the code in main.py!

Your program needs to start with a blue background. When you press the 'R' key, the background should turn red and stay red.

Here are some examples of how Pygame works. These examples show different parts of a Pygame program. You will need to use these ideas to build your own complete program.

import pygame

# Example: Changing a color variable based on a key press
def color_changer():
    current_color = (0, 0, 255) # Blue
    # Imagine a game loop here
    # if event.type == pygame.KEYDOWN:
    #     if event.key == pygame.K_g: # If 'G' key is pressed
    #         current_color = (0, 255, 0) # Change to Green
    print(f"Current color is: {current_color}")

# Example: Filling the screen with a color and updating the display
def draw_background():
    pygame.init()
    screen_size = (600, 400)
    display = pygame.display.set_mode(screen_size)
    some_color = (255, 255, 0) # Yellow
    display.fill(some_color) # Fill the screen
    pygame.display.flip() # Show it on screen
    pygame.quit()

# Example: A simple game loop structure
def game_loop_example():
    pygame.init()
    window = pygame.display.set_mode((500, 500))
    game_running = True
    while game_running:
        for event_item in pygame.event.get():
            if event_item.type == pygame.QUIT:
                game_running = False
        # Drawing and updating display would go here
    pygame.quit()

See If It Works

Run this command again to see if your code works.

pytest module-1.2/blueprint-2/quest-40

Here's what we want to see:

  • test_color_switch_logic

Documentation

Pygame Event Handling and Drawing Cheat Sheet

This document provides a quick reference for key concepts and commands used in Pygame to handle user input and draw shapes.

The Game Loop and Event Queue

Pygame programs typically run inside a main loop. In each iteration (frame), the program checks for events, updates the game state, and draws to the screen.

User actions (like key presses, mouse clicks, or closing the window) generate events. These events are placed in an event queue. You process events by iterating through this queue.

import pygame

# Basic setup (usually done before the loop)
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("My Pygame Window")

running = True
while running:
    # --- Event Handling ---
    # Get all user events from the queue
    for event in pygame.event.get():
        # Check if the user clicked the window's close button
        if event.type == pygame.QUIT:
            running = False
        
        # --- Your event handling code goes here ---
        # Check for key presses, mouse clicks, etc.

    # --- Game State Updates (if any) ---
    # e.g., move a character, update a score

    # --- Drawing ---
    # Clear the screen (optional, but common)
    screen.fill((255, 255, 255)) # Fill with white

    # --- Your drawing code goes here ---
    # Draw shapes, images, etc. based on the current state

    # --- Update Display ---
    # Show the newly drawn frame
    pygame.display.flip() # or pygame.display.update()

# Quit Pygame when the loop ends
pygame.quit()

Event Queue Diagram

Handling Keyboard Events

Keyboard actions generate KEYDOWN (key pressed down) and KEYUP (key released) events.

Detecting a Key Press

To check if any key was pressed down in the current frame, check the event's type:

for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        # A key was pressed!
        print("A key was pressed.")

Identifying the Specific Key

When a KEYDOWN or KEYUP event occurs, the event object has a key attribute. This attribute holds an integer value representing the specific key. Pygame provides constants for most keys, starting with pygame.K_.

for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        # Print the integer code of the pressed key
        print(f"Key code: {event.key}")

Checking for a Specific Key

Use the event.key attribute in a conditional statement to react to a particular key:

# Example: Reacting to the Spacebar
for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE:
            print("Spacebar pressed!")

# Example: Reacting to the 'A' key
for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_a:
            print("'A' key pressed!")

Common key constants include:

  • pygame.K_a, pygame.K_b, ..., pygame.K_z for letter keys.
  • pygame.K_0, pygame.K_1, ..., pygame.K_9 for number keys.
  • pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN for arrow keys.
  • pygame.K_ESCAPE for the Esc key.
  • pygame.K_RETURN for the Enter key.
  • pygame.K_SPACE for the spacebar.

Handling Mouse Events

Mouse actions also generate events.

Inspecting Mouse Attributes

  • pygame.MOUSEMOTION: Occurs when the mouse is moved.
  • pygame.MOUSEBUTTONDOWN: Occurs when a mouse button is pressed.
  • pygame.MOUSEBUTTONUP: Occurs when a mouse button is released.
  • event.pos: A tuple (x, y) with the mouse coordinates. Available for motion and button events.
  • event.button: An integer representing the button. Available for button events (MOUSEBUTTONDOWN, MOUSEBUTTONUP).
    • 1: Left button
    • 2: Middle button
    • 3: Right button
    • 4: Scroll wheel up
    • 5: Scroll wheel down
for event in pygame.event.get():
    # Check for mouse movement
    if event.type == pygame.MOUSEMOTION:
        print(f"Mouse moved to: {event.pos}")

    # Check for a mouse click (button down)
    if event.type == pygame.MOUSEBUTTONDOWN:
        print(f"Mouse button {event.button} pressed at {event.pos}")

Mouse Event Demo (Console Output)

Conditional Logic with Mouse Buttons

Use the event.button attribute to perform different actions based on which mouse button was clicked:

for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN:
        if event.button == 1: # Left click
            print("Left button clicked!")
        elif event.button == 3: # Right click
            print("Right button clicked!")
        else: # Other buttons (middle, scroll wheel)
            print(f"Other button ({event.button}) clicked.")

Core Python Concepts in Action

Pygame event handling and game logic rely heavily on fundamental Python concepts.

  • Boolean State Management: Using True/False variables to track states (like whether a light is on or off, or if a circle should be drawn). Toggling a boolean variable is done with variable = not variable.

    # Simulates flipping a light switch
    is_light_on = False
    # ... user types 'flip' ...
    is_light_on = not is_light_on # Toggles from False to True, or True to False
    
  • Stateful Loops (Accumulators): Using a loop (like while True:) that continues until a condition is met (like a 'quit' command or a running = False flag). Variables inside or outside the loop can accumulate or change state over time.

    # Simulates keeping score
    total_score = 0
    while True:
        user_input = input("Enter points or 'q': ")
        if user_input.lower() == 'q':
            break # Exit the loop
        try:
            points = int(user_input)
            total_score += points # Add to the running total
            print(f"Current score: {total_score}")
        except ValueError:
            print("Invalid input.")
    print(f"Final score: {total_score}")
    
  • Complex Conditional Logic: Using if/elif/else structures to handle multiple distinct conditions or ranges of values.

    # Simulates assigning a grade based on a score
    score = 85 # Example score
    if score >= 90:
        grade = 'A'
    elif score >= 80:
        grade = 'B'
    elif score >= 70:
        grade = 'C'
    elif score >= 60:
        grade = 'D'
    else:
        grade = 'F'
    print(f"The grade is: {grade}")
    
  • Error Handling (try/except): Using try and except blocks to gracefully handle potential errors, such as trying to convert non-numeric input to an integer.

    Try Except Explanation

Drawing Shapes

The pygame.draw module provides functions for drawing common shapes onto a Surface (like your screen object).

Pygame Draw Module

  • Drawing a Rectangle: pygame.draw.rect(surface, color, rect, width=0)

    • surface: The Surface to draw on (e.g., screen).
    • color: The color (R, G, B tuple).
    • rect: A pygame.Rect object or a tuple (x, y, width, height) defining the rectangle's position and size.
    • width: If 0 (default), fills the rectangle. If > 0, draws the outline of the specified width.
    # Example: Draw a blue rectangle outline
    pygame.draw.rect(screen, (0, 0, 255), (100, 100, 200, 100), 5)
    
  • Drawing a Circle: pygame.draw.circle(surface, color, center_pos, radius, width=0)

    • surface: The Surface to draw on.
    • color: The color.
    • center_pos: A tuple (x, y) for the center of the circle.
    • radius: The radius of the circle.
    • width: If 0 (default), fills the circle. If > 0, draws the outline.
    # Example: Draw a filled green circle
    pygame.draw.circle(screen, (0, 255, 0), (400, 300), 50)
    
  • Drawing a Line: pygame.draw.line(surface, color, start_pos, end_pos, width=1)

    • surface: The Surface to draw on.
    • color: The color.
    • start_pos: A tuple (x, y) for the start point.
    • end_pos: A tuple (x, y) for the end point.
    • width: The thickness of the line.
    # Example: Draw a thick red line
    pygame.draw.line(screen, (255, 0, 0), (50, 50), (750, 550), 10)
    

Connecting Events to Drawing

You can use information from events (like event.pos or event.button) to determine what or where to draw.

Drawing on Click

To draw a shape where the user clicks, get the position from event.pos and pass it to a drawing function:

# In the event loop:
for event in pygame.event.get():
    if event.type == pygame.MOUSEBUTTONDOWN:
        # Draw a red circle exactly where the user clicked
        click_position = event.pos
        pygame.draw.circle(screen, (255, 0, 0), click_position, 10)

Draw on Click

Preserving State Between Events

Sometimes you need to remember information from one event (e.g., a mouse click position) and use it later when another event occurs (e.g., a key press). To do this, store the information in a variable that exists outside the event loop, typically initialized before the while running: loop starts.

# Variable outside the loop to store state
last_click_pos = None

while running:
    for event in pygame.event.get():
        if event.type == pygame.MOUSEBUTTONDOWN:
            # Store the position from this event
            last_click_pos = event.pos
            print(f"Stored position: {last_click_pos}")

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_d: # Example: 'D' key
                # Use the stored position from a previous event
                if last_click_pos:
                    print(f"Drawing at stored position: {last_click_pos}")
                    # Example: Draw something at last_click_pos
                    # pygame.draw.circle(screen, (0, 0, 0), last_click_pos, 5)

Preserving State