使用Matplotlib / Python的动画线图

时间:2018-01-21 17:07:08

标签: python animation matplotlib

我无法将线图设置为动画。

背景:我正在开发一个能够处理模拟网络延迟的程序,我正在尝试绘制延迟图,这样我就可以看到我的程序跟不上来自控制器的命令的负载。< / p>

我设定了我的身材:

# 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)

设置init()animate()功能

# 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,

然后在我的DelayedTask.process()函数中(我测量预期执行和实际执行之间的时间)我将值和索引附加到我的x,y列表中。

delta =  self.time - datetime.now()
lock = threading.Lock()
lock.acquire()
x.append(len(x))
y.append(delta.total_seconds())
lock.release()

最后在我的程序底部,我创建了动画功能。

anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)

不幸的是,图表显示,但数字不会绘制。我在animate()函数中放置了一个断点,并且在增量中填充了列表,但它不会在图表上显示任何行。

以下是完整代码:

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)

1 个答案:

答案 0 :(得分:0)

您的问题出在update函数中。使用声明

line.set_data(x[i], y[i])

每次调用update时,只为您的行分配一个数据点。因此,您无法看到任何行,因为行仅在数据点之间绘制。要解决此问题,请忽略索引:

line.set_data(x, y)

这样就可以绘制您收集的所有数据。