GUI绘图程序在Spyder/CMD正常运行,打包为.exe后异常崩溃
解决cx_Freeze打包Matplotlib-Tkinter程序点击Plot崩溃的问题
首先,我注意到你的代码本身存在几个变量作用域和方法调用的问题,这些问题在IDE环境中可能因环境兼容性没暴露,但打包后会直接导致崩溃;另外cx_Freeze对Matplotlib的打包需要额外配置资源文件,我会一步步帮你解决:
一、修正主程序代码的错误
先修复代码里的逻辑和API调用问题,确保程序本身的健壮性:
import tkinter as tk from tkinter import ttk, messagebox from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk from matplotlib.figure import Figure class DataPlotter: def __init__(self, master): self.master = master # 把父窗口实例保存为类属性,后续方法可直接调用 master.geometry('300x100+200+100') master.title('Data Plotter') master.resizable(False,False) # 将按钮创建逻辑移入__init__,直接绑定实例方法无需lambda ttk.Button(master, text='Plot', command=self.Run).pack(anchor='center') def PlotFigure(self, fig): # 使用保存的父窗口实例创建新窗口 self.Window = tk.Toplevel(self.master) self.canvas = FigureCanvasTkAgg(fig, self.Window) self.canvas.draw() # 替换过时的show(),使用matplotlib推荐的draw()方法 self.frametool = tk.Frame(self.Window) # 替换已弃用的NavigationToolbar2TkAgg为NavigationToolbar2Tk self.toolbar = NavigationToolbar2Tk(self.canvas, self.frametool) self.toolbar.update() self.frametool.pack(side=tk.TOP, fill=tk.BOTH) self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) # 无需重复打包canvas._tkcanvas,get_tk_widget()已返回该组件 def Run(self): try: app = Plotter(x=range(0,10), y=range(0,10)) self.PlotFigure(app.plotting()) # 正确调用类的实例方法 except Exception as ex: messagebox.showinfo('Caught Error','Exception caught: ' + str(ex)) class Plotter(object): def __init__(self, x=None, y=None): self.x = x self.y = y def plotting(self): fig = Figure() ax = fig.add_subplot(111) ax.plot(self.x, self.y, 'o-') return fig def main(): root = tk.Tk() root.grab_set() root.focus() DataPlotter(root) root.mainloop() if __name__ == "__main__": main()
主要修正点:
- 在
DataPlotter.__init__中保存父窗口实例,避免后续方法找不到master变量 - 修正按钮的方法绑定逻辑,直接传递实例方法而非lambda包装
- 替换matplotlib中已弃用的Toolbar类和显示方法,适配新版本API
- 恢复异常捕获逻辑,方便打包后排查运行时错误
二、修正cx_Freeze的setup配置
cx_Freeze打包Matplotlib时,必须手动包含依赖的资源文件,否则会因缺失字体、配置文件导致崩溃,更新后的setup.py如下:
from cx_Freeze import setup, Executable import sys import os import matplotlib # 设置tkinter的库文件路径,请确保路径与你的Anaconda环境一致 os.environ['TCL_LIBRARY'] = r'C:\ProgramData\Anaconda3\tcl\tcl8.6' os.environ['TK_LIBRARY'] = r'C:\ProgramData\Anaconda3\tcl\tk8.6' base = None if sys.platform == 'win32': base = 'Win32GUI' # 隐藏命令行窗口,仅显示GUI # 获取matplotlib的资源文件目录 matplotlib_data_files = matplotlib.get_data_path() build_exe_options = { "packages": ["os", "tkinter", "matplotlib"], "include_files": [ # 复制tkinter的核心dll文件到打包目录 (os.path.join(os.environ['TCL_LIBRARY'], 'tcl86t.dll'), 'tcl86t.dll'), (os.path.join(os.environ['TK_LIBRARY'], 'tk86t.dll'), 'tk86t.dll'), # 包含matplotlib的所有资源文件(字体、配置等) (matplotlib_data_files, 'matplotlib/mpl-data'), ], # 排除不必要的测试模块,减少打包体积 "excludes": ["matplotlib.tests", "numpy.tests"] } executables = [Executable('Data_Plotter.py', base=base)] setup( name = "Data_Plotter", options = {"build_exe": build_exe_options}, version = "1.0", description = "This program plots data and displays the graph in a new window", executables = executables )
主要配置修正点:
- 添加
include_files,手动复制tkinter的dll文件,避免运行时找不到核心库 - 包含matplotlib的
mpl-data目录,该目录包含绘图必需的字体和配置文件 - 排除测试模块,减少打包后的文件体积和冗余内容
三、打包与测试步骤
- 确保激活你的Anaconda环境,安装cx_Freeze:
pip install cx_Freeze - 将修正后的
Data_Plotter.py和setup.py放在同一目录 - 打开命令提示符,切换到该目录,执行打包命令:
python setup.py build - 进入
build目录下的子文件夹,运行生成的Data_Plotter.exe,点击Plot按钮即可正常显示图形
如果仍有问题,可以暂时将base设为None,这样运行exe时会显示命令行窗口,能看到具体的错误信息,方便进一步排查。
内容的提问来源于stack exchange,提问作者Sammyjay
相关产品推荐
相关产品推荐

