Jupyter nbAgg环境下Matplotlib滑块更新时图表不刷新问题
问题根因
滑块拖动后无响应是代码逻辑错误导致,和nbAgg后端本身无关,共有4个问题点:
- 滑块回调函数签名不符合要求:
Slider.on_changed触发回调时会自动传入滑块当前值作为第一个参数,原代码写的def update()没有定义入参,函数触发时直接抛出参数不匹配错误。而nbAgg后端默认不会把这类JS/Python侧的报错直接弹出到页面,视觉上就表现为操作无响应。 - 回调内新建的动画对象
anim_2是函数局部变量,函数执行完成后会被Python垃圾回收机制直接销毁,动画根本没有执行机会。 - 核心绘图逻辑
updateData里的分箱除数divider是硬编码的默认值7,没有读取滑块的实时取值,就算动画能正常运行,分箱数也不会跟着滑块调整变化。 - 原代码重复定义了两次初始
simulation动画对象,第一次定义的实例直接被第二次覆盖,属于冗余无效代码。
修复方法
按以下逻辑调整代码即可:
- 给滑块回调增加入参,用于接收滑块传递的当前值
- 用全局变量存储正在运行的动画实例,避免被垃圾回收
- 修改
updateData逻辑,运行时直接读取滑块当前值作为分箱除数,去掉硬编码 - 滑块触发新动画前,先终止正在运行的旧动画实例,避免多个动画冲突
- 删除重复的初始动画定义
- 原代码设置的帧间隔
interval=1速度过快(1毫秒/帧,10帧动画10毫秒就跑完),人眼无法感知逐帧过程,可按需调大到50-200毫秒区间。
修复后完整可运行代码
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from matplotlib import use as m_use from matplotlib.widgets import Slider m_use('nbAgg') # 生成测试分布数据 x1 = np.random.normal(-2.5, 1, 10000) x2 = np.random.gamma(2, 1.5, 10000) x3 = np.random.exponential(2, 10000)+7 x4 = np.random.uniform(14,20, 10000) fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2,figsize=(7, 5)) plt.subplots_adjust(left=0.1,right=.8) # 全局变量存储当前运行的动画实例,防止被垃圾回收 current_anim = None def updateData(curr): # 清空所有子图 for ax in (ax1, ax2, ax3, ax4): ax.cla() # 实时读取滑块值作为分箱除数 divider = slider.val if curr <= 679: my_b = int(np.around(curr/divider)+3) else: my_b = 100 multi = 100 ax1.hist(x1[:curr*multi], bins=my_b) ax2.hist(x2[:curr*multi], bins=my_b) ax3.hist(x3[:curr*multi], bins=my_b) ax4.hist(x4[:curr*multi], bins=my_b) fig.suptitle(f'Frame {curr+1} on 100') # 初始化垂直滑块 slider_ax = plt.axes([.9, 0.45, 0.01, 0.3]) slider = Slider( ax=slider_ax, label='Divider \nfor bins', valmin=1, valmax=15, valinit=7, orientation='vertical', valfmt='%.0f', track_color='black', facecolor='red', handle_style={'facecolor':'k', 'edgecolor':'#86a2cf', 'size':20} ) def slider_trigger(val): global current_anim # 终止已在运行的旧动画 if current_anim is not None: current_anim.event_source.stop() # 创建新动画并存到全局变量 current_anim = animation.FuncAnimation( fig=fig, func=updateData, frames=20, blit=False, interval=100, repeat=False ) # 触发画布重绘 fig.canvas.draw_idle() # 绑定滑块值变化事件 slider.on_changed(slider_trigger) # 启动页面加载后的初始动画 current_anim = animation.FuncAnimation( fig=fig, func=updateData, frames=10, blit=False, interval=100, repeat=False ) plt.show()
内容的提问来源于stack exchange,提问作者Idr SEK
相关产品推荐
相关产品推荐

