将matplotlib FuncAnimation嵌入tkinter时出现双绘图问题
解决方案
问题出在plt.show()调用——当你把matplotlib绘图嵌入tkinter窗口时,这个函数会触发matplotlib默认后端创建独立的绘图窗口,而我们已经通过FigureCanvasTkAgg将画布绑定到tkinter组件上,完全不需要这个调用。
按以下步骤修改代码即可解决:
- 删除
plt.show():这是产生额外窗口的直接原因,移除后不会再弹出独立的matplotlib窗口。 - 保留动画对象引用:将
ani1设为实例属性(self.ani1),避免Python垃圾回收机制回收动画对象导致更新停止。 - 修正方法调用:原代码中
plotting_function()应该改为self.plotting_function(),否则无法调用实例方法。 - (可选)指定tkinter后端:在文件开头添加
plt.switch_backend('TkAgg'),确保matplotlib使用tkinter作为绘图后端,避免潜在的后端冲突。
修改后的VisualizationPage1类代码:
import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.animation import FuncAnimation plt.switch_backend('TkAgg') # 可选但推荐 class VisualizationPage1(): def __init__(self, parent, controller): super().__init__(parent, controller) # Define self.body_sublabelframe2 and other frames self.plotting_function() # 修正为实例方法调用 def plotting_function(self): x_val = [] y_val = [] figure1 = plt.figure(figsize=(8, 8), linewidth=5, edgecolor="#04253a", dpi=35) ax1 = figure1.add_subplot(111) canvas_1 = FigureCanvasTkAgg(figure1, self.body_sublabelframe2) canvas_1.get_tk_widget().grid(row=0, column=0) ax1.legend(["Some Legend"]) ax1.set_xlabel("X Label") ax1.set_ylabel("Y Label") ax1.set_title("Plot Title") def animate(i): x_val.append(len(x_val)) y_val.append(2 ** len(y_val)) ax1.cla() ax1.plot(x_val, y_val) ax1.legend(["Some Legend"]) # 每次cla后需要重新添加图例,否则会消失 ax1.set_xlabel("X Label") ax1.set_ylabel("Y Label") ax1.set_title("Plot Title") self.ani1 = FuncAnimation(figure1, animate, interval=100) # 设为实例属性 # 移除plt.show()
优化补充
因为每次animate函数里调用ax1.cla()会清除坐标轴的所有设置(包括图例、标签、标题),所以需要在animate里重新添加这些元素。如果想提升动画性能并避免重复设置样式,可以改用更新线条数据的方式,示例如下:
def plotting_function(self): x_val = [] y_val = [] figure1 = plt.figure(figsize=(8, 8), linewidth=5, edgecolor="#04253a", dpi=35) ax1 = figure1.add_subplot(111) line, = ax1.plot(x_val, y_val) # 保存线条对象 ax1.legend(["Some Legend"]) ax1.set_xlabel("X Label") ax1.set_ylabel("Y Label") ax1.set_title("Plot Title") def animate(i): x_val.append(len(x_val)) y_val.append(2 ** len(y_val)) line.set_data(x_val, y_val) # 直接更新线条数据 ax1.relim() # 更新坐标轴范围 ax1.autoscale_view() # 自动调整视图 self.ani1 = FuncAnimation(figure1, animate, interval=100)
这种方式避免了每次清除和重绘坐标轴,动画性能更好,样式也能保持一致。
内容的提问来源于stack exchange,提问作者Amazonian_panda
相关产品推荐
相关产品推荐

