Python Programming in Context Chapter 13. Objectives To write an event driven program To understand and write callback functions To practice with lists.

Post on 03-Jan-2016

233 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

Transcript

Python Programming in Context

Chapter 13

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

Figure 13.1

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

Figure 13.2

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()

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.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

Using a Static Variable

ClassName.methodname

AnimatedTurtle.allTurtles.append(self)

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

Figure 13.3

Figure 13.4

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)

top related