简体   繁体   English

如何在数据文件更改时动态更新我的 matplotlib 图?

[英]How can I dynamically update my matplotlib figure as the data file changes?

I have a python script that reads in a data file and displays one figure with four plots using the matplotlib library.我有一个 python 脚本,它读入一个数据文件并使用 matplotlib 库显示一个带有四个图的图形。 The data file is being updated every few seconds since it is an output file for a different piece of software that is running concurrently.数据文件每隔几秒钟更新一次,因为它是同时运行的不同软件的输出文件。 I would like the four plots in my matplotlib figure to refresh themselves using the updated data file every 20 seconds.我希望我的 matplotlib 图中的四个图每 20 秒使用更新的数据文件刷新自己。 The way I've implemented this is as follows:我实现的方式如下:

import pylab as pl
import time

pl.ion()
fig = pl.figure()
while True:
    f = open('data.out', 'rb')
    #code to parse data and plot four charts
    ax = fig.add_subplot(2,2,1)
    #...
    ax = fig.add_subplot(2,2,4)
    #...
    pl.draw()
    time.sleep(20)

This works, but I lose functionality of the zoom and pan buttons which normally work if pl.show() is called.这有效,但我失去了缩放和平移按钮的功能,如果调用 pl.show() ,这些按钮通常会起作用。 This is not optimal.这不是最优的。 However, if pl.show() is substituted for pl.draw(), the script no longer updates the plots.但是,如果 pl.show() 替换为 pl.draw(),脚本将不再更新绘图。 Is there a way to dynamically update a plot without completely losing the zoom/pan functionality?有没有办法在不完全失去缩放/平移功能的情况下动态更新绘图?

Your code is a little too vague to know what is going on.你的代码有点太模糊,不知道发生了什么。

I can offer this: You should retain normal functionality if you create your subplots once, saving all the axes objects and then calling show().我可以提供:如果您创建一次子图,保存所有轴对象,然后调用 show(),您应该保留正常功能。

Subsequent changes to those subplots could be done like this:对这些子图的后续更改可以这样完成:

#inside while loop
for i in #subplotlist
    ax[i].clear()    #ax[i] is the axis object of the i'th subplot
    ax[i].plot(#plotstuff)
    ax[i].draw()

The toolbar for zooming and panning can be added by hand if you so desire.如果您愿意,可以手动添加用于缩放和平移的工具栏。

As you are developping a sofware, I supposed you may have a multi-threaded approach.当您正在开发软件时,我想您可能有一种多线程方法。 So in this case using an infinite while loop is a bad idea, like you are holding up your main thread.因此,在这种情况下,使用无限 while 循环是一个坏主意,就像您正在阻止主线程一样。

In addition when it comes to GUI it's also a bad idea to interfere abruptly with GUI internal threads (wxPython for instance) and you should have an event driven design approach in order to not abruptly interrupt other threads (and that will cause the crash of your application).此外,当涉及到 GUI 时,突然干扰 GUI 内部线程(例如 wxPython)也是一个坏主意,您应该采用事件驱动的设计方法,以免突然中断其他线程(这会导致您的程序崩溃)应用)。

The use of a timer will do the job.使用计时器将完成这项工作。

A timer would do these actions in the following script :计时器将在以下脚本中执行这些操作:

1/ call a function to clear previous artist 1/ 调用一个函数来清除以前的艺术家

2 / replot the data 2 / 重新绘制数据

3/ apply changes to canvas 3/ 将更改应用到画布

4/ create another identical timer in the following design way : a timer who calls another identical timer after doing its job 4/以下列设计方式创建另一个相同的定时器:一个在完成工作后调用另一个相同定时器的定时器

Like I do not have access to your datas, I created a random data provider for the illustration.就像我无法访问您的数据一样,我为插图创建了一个随机数据提供程序。 The defined variable delay_repeat allows you to program in seconds the refresh.定义的变量 delay_repeat 允许您在几秒钟内对刷新进行编程。

import pylab as pl
import random
from threading import Timer

def dataprovider():
    return [random.randint(0, 8) for i in range(8)]

def random_col():
    return ['blue', 'red', 'green', 'orange'][random.randint(0,3)]

# .... #
fig = pl.figure()
axes = [fig.add_subplot(2,2,i) for i in range(1,5)]
paths = [ax.scatter(x=dataprovider(), y=dataprovider(), marker = '+', c=random_col()) for ax in axes]
# .... #

def clear_arts(paths, all_arts=-1):
    if all_arts < 0:
        all_arts = len(paths)
    for path in paths[:all_arts]:
        path.remove()   

def refresh_arts(paths, delay_repeat):
    # 1 - clear previous artists
    clear_arts(paths,all_arts=-1)
    # 2 - Get artists paths for cleaning
    paths = [ax.scatter(x=dataprovider(), y=dataprovider(), marker = '+', c=random_col()) for ax in axes]
    # 3 - Apply changes
    fig.canvas.draw_idle()    
    # 4 - Create another timer
    Timer(delay_repeat, refresh_arts, (paths, delay_repeat)).start()

# 4- Create a timer that will run function with arguments args and keyword arguments kwargs, 
# after interval seconds have passed.
delay_repeat = 2
Timer(delay_repeat, refresh_arts, (paths, delay_repeat)).start()

# print("process continues here")

pl.show()

You can do it like this.你可以这样做。 It accept x,y as list and output a scatter plot plus a linear trend on the same plot.它接受 x,y 作为列表并在同一图上输出散点图和线性趋势。

from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
    
def live_plot(x, y, figsize=(7,5), title=''):
    clear_output(wait=True)
    plt.figure(figsize=figsize)
    plt.xlim(0, training_steps)
    plt.ylim(0, 100)
    x= [float(i) for i in x]
    y= [float(i) for i in y]
    
    if len(x) > 1:
        plt.scatter(x,y, label='axis y', color='k') 
        m, b = np.polyfit(x, y, 1)
        plt.plot(x, [x * m for x in x] + b)

    plt.title(title)
    plt.grid(True)
    plt.xlabel('axis x')
    plt.ylabel('axis y')
    plt.show();

you just need to call live_plot(x, y) inside a loop.你只需要在循环内调用live_plot(x, y) here's how it looks:这是它的外观: 在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM