I'm having trouble getting my line graph to animate.
Background: I'm working on a program that will handle simulating network latency and I'm trying to graph the latency so I can see how well my program is keeping up with the load of commands coming out of the controller.
I've setup my figure:
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0,2), ylim = (-2,2))
line, = ax.plot([], [], lw=2)
setup an init()
and animate()
function
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(x[i], y[i])
return line,
then in my DelayedTask.process()
function (where I measure the time between the intended execution and actual execution) I append the values and index to my x,y lists.
delta = self.time - datetime.now()
lock = threading.Lock()
lock.acquire()
x.append(len(x))
y.append(delta.total_seconds())
lock.release()
finally at the bottom of my program, I create the animation function.
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)
Unfortunately, the graph shows up, but the numbers won't plot. I've put a breakpoint in the animate()
function and in the deltas are filling in the list, but it won't show any lines on the graph.
Here is the full code:
import multiprocessing, requests, threading
import decimal
import random
import time
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from queue import Queue
from multiprocessing.dummy import Pool as ThreadPool
from threading import Thread
from datetime import datetime, timedelta
class WorkQueue:
def __init__(self, threads=6):
self.threads = threads
def process(self, work):
pool = ThreadPool(self.threads)
results = pool.map(DelayedTask.process, work)
pool.close()
pool.join()
class DelayedTask:
def __init__(self, func, delay, message):
print("DelayTask.__init__: {0}".format((func.__name__, delay, message)))
self.func = func
self.time = datetime.now() + timedelta(milliseconds=delay)
self.message = message
def process(self):
delta = self.time - datetime.now()
lock = threading.Lock()
lock.acquire()
x.append(len(x))
y.append(delta.total_seconds())
lock.release()
if delta.total_seconds() > 0.01:
print('DelayTask.Process: Sleeping {0} milliseconds\n'.format(round(delta.total_seconds() * 1000)))
time.sleep(delta.total_seconds())
self.func(self.message)
elif delta.total_seconds() < 0.01 and delta.total_seconds() > 0:
print('DelayTask.Process: Processing with {0} milliseconds remaining\n'.format(round(delta.total_seconds() * 1000)))
self.func(self.message)
else:
print("DelayTask.Process: Processing task: {0} milliseconds late\n".format(round(delta.total_seconds() * -1000)))
self.func(self.message)
return True
def __str__(self):
return str((self.func.__name__, self.time, self.message))
def get(url):
print("Requesting {0}".format(url))
r = requests.get(url=url)
print("get(url): Received response for {0} with Status Code {1}".format(url, r.status_code))
aggregatorq = multiprocessing.Queue()
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0,2), ylim = (-2,2))
line, = ax.plot([], [], lw=2)
x = []
y = []
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(x[i], y[i])
return line,
def collector():
bucket = []
while len(bucket) <= 10:
task = aggregatorq.get()
print("collector: aggregating Tasks\n")
bucket.append(DelayedTask(task['func'], task['delay'], task['message']))
if(len(bucket) == 10):
bucket.sort(key=lambda x: x.time, reverse=False)
firsttask = bucket[0]
firsttime = firsttask.time - datetime.now()
if firsttime.total_seconds() >= 0:
print('collector: Sleeping {0} seconds until first task in bucket\n'.format(firsttime.total_seconds()))
time.sleep(firsttime.total_seconds())
queue = WorkQueue(10)
queue.process(bucket)
bucket.clear()
def controller():
print("Starting Controller\n")
finishtime = datetime.now() + timedelta(seconds=5)
print("controller: Will finish at {0}\n".format(finishtime))
sites = ["att", "google", "hulu", "msn", "yahoo", "gmail"]
while True:
if datetime.now() > finishtime:
print("Controller Finished")
return;
else:
pass
print("{0} remaining in controller..".format(finishtime - datetime.now()))
requestdelay = random.randint(1, 20)
randomsite = random.randint(0, len(sites)-1)
aggregatorq.put({'func': get, 'delay': requestdelay, 'message': 'http://www.{0}.com'.format(sites[randomsite])})
t = threading.Thread(target=controller)
t2 = threading.Thread(target=collector)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
def main():
t.start()
t2.start()
plt.show()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('Interrupted')
t.join()
t2.join()
try:
sys.exit(0)
except SystemExit:
os._exit(0)