Python Programming in Context Chapter 13 Objectives To
Python Programming in Context Chapter 13
Objectives • • • To write an event driven program To understand write callback functions To practice with lists of objects To see another pattern for using inheritance To learn about static variables
Event Driven Programming • Events are placed on a queue • While the queue is not empty – Take an event off of the queue – Execute a callback function for that event
Figure 13. 1
Listing 13. 1 class Event. Handler: def __init__(self): self. queue = [] self. event. Keeper = {} def add. Event(self, event. Name): self. queue. append(event. Name) def register. Callback(self, event, func): self. event. Keeper[event] = func def run(self): while(True): if len(self. queue) > 0: next. Event = self. queue. pop(0) self. event. Keeper[next. Event]() else: print('queue is empty')
Multithreading the Event Loop • • Multiple things can happen at the same time Import Thread from the threading module Define a class that inherits from Thread Implement a run method that will be executed when the thread is started
Figure 13. 2
Listing 13. 2 from threading import Thread import time class Event. Handler(Thread): def __init__(self): super(). __init__() self. queue = [] self. event. Keeper = {} def add. Event(self, event. Name): self. queue. append(event. Name) def register. Callback(self, event, func): self. event. Keeper[event] = func def run(self): while(True): if len(self. queue) > 0: next. Event = self. queue. pop(0) call. Back = self. event. Keeper[next. Event] call. Back() else: time. sleep(1)
Etch-A-Sketch • Create a simple event controlled drawing program • Model after the Etch-A-Sketch drawing toy • Use arrow keys to control a drawing turtle • Event controlled since the turtle will only move when a key event occurs
Listing 13. 3 (Part 1) import turtle class Etch: def __init__(self): self. my. T = turtle. Turtle() self. my. Screen = turtle. Screen() self. my. T. color('blue') self. my. T. pensize(2) self. my. T. speed(0) self. distance = 5 self. turn = 10 self. my. Screen. onkey(self. fwd, "Up") self. my. Screen. onkey(self. bkwd, "Down") self. my. Screen. onkey(self. left, "Left") self. my. Screen. onkey(self. right, "Right") self. my. Screen. onkey(self. quit, "q") self. my. Screen. listen()
Listing 13. 3 (Part 2) def fwd(self): self. my. T. forward(self. distance) def bkwd(self): self. my. T. backward(self. distance) def left(self): self. my. T. left(self. turn) def right(self): self. my. T. right(self. turn) def quit(self): self. my. Screen. bye() def main(self): turtle. mainloop()
Redesign Etch-A-Sketch • Use Inheritance • Class Etch is-a Turtle
Listing 13. 4 (Part 1) from turtle import Turtle, mainloop class Etch(Turtle): def __init__(self): super(). __init__() self. screen = self. getscreen() self. color('blue') self. pensize(2) self. speed(0) self. distance = 5 self. turn = 10 self. screen. onkey(self. fwd, "Up") self. screen. onkey(self. bkwd, "Down") self. screen. onkey(self. left 5, "Left") self. screen. onkey(self. right 5, "Right") self. screen. onkey(self. quit, "q") self. screen. listen() self. main()
Listing 13. 4 (Part 2) def fwd(self): self. forward(self. distance) def bkwd(self): self. backward(self. distance) def left 5(self): self. left(self. turn) def right 5(self): self. right(self. turn) def quit(self): self. screen. bye() def main(self): mainloop() if __name__ == '__main__': etch = Etch()
Placing Turtles • Use onclick method • Place a turtle where the click occurs • onclick is a method of the Screen
Listing 13. 5 (Part 1) from turtle import Turtle, mainloop import random class Turtle. Place: def __init__(self, max. Turtles, h. Wall=200, v. Wall=200): self. big. T = Turtle() self. big. Tscreen = self. big. T. getscreen() self. big. T. shape('turtle') self. turtle. List = [] self. big. Tscreen. onclick(self. place. Turtle) self. big. T. hideturtle() self. num. Turtles = 0 self. max. Turtles = max. Turtles self. h. Wall = h. Wall self. v. Wall = v. Wall self. draw. Field(h. Wall, v. Wall) mainloop() def place. Turtle(self, x, y): new. T = Turtle()
Listing 13. 5 (Part 2) new. Tscreen = new. T. getscreen() new. Tscreen. tracer(0) new. T. up() new. T. goto(x, y) new. T. shape('turtle') new. T. setheading(random. randint(1, 359)) new. Tscreen. tracer(1) self. num. Turtles = self. num. Turtles + 1 self. turtle. List. append(new. T) if self. num. Turtles >= self. max. Turtles: self. big. Tscreen. onclick(None) def draw. Field(self, h. Wall, v. Wall): self. big. Tscreen. tracer(0) self. big. T. up() self. big. T. goto(-h. Wall, -v. Wall) self. big. T. down() for i in range(4): self. big. T. forward(2*h. Wall) self. big. T. left(90) self. big. Tscreen. tracer(1)
Animate the Turtles • Move the turtles • If they hit a wall, turn them around and “bounce” them in the other direction
Listing 13. 6 (Part 1) class Animated. Turtle(Turtle): def __init__(self, h. Wall, v. Wall): super(). __init__() self. scr = self. getscreen() self. xmin = -v. Wall self. xmax = v. Wall self. ymin = -h. Wall self. y. Max = h. Wall self. scr. ontimer(self. __move. One. Step, 100) def __move. One. Step(self): self. __compute. New. Heading() self. forward(5)
Listing 13. 6 (Part 2) self. scr. ontimer(self. __move. One. Step, 100) def __compute. New. Heading(self): xpos, ypos = self. position() old. Head = self. heading() new. Head = old. Head if xpos > 190 or xpos < -190: new. Head = 180 -old. Head if ypos > 190 or ypos < -190: new. Head = 360 -old. Head if new. Head != old. Head: self. setheading(new. Head)
Listing 13. 7 def place. Turtle(self, x, y): new. T = Animated. Turtle(self. h. Wall, self. v. Wall) new. Tscreen = new. T. getscreen() new. Tscreen. tracer(0) new. T. up() new. T. goto(x, y) new. T. shape('turtle') new. T. setheading(random. randint(1, 359)) new. Tscreen. tracer(1) self. num. Turtles = self. num. Turtles + 1 self. turtle. List. append(new. T) if self. num. Turtles >= self. max. Turtles: self. big. Tscreen. onclick(None)
Collisions • If two turtles “collide”, bounce them off one another • Need to have a list of all the turtles. • List must be available to all other turtles • Need it to be Static – One instance, belongs to the class, not an instance – All objects share it
Using a Static Variable Class. Name. methodname Animated. Turtle. all. Turtles. append(self)
Listing 13. 8 (Part 1) class Animated. Turtle(Turtle): all. Turtles = [] def __init__(self, h. Wall, v. Wall): super(). __init__() self. scr = self. getscreen() self. xmin = -v. Wall+10 self. xmax = v. Wall-10 self. ymin = -h. Wall+10 self. ymax = h. Wall-10 self. scr. ontimer(self. __move. One. Step, 100) Animated. Turtle. all. Turtles. append(self) def __move. One. Step(self): self. __compute. New. Heading() self. forward(5) self. __check. Collisions() self. scr. ontimer(self. __move. One. Step, 100) def __compute. New. Heading(self):
Listing 13. 8 (Part 2) xpos, ypos = self. position() old. Head = self. heading() new. Head = old. Head if xpos > self. xmax or xpos < self. xmin: new. Head = 180 -old. Head if ypos > self. ymax or ypos < self. ymin: new. Head = 360 -old. Head if new. Head != old. Head: self. setheading(new. Head) def __check. Collisions(self): for other. T in Animated. Turtle. all. Turtles: if self != other. T: if self. distance(other. T) < 20: temp. Heading = self. heading() self. setheading(other. T. heading()) other. T. setheading(temp. Heading) while self. distance(other. T) < 20: self. forward(1) other. T. forward(1)
Simple Video Game • • • Modeled after space invaders Aliens Laser Cannon, Bombs Aiming the Cannon Bomb moves in the direction it was fired Need to know when a bomb collides with an alien
Figure 13. 3
Figure 13. 4
Listing 13. 9 class Laser. Cannon(Turtle): def __init__(self, xmin, xmax, ymin, ymax): super(). __init__() self. screen = self. getscreen() self. screen. bgcolor('light green') self. screen. setworldcoordinates(xmin, ymin, xmax, ymax) self. screen. onclick(self. aim, 1) self. screen. onkey(self. shoot, "s") self. screen. onkey(self. quit, 'q') def aim(self, x, y): heading = self. towards(x, y) self. setheading(heading) def shoot(self): Bomb(self. heading(), 5) def quit(self): self. screen. bye()
Listing 13. 10 class Bounded. Turtle(Turtle): def __init__(self, speed, xmin=-200, xmax=200, ymin=0, ymax=400): super(). __init__() self. xmin = xmin self. xmax = xmax self. ymin = ymin self. ymax = ymax self. speed = speed def out. Of. Bounds(self): xpos, ypos = self. position() out = False if xpos < self. xmin or xpos > self. xmax: out = True if ypos < self. ymin or ypos > self. ymax: out = True return out def move(self): self. forward(self. speed) if self. out. Of. Bounds(): self. remove() else: self. getscreen(). ontimer(self. move, 200) def remove(self): self. hideturtle()
Listing 13. 11 class Alien(Bounded. Turtle): alien. List = [] @staticmethod def get. Aliens(): return [x for x in Alien. alien. List if x. alive] def __init__(self, speed, xmin, xmax, ymin, ymax): super(). __init__(speed, xmin, xmax, ymin, ymax) self. getscreen(). tracer(0) self. up() if 'Purple. Alien. gif' not in self. getscreen(). getshapes(): self. getscreen(). addshape('Purple. Alien. gif') self. goto(random. randint(xmin-1, xmax-1), ymax-20) self. setheading(random. randint(250, 290)) self. getscreen(). tracer(1) Alien. alient. List = [x for x in Alien. alien. List if x. alive] Alien. alien. List. append(self) self. alive = True self. getscreen(). ontimer(self. move, 200) def remove(self): self. alive = False self. hideturtle()
Listing 13. 12 (Part 1) class Bomb(Bounded. Turtle): def __init__(self, init. Heading, speed): super(). __init__(speed) self. init. Heading = init. Heading self. resizemode('user') self. color('red', 'red') self. shape('circle') self. setheading(init. Heading) self. up() self. turtlesize(. 25) self. getscreen(). ontimer(self. move, 100) def move(self): exploded = False self. forward(self. speed) for i in Alien. get. Aliens():
Listing 13. 12 (Part 2) if self. distance(i) < 5: i. remove() exploded = True if self. out. Of. Bounds() or exploded: self. remove() else: self. getscreen(). ontimer(self. move, 100) def distance(self, other): p 1 = self. position() p 2 = other. position() a = p 1[0]-p 2[0] b = p 1[1]-p 2[1] dist = math. sqrt(a**2 + b**2) return dist
Listing 13. 13 class Alien. Invaders: def __init__(self, xmin, xmax, ymin, ymax): super(). __init__() self. xmin = xmin self. xmax = xmax self. ymin = ymin self. ymax = ymax def play(self): self. main. Win = Laser. Cannon(self. xmin, self. xmax, self. ymin, self. ymax). getscreen() self. main. Win. ontimer(self. add. Alien, 1000) self. main. Win. listen() mainloop() def add. Alien(self): if len(Alien. get. Aliens()) < 7: Alien(1, self. xmin, self. xmax, self. ymin, self. ymax) self. main. Win. ontimer(self. add. Alien, 1000)
- Slides: 34