#!/usr/bin/python # events-example3.py # Demos timer, mouse, and keyboard events from tkinter import * import math import random class Vector(object): """N-dimensional vector class.""" def __init__(self, *components): """Create a vector from its components.""" self.components = components def __add__(self, other): """Add two vectors.""" return self.__class__( *map(lambda xy: xy[0] + xy[1], zip(self.components, other.components)) ) def __sub__(self, other): """Subtract two vectors.""" return self.__class__( *map(lambda xy: xy[0] - xy[1], zip(self.components, other.components)) ) def __mul__(self, other): """Multiply a vector by a constant.""" return self.__class__(*map(lambda x: x * other, self.components)) def __rmul__(self, other): """Multiply a constant by a vector.""" return self * other def __abs__(self): """Return the 2-norm of the vector.""" return math.sqrt(sum(map(lambda x: x * x, self.components))) def __truediv__(self, scalar): """Return self * 1 / scalar.""" return self * (1 / scalar) def __eq__(self, other): return self.components == other.components def init(data): data.squareLeft = 50 data.squareTop = 0 data.squareFill = "red" data.squareSize = 25 data.circleCenters = [ ] data.counter = 0 data.headingRight = True data.headingDown = True data.isPaused = False data.timerDelay = 50 data.speed = Vector(10, 0) data.drag = -1 data.tailAcceleration = Vector(5, 0) data.tailDuration = 2 data.tailMoving = False data.tailThreshold = 5 data.tailMoveCount = 0 def mousePressed(event, data): newCircleCenter = (event.x, event.y) data.circleCenters.append(newCircleCenter) def keyPressed(event, data): if (event.char == "d"): if (len(data.circleCenters) > 0): data.circleCenters.pop(0) else: print("No more circles to delete!") elif (event.char == "p"): data.isPaused = not data.isPaused elif (event.char == "s"): doStep(data) if (event.keysym == "Left"): moveLeft(data) elif (event.keysym == "Right"): moveRight(data) def moveLeft(data): data.squareLeft -= 20 def moveRight(data): data.squareLeft += 20 def moveUp(data): data.squareTop -= 20 def moveDown(data): data.squareTop += 20 def timerFired(data): if (not data.isPaused): doStep(data) def fixDirection(data, theta=None): if theta is None: theta = random.random() * 2 * math.pi costheta = math.cos(theta) sintheta = math.sin(theta) speedx = data.tailAcceleration.components[0] speedy = data.tailAcceleration.components[1] # From en.wikipedia.org/wiki/Rotation_matrix data.tailAcceleration = Vector( speedx * costheta - speedy * sintheta, speedx * sintheta + speedy * costheta ) def doStep(data): data.counter += 1 if random.random() < 1 / 50: fixDirection(data) #print(abs(data.tailAcceleration)) data.speed += data.drag * data.speed / abs(data.speed) if abs(data.speed) < data.tailThreshold: data.tailMoving = True if data.tailMoveCount > data.tailDuration: data.tailMoveCount = 0 data.tailMoving = False if data.tailMoving: data.speed += data.tailAcceleration data.tailMoveCount += 1 data.squareLeft += data.speed.components[0] data.squareTop += data.speed.components[1] if data.squareLeft + data.squareSize > data.width: data.squareLeft = 0 if data.squareLeft < 0: data.squareLeft = data.width - data.squareSize if data.squareTop < 0: data.squareTop = data.height - data.squareSize if data.squareTop + data.squareSize > data.height: data.squareTop = 0 def redrawAll(canvas, data): # draw the square canvas.create_rectangle(data.squareLeft, data.squareTop, data.squareLeft + data.squareSize, data.squareTop + data.squareSize, fill=data.squareFill) # draw the circles for circleCenter in data.circleCenters: (cx, cy) = circleCenter r = 20 canvas.create_oval(cx-r, cy-r, cx+r, cy+r, fill="cyan") # draw the text canvas.create_text(150,20,text="events-example3.py") canvas.create_text(150,40,text="Mouse clicks create circles") canvas.create_text(150,60,text="Pressing 'd' deletes circles") canvas.create_text(150,80,text="Pressing 'p' pauses/unpauses timer") canvas.create_text(150,100,text="Pressing 's' steps the timer once") canvas.create_text(150,120,text="Left arrow moves square left") canvas.create_text(150,140,text="Right arrow moves square right") canvas.create_text(150,160,text="Timer changes color of square") #################################### # use the run function as-is #################################### def run(width=300, height=300): def redrawAllWrapper(canvas, data): canvas.delete(ALL) redrawAll(canvas, data) canvas.update() def mousePressedWrapper(event, canvas, data): mousePressed(event, data) redrawAllWrapper(canvas, data) def keyPressedWrapper(event, canvas, data): keyPressed(event, data) redrawAllWrapper(canvas, data) def timerFiredWrapper(canvas, data): timerFired(data) redrawAllWrapper(canvas, data) # pause, then call timerFired again canvas.after(data.timerDelay, timerFiredWrapper, canvas, data) # Set up data and call init class Struct(object): pass data = Struct() data.width = width data.height = height data.timerDelay = 100 # milliseconds init(data) # create the root and the canvas root = Tk() canvas = Canvas(root, width=data.width, height=data.height) canvas.pack() # set up events root.bind("", lambda event: mousePressedWrapper(event, canvas, data)) root.bind("", lambda event: keyPressedWrapper(event, canvas, data)) timerFiredWrapper(canvas, data) # and launch the app root.mainloop() # blocks until window is closed print("bye!") run(600, 1000)