#Python 2.7.1 import tkinter import time import threading import random import queue crashTkinter = True """ This class is derived from threading.Thread, so if you create one of these and call the (inherited) start() function, a thread gets created and starts running in our run() function. """ class Track(threading.Thread): """ The function is automagically called when you create a Track. (C++ folk, think "constructor".) We get passed the usual "self" argument that all class functions get, and a target number. """ def __init__(self, target, deliverToQueue): threading.Thread.__init__(self) #Get parent class set up self.setDaemon(True) #Mark this thread as temporary self.target = target #store the target number in self. self.deliverToQueue = deliverToQueue def run(self): #set up the starting point for the track, y & z coordinates y = 0.0001 #height z = 999.0 #range #Set up the initial velocities, with some randomness yVel = 600. + random.random() * 300. zVel = -200. - random.random() * 150. #As long as the track is above ground, sleep for # a bit, then compute the new track position and send # a 1.2.2 message with it. while y > 0: #while above ground.... #How long to sleep, in seconds, between track updates time.sleep(0.02) #realism: >1 Fun: 0.01 #Alter the velocity for gravity and air resistance yVel -= 9.8 #gravity, more or less zVel *= .9998 #air resistance #get the new position y += yVel z += zVel self.deliverToQueue((self.target, z, y)) """ It's not necessary to create a class to represent your application - Python isn't Java. But it's a nice way to organize data, which gets important in large scripts. """ class App: #App's constructor def __init__(self, window): self.me = self #live forever #A source of track numbers self.tracks = 0 """ Here, we create an empty dictionary, which is where we're going to store the last known position for every track we see in arrival_122(), below. The key will be the track number and the value will be a pair of numbers, the track's last y and z coordinates. """ self.trackCoordinates = {} #received track coordinates #Here we set up our display windows. Tkinter is a book in itself, # but we are keeping it simple here. We create a window frame, and # put a label (a place to display text), a canvas (a place to display # tracks), and a launch button. self.theWindow = window #Store off the window handle in case we need it self.frame = tkinter.Frame(window) #create a frame self.frame.pack() #All Tkinter objects need to do this; it sets positioning self.label = tkinter.Label(self.frame, text = "") #Create a label, no text self.label.pack() #... self.graph = tkinter.Canvas(self.frame, width=550, height=333) #Create a canvas self.graph.pack() #... #Create a launch button, and tell it that when clicked, it should call # this class's runLocal() function. self.buttonLocal = tkinter.Button(self.frame, text="Launch", command=self.runLocal) self.buttonLocal.pack() #... self.queue = queue.Queue() #This gets called when the Launch button gets clicked. #Create a new track, and start a thread to animate it. def runLocal(self): self.tracks += 1 #Make a new track number Track(self.tracks, self.arrival_122).start() #Make a Track object, and start a thread in it """ We fetch 3 fields from the message: Track_number, and the y and z position. """ def arrival_122(self, message): global crashTkinter """ Here we get the y and z coordinates from the message we got passed. We scale them by 100 so they will fit on the canvas, and do some extra math to account for the fact that Canvas draws things upside down. ... We're bothering with a tuple here because we want a single thing to store into the dictionary we use to hold positions in, and because the line drawing function we use here is happy to take a tuple for drawing coordinates. """ new_yz = (message[1]/100.+500, 300.-message[2]/100.) tn = message[0] """ Now we cash in on the power of dictionaries. The IN statement here asks the dictionary if it contains anything for the value of tn. If it does, we've stored a coordinate for this track sometime in the past, and we want to draw a line from that position to the new one. """ if tn in self.trackCoordinates: """ We have a previous position, and can get it, using [tn]. Draw on the canvas, a line from that position to the new one. """ """self.queue.put( (self.trackCoordinates[tn][0], self.trackCoordinates[tn][1], new_yz[0], new_yz[1]) )""" self.graph.create_line(self.trackCoordinates[tn][0], self.trackCoordinates[tn][1], new_yz[0], new_yz[1]) """ Now store the new position. If there was an old one for this track number, this replaces the old position with the new one. If there wasn't, this adds it to the dictionary. """ self.trackCoordinates[tn] = new_yz def tickHook(self): while True: try: element = self.queue.get(block=False) got = True except: got = False #expeced finally: if not got: break if element == None: #End marker? return self.graph.create_line(element[0], element[1], element[2], element[3]) self.theWindow.after(20, self.tickHook) #Call here to run the application def go(self): self.theWindow.title("Click Launch over and over, fast") """self.tickHook()""" """ Here we sit, handling mouse clicks and other windowing events, until the application is X'd out of. Then this returns. """ self.theWindow.mainloop() self.queue.put(None) #send the done signal #Create the application, pass it the windowing toolkit, and animate it App(tkinter.Tk()).go()