summaryrefslogtreecommitdiffstats
path: root/events-example3.py
diff options
context:
space:
mode:
authorRaymond Hogenson <rayhogenson@openmailbox.org>2016-10-09 23:26:42 -0400
committerRaymond Hogenson <rayhogenson@openmailbox.org>2016-10-09 23:26:42 -0400
commit9d8c8fa4251081b43610ad0805d14cc26e4b4e19 (patch)
treea7c964f7025d54f2fd33273bd152e2e992d29dfb /events-example3.py
parentf05ccbe166fd2501be3502f81f0562e73291fa04 (diff)
downloadbridge-9d8c8fa4251081b43610ad0805d14cc26e4b4e19.tar.zst
Merge
Diffstat (limited to 'events-example3.py')
-rwxr-xr-xevents-example3.py206
1 files changed, 206 insertions, 0 deletions
diff --git a/events-example3.py b/events-example3.py
new file mode 100755
index 0000000..d21877b
--- /dev/null
+++ b/events-example3.py
@@ -0,0 +1,206 @@
+#!/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("<Button-1>", lambda event:
+ mousePressedWrapper(event, canvas, data))
+ root.bind("<Key>", 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)