Skip to content

Screen Base Class Reference

Screen Base Class Reference

This page documents the Screen base class used for creating game screens.

Module Location

from skeletons.screen import Screen

Class Overview

class Screen:
"""Base class for all game screens."""
# Attributes
screen: pygame.Surface
caption: str
running: bool
# Methods
def __init__(self, screen: pygame.Surface, caption: str) -> None: ...
def set_background(self, r: int, g: int, b: int) -> None: ...
def set_backgroundImage(self, name: str, scale_width: int = 1280, scale_height: int = 720) -> None: ...
def draw_text(self, text: str, font: pygame.font.Font, r: int, g: int, b: int, x: float, y: float) -> None: ...
def draw_sprite(self, name: str, x: float, y: float, scale_width: int = None, scale_height: int = None) -> None: ...
def clear_widgets(mode: str = "reset") -> None: ...
def run(self) -> None: ...

Attributes

screen

screen: pygame.Surface

The pygame display Surface to render to.


caption

caption: str

The window title for this screen.


running

running: bool

Controls the main loop. Set to False to exit the screen and allow transitions.


Constructor

__init__

def __init__(self, screen: pygame.Surface, caption: str) -> None

Initialize the screen and start the game loop.

Parameters:

NameTypeDescription
screenpygame.SurfaceThe pygame display surface
captionstrWindow title for this screen

Important: The constructor immediately starts the game loop by calling run() repeatedly until self.running is False. All initialization (widgets, state) must happen BEFORE calling super().__init__().

Example:

class MyScreen(Screen):
def __init__(self, screen, caption):
# 1. Clear widgets first
self.clear_widgets()
# 2. Initialize state
self.score = 0
self.clock = pygame.time.Clock()
# 3. Create UI elements
self.button = Button(...)
# 4. Call parent LAST (starts game loop)
super().__init__(screen, caption)

Methods

set_background

def set_background(self, r: int, g: int, b: int) -> None

Fill the entire screen with a solid color.

Parameters:

NameTypeDescription
rintRed component (0-255)
gintGreen component (0-255)
bintBlue component (0-255)

Example:

def run(self):
# Light blue background
self.set_background(135, 206, 235)

set_backgroundImage

def set_backgroundImage(
self,
name: str,
scale_width: int = 1280,
scale_height: int = 720,
) -> None

Set a background image from the assets folder.

This method caches scaled backgrounds by filename and size so repeated calls in the main loop don’t reload images every frame.

Parameters:

NameTypeDefaultDescription
namestr-Filename in assets/backgrounds/
scale_widthint1280Target width in pixels
scale_heightint720Target height in pixels

Raises:

  • FileNotFoundError if image doesn’t exist

Example:

def run(self):
self.set_backgroundImage("title.jpg")
# Or custom size
self.set_backgroundImage("title.jpg", 1920, 1080)
# Sprite draws are cached by name and scale
self.draw_sprite("player.png", 100, 100, 64, 64)

draw_text

def draw_text(
self,
text: str,
font: pygame.font.Font,
r: int,
g: int,
b: int,
x: float,
y: float,
) -> None

Render text to the screen.

Parameters:

NameTypeDescription
textstrText string to display
fontpygame.font.FontFont to use
rintRed component (0-255)
gintGreen component (0-255)
bintBlue component (0-255)
xfloatX position in pixels
yfloatY position in pixels

Example:

from assets.assets import getFont
def run(self):
# White text
self.draw_text("Hello World", getFont(32), 255, 255, 255, 100, 100)
# Centered text
text = "Centered"
width = getFont(32).size(text)[0]
self.draw_text(
text, getFont(32), 255, 255, 255,
(self.screen.get_width() / 2) - (width / 2),
200
)

draw_sprite

def draw_sprite(
self,
name: str,
x: float,
y: float,
scale_width: int = None,
scale_height: int = None,
) -> None

Draw a sprite from the assets folder.

Parameters:

NameTypeDefaultDescription
namestr-Filename in assets/sprites/
xfloat-X position in pixels
yfloat-Y position in pixels
scale_widthintNoneOptional target width
scale_heightintNoneOptional target height

Scaling only occurs if BOTH scale_width and scale_height are provided.

Example:

def run(self):
# Original size
self.draw_sprite("icon.png", 100, 100)
# Scaled to 64x64
self.draw_sprite("icon.png", 200, 100, 64, 64)

run

def run(self) -> None

Main update method called every frame. Override this in subclasses.

This method should:

  1. Handle pygame events (especially QUIT)
  2. Process input
  3. Update game state
  4. Render the screen
  5. Call pygame.display.update() or flip()

Example:

