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

无法在Tkinter窗口嵌入多个Matplotlib动态图的技术问题

问题描述
  • 首次点击“Live Secondary Mission Graphs”按钮时,次级动态图表可嵌入GUI并正常动画,但会生成两个空白的主图表(分别由primary对象和secondary对象创建)。
  • 后续点击“Live Primary Mission Graphs”按钮时,仅重载图表但GUI中无新图表渲染,终端弹出警告:

UserWarning: Animation was deleted without rendering anything. This is most likely not intended. To prevent deletion, assign the Animation to a variable, e.g. anim, that exists until you output the Animation using plt.show() or anim.save().

问题根源
  • 使用plt.gcf()获取当前图表时,会混淆不同对象创建的Figure实例,导致动画绑定到错误的图表。
  • plt.show()会阻塞Tkinter主线程,且在Matplotlib嵌入GUI的场景下无需调用,应依赖Tkinter自身的事件循环。
  • 动画对象(ani/anisecondary)是局部变量,函数执行结束后被垃圾回收,触发警告。
  • plt.cla()会清除当前活跃轴,但未明确指定目标轴,导致误操作其他图表的子轴。
解决方案

1. 绑定动画到自身实例的Figure

在图表类中创建FuncAnimation时,直接使用自身初始化的primaryfig/secondaryfig,而非全局的plt.gcf()。

2. 将动画保存为实例属性

把动画对象赋值给实例变量(如self.ani),避免函数执行结束后被垃圾回收。

3. 移除plt.show()

Matplotlib嵌入Tkinter时,图表渲染由FigureCanvasTkAgg负责,无需调用plt.show()。

4. 明确清除指定子轴

替换plt.cla()为针对每个子轴调用cla(),确保只操作当前图表的轴。


修改后的PrimaryGraphData类

import pandas as pd
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt

class PrimaryGraphData():
    def __init__(self) -> None:
        self.PRIMARYDATASOURCE = "data.csv"
        self.primaryfig, self.primaryaxs = plt.subplots(2, 2)
        self.ani = None  # 初始化动画对象属性

    def animate(self, i):
        data = pd.read_csv(self.PRIMARYDATASOURCE)
        time = data["Time"]
        temp = data["Temperature"]
        press = data["Pressure"]
        alt = data["Altitude"]

        # 明确清除每个子轴
        self.primaryaxs[0, 0].cla()
        self.primaryaxs[0, 1].cla()
        self.primaryaxs[1, 0].cla()
        self.primaryaxs[1, 1].cla()

        # 绘制子图
        self.primaryaxs[0, 0].plot(time, temp, "tab:orange")
        self.primaryaxs[0, 1].plot(time, press, "tab:green")
        self.primaryaxs[1, 0].plot(alt, temp)
        self.primaryaxs[1, 1].plot(time, alt, "tab:blue")

    def plot_primary_graphs(self):
        # 绑定到自身的Figure实例
        self.ani = FuncAnimation(self.primaryfig, func=self.animate, interval=500, frames=50)
        self.primaryfig.tight_layout()

修改后的SecondaryGraphData类

import pandas as pd
from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt

class SecondaryGraphData():
    def __init__(self) -> None:
        self.SECONDARYDATASOURCE = "data2.csv"
        self.secondaryfig, self.secondaryaxs = plt.subplots(2, 2)
        self.ani = None  # 初始化动画对象属性

    def secondaryanimate(self, i):
        data = pd.read_csv(self.SECONDARYDATASOURCE)
        time = data["Time"]
        alt = data["Altitude"]
        TVOC = data["TVOC"]
        eCO2 = data["eCO2"]
        H2 = data["H2"]
        Ethanol = data["Ethanol"]

        # 明确清除每个子轴
        self.secondaryaxs[0, 0].cla()
        self.secondaryaxs[0, 1].cla()
        self.secondaryaxs[1, 0].cla()
        self.secondaryaxs[1, 1].cla()

        # 绘制子图
        self.secondaryaxs[0, 0].plot(time, TVOC, "tab:green")
        self.secondaryaxs[0, 1].plot(alt, eCO2, "tab:green")
        self.secondaryaxs[1, 0].plot(alt, H2)
        self.secondaryaxs[1, 1].plot(time, Ethanol, "tab:blue")

    def plot_secondary_graphs(self):
        # 绑定到自身的Figure实例
        self.ani = FuncAnimation(self.secondaryfig, func=self.secondaryanimate, interval=500, frames=50)
        self.secondaryfig.tight_layout()

修改后的GUI类(App)

import customtkinter as ctk
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from data_processing.primary_graph_data import PrimaryGraphData
from data_processing.secondary_graph_data import SecondaryGraphData

class App(ctk.CTk):
    def __init__(self) -> None:
        super().__init__()

        self.geometry(f"{1100}x{580}")

        self.tabview = ctk.CTkTabview(self, width=1080, height=570)
        self.tabview.pack()
        
        self.tabview.add("Live Primary Mission Graphs")
        self.tabview.add("Live Secondary Mission Graphs")

        # LIVE GRAPHS SETUP
        self.primary = PrimaryGraphData()  # 保存为实例属性,避免被回收
        canvas_primary = FigureCanvasTkAgg(self.primary.primaryfig, self.tabview.tab("Live Primary Mission Graphs"))
        canvas_primary.draw()
        canvas_primary.get_tk_widget().grid(row=0, column=0)

        primary_graphs_button = ctk.CTkButton(
            self.tabview.tab("Live Primary Mission Graphs"), 
            text="Start Primary Mission Graphs", 
            command=self.primary.plot_primary_graphs
        )
        primary_graphs_button.place(x=850, y=190)

        self.secondary = SecondaryGraphData()  # 保存为实例属性,避免被回收
        canvas_secondary = FigureCanvasTkAgg(self.secondary.secondaryfig, self.tabview.tab("Live Secondary Mission Graphs"))
        canvas_secondary.draw()
        canvas_secondary.get_tk_widget().grid(row=0, column=0)

        secondary_graphs_button = ctk.CTkButton(
            self.tabview.tab("Live Secondary Mission Graphs"), 
            text="Start Secondary Mission Graphs", 
            command=self.secondary.plot_secondary_graphs
        )
        secondary_graphs_button.place(x=850, y=190)

if __name__ == "__main__":
    app = App()
    app.mainloop()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 18:16:06