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 ID: 918787
Download Presentation The PPT/PDF document "Python Programming in Context" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Python Programming in Context
Chapter
13
Slide2Objectives
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
Slide3Event 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
Slide4Figure 13.1
Slide5Listing 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')
Slide6Multithreading 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
Slide7Figure 13.2
Slide8Listing 13.2
from threading import Thread
import 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)
Slide9Etch-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
Slide10Listing 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
()
Slide11Listing 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
()
Slide12Redesign Etch-A-Sketch
Use Inheritance
Class Etch is-a Turtle
Slide13Listing 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
()
Slide14Listing 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()
Slide15Placing Turtles
Use
onclick
method
Place a turtle where the click occurs
onclick
is a method of the Screen
Slide16Listing 13.5 (Part 1)
from turtle import Turtle,
mainloop
import 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()
Slide17Listing 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)
Slide18Animate the Turtles
Move the turtles
If they hit a wall, turn them around and “bounce” them in the other direction
Slide19Listing 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)
Slide20Listing 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
)
Slide21Listing 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
)
Slide22Collisions
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
Slide23Using a Static Variable
ClassName.methodname
AnimatedTurtle.allTurtles.append
(self
)
Slide24Listing 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
):
Slide25Listing 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)
Slide26Simple 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
Slide27Figure 13.3
Slide28Figure 13.4
Slide29Listing 13.9
class
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
()
Slide30Listing 13.10
class
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
()
Slide31Listing 13.11
class
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
()
Slide32Listing 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
():
Slide33Listing 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
Slide34Listing 13.13
class
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)