def run(self):
# 1. Handle events
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.running = False
pygame.quit()
exit()
# 2. Process input
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
self.running = False
# 3. Update state
self.timer += self.dt
# 4. Render
self.set_backgroundImage("background.jpg")
self.draw_text("Timer: " + str(self.timer), getFont(24), 255, 255, 255, 10, 10)
self.button.draw()
# 5. Update display
pygame_widgets.update(events)
pygame.display.update()
# Update delta time
self.dt = min(self.clock.tick() / 1000, 0.0167)

Screen Lifecycle

1. __init__ called
├── Clear previous widgets
├── Initialize state variables
├── Create UI elements (buttons, etc.)
└── Call super().__init__()
└── Sets caption
└── Enters game loop
2. Game Loop (while self.running)
└── run() called repeatedly
├── Handle events
├── Update state
├── Render
└── Display update
3. Exit (self.running = False)
└── Loop exits
└── Transition to next screen

Common Patterns

Widget Cleanup

Always clear widgets at screen start to prevent UI artifacts:

def __init__(self, screen, caption):
from pygame_widgets.widget import WidgetHandler
widgets = WidgetHandler.getWidgets()
WidgetHandler._widgets = widgets.__class__()
# ... rest of init

Screen Transition

def go_to_game(self):
self.running = False # Exit current loop
from screens.GameScreen import GameScreen
GameScreen(self.screen, "Game") # Start new screen

Event Handling with Widgets

def run(self):
events = pygame.event.get()
for event in events:
# Your event handling
pass
# Pass events to widgets
pygame_widgets.update(events)

Delta Time

For smooth animations:

def __init__(self, screen, caption):
self.clock = pygame.time.Clock()
self.dt = 0
# ...
def run(self):
# ... rendering ...
# Cap at ~60 FPS to prevent physics issues
self.dt = min(self.clock.tick() / 1000, 0.0167)

Existing Screens

ScreenPurposeLocation
TitleScreenMain menuscreens/TitleScreen.py
WorldSelectScreenWorld browserscreens/WorldSelectScreen.py
LevelSelectScreenLevel browserscreens/LevelSelectScreen.py
CharacterScreenCharacter selectionscreens/CharacterScreen.py
SettingsScreenGame settingsscreens/SettingsScreen.py
GameScreenMain gameplayscreens/GameScreen.py
FinishScreenLevel completescreens/FinishScreen.py

Complete Example

"""Custom screen example."""
import pygame
import pygame_widgets
from pygame_widgets.button import Button
from pygame_widgets.widget import WidgetHandler
from skeletons.screen import Screen
from assets.assets import getFont
class CreditsScreen(Screen):
"""Display game credits."""
def __init__(self, screen, caption):
# Clear widgets
widgets = WidgetHandler.getWidgets()
WidgetHandler._widgets = widgets.__class__()
# State
self.clock = pygame.time.Clock()
self.dt = 0
self.scroll_y = screen.get_height()
# Credits text
self.credits = [
("Game Design", "Your Name"),
("Programming", "Your Name"),
("Art", "Various Artists"),
("Music", "Creative Commons"),
]
# Back button
self.back_btn = Button(
screen,
20, 20,
100, 40,
False,
text="Back",
onClick=self.go_back,
font=getFont(20),
radius=8,
)
super().__init__(screen, caption)
def go_back(self):
self.running = False
from screens.TitleScreen import TitleScreen
TitleScreen(self.screen, "Title")
def run(self):
# Events
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
self.running = False
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.go_back()
# Update scroll
self.scroll_y -= 30 * self.dt
if self.scroll_y < -len(self.credits) * 60:
self.scroll_y = self.screen.get_height()
# Render
self.set_background(0, 0, 0)
# Draw scrolling credits
for i, (role, name) in enumerate(self.credits):
y = self.scroll_y + i * 60
if -50 < y < self.screen.get_height() + 50:
self.draw_text(role, getFont(24), 150, 150, 150,
self.screen.get_width() / 2 - 100, y)
self.draw_text(name, getFont(20), 255, 255, 255,
self.screen.get_width() / 2 - 100, y + 25)
# Draw button
self.back_btn.draw()
# Update
pygame_widgets.update(events)
pygame.display.update()
self.dt = min(self.clock.tick() / 1000, 0.0167)

clear_widgets

def clear_widgets(mode: str = "reset") -> None

Clear pygame_widgets state between screens.

Parameters:

NameTypeDefaultDescription
modestr"reset""reset" to replace handler storage, "remove" to detach widgets

Example:

def __init__(self, screen, caption):
self.clear_widgets()
# build UI, then start loop
super().__init__(screen, caption)