如何修改matplotlib FuncAnimation init_func以删除使用twinx引入的瑕疵?

如何修改matplotlib FuncAnimation init_func以删除使用twinx引入的瑕疵?,animation,matplotlib,Animation,Matplotlib,小结:我创建了一个脚本,用于使用matplotlib动画框架绘制条形图,以支持使用axes.twinx()的多个y轴,这导致绘图上出现无法删除的瑕疵。我想我需要修改动画init_func以考虑多个轴 Python 2.7、matplotlib 2.0.2和Qt后端,以及conda 4.3.18,运行在Ubuntu Linux 17.04上 完整说明:我基于matplotlib条形图演示创建了条形图绘图程序: 我对它进行了修改,以帮助项目中的一些EE,包括以更为程序化的方式重写它,使他们能够更轻

小结:我创建了一个脚本,用于使用matplotlib动画框架绘制条形图,以支持使用axes.twinx()的多个y轴,这导致绘图上出现无法删除的瑕疵。我想我需要修改动画init_func以考虑多个轴

Python 2.7、matplotlib 2.0.2和Qt后端,以及conda 4.3.18,运行在Ubuntu Linux 17.04上

完整说明:我基于matplotlib条形图演示创建了条形图绘图程序:

我对它进行了修改,以帮助项目中的一些EE,包括以更为程序化的方式重写它,使他们能够更轻松地使用它(不确定这有多成功,我们仍在讨论它),添加了对多行的支持,并对其进行了更改,使其不断向左滚动

该脚本包含一个makeChart()函数,该函数在循环中创建一系列Line2D,并将它们添加到列表中,然后将它们返回给调用方:

lines = [] 
for iline in range(0,linesPerPlot):
    lines.append(makeLine(ax, maxt, dt, ymin, ymax, colors[iline % len(colors)]))

return lines
当我添加多行时,在使用blit=True运行时出现了一个工件,我通过添加init_函数消除了这个工件

init函数很简单,如下所示:

def initDisplay(lines):
    """Init display."""
    return lines    
当调用makeChart()并返回正在打印的Line2D列表时,脚本将创建一个lambda,该lambda包装init函数,然后将该函数传递给FuncAnimation:

lines = makeChart(ax, secondsPerPlot, secondsPerSample, linesPerPlot, ymin, ymax)
...
init = lambda: initDisplay(lines)
ani = animation.FuncAnimation(fig, update, emitter, init_func=init, interval=millisPerFrame, blit=True)
其结果非常有效:

下面是一个显示生成的正弦波的示例:

后来,我使用Axes.twinx进行了额外的修改,使每条线都有一个独立的y轴。在修改之后,现在有一个我无法移除的工件,它似乎是第一次渲染第一行时留下的

makeChart()中的新内部循环如下所示:

lines = [] 
lines.append(makeLine(ax, maxt, dt, ymin, ymax, colors[0]))    
for iline in range(1,linesPerPlot):
    twin_ax = ax.twinx()
    lines.append(makeLine(twin_ax, maxt, dt, ymin, ymax, colors[iline % len(colors)]))

return lines
完整代码如下:

我尝试修改init函数,使其返回一个包含行和轴的列表:

def initDisplay(lines, axs):
    """Init display."""
    return lines + axs
以及makeChart()函数,因此它返回了轴以及它返回的艺术家序列中的线条:

lines = []
axs = []

# Add first line, then add subsequent lines sharing the x-axis.  
lines.append(makeLine(ax, maxt, dt, ymin, ymax, colors[0]))   
axs.append(ax) 
for iline in range(1,linesPerPlot):
    twin_ax = ax.twinx()
    lines.append(makeLine(twin_ax, maxt, dt, ymin, ymax, colors[iline % len(colors)]))
    axs.append(twin_ax)

return lines, axs
完整代码如下:

但此版本失败,因为“AttributeError:draw\u只能在缓存渲染的初始绘制之后使用艺术家”


我的想法仍然是init函数需要返回轴和线,但是我需要在调用init函数之前,以某种方式对轴进行初始绘制。有什么我可以预先渲染轴,或者有什么我需要做的吗?

似乎每个轴都会执行blitting。所以这个过程可能是

for ax in all axes:
    get axes background
    draw line
这意味着第一行是来自第二轴的背景的一部分,因此将是每个连续帧的一部分

目前我能想到的唯一解决方案是使线条不可见,直到所有轴的背景都被存储以进行光点显示

line = Line2D(tdata, ydata, color=color, visible=False)
仅在第一次调用
updateLines
之后,才将其再次显示

n = [0]
def updateLines(lines, arrays):
    """Update individual lines and return a sequence of artists to the animator."""
    artists = []
    for iline in range(len(lines)):
        artists.append(updateLine(lines[iline], arrays[iline]))
        if n[0] > 0:
            lines[iline].set_visible(True)
    n[0] += 1
    return artists

完整代码:

导入matplotlib.pyplot作为plt
将matplotlib.animation导入为动画
从matplotlib.lines导入Line2D
输入数学
#初始化脚本常量
ymin=-1.1
ymax=1.1
linesPerPlot=3
samplesPerFrame=1
framesPerSecond=20
第二个sperplot=5
#计算相依常数
samplesPerSecond=samplesPerFrame*framesPerSecond
samplesPerPlot=samplesPerSecond*secondsPerPlot
secondsPerSample=1.0/samplesPerSecond
毫秒帧=1000.0/framesPerSecond
#定义核心功能
def生成线(ax、maxt、dt、ymin、ymax、颜色):
“”“为初始图表创建一个空的Line2D。”“”
n值=整数(四舍五入(最大值/dt))
tdata=[dt*tm表示范围内的tm(N值)]
ydata=[0表示范围内的tm(N值)]
line=Line2D(tdata,ydata,color=color,visible=False)###0:
行[iline]。设置_可见(True)
n[0]+=1
回归艺术家
def排放数据(linesPerPlot、samplesPerFrame):
“”“创建要打印的数据。”“”
nsample=0
尽管如此:
样本=[]对于范围内的i(linesPerPlot)]
例如,范围内的示例(samplesPerFrame):
nsample=nsample+1
对于范围内的iline(linesPerPlot):
pi_增量=(数学pi/(10.0*(iline+1)))
示例[iline].append(math.sin(nsample*pi_增量))
产量样本
#制作图表。
图,ax=plt.子批次()
lines=makeChart(ax、secondsPerPlot、secondsPerPlot、linesPerPlot、ymin、ymax)
#启动动画师。
更新=lambda示例:更新线(行、示例)
发射器=lambda:emitData(linesPerPlot、samplesPerFrame)
init=lambda:initDisplay(行)
ani=animation.FuncAnimation(图,更新,发射器,初始化函数=init,间隔=millisPerFrame,blit=True)

plt.show()
适合我,谢谢!这对我来说不是很直观,是否有对maplotlib项目的建议,或者只是在使用matplotlib之后会变得更加明显?不,这不是直观的,可能不是最好的方法。但这是我唯一能想到的。我还花了两个小时才想出一个主意,通过使第一帧不可见来省去它。我觉得其实在设计动画的时候,从来没有人考虑过这样的问题。
n = [0]
def updateLines(lines, arrays):
    """Update individual lines and return a sequence of artists to the animator."""
    artists = []
    for iline in range(len(lines)):
        artists.append(updateLine(lines[iline], arrays[iline]))
        if n[0] > 0:
            lines[iline].set_visible(True)
    n[0] += 1
    return artists