PySimpleGUI更新Matplotlib图表时画布逐次变大问题求解
问题根因
窗口逐次变大的核心诱因有三点:
- 每次更新绘图时都通过
plt.gcf()获取全局画布,matplotlib在无活跃画布时会自动创建新的默认尺寸画布,反复往Tk容器中嵌入新画布控件时,控件会自动计算可用空间,导致尺寸逐次累加变大。 - 旧画布清理逻辑不完整,仅用
forget()把控件从布局中隐藏,没有彻底销毁控件实例,残留的尺寸属性会干扰新画布的渲染计算。 - 事件循环未做拦截,窗口初始化、关闭等非更新事件也会触发绘图逻辑,进一步放大尺寸异常。
修复方案
- 初始化阶段就创建固定尺寸的matplotlib画布和绘图轴,后续更新仅清空轴内曲线重绘,全程不新建画布实例。
- 补全旧控件销毁逻辑,清理画布时直接调用
destroy()彻底释放控件实例,不要仅用forget()隐藏。 - 给事件循环加判断逻辑,仅滑块拖动、Plot按钮点击时触发重绘,窗口关闭事件直接退出循环。
- 可手动固定绘图轴的坐标范围,避免重绘时视图自动缩放导致的显示跳动。
修复后完整代码
import numpy as np import PySimpleGUI as sg from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure # 初始化固定尺寸的画布,全程复用不新建 fig = Figure(figsize=(4, 4), dpi=100) ax = fig.add_subplot(111) def fun(ax, nu): ax.clear() # 每次重绘前清空旧曲线 x = np.arange(1, 10, 0.1) ax.plot(x, nu * x) ax.set_ylim(0, 10) # 固定Y轴范围,避免视图跳动 sg.theme('SandyBeach') # 参数输入窗口 param_layout = [ [sg.Text('nu_min', size=(15, 1)), sg.InputText(0.2, key='-nu_min-')], [sg.Text('nu_max', size=(15, 1)), sg.InputText(1, key='-nu_max-')], [sg.Text('nu_step', size=(15, 1)), sg.InputText(0.1, key='-nu_step-')], [sg.Submit('Run'), sg.Cancel()] ] param_window = sg.Window('参数设置', param_layout) event, values = param_window.read() param_window.close() if event in (sg.WIN_CLOSED, 'Cancel'): exit() nu_min = float(values['-nu_min-']) nu_max = float(values['-nu_max-']) nu_step = float(values['-nu_step-']) def draw_figure(canvas, figure): figure_canvas_agg = FigureCanvasTkAgg(figure, canvas) figure_canvas_agg.draw() figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1) return figure_canvas_agg # 绘图窗口布局 figure_w, figure_h = 400, 400 main_layout = [ [sg.Text('滑块控制函数绘图', justification='center', relief=sg.RELIEF_SUNKEN, expand_x=True, font='Courier 18')], [sg.Button('Plot'), sg.Cancel(), sg.Button('Popup')], [sg.Canvas(size=(figure_w, figure_h), key='-CANVAS-')], [sg.Text('nu', font='Courier 14'), sg.Slider((nu_min, nu_max), default_value=nu_min, resolution=nu_step, orientation='h', enable_events=True, key='-slider_nu-', expand_x=True)] ] main_window = sg.Window('绘图界面', main_layout, finalize=True) # 首次渲染画布 figure_agg = draw_figure(main_window['-CANVAS-'].TKCanvas, fig) fun(ax, nu_min) figure_agg.draw() # 事件循环 while True: event, values = main_window.read() if event in (sg.WIN_CLOSED, 'Cancel'): break # 仅滑块拖动/Plot按钮触发重绘 if event in ('-slider_nu-', 'Plot'): fun(ax, values['-slider_nu-']) figure_agg.draw() main_window.close()
内容的提问来源于stack exchange,提问作者khaled belkadhi
相关产品推荐
相关产品推荐

