- Import File (eventBasedAnimation.py)
To run these examples, download
eventBasedAnimation.py and save it
in the same folder where you are editing and running your
animation.
- All Examples (eventBasedAnimationNotesExamples.py)
All these examples are in this file:
eventBasedAnimationNotesExamples.py
- Basic step animation (using drawFn)
import eventBasedAnimation
def simpleStepAnimationDrawFn(canvas, data):
(cx, cy, r) = ((5 * data.step) % data.width, data.height/2, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="blue")
eventBasedAnimation.run(
drawFn=simpleStepAnimationDrawFn,
timerDelay=10
)
- Using initFn and stepFn
import eventBasedAnimation
def customStepAnimationInitFn(data):
data.counter = 1
data.fill = "blue"
def customStepAnimationStepFn(data):
data.counter += 1
if (data.counter % 20 == 0):
data.fill = "cyan" if (data.fill == "blue") else "blue"
def customStepAnimationDrawFn(canvas, data):
(cx, cy, r) = ((5 * data.counter) % data.width, data.height/2, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill=data.fill)
eventBasedAnimation.run(
initFn=customStepAnimationInitFn,
stepFn=customStepAnimationStepFn,
drawFn=customStepAnimationDrawFn,
timerDelay=10
)
- Custom Title and About Text
import eventBasedAnimation
def customTitleAndAboutTextInitFn(data):
data.counter = 1
data.fill = "blue"
data.aboutText = "This is the 'about' text!\nPut anything here!"
data.windowTitle = "Put your window title here!"
def customTitleAndAboutTextStepFn(data):
data.counter += 1
if (data.counter % 20 == 0):
data.fill = "cyan" if (data.fill == "blue") else "blue"
def customTitleAndAboutTextDrawFn(canvas, data):
(cx, cy, r) = ((5 * data.counter) % data.width, data.height/2, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill=data.fill)
canvas.create_text(data.width/2, 20, text="Check out the window title, and")
canvas.create_text(data.width/2, 40, text="press ctrl-a for the about box!")
eventBasedAnimation.run(
initFn=customTitleAndAboutTextInitFn,
stepFn=customTitleAndAboutTextStepFn,
drawFn=customTitleAndAboutTextDrawFn,
timerDelay=10
)
- Basic non-step (non-parametric) animation (using data fields)
import eventBasedAnimation
def nonStepAnimationInitFn(data):
data.x = 0
data.dx = +5
data.aboutText = data.windowTitle = "non-step (non-parametric) animation"
def nonStepAnimationStepFn(data):
data.x += data.dx
if (data.x > data.width):
data.x = data.width
data.dx = -data.dx
elif (data.x < 0):
data.x = 0
data.dx = -data.dx
def nonStepAnimationDrawFn(canvas, data):
(cx, cy, r) = (data.x, data.height/2, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="orange")
eventBasedAnimation.run(
initFn=nonStepAnimationInitFn,
stepFn=nonStepAnimationStepFn,
drawFn=nonStepAnimationDrawFn,
timerDelay=10
)
- Alternate basic non-step animation
import eventBasedAnimation
def nonStepAnimationInitFn(data):
data.x = 0
data.headingRight = True
data.speed = 5
data.aboutText = data.windowTitle = "alternate non-step animation"
def nonStepAnimationStepFn(data):
if (data.headingRight == True):
data.x += data.speed
if (data.x > data.width):
data.x = data.width
data.headingRight = False
else:
data.x -= data.speed
if (data.x < 0):
data.x = 0
data.headingRight = True
def nonStepAnimationDrawFn(canvas, data):
(cx, cy, r) = (data.x, data.height/2, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="magenta")
eventBasedAnimation.run(
initFn=nonStepAnimationInitFn,
stepFn=nonStepAnimationStepFn,
drawFn=nonStepAnimationDrawFn,
timerDelay=10
)
- Using mouseFn
import eventBasedAnimation
def mouseDemoInitFn(data):
(data.x, data.y) = (data.width/2, data.height/2)
data.aboutText = data.windowTitle = "mouseDemo (click to move ball)"
def mouseDemoMouseFn(event, data):
(data.x, data.y) = (event.x, event.y)
def mouseDemoDrawFn(canvas, data):
(cx, cy, r) = (data.x, data.y, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="darkGreen")
eventBasedAnimation.run(
initFn=mouseDemoInitFn,
mouseFn=mouseDemoMouseFn,
drawFn=mouseDemoDrawFn,
)
- Using keyFn
import eventBasedAnimation
def keyDemoInitFn(data):
(data.x, data.y) = (data.width/2, data.height/2)
data.speed = 20
data.aboutText = data.windowTitle = "keyDemo (use arrows to move ball)"
def keyDemoKeyFn(event, data):
if (event.keysym == "Up"): data.y -= data.speed
elif (event.keysym == "Down"): data.y += data.speed
elif (event.keysym == "Left"): data.x -= data.speed
elif (event.keysym == "Right"): data.x += data.speed
def keyDemoDrawFn(canvas, data):
(cx, cy, r) = (data.x, data.y, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="darkGreen")
eventBasedAnimation.run(
initFn=keyDemoInitFn,
keyFn=keyDemoKeyFn,
drawFn=keyDemoDrawFn,
)
- Sweeping ball demo (using mouseFn, keyFn, and stepFn)
import eventBasedAnimation
def sweepingBallInitFn(data):
(data.x, data.y) = (data.width/2, data.height/2)
data.speed = 25
data.aboutText = data.windowTitle = "Sweeping ball demo"
def sweepingBallKeyFn(event, data):
if (event.keysym == "Up"):
data.y = (data.y - data.speed) % data.height
elif (event.keysym == "Down"):
data.y = (data.y + data.speed) % data.height
def sweepingBallMouseFn(event, data):
(data.x, data.y) = (event.x, event.y)
def sweepingBallStepFn(data):
data.x = (data.x + 10) % data.width
def sweepingBallDrawFn(canvas, data):
(cx, cy, r) = (data.x, data.y, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="chartreuse")
canvas.create_text(data.width/2, 20,
text="Try up/down keys + mouse press anywhere")
eventBasedAnimation.run(
initFn=sweepingBallInitFn,
stepFn=sweepingBallStepFn,
mouseFn=sweepingBallMouseFn,
keyFn=sweepingBallKeyFn,
drawFn=sweepingBallDrawFn,
timerDelay=100,
)
- More events demo (motion, drag, and release events)
import eventBasedAnimation
def moreEventsDemoInitFn(data):
(data.x, data.y) = (data.width/2, data.height/2)
data.lastAction = "Waiting"
data.fill = "yellow"
data.aboutText = data.windowTitle = "moreEventsDemo (use mouse + keys)"
def moreEventsDemoMouseMoveFn(event, data):
(data.x, data.y) = (event.x, event.y)
data.lastAction = "Mouse Move at " + str((event.x, event.y))
data.fill = "red"
def moreEventsDemoMouseFn(event, data):
(data.x, data.y) = (event.x, event.y)
data.lastAction = "Mouse Press at " + str((event.x, event.y))
data.fill = "purple"
def moreEventsDemoMouseDragFn(event, data):
(data.x, data.y) = (event.x, event.y)
data.lastAction = "Mouse Drag at " + str((event.x, event.y))
data.fill = "orange"
def moreEventsDemoMouseReleaseFn(event, data):
(data.x, data.y) = (event.x, event.y)
data.lastAction = "Mouse Release at " + str((event.x, event.y))
data.fill = "cyan"
def moreEventsDemoKeyFn(event, data):
data.lastAction = "Key Press: " + event.keysym
def moreEventsDemoKeyReleaseFn(event, data):
data.lastAction = "Key Release: " + event.keysym
def moreEventsDemoDrawFn(canvas, data):
(cx, cy, r) = (data.x, data.y, 20)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill=data.fill)
canvas.create_text(data.width/2, 20,
text=data.lastAction, font="Arial 20 bold")
eventBasedAnimation.run(
initFn=moreEventsDemoInitFn,
mouseFn=moreEventsDemoMouseFn,
mouseMoveFn=moreEventsDemoMouseMoveFn,
mouseDragFn=moreEventsDemoMouseDragFn,
mouseReleaseFn=moreEventsDemoMouseReleaseFn,
keyFn=moreEventsDemoKeyFn,
keyReleaseFn=moreEventsDemoKeyReleaseFn,
drawFn=moreEventsDemoDrawFn,
)
- Print data for debugging
import eventBasedAnimation
def debuggingDemoInitFn(data):
data.foo = "wow"
data.bar = [1,2,3]
data.aboutText = data.windowTitle = "print-data-for-debugging demo"
def debuggingDemoKeyFn(event, data):
print data
def debuggingDemoDrawFn(canvas, data):
canvas.create_text(data.width/2, data.height/2,
text="Press any key to print\ndebugging data to console",
font="Arial 20 bold")
eventBasedAnimation.run(
initFn=debuggingDemoInitFn,
keyFn=debuggingDemoKeyFn,
drawFn=debuggingDemoDrawFn
)
- Error case #1: creating a new data field in a draw fn
import eventBasedAnimation
def drawFnWithModelWriteError1(canvas, data):
data.foo = "this should fail!"
eventBasedAnimation.run(drawFn=drawFnWithModelWriteError1)
- Error case #2: modifying a mutable data field in a draw fn
import eventBasedAnimation
def initFn(data):
data.foo = [42]
def drawFnWithModelWriteError2(canvas, data):
data.foo[0] = "this should fail!"
eventBasedAnimation.run(initFn=initFn, drawFn=drawFnWithModelWriteError2)
- Error case #3: saving canvas to global and trying to draw in an event fn
import eventBasedAnimation
canvas_as_global = None
def stepFnCanvasAsGlobalError(data):
if (canvas_as_global != None):
msg = "This should fail, using a global canvas from a stepFn!"
canvas_as_global.create_text(data.width/2, data.height/2, text=msg)
canvas_as_global.update()
def drawFnCanvasAsGlobalError(canvas, data):
canvas.create_oval(10, 10, 20, 20, fill="blue")
global canvas_as_global
canvas_as_global = canvas
eventBasedAnimation.run(drawFn=drawFnCanvasAsGlobalError,
stepFn=stepFnCanvasAsGlobalError)
- Error case #4: add new canvas to data
import eventBasedAnimation
from Tkinter import *
def initFnWithExtraCanvasError(data):
data.canvas = Canvas() # this should fail!
eventBasedAnimation.run(initFn=initFnWithExtraCanvasError)
- Polygon demo with undo/redo
import eventBasedAnimation
def polygonDemoInitFn(data):
data.points = [ ]
data.undoList = [ ]
data.aboutText = data.windowTitle = "Polygon Demo"
def polygonDemoMouseFn(event, data):
data.points.append((event.x, event.y))
data.undoList = [ ]
def polygonDemoKeyFn(event, data):
if (event.keysym == "u"):
if (len(data.points) > 0):
data.undoList.append(data.points.pop())
elif (event.keysym == "r"):
if (len(data.undoList) > 0):
data.points.append(data.undoList.pop())
elif (event.keysym == "c"):
data.points = [ ]
data.undoList = [ ]
def polygonDemoDrawFn(canvas, data):
if (data.points != []):
canvas.create_polygon(data.points, fill="gold", outline="black")
canvas.create_text(data.width/2, 20,
text="click to add point. u=undo. r=redo. c=clear.")
canvas.create_text(data.width/2, 40,
text=str(len(data.points)) + " point(s) in polygon")
canvas.create_text(data.width/2, 60,
text=str(len(data.undoList)) + " point(s) on undoList")
eventBasedAnimation.run(
initFn=polygonDemoInitFn,
mouseFn=polygonDemoMouseFn,
keyFn=polygonDemoKeyFn,
drawFn=polygonDemoDrawFn,
)
- 2d grid animation (with viewToModel and modelToView)
import eventBasedAnimation
def gridDemoInitFn(data):
data.rows = 4
data.cols = 8
data.margin = 5 # margin around grid
data.ballRow = data.ballCol = 0
data.rectRow = data.rectCol = 0
(data.foodRow, data.foodCol) = (data.rows/4, data.cols/4)
data.score = 0
data.aboutText = data.windowTitle = "Grid demo (click to move food)"
def gridDemoStepFn(data):
# sweep ball left-to-right, top-to-bottom
if (data.ballRow < data.rows-1):
data.ballRow += 1
else:
data.ballRow = 0
data.ballCol = (1 + data.ballCol) % data.cols
# sweep rect clockwise around edges
if ((data.rectRow == 0) and (data.rectCol < data.cols-1)):
data.rectCol += 1
elif ((data.rectCol == data.cols-1) and (data.rectRow < data.rows-1)):
data.rectRow += 1
elif ((data.rectRow == data.rows-1) and (data.rectCol > 0)):
data.rectCol -= 1
else:
data.rectRow -= 1
# check if ball ate food
if ((data.ballRow == data.foodRow) and (data.ballCol == data.foodCol)):
data.score += 1
def pointInGrid(x, y, data):
# return True if (x, y) is inside the grid defined by data.
# data includes rows, cols, width, height, and margin
return ((data.margin <= x <= data.width-data.margin) and
(data.margin <= y <= data.height-data.margin))
def getCell(x, y, data):
# aka "viewToModel"
# return the (row, col) in which (x, y) occurred or None if outside grid.
if (not pointInGrid(x, y, data)):
return None
gridWidth = data.width - 2*data.margin
gridHeight = data.height - 2*data.margin
row = int(round((y - data.margin) * data.rows / gridHeight))
col = int(round((x - data.margin) * data.cols / gridWidth))
# triple-check that we are in bounds
row = min(data.rows-1, max(0, row))
col = min(data.cols-1, max(0, col))
return (row, col)
def getCellBounds(row, col, data):
# aka "modelToView"
# data includes rows, cols, width, height, and margin
# returns (x0, y0, x1, y1) corners/bounding box of given cell in grid
gridWidth = data.width - 2*data.margin
gridHeight = data.height - 2*data.margin
x0 = data.margin + gridWidth * col / data.cols
x1 = data.margin + gridWidth * (col+1) / data.cols
y0 = data.margin + gridHeight * row / data.rows
y1 = data.margin + gridHeight * (row+1) / data.rows
return (x0, y0, x1, y1)
def getCellCircleBounds(row, col, data):
# another "modelToView"
# Instead of bounding box, return (cx, cy, r) of center
# of cell, where r is largest radius that still fits in cell
(x0, y0, x1, y1) = getCellBounds(row, col, data)
(cx, cy, r) = ((x0+x1)/2, (y0+y1)/2, min(x1-x0,y1-y0)/2)
return (cx, cy, r)
def gridDemoMouseFn(event, data):
if (pointInGrid(event.x, event.y, data)):
print getCell(event.x, event.y, data)
(data.foodRow, data.foodCol) = getCell(event.x, event.y, data)
def gridDemoDrawFn(canvas, data):
# draw grid of cells
for row in xrange(data.rows):
for col in xrange(data.cols):
(x0, y0, x1, y1) = getCellBounds(row, col, data)
canvas.create_rectangle(x0, y0, x1, y1)
# draw score
canvas.create_text(data.width/2, data.height/2,
text=str(data.score), font="Arial 64 bold", fill="darkGray")
# draw rect
(x0, y0, x1, y1) = getCellBounds(data.rectRow, data.rectCol, data)
canvas.create_rectangle(x0, y0, x1, y1, fill="cyan")
# draw food (before drawing the ball)
(cx, cy, r) = getCellCircleBounds(data.foodRow, data.foodCol, data)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="salmon")
# draw ball
(cx, cy, r) = getCellCircleBounds(data.ballRow, data.ballCol, data)
canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="green")
eventBasedAnimation.run(
initFn=gridDemoInitFn,
stepFn=gridDemoStepFn,
mouseFn=gridDemoMouseFn,
drawFn=gridDemoDrawFn,
timerDelay=100
)
- Playing card demo (with images)
Note: To run this, you must download
playing-card-gifs.zip, and unzip
this file, and save the resulting folder (playing-card-gifs) at the
same level as your animation code (so images are loaded using the
path "playing-card-gifs/cardName.gif". For hw problems that require
using card images, we will include that folder in this way, so you
do not have to submit the card images with your hw.
import eventBasedAnimation
from Tkinter import *
def playingCardDemoInitFn(data):
data.aboutText = data.windowTitle = "Playing Card Demo"
loadPlayingCardImages(data) # always load images in init!
def loadPlayingCardImages(data):
cards = 55 # cards 1-52, back, joker1, joker2
data.cardImages = [ ]
for card in xrange(cards):
rank = (card%13)+1
suit = "cdhsx"[card/13]
filename = "playing-card-gifs/%s%d.gif" % (suit, rank)
data.cardImages.append(PhotoImage(file=filename))
def getPlayingCardImage(data, rank, suitName):
suitName = suitName[0].lower() # only car about first letter
suitNames = "cdhsx"
assert(1 <= rank <= 13)
assert(suitName in suitNames)
suit = suitNames.index(suitName)
return data.cardImages[13*suit + rank - 1]
def getSpecialPlayingCardImage(data, name):
specialNames = ["back", "joker1", "joker2"]
return getPlayingCardImage(data, specialNames.index(name)+1, "x")
def playingCardDemoDrawFn(canvas, data):
suitNames = ["Clubs", "Diamonds", "Hearts", "Spades", "Xtras"]
suit = (data.step/10) % len(suitNames)
suitName = suitNames[suit]
cards = 3 if (suitName == "Xtras") else 13
margin = 10
(left, top) = (margin, 40)
for rank in xrange(1,cards+1):
image = getPlayingCardImage(data, rank, suitName)
if (left + image.width() > data.width):
(left, top) = (margin, top + image.height() + margin)
canvas.create_image(left, top, anchor=NW, image=image)
left += image.width() + margin
canvas.create_text(data.width/2, 20, text=suitName, font="Arial 28 bold")
eventBasedAnimation.run(
initFn=playingCardDemoInitFn,
drawFn=playingCardDemoDrawFn,
width=420, height=360, timerDelay=250
)
- Background audio demo (and using quitFn)
Note: To run this, you must download
simpleAudio.py, and save it at the
same level as your animation code. For hw problems that require
using background audio, we will include that file in this way, along
with the required audio files, so you
do not have to submit these with your submission.
To run this, you must also download sample.wav,
or provide your own sample.wav file in the same folder. This particular
sample.wav file is from
here.
import eventBasedAnimation
import simpleAudio
def backgroundAudioInitFn(data):
simpleAudio.stopSound() # in case we re-init with ctrl-r
simpleAudio.startSound("sample.wav", async=True, loop=True)
def backgroundAudioDrawFn(canvas, data):
canvas.create_text(data.width/2, data.height/2,
text="You should hear music now", font="Arial 16 bold")
def backgroundAudioQuitFn(data):
simpleAudio.stopSound()
eventBasedAnimation.run(
initFn=backgroundAudioInitFn,
drawFn=backgroundAudioDrawFn,
quitFn=backgroundAudioQuitFn,
)