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

PySimpleGUI绘图GUI打包exe后循环选择无法更新图表求助

解决PyInstaller打包后PySimpleGUI+Matplotlib无法更新图表的问题

我之前碰到过完全一样的问题,打包后图表无法更新通常和绘图资源未清理、后端兼容性或者代码作用域混乱有关。下面是具体的分析和修复方案:

问题根源拆解

  1. 函数定义位置错误:你把plotting()放在了while循环内部,每次窗口循环都会重新定义这个函数,在Spyder的交互式环境里没问题,但打包成exe后会导致作用域混乱,第二次调用时函数逻辑无法正常执行。
  2. 旧绘图资源未释放:每次点击Show都会创建新的fig对象,但之前的图表窗口没有关闭,打包后Matplotlib的后端会被旧资源阻塞,新图无法渲染。
  3. Matplotlib后端适配问题:PyInstaller打包时,默认的Matplotlib后端可能和PySimpleGUI依赖的Tkinter环境不兼容,导致后续绘图请求无法触发。

分步修复方案

1. 调整函数位置并清理绘图资源

把plotting()函数移到while循环外面,避免重复定义;同时在每次绘图前调用plt.close('all'),强制关闭所有旧图表,释放资源。

2. 指定兼容的Matplotlib后端

在导入Matplotlib前,明确指定使用TkAgg后端,和PySimpleGUI的Tkinter框架对齐:

import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

3. 优化数据处理逻辑

把filtered_df的处理逻辑移到event=="Show"分支内,避免初始状态下values["Liste"]为空时引发错误,同时减少不必要的重复计算。

修正后的完整代码

import PySimpleGUI as sg
import matplotlib
matplotlib.use('TkAgg')  # 指定与Tkinter兼容的后端
import matplotlib.pyplot as plt
import pandas as pd

def plotting(filtered_df, cycle_num):
    def make_format(current, other):
        def format_coord(x, y):
            display_coord = current.transData.transform((x,y))
            inv = other.transData.inverted()
            ax_coord = inv.transform(display_coord)
            coords = [ax_coord, (x, y)]
            return ('Left: {:<40} Right: {:<}' .format(*['({:.3f}, {:.3f})'.format(x, y) for x,y in coords]))
        return format_coord

    plt.close('all')  # 清理所有旧图表资源
    fig, ax = plt.subplots(2,2,sharex=True)
    fig.suptitle(f"Temperature / Pressure Curves for Cycle-Number {cycle_num}", fontsize=16)
    
    # 子图1:t1/p1
    pl1_1 = ax[0,0].plot(filtered_df.index, filtered_df["eBar1"], 'r-',label="eBar1")
    ax2 = ax[0,0].twinx()
    ax2.format_coord = make_format(ax2, ax[0,0])
    pl1_2 = ax2.plot(filtered_df.index, filtered_df["eCelcius1"], 'g-',label="eCelcius1")
    plots1_1 = pl1_1 + pl1_2
    legends_1 = [l.get_label() for l in plots1_1]
    ax[0,0].legend(plots1_1, legends_1, loc="best")
    
    # 子图2:t2/p2
    pl2_1 = ax[0,1].plot(filtered_df.index, filtered_df["eBar2"], 'r-',label="eBar2")
    ax2_2 = ax[0,1].twinx()
    ax2_2.format_coord = make_format(ax2_2, ax[0,1])
    pl2_2 = ax2_2.plot(filtered_df.index, filtered_df["eCelcius2"], 'g-',label="eCelcius2")
    plots2_2 = pl2_1 + pl2_2
    legends_2 = [l.get_label() for l in plots2_2]
    ax[0,1].legend(plots2_2, legends_2, loc="best")
    
    # 子图3:t3/p3
    pl3_1 = ax[1,0].plot(filtered_df.index, filtered_df["eBar3"], 'r-',label="eBar3")
    ax3_2 = ax[1,0].twinx()
    ax3_2.format_coord = make_format(ax3_2, ax[1,0])
    pl3_2 = ax3_2.plot(filtered_df.index, filtered_df["eCelcius3"], 'g-',label="eCelcius3")
    plots3_2 = pl3_1 + pl3_2
    legends_3 = [l.get_label() for l in plots3_2]
    ax[1,0].legend(plots3_2, legends_3, loc="best")
    
    # 子图4:t4/p4
    pl4_1 = ax[1,1].plot(filtered_df.index, filtered_df["eBar4"], 'r-',label="eBar4")
    ax4_2 = ax[1,1].twinx()
    ax4_2.format_coord = make_format(ax4_2, ax[1,1])
    pl4_2 = ax4_2.plot(filtered_df.index, filtered_df["eCelcius4"], 'g-',label="eCelcius4")
    plots4_2 = pl4_1 + pl4_2
    legends_4 = [l4.get_label() for l4 in plots4_2]
    ax[1,1].legend(plots4_2, legends_4, loc="best")
    
    plt.show(block=False)  # 非阻塞显示,避免卡住主窗口

# 主程序入口
excel_path = sg.PopupGetFile("Select CSV file", title="Temperature / Pressure Plots")
if not excel_path:
    sg.popup("No file selected, exiting...")
    exit()

df = pd.read_csv(str(excel_path), sep=";")
df.drop(df.columns[df.columns.str.contains("Unnamed")], axis=1, inplace=True)
liste_cycle_number = list(df["CycleNr"].unique())

layout = [
    [sg.Text("Cycle Number", size=(20,1), justification="left"),
     sg.DropDown(values=liste_cycle_number, size=(10,1), key="Liste", default_value=liste_cycle_number[0])],
    [sg.Button('Show'), sg.Button('Exit')]
]
window = sg.Window('Temperature/Pressure Plots', layout)

while True:
    event, values = window.read()
    
    if event == None or event == 'Exit':
        break
    
    if event == "Show":
        cycle_num = values["Liste"]
        if not cycle_num:
            sg.popup("Please select a cycle number first!")
            continue
        
        # 仅在点击Show时处理数据
        filtered_df = df.loc[df["CycleNr"] == cycle_num]
        usecols = filtered_df.columns[12:]
        filtered_df = filtered_df[usecols].T
        new_columns = ["eBar1","eCelcius1","eBar2","eCelcius2", "eBar3","eCelcius3", "eBar4","eCelcius4"]
        filtered_df.columns = new_columns
        filtered_df = filtered_df.iloc[1:]
        filtered_df = filtered_df.apply(pd.to_numeric, errors='coerce')
        filtered_df.index = pd.to_numeric(filtered_df.index)
        
        # 触发绘图
        plotting(filtered_df, cycle_num)

window.close()
plt.close('all')  # 退出时清理所有残留图表

打包时的额外建议

用PyInstaller打包时,添加以下参数确保Matplotlib资源被正确打包:

pyinstaller --onefile --windowed --hidden-import=matplotlib.backends.backend_tkagg your_script_name.py

参数说明:

  • --onefile:生成单个独立exe文件
  • --windowed:隐藏控制台窗口(调试时可去掉)
  • --hidden-import:强制导入Tk后端,避免打包时遗漏依赖

这样修改后,打包后的exe就能正常更新图表了,每次点击Show都会关闭旧图并渲染新的温压对比子图。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:42:42