18. Using the Window Class¶
We can use a class to represent our program. The Arcade library has a built-in class that represents a window on the screen. We can create our own child class and override functions to handle:
Start-up and initialization
Drawing the items on our screen
Animating/Updating the positions of items on our screen
Responding to the keyboard
Responding to the mouse
One of the best ways of learning to program is to look at sample code. This chapter has several examples designed to learn how to:
Open a window using an object-oriented approach
Animating objects
Moving objects with the mouse
Moving objects with the keyboard
Moving objects with the joystick
18.1. Creating a Window with a Class¶
Up to now, we have used a function called open_window
to open a window.
Here’s the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
def main():
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Drawing Example")
arcade.run()
main()
|
We can also create an instance of a class called Window
to open a window.
The code is rather straight-forward:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
def main():
window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, "Drawing Example")
arcade.run()
main()
|
Function calls, and calls to create an instance of an object look very similar.
The tell-tale clue that we are creating an instance of an object in the second
example is the fact that Window
is capitalized.
18.2. Extending the Window Class¶
Arcade’s Window
class has a lot of built-in methods that are automatically
called when needed. Methods for drawing, for responding to the keyboard, the
mouse, and more. You can see all the methods by looking at the
Window Class Documentation.
But by default, these methods don’t do anything. We need
to change that.
As we learned from the prior chapter, we can extend the functionality of a class
by creating a child class.
Therefore, we can extend the Window
class by creating
a child class of it. I’m going to call my child class MyGame
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import arcade
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
18.3. Drawing with the Window Class¶
To draw with the Window
class, we need to create our own method called
on_draw
. This will override the default on_draw
method built into the Window
class. We will put our drawing code in there.
The on_draw
method gets called about 60 times per second. We’ll use this
fact when we do animation.
We also need to set the background color. Since we only need to do this once,
we will do that in the __init__
method. No sense setting the background
60 times per second when it isn’t changing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import arcade
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Set the background color
arcade.set_background_color(arcade.color.ASH_GREY)
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
arcade.draw_circle_filled(50, 50, 15, arcade.color.AUBURN)
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
The result of this program just looks like:

18.4. Animating¶
By overriding the update
method, we can update our ball position and
animate our scene:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import arcade
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
# Set the background color
arcade.set_background_color(arcade.color.ASH_GREY)
# Attributes to store where our ball is
self.ball_x = 50
self.ball_y = 50
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
arcade.draw_circle_filled(self.ball_x, self.ball_y, 15, arcade.color.AUBURN)
def update(self, delta_time):
""" Called to update our objects. Happens approximately 60 times per second."""
self.ball_x += 1
self.ball_y += 1
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
18.4.1. Encapsulating Our Animation Object¶
It doesn’t take much imagination to realize that adding more parameters to
the ball, getting it to bounce, or even having several balls on the screen would
make our MyApplication
class very complex.
If only there was a way to encapsulate all that “ball” stuff together. Wait! There is! Using classes!
Here is a more complex example, but all the logic for the ball has been moved
into a new Ball
class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
class Ball:
""" This class manages a ball bouncing on the screen. """
def __init__(self, position_x, position_y, change_x, change_y, radius, color):
""" Constructor. """
# Take the parameters of the init function above, and create instance variables out of them.
self.position_x = position_x
self.position_y = position_y
self.change_x = change_x
self.change_y = change_y
self.radius = radius
self.color = color
def draw(self):
""" Draw the balls with the instance variables we have. """
arcade.draw_circle_filled(self.position_x, self.position_y, self.radius, self.color)
def update(self):
""" Code to control the ball's movement. """
# Move the ball
self.position_y += self.change_y
self.position_x += self.change_x
# See if the ball hit the edge of the screen. If so, change direction
if self.position_x < self.radius:
self.change_x *= -1
if self.position_x > SCREEN_WIDTH - self.radius:
self.change_x *= -1
if self.position_y < self.radius:
self.change_y *= -1
if self.position_y > SCREEN_HEIGHT - self.radius:
self.change_y *= -1
class MyGame(arcade.Window):
""" My window class. """
def __init__(self, width, height, title):
""" Constructor. """
# Call the parent class's init function
super().__init__(width, height, title)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create our ball
self.ball = Ball(50, 50, 3, 3, 15, arcade.color.AUBURN)
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
self.ball.draw()
def update(self, delta_time):
""" Called to update our objects. Happens approximately 60 times per second."""
self.ball.update()
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
Here it is in action:

18.4.2. Animating a List¶
Wouldn’t it be nice to animate multiple items? How do we track multiple items? With a list! This takes our previous example and animates three balls at once.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | import arcade
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
class Ball:
""" This class manages a ball bouncing on the screen. """
def __init__(self, position_x, position_y, change_x, change_y, radius, color):
""" Constructor. """
# Take the parameters of the init function above, and create instance variables out of them.
self.position_x = position_x
self.position_y = position_y
self.change_x = change_x
self.change_y = change_y
self.radius = radius
self.color = color
def draw(self):
""" Draw the balls with the instance variables we have. """
arcade.draw_circle_filled(self.position_x, self.position_y, self.radius, self.color)
def update(self):
""" Code to control the ball's movement. """
# Move the ball
self.position_y += self.change_y
self.position_x += self.change_x
# See if the ball hit the edge of the screen. If so, change direction
if self.position_x < self.radius:
self.change_x *= -1
if self.position_x > SCREEN_WIDTH - self.radius:
self.change_x *= -1
if self.position_y < self.radius:
self.change_y *= -1
if self.position_y > SCREEN_HEIGHT - self.radius:
self.change_y *= -1
class MyGame(arcade.Window):
def __init__(self, width, height, title):
# Call the parent class's init function
super().__init__(width, height, title)
arcade.set_background_color(arcade.color.ASH_GREY)
# Create a list for the balls
self.ball_list = []
# Add three balls to the list
ball = Ball(50, 50, 3, 3, 15, arcade.color.AUBURN)
self.ball_list.append(ball)
ball = Ball(100, 150, 2, 3, 15, arcade.color.PURPLE_MOUNTAIN_MAJESTY)
self.ball_list.append(ball)
ball = Ball(150, 250, -3, -1, 15, arcade.color.FOREST_GREEN)
self.ball_list.append(ball)
def on_draw(self):
""" Called whenever we need to draw the window. """
arcade.start_render()
# Use a "for" loop to pull each ball from the list, then call the draw
# method on that ball.
for ball in self.ball_list:
ball.draw()
def update(self, delta_time):
""" Called to update our objects. Happens approximately 60 times per second."""
# Use a "for" loop to pull each ball from the list, then call the update
# method on that ball.
for ball in self.ball_list:
ball.update()
def main():
window = MyGame(640, 480, "Drawing Example")
arcade.run()
main()
|
