python - 更新 plt.fill_ Between() 的 y 数据

标签 python matplotlib matplotlib-widget

因此,我想设置 fill Between 对象的 2 y 数据,相当于使用 some_line.set_ydata(new_y)< 设置 Line2D 的 (1) y 数据.

天真的尝试会导致此错误:

AttributeError: 'PolyCollection' object has no attribute 'set_ydata'. 

有没有办法直接访问和设置PolyCollection的数据?

下面是 matplotlib's Slider demo第 21 行和第 55 行添加的部分显示了我想要解决的问题。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button


# The parametrized function to be plotted
def f(t, amplitude, frequency):
    return amplitude * np.sin(2 * np.pi * frequency * t)

t = np.linspace(0, 1, 1000)

# Define initial parameters
init_amplitude = 5
init_frequency = 3

# Create the figure and the line that we will manipulate
fig, ax = plt.subplots()
line, = plt.plot(t, f(t, init_amplitude, init_frequency), lw=2)

# added part
fill = plt.fill_between(t, 0, f(t, init_amplitude, init_frequency))

ax.set_xlabel('Time [s]')

# adjust the main plot to make room for the sliders
plt.subplots_adjust(left=0.25, bottom=0.25)

# Make a horizontal slider to control the frequency.
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
    ax=axfreq,
    label='Frequency [Hz]',
    valmin=0.1,
    valmax=30,
    valinit=init_frequency,
)

# Make a vertically oriented slider to control the amplitude
axamp = plt.axes([0.1, 0.25, 0.0225, 0.63])
amp_slider = Slider(
    ax=axamp,
    label="Amplitude",
    valmin=0,
    valmax=10,
    valinit=init_amplitude,
    orientation="vertical"
)


# The function to be called anytime a slider's value changes
def update(val):
    line.set_ydata(f(t, amp_slider.val, freq_slider.val))
    
    # added part 
    fill.set_ydata(0, f(t, amp_slider.val, freq_slider.val))
    
    fig.canvas.draw_idle()


# register the update function with each slider
freq_slider.on_changed(update)
amp_slider.on_changed(update)

# Create a `matplotlib.widgets.Button` to reset the sliders to initial values.
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', hovercolor='0.975')


def reset(event):
    freq_slider.reset()
    amp_slider.reset()
button.on_clicked(reset)


plt.show()

最佳答案

Matplotlib 的 fill_ Between 不返回 Line2D对象但一个 PolyCollection 。因此,我们必须更新包含路径的顶点,计算新顶点的最简单方法是绘制一个不可见的虚拟对象并在修改后的更新函数中提取顶点:

def update(val): 
        #optional preventing autoscaling of y-axis 
        ax.autoscale(False)
        #create invisible dummy object to extract the vertices 
        dummy = ax.fill_between(t, 0, f(t, amp_slider.val, freq_slider.val), alpha=0)
        dp = dummy.get_paths()[0]
        dummy.remove()
        #update the vertices of the PolyCollection
        fill.set_paths([dp.vertices])
        
        fig.canvas.draw_idle()

除了修改的 update 函数之外,我只删除了线图,因为如果您有填充的路径对象,它不会向您的绘图添加任何内容。 MCVE代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button


def f(t, amplitude, frequency):
    return amplitude * np.sin(2 * np.pi * frequency * t)

t = np.linspace(0, 1, 1000)

# Define initial parameters
init_amplitude = 5
init_frequency = 3

# Create the figure and the line that we will manipulate
fig, ax = plt.subplots()

# added part
fill = plt.fill_between(t, 0, f(t, init_amplitude, init_frequency))

ax.set_xlabel('Time [s]')

# adjust the main plot to make room for the sliders
plt.subplots_adjust(left=0.25, bottom=0.25)

# Make a horizontal slider to control the frequency.
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
    ax=axfreq,
    label='Frequency [Hz]',
    valmin=0.1,
    valmax=30,
    valinit=init_frequency,
)

# Make a vertically oriented slider to control the amplitude
axamp = plt.axes([0.1, 0.25, 0.0225, 0.63])
amp_slider = Slider(
    ax=axamp,
    label="Amplitude",
    valmin=0,
    valmax=10,
    valinit=init_amplitude,
    orientation="vertical"
)


# The function to be called anytime a slider's value changes
def update(val): 
    #optional preventing autoscaling of y-axis 
    ax.autoscale(False)
    #create invisible dummy object to extract the vertices 
    dummy = ax.fill_between(t, 0, f(t, amp_slider.val, freq_slider.val), alpha=0)
    dp = dummy.get_paths()[0]
    dummy.remove()
    #update the vertices of the PolyCollection
    fill.set_paths([dp.vertices])
    
    fig.canvas.draw_idle()


# register the update function with each slider
freq_slider.on_changed(update)
amp_slider.on_changed(update)

# Create a `matplotlib.widgets.Button` to reset the sliders to initial values.
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', hovercolor='0.975')


def reset(event):
    freq_slider.reset()
    amp_slider.reset()
button.on_clicked(reset)


plt.show()

关于python - 更新 plt.fill_ Between() 的 y 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71037086/

相关文章:

python Pandas : Passing Multiple Functions to agg() with Arguments

python - 如何在 RadioButtons 小部件上获得较窄的宽度和较大的高度,并且仍然具有不重叠的圆形单选按钮?

python - 当 tkinter 窗口关闭时,python 程序并未结束

python - 带有图例的 matplotlib 散点图

python - 在 Python/matplotlib 中使用 xaxis_date() 手动设置 xticks

python - Matplotlib:如何显示已关闭的图形

python - matplotlib, plt.show() 在不同的方法 = 没有 on_clicked

python - 尝试在 python 中的环内生成随机 x,y 坐标

python - 带有 Python 3.3 的蓝牙服务器

python - 使用python PIL将图像转换为base64