MacOS下Tkinter应用双击启动报错:Invalid parameter not satisfying: aString != nil
Py2app打包Tkinter应用双击崩溃,命令行启动正常
问题现象
- 通过命令行执行
./org_chart.app/Contents/MacOS/org_chart时,应用运行完全正常 - 双击.app文件启动时,抛出如下异常:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: aString != nil'
应用代码(org_chart_min.py)
import os, shutil import tkinter as tk from tkinter import filedialog, messagebox, Tk, Canvas, Entry, Text, Button, PhotoImage from tkinter import font as tkFont def build_org_chart(): print("im making a chart") return 'Done chart created!' if __name__ == "__main__": window = Tk() window.title("Org Chart Spreadsheet Generator") window.geometry("1012x506") window.configure(bg = "#00403D") # 定义字体属性 my_font = tkFont.Font(family="Montserrat SemiBold", size=16, weight="normal") canvas = Canvas( window, bg = "#00403D", height = 506, width = 1012, bd = 0, highlightthickness = 0, relief = "ridge" ) canvas.place(x = 0, y = 0) canvas.create_rectangle( 308.0, 0.0, 1012.0, 506.0, fill="#FFFFFF", outline="") canvas.create_text( 320.0, 18.0, anchor="nw", text="Org Chart", fill="#000000", font=("Montserrat Bold", 64 * -1) ) window.resizable(False, False) window.mainloop()
Py2app配置文件(setup.py)
import os from setuptools import setup def list_files(directory): base_path = os.path.abspath(directory) paths = [] for root, directories, filenames in os.walk(base_path): for filename in filenames: # 排除macOS的.DS_Store文件 if filename != '.DS_Store': paths.append(os.path.join(root, filename)) return paths # 资源文件夹 assets_folder = 'assets' assets_files = list_files(assets_folder) APP = ['org_chart_min.py'] DATA_FILES = [('assets', assets_files)] OPTIONS = { 'argv_emulation': True, 'packages': ['pandas', 'openpyxl','xlsxwriter'], 'plist': { 'CFBundleName': '_org_chart', 'CFBundleDisplayName': ' Org Chart', 'CFBundleGetInfoString': "Create a spreadsheet that populates our Lucid org chart template", 'CFBundleIdentifier': 'com.yourdomain.orgchart', 'CFBundleVersion': '0.1', 'CFBundleShortVersionString': '0.1', 'NSRequiresAquaSystemAppearance': True }, 'iconfile': 'org_chart.icns', } setup( app=APP, data_files=DATA_FILES, options={'py2app': OPTIONS}, setup_requires=['py2app'], )
调试与解决方向
字体缺失问题
代码中使用的Montserrat SemiBold和Montserrat Bold属于第三方字体,命令行启动时系统能全局检索到字体,但双击.app包时,打包后的应用可能无法正确定位字体文件,触发空字符串异常。- 测试方案:临时改用系统默认字体(如
Arial)替换代码中的字体设置,重新打包验证是否还崩溃。 - 永久解决:将Montserrat字体文件加入
DATA_FILES,打包时一同包含进应用包,或者通过代码指定字体文件的绝对路径。
- 测试方案:临时改用系统默认字体(如
plist配置修正
- 去掉
CFBundleDisplayName开头的空格:当前设置为' Org Chart',空格可能导致系统解析时出现空字符串,改为'Org Chart'。 - 关闭
argv_emulation:Tkinter GUI应用不需要该选项,设置为False,避免模拟命令行参数引发的启动异常。
- 去掉
工作目录差异处理
命令行启动时工作目录是终端当前路径,双击.app时工作目录为系统根目录或应用包内部路径,若代码涉及相对路径(如assets资源),会导致文件找不到。- 修正方案:通过
sys._MEIPASS获取py2app打包后的资源路径,修改代码中资源定位逻辑:import sys def get_asset_path(filename): if hasattr(sys, '_MEIPASS'): return os.path.join(sys._MEIPASS, 'assets', filename) return os.path.join('assets', filename)
- 修正方案:通过
添加全局异常日志
在代码中加入全局异常捕获,将错误信息写入本地日志,方便排查双击启动时的详细错误:import traceback if __name__ == "__main__": try: window = Tk() window.title("Org Chart Spreadsheet Generator") # 剩余原代码... window.mainloop() except Exception as e: with open(os.path.expanduser("~/org_chart_error.log"), "w") as f: f.write(str(e) + "\n") traceback.print_exc(file=f) raise崩溃后查看
~/org_chart_error.log获取完整错误栈。
内容的提问来源于stack exchange,提问作者AlexW
相关产品推荐
相关产品推荐

