如何让Python费米-狄拉克分布程序实现交互?排查非交互问题
问题排查:Matplotlib动态可视化无法实现交互功能(非Jupyter环境)
我修改了一篇动态可视化示例代码,目标是让它能在Jupyter Notebook以外的环境运行,但当前程序仅能生成动画GIF文件,滑块交互功能完全失效,以下是原代码:
#Based on the example here # https://towardsdatascience.com/intro-to-dynamic-visualization-with-python-animations-and-interactive-plots-f72a7fb69245 # # Import packages import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl import numpy as np import numpy.typing as npt from matplotlib.animation import FuncAnimation from matplotlib.widgets import Slider # Fermi-Dirac Distribution def fermi(E: npt.NDArray[np.float64], E_f: float, T: float) -> npt.NDArray[np.float64]: k_b = 8.617 * (10**-5) # eV/K return 1/(np.exp((E - E_f)/(k_b * T)) + 1) # Animation function def animate(i): x = np.linspace(0, 1, 100) y = fermi(x, 0.5, T[i]) f_d.set_data(x, y) f_d.set_color(colors(i)) temp.set_text(str(int(T[i])) + ' K') temp.set_color(colors(i)) # Update values def update(val): Ef = s_Ef.val T = s_T.val f_d.set_data(x, fermi(x, Ef, T)) fig.canvas.draw_idle() # Create sliders s_Ef = Slider(ax=ax_Ef, label='Fermi Energy ', valmin=0, valmax=1.0, valfmt=' %1.1f eV', facecolor='#cc7000') s_T = Slider(ax=ax_T, label='Temperature ', valmin=100, valmax=1000, valinit=100, valfmt='%i K', facecolor='#cc7000') # General plot parameters mpl.rcParams['font.size'] = 18 mpl.rcParams['axes.linewidth'] = 2 mpl.rcParams['axes.spines.top'] = False mpl.rcParams['axes.spines.right'] = False mpl.rcParams['xtick.major.size'] = 10 mpl.rcParams['xtick.major.width'] = 2 mpl.rcParams['ytick.major.size'] = 10 mpl.rcParams['ytick.major.width'] = 2 # Temperature values T = np.linspace(100, 1000, 10) # Get colors colors = mpl.colormaps['copper'].resampled(8) # Create figure and add axes fig = plt.figure(figsize=(6, 4)) ax = fig.add_subplot(111) # Add legend labels = ['100 K', '200 K', '300 K', '400 K', '500 K', '600 K', '700 K', '800 K', '900 K', '1000 K'] ax.legend(labels, bbox_to_anchor=(1.05, -0.1), loc='lower left', frameon=False, labelspacing=0.2) # Create variable reference to plot f_d, = ax.plot([], [], linewidth=2.5) # Add text annotation and create variable reference temp = ax.text(1, 1, '', ha='right', va='top', fontsize=24) # Create main axis fig.subplots_adjust(bottom=0.2, top=0.75) # Create axes for sliders ax_Ef = fig.add_axes([0.3, 0.85, 0.4, 0.05]) ax_Ef.spines['top'].set_visible(True) ax_Ef.spines['right'].set_visible(True) ax_T = fig.add_axes([0.3, 0.92, 0.4, 0.05]) ax_T.spines['top'].set_visible(True) ax_T.spines['right'].set_visible(True) # Plot default data x = np.linspace(0, 1, 100) Ef_0 = 0.5 T_0 = 100 y = fermi(x, Ef_0, T_0) f_d, = ax.plot(x, y, linewidth=2.5) s_Ef.on_changed(update) s_T.on_changed(update) # Create animation ani = FuncAnimation(fig=fig, func=animate, frames=range(len(T)), interval=500, repeat=True) # Save and show animation ani.save('AnimatedPlot.gif', writer='pillow', fps=2)
问题根源及修复步骤
滑块初始化顺序错误
原代码在创建滑块轴ax_Ef、ax_T之前就初始化了滑块s_Ef、s_T,导致程序找不到对应轴对象直接崩溃。必须先创建滑块轴,再初始化滑块。变量名冲突
全局温度数组T和update函数内的局部变量T = s_T.val重名,覆盖了全局数组,导致动画功能失效。将局部变量重命名为temp_val避免冲突。重复创建绘图对象
原代码先后两次执行f_d, = ax.plot(...),第二次创建的对象覆盖了第一次的引用,导致动画的绘图更新失效。只保留一次初始化即可。缺失交互窗口启动代码
非Jupyter环境下,必须调用plt.show()启动Matplotlib交互窗口,原代码仅保存GIF未启动窗口,因此看不到交互界面。颜色映射调用错误
colors(i)的调用方式错误,Matplotlib colormap需要接收0-1之间的数值,应改为colors(i/len(T))来正确映射颜色梯度。无效图例设置
原代码直接添加温度标签作为图例,但没有对应的绘图系列,图例显示为空。可删除该部分,或调整为对应动画的颜色图例。
修复后的完整代码
import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl import numpy.typing as npt from matplotlib.animation import FuncAnimation from matplotlib.widgets import Slider # Fermi-Dirac Distribution def fermi(E: npt.NDArray[np.float64], E_f: float, T: float) -> npt.NDArray[np.float64]: k_b = 8.617 * (10**-5) # eV/K return 1/(np.exp((E - E_f)/(k_b * T)) + 1) # Animation function def animate(i): x = np.linspace(0, 1, 100) y = fermi(x, 0.5, temp_array[i]) f_d.set_data(x, y) color = colors(i/len(temp_array)) f_d.set_color(color) temp.set_text(f"{int(temp_array[i])} K") temp.set_color(color) # Update values def update(val): Ef = s_Ef.val temp_val = s_T.val y = fermi(x, Ef, temp_val) f_d.set_data(x, y) # 更新文本显示当前滑块温度 temp.set_text(f"{int(temp_val)} K") fig.canvas.draw_idle() # General plot parameters mpl.rcParams['font.size'] = 18 mpl.rcParams['axes.linewidth'] = 2 mpl.rcParams['axes.spines.top'] = False mpl.rcParams['axes.spines.right'] = False mpl.rcParams['xtick.major.size'] = 10 mpl.rcParams['xtick.major.width'] = 2 mpl.rcParams['ytick.major.size'] = 10 mpl.rcParams['ytick.major.width'] = 2 # Temperature values (重命名避免冲突) temp_array = np.linspace(100, 1000, 10) # Get colors colors = mpl.colormaps['copper'].resampled(len(temp_array)) # Create figure and add axes fig = plt.figure(figsize=(6, 4)) ax = fig.add_subplot(111) # 设置坐标轴范围,避免动画/交互时图形偏移 ax.set_xlim(0, 1) ax.set_ylim(0, 1.1) ax.set_xlabel("Energy (eV)") ax.set_ylabel("Fermi-Dirac Probability") # Add text annotation and create variable reference temp = ax.text(1, 1, '', ha='right', va='top', fontsize=24) # Create main axis layout fig.subplots_adjust(bottom=0.2, top=0.75) # Create axes for sliders (先创建轴) ax_Ef = fig.add_axes([0.3, 0.85, 0.4, 0.05]) ax_Ef.spines['top'].set_visible(True) ax_Ef.spines['right'].set_visible(True) ax_T = fig.add_axes([0.3, 0.92, 0.4, 0.05]) ax_T.spines['top'].set_visible(True) ax_T.spines['right'].set_visible(True) # Create sliders (后初始化滑块) s_Ef = Slider(ax=ax_Ef, label='Fermi Energy ', valmin=0, valmax=1.0, valinit=0.5, valfmt=' %1.1f eV', facecolor='#cc7000') s_T = Slider(ax=ax_T, label='Temperature ', valmin=100, valmax=1000, valinit=100, valfmt='%i K', facecolor='#cc7000') # Plot default data and get reference x = np.linspace(0, 1, 100) Ef_0 = 0.5 T_0 = 100 y = fermi(x, Ef_0, T_0) f_d, = ax.plot(x, y, linewidth=2.5) # Bind slider update events s_Ef.on_changed(update) s_T.on_changed(update) # Create animation ani = FuncAnimation(fig=fig, func=animate, frames=range(len(temp_array)), interval=500, repeat=True) # Save animation (可选) ani.save('AnimatedPlot.gif', writer='pillow', fps=2) # 启动交互窗口,非Jupyter环境必须调用 plt.show()
内容的提问来源于stack exchange,提问作者user3877654
相关产品推荐
相关产品推荐

