如何在Tkinter中清除动画绘图并重新加载新数据动画
解决Tkinter中Matplotlib动画清除后无法重新加载新数据的问题
问题描述
将Arduino连接到压力传感器进行数据采集,已通过代码在Tkinter窗口中实现点击graph按钮加载动画绘图的功能。打开窗口后点击graph按钮动画可正常加载,但点击clear按钮删除当前动画后,再次点击graph无法加载新数据。尝试过plt.clear、frame forget等方法无效;隐藏frame后重新显示仍会出现旧动画;animate函数中data和times变量已更新为新值,但无法重新绘制动画。
修改后的完整代码
import tkinter as tk from tkinter import * import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.animation as animation import serial import datetime root = tk.Tk() root.state('zoomed') width, height = root.winfo_screenwidth(), root.winfo_screenheight() root.geometry('%dx%d+0+0' % (width, height)) root.configure(background='white') root.title("CMJ") # 全局变量统一管理动画、绘图及数据对象 global data, times, ani, fig, ax, canvas times = [] data = [] ani = None fig, ax = None, None canvas = None def init_plot(): """封装绘图环境初始化逻辑""" global fig, ax fig, ax = plt.subplots() ax.set_ylim(0, 40) plt.xticks(rotation=45, ha='right') # 初始化首次绘图环境 init_plot() def animate(i): # 仅在动画第一帧打开串口,避免重复占用 if i == 0: global ser ser = serial.Serial('/dev/cu.usbserial-1420', 115200, timeout=1) raw = ser.readline() dec = raw.decode().strip() # 跳过空数据行 if not dec: return strline = dec.split(" ") try: lines = float(strline[0]) except (IndexError, ValueError): # 捕获无效数据格式异常,避免程序崩溃 return now = datetime.datetime.now() now1 = now.strftime('%M:%S.%f') now = now1[:-5] data.append(lines) times.append(now) # 清空坐标轴并重新绘制,彻底清除旧线条 ax.clear() ax.set_ylim(0, 40) plt.xticks(rotation=45, ha='right') line, = ax.plot(times, data, label="Force", color="blue") # 更新最大值显示标签 if data: x = max(data) most.configure(text=f"{x:.2f}") # 动画最后一帧关闭串口 if i == 19: ser.close() return line, def graph(): global ani, canvas # 停止旧动画并释放资源 if ani is not None: ani.event_source.stop() ani = None # 销毁旧画布,避免叠加显示 if canvas is not None: canvas.get_tk_widget().destroy() # 重置数据和最大值标签 global data, times data = [] times = [] most.configure(text="0") # 重新初始化绘图环境并创建新画布 init_plot() graphframe.pack(pady=20) canvas = FigureCanvasTkAgg(fig, master=graphframe) canvas.get_tk_widget().pack() # 创建新动画实例 ani = animation.FuncAnimation(fig, animate, frames=20, repeat=False, interval=20, cache_frame_data=False) canvas.draw() def clear(): global ani, canvas, data, times # 停止当前动画 if ani is not None: ani.event_source.stop() ani = None # 销毁画布释放资源 if canvas is not None: canvas.get_tk_widget().destroy() canvas = None # 重置数据和标签 data = [] times = [] most.configure(text="0") # 隐藏绘图框架 graphframe.pack_forget() buttonframe = Frame(root) buttonframe.pack() graphframe = Frame(root) button = tk.Button(buttonframe, text='graph', command=graph, width=15) button.pack(side=LEFT, padx=5) button2 = tk.Button(buttonframe, text='clear', command=clear, width=15) button2.pack(side=LEFT, padx=5) most = tk.Label(buttonframe, text="0") most.pack(side=LEFT, padx=5) root.mainloop()
关键修改说明
- 全局对象生命周期管理:将
ani、fig、ax、canvas设为全局变量,在graph和clear函数中直接控制其创建与销毁,避免旧资源残留。 - 串口逻辑优化:仅在动画第一帧打开串口,最后一帧关闭,解决重复打开串口导致的占用问题。
- 绘图重置机制:用
init_plot封装绘图初始化逻辑,每次点击graph都重新创建绘图对象,彻底清除旧绘图数据。 - 动画与画布清理:点击
graph前先停止旧动画、销毁旧画布;clear函数彻底释放所有绘图资源并隐藏框架。 - 数据异常处理:添加串口数据格式校验与异常捕获,避免无效数据导致程序崩溃。
内容的提问来源于stack exchange,提问作者kgall89
相关产品推荐
相关产品推荐

