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

Tkinter嵌入Matplotlib时出现额外绘图窗口,如何解决?

问题

使用Tkinter开发GUI并绘制流式数据时,程序会额外弹出一个Matplotlib绘图窗口,相关代码如下:

应用代码

import tkinter as tk
from tkinter import ttk
from random import randint
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


class App(tk.Tk):

    def __init__(self):
        super().__init__()
        self.button_run = tk.Button(master=self, text='run', bg='grey', command=self.run)
        self.button_run.pack()
        self.fig = plt.Figure()
        self.fig, self.axes_dict = plt.subplot_mosaic([['o', 'o'], ['_', '__']])
        self.canvas = FigureCanvasTkAgg(figure=self.fig, master=self)
        self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
        self.canvas.draw_idle()
        self.fig.canvas.flush_events()

    def run(self):
        S = Streamer(parent=self)
        S.start()

流式数据绘制代码

class Streamer:  

    def __init__(self, parent):
        self.parent = parent

    def start(self):
    # plot random data
        for x in range(6):
            self.parent.axes_dict['o'].add_patch(Rectangle((x, randint(x, x*2)), height=0.4, width=0.4))
            self.parent.axes_dict['o'].relim()
            self.parent.axes_dict['o'].autoscale_view(True, True, True)
            self.parent.fig.canvas.draw_idle()
            self.parent.fig.canvas.flush_events()
            plt.pause(0.4)

启动代码

if __name__ == '__main__':
    A = App()
    A.mainloop()

请问这个额外的Matplotlib窗口为何会出现?如何关闭它?


额外窗口出现的原因

  • 你先执行了self.fig = plt.Figure(),这行代码创建了一个独立的Figure对象,由于没有将它绑定到Tkinter画布,Matplotlib的pyplot模块会默认给这个无主Figure生成独立绘图窗口。
  • 流式绘制中的plt.pause(0.4)会触发Matplotlib的全局事件循环,强制显示所有未关联到自定义画布的Figure窗口,直接导致多余窗口弹出。

解决方法

步骤1:移除多余的Figure创建语句

删除self.fig = plt.Figure()这一行,因为后续plt.subplot_mosaic已经会返回新的Figure对象,之前的创建完全多余,还会产生无主窗口。

步骤2:替换plt.pause为Tkinter原生延迟

plt.pause会触发Matplotlib全局状态,改用Tkinter的after()方法实现延迟,既符合Tkinter事件循环逻辑,又不会触发多余窗口。

修改后的完整代码

应用代码调整后

import tkinter as tk
from random import randint
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


class App(tk.Tk):

    def __init__(self):
        super().__init__()
        self.button_run = tk.Button(master=self, text='run', bg='grey', command=self.run)
        self.button_run.pack()
        # 移除多余的plt.Figure()创建
        self.fig, self.axes_dict = plt.subplot_mosaic([['o', 'o'], ['_', '__']])
        self.canvas = FigureCanvasTkAgg(figure=self.fig, master=self)
        self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
        self.canvas.draw_idle()

    def run(self):
        self.streamer = Streamer(parent=self)
        self.streamer.start()

流式数据绘制代码调整后

class Streamer:  

    def __init__(self, parent):
        self.parent = parent
        self.x = 0  # 用实例变量记录循环次数

    def start(self):
        if self.x < 6:
            self.parent.axes_dict['o'].add_patch(Rectangle((self.x, randint(self.x, self.x*2)), height=0.4, width=0.4))
            self.parent.axes_dict['o'].relim()
            self.parent.axes_dict['o'].autoscale_view(True, True, True)
            self.parent.fig.canvas.draw_idle()
            self.x += 1
            # 用Tkinter的after方法实现延迟,避免触发Matplotlib全局事件循环
            self.parent.after(400, self.start)

启动代码不变

if __name__ == '__main__':
    A = App()
    A.mainloop()

这样修改后,多余的Matplotlib窗口就不会再出现,同时流式绘制逻辑符合Tkinter事件循环机制,避免了主进程阻塞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 15:11:13