Upload
maria-holland
View
236
Download
0
Embed Size (px)
Citation preview
Objectives
• To write an event driven program• To understand and 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
Listing 13.1 class EventHandler: def __init__(self): self.queue = [] self.eventKeeper = {}
def addEvent(self,eventName): self.queue.append(eventName)
def registerCallback(self,event,func): self.eventKeeper[event] = func
def run(self): while(True): if len(self.queue) > 0: nextEvent = self.queue.pop(0) self.eventKeeper[nextEvent]() 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
Listing 13.2from threading import Threadimport time
class EventHandler(Thread): def __init__(self): super().__init__() self.queue = [] self.eventKeeper = {}
def addEvent(self,eventName): self.queue.append(eventName)
def registerCallback(self,event,func): self.eventKeeper[event] = func
def run(self): while(True): if len(self.queue) > 0: nextEvent = self.queue.pop(0) callBack = self.eventKeeper[nextEvent] callBack() 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.myT = turtle.Turtle() self.myScreen = turtle.Screen() self.myT.color('blue') self.myT.pensize(2) self.myT.speed(0) self.distance = 5 self.turn = 10 self.myScreen.onkey(self.fwd,"Up") self.myScreen.onkey(self.bkwd,"Down") self.myScreen.onkey(self.left,"Left") self.myScreen.onkey(self.right,"Right") self.myScreen.onkey(self.quit,"q") self.myScreen.listen()
Listing 13.3 (Part 2) def fwd(self): self.myT.forward(self.distance)
def bkwd(self): self.myT.backward(self.distance)
def left(self): self.myT.left(self.turn)
def right(self): self.myT.right(self.turn)
def quit(self): self.myScreen.bye()
def main(self): turtle.mainloop()
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.left5,"Left") self.screen.onkey(self.right5,"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 left5(self): self.left(self.turn)
def right5(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, mainloopimport random
class TurtlePlace: def __init__(self,maxTurtles,hWall=200,vWall=200): self.bigT = Turtle() self.bigTscreen = self.bigT.getscreen() self.bigT.shape('turtle') self.turtleList = [] self.bigTscreen.onclick(self.placeTurtle) self.bigT.hideturtle() self.numTurtles = 0 self.maxTurtles = maxTurtles self.hWall = hWall self.vWall = vWall self.drawField(hWall,vWall) mainloop()
def placeTurtle(self,x,y): newT = Turtle()
Listing 13.5 (Part 2) newTscreen = newT.getscreen() newTscreen.tracer(0) newT.up() newT.goto(x,y) newT.shape('turtle') newT.setheading(random.randint(1,359)) newTscreen.tracer(1) self.numTurtles = self.numTurtles + 1 self.turtleList.append(newT) if self.numTurtles >= self.maxTurtles: self.bigTscreen.onclick(None)
def drawField(self,hWall,vWall): self.bigTscreen.tracer(0) self.bigT.up() self.bigT.goto(-hWall,-vWall) self.bigT.down() for i in range(4): self.bigT.forward(2*hWall) self.bigT.left(90) self.bigTscreen.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 AnimatedTurtle(Turtle): def __init__(self,hWall,vWall): super().__init__() self.scr = self.getscreen() self.xmin = -vWall self.xmax = vWall self.ymin = -hWall self.yMax = hWall self.scr.ontimer(self.__moveOneStep,100)
def __moveOneStep(self): self.__computeNewHeading() self.forward(5)
Listing 13.6 (Part 2) self.scr.ontimer(self.__moveOneStep,100)
def __computeNewHeading(self): xpos,ypos = self.position() oldHead = self.heading() newHead = oldHead
if xpos > 190 or xpos < -190: newHead = 180-oldHead if ypos > 190 or ypos < -190: newHead = 360-oldHead if newHead != oldHead: self.setheading(newHead)
Listing 13.7 def placeTurtle(self,x,y): newT = AnimatedTurtle(self.hWall,self.vWall) newTscreen = newT.getscreen() newTscreen.tracer(0) newT.up() newT.goto(x,y) newT.shape('turtle') newT.setheading(random.randint(1,359)) newTscreen.tracer(1) self.numTurtles = self.numTurtles + 1 self.turtleList.append(newT) if self.numTurtles >= self.maxTurtles: self.bigTscreen.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
Listing 13.8 (Part 1)class AnimatedTurtle(Turtle): allTurtles = [] def __init__(self,hWall,vWall): super().__init__() self.scr = self.getscreen() self.xmin = -vWall+10 self.xmax = vWall-10 self.ymin = -hWall+10 self.ymax = hWall-10 self.scr.ontimer(self.__moveOneStep,100) AnimatedTurtle.allTurtles.append(self)
def __moveOneStep(self): self.__computeNewHeading() self.forward(5) self.__checkCollisions() self.scr.ontimer(self.__moveOneStep,100)
def __computeNewHeading(self):
Listing 13.8 (Part 2) xpos,ypos = self.position() oldHead = self.heading() newHead = oldHead
if xpos > self.xmax or xpos < self.xmin: newHead = 180-oldHead if ypos > self.ymax or ypos < self.ymin: newHead = 360-oldHead if newHead != oldHead: self.setheading(newHead)
def __checkCollisions(self): for otherT in AnimatedTurtle.allTurtles: if self != otherT: if self.distance(otherT) < 20: tempHeading = self.heading() self.setheading(otherT.heading()) otherT.setheading(tempHeading) while self.distance(otherT) < 20: self.forward(1) otherT.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
Listing 13.9class LaserCannon(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.10class BoundedTurtle(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 outOfBounds(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.outOfBounds(): self.remove() else: self.getscreen().ontimer(self.move,200) def remove(self): self.hideturtle()
Listing 13.11class Alien(BoundedTurtle): alienList = []
@staticmethod def getAliens(): return [x for x in Alien.alienList 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 'PurpleAlien.gif' not in self.getscreen().getshapes(): self.getscreen().addshape('PurpleAlien.gif') self.shape('PurpleAlien.gif') self.goto(random.randint(xmin-1,xmax-1),ymax-20) self.setheading(random.randint(250,290)) self.getscreen().tracer(1) Alien.alientList = [x for x in Alien.alienList if x.alive] Alien.alienList.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(BoundedTurtle): def __init__(self, initHeading,speed): super().__init__(speed) self.initHeading = initHeading self.resizemode('user') self.color('red','red') self.shape('circle') self.setheading(initHeading) self.up() self.turtlesize(.25) self.getscreen().ontimer(self.move,100) def move(self): exploded = False self.forward(self.speed) for i in Alien.getAliens():
Listing 13.12 (Part 2) if self.distance(i) < 5: i.remove() exploded = True if self.outOfBounds() or exploded: self.remove() else: self.getscreen().ontimer(self.move,100) def distance(self,other): p1 = self.position() p2 = other.position() a = p1[0]-p2[0] b = p1[1]-p2[1] dist = math.sqrt(a**2 + b**2) return dist
Listing 13.13class AlienInvaders: 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.mainWin = LaserCannon(self.xmin,self.xmax,self.ymin,self.ymax).getscreen() self.mainWin.ontimer(self.addAlien,1000) self.mainWin.listen() mainloop()
def addAlien(self): if len(Alien.getAliens()) < 7: Alien(1,self.xmin,self.xmax,self.ymin,self.ymax) self.mainWin.ontimer(self.addAlien,1000)