You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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()

关键修改说明

  1. 全局对象生命周期管理:将ani、fig、ax、canvas设为全局变量,在graph和clear函数中直接控制其创建与销毁,避免旧资源残留。
  2. 串口逻辑优化:仅在动画第一帧打开串口,最后一帧关闭,解决重复打开串口导致的占用问题。
  3. 绘图重置机制:用init_plot封装绘图初始化逻辑,每次点击graph都重新创建绘图对象,彻底清除旧绘图数据。
  4. 动画与画布清理:点击graph前先停止旧动画、销毁旧画布;clear函数彻底释放所有绘图资源并隐藏框架。
  5. 数据异常处理:添加串口数据格式校验与异常捕获,避免无效数据导致程序崩溃。

内容的提问来源于stack exchange,提问作者kgall89

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 15:05:15