Nuitka打包Tkinter生成的exe闪退无反馈问题求助
问题描述
直接运行Python脚本时程序完全正常,但使用Nuitka打包生成exe文件后,双击exe控制台会短暂打开后关闭,无任何反应;通过CMD运行exe,程序仅执行完毕却无输出。已尝试添加主循环,问题仍存在。
运行要求:用户桌面需存在名为“Project Fluvius”的文件夹,内含若干PDF文件和一个XLS表格。
代码示例
import os import re import pandas as pd import PyPDF2 import getmac # MAC地址校验用 import requests # 获取在线时间用 from reportlab.lib.pagesizes import A4 from reportlab.pdfgen import canvas from tkinter import Tk, simpledialog, messagebox from datetime import datetime, timedelta import sys # 用于sys.exit(1) import traceback # 日志记录错误用 # 日志文件设置 log_file = open("error_log.txt", "w") sys.stdout = log_file sys.stderr = log_file # 定义路径和全局变量 desktop_path = os.path.join(os.path.expanduser('~'), 'Desktop') project_folder = os.path.join(desktop_path, 'Project XXXXXX') # 注意:此处需改为"Project Fluvius" pdf_folder_path = project_folder # 假设PDF文件在"Project XXXXXX"文件夹内 master_file_path = os.path.join(project_folder, 'lege meetstaat.1.xls') output_pdf_path = os.path.join(project_folder, 'pdf_totals_output.pdf') timestamp_file = os.path.join(project_folder, 'timestamp.txt') # 存储首次运行日期 TRIAL_PERIOD_DAYS = 90 # 90天试用期限 AUTHORIZED_MAC_ADDRESS = "XX:XX:XX:XX:XX:XX" # 替换为授权MAC地址 # 函数定义 def get_mac_address(): """获取当前设备的MAC地址""" mac_address = getmac.get_mac_address() print(f"检测到MAC地址: {mac_address}") return mac_address def check_mac_address(): """校验设备是否授权""" print("MAC地址校验") mac_address = get_mac_address() if mac_address != AUTHORIZED_MAC_ADDRESS: print(f"未授权设备,访问被拒绝。当前MAC地址: {mac_address}") messagebox.showerror("未授权访问", "该设备无权使用此应用。") sys.exit(1) def get_online_time(): """从在线API获取当前时间""" try: print("获取在线时间") response = requests.get('http://worldtimeapi.org/api/ip') if response.status_code == 200: online_time = response.json()['datetime'] print(f"获取到在线时间: {online_time}") return datetime.strptime(online_time, "%Y-%m-%dT%H:%M:%S.%f%z").replace(tzinfo=None) except Exception as e: print(f"获取在线时间出错: {e}") return None # 无法获取在线时间时返回None def store_first_run_date(first_run_date): """将首次运行日期写入文件""" with open(timestamp_file, 'w') as f: f.write(first_run_date.strftime('%Y-%m-%d')) print(f"首次运行日期已存储: {first_run_date.strftime('%Y-%m-%d')}") def load_first_run_date(): """从文件加载首次运行日期""" if os.path.exists(timestamp_file): with open(timestamp_file, 'r') as f: date_str = f.read().strip() print(f"加载到首次运行日期: {date_str}") return datetime.strptime(date_str, '%Y-%m-%d') print("未找到首次运行日期记录。") return None def check_trial_period(): """校验试用期限是否过期""" print("试用期限校验") first_run_date = load_first_run_date() current_time = get_online_time() or datetime.now() if first_run_date is None: first_run_date = current_time store_first_run_date(first_run_date) else: days_since_first_run = (current_time - first_run_date).days print(f"距首次运行已过去: {days_since_first_run}天") if days_since_first_run > TRIAL_PERIOD_DAYS: root = Tk() root.withdraw() messagebox.showinfo("订阅过期", "您的订阅已过期,请续费以继续使用。") sys.exit(1) def try_remove_output_pdf(): """删除已存在的输出PDF文件""" print("检查是否存在已生成的PDF") if os.path.exists(output_pdf_path): try: os.remove(output_pdf_path) print(f"已删除旧PDF文件: {output_pdf_path}") except PermissionError: root = Tk() root.withdraw() messagebox.showerror("文件占用", f"请先关闭文件'{output_pdf_path}'再重试。") raise def extract_text_from_pdf(pdf_path): """从PDF文件中提取文本""" print(f"正在提取PDF文本: {pdf_path}") text = "" try: with open(pdf_path, 'rb') as pdf_file: reader = PyPDF2.PdfReader(pdf_file) for page in range(len(reader.pages)): text += reader.pages[page].extract_text() except Exception as e: print(f"读取PDF出错: {e}") return text def find_codes_in_pdf_text(pdf_text): """从PDF文本中匹配目标编码""" return re.findall(r'\b3\d{6}\b|\b3\d{8}\b', pdf_text) def calculate_total_from_codes(codes, master_data, coefficient): """根据匹配到的编码计算总金额""" print("正在计算总金额") total = 0.0 fixed_coefficient_codes = ['3003597', '3003869', '3003868'] for code in codes: code = str(code.strip()) if code in master_data['Code'].values: price = master_data.loc[master_data['Code'] == code, 'Price'].values[0] if code in fixed_coefficient_codes: total += price else: total += price * coefficient return total def get_calculation_coefficient(): """获取用户输入的计算系数""" root = Tk() root.withdraw() coefficient = simpledialog.askfloat("输入", "请输入计算系数:") return coefficient if coefficient else 1 # 未输入时默认系数为1 def generate_pdf(pdf_totals, output_pdf_path): """生成汇总PDF报告""" print("正在生成PDF报告") c = canvas.Canvas(output_pdf_path, pagesize=A4) width, height = A4 c.setFont("Helvetica-Bold", 16) c.drawCentredString(width / 2, height - 50, "Fluvius totaal meetstaat") c.line(width / 2 - 100, height - 60, width / 2 + 100, height - 60) c.setFont("Helvetica", 12) line_height = 15 margin_left = 40 margin_right = width - 40 total_sum = 0.0 y_position = height - 100 for pdf_file, total in pdf_totals.items(): pdf_title = os.path.splitext(pdf_file)[0] total_sum += total c.drawString(margin_left, y_position, pdf_title) c.drawRightString(margin_right, y_position, f"€{total:.2f}") y_position -= line_height c.setFont("Helvetica-Bold", 12) c.drawCentredString(width / 2, y_position - 20, f"Totaal van meetstaten: €{total_sum:.2f}") c.showPage() c.save() # 主函数 def main(): try: # 打印执行节点用于调试 print("脚本开始执行") # 加载主数据表格 master_data = pd.read_excel(master_file_path, sheet_name='Grondw') master_data = master_data.iloc[:, [1, 25]] master_data.columns = ['Code', 'Price'] master_data['Code'] = master_data['Code'].astype(str).str.replace(r'\.0$', '', regex=True).str.strip() check_mac_address() check_trial_period() try_remove_output_pdf() pdf_files = [f for f in os.listdir(pdf_folder_path) if f.endswith('.pdf') and f != os.path.basename(output_pdf_path)] pdf_totals = {} coefficient = get_calculation_coefficient() for pdf_file in pdf_files: pdf_path = os.path.join(pdf_folder_path, pdf_file) pdf_text = extract_text_from_pdf(pdf_path) codes = find_codes_in_pdf_text(pdf_text) total = calculate_total_from_codes(codes, master_data, coefficient) pdf_totals[pdf_file] = total generate_pdf(pdf_totals, output_pdf_path) print(f"PDF报告已生成: {output_pdf_path}") except Exception as e: traceback.print_exc(file=log_file) sys.exit(1) finally: log_file.close() # 脚本入口 if __name__ == "__main__": main() main_window.mainloop() # 错误:main_window未定义
解决方案
1. 修复未定义变量错误
删除代码最后一行的main_window.mainloop(),代码中从未创建main_window对象,这行代码会触发NameError,导致程序打包后直接崩溃退出。
2. 修正项目文件夹路径
将代码中project_folder定义里的"Project XXXXXX"改为"Project Fluvius",否则程序会找不到指定的文件夹,导致后续文件加载失败:
project_folder = os.path.join(desktop_path, 'Project Fluvius')
3. 调整日志输出路径
将日志文件生成到项目文件夹内,避免因当前工作目录不确定导致日志丢失:
# 替换原日志初始化代码 log_file_path = os.path.join(project_folder, "error_log.txt") log_file = open(log_file_path, "w") sys.stdout = log_file sys.stderr = log_file
4. 优化异常提示
在异常处理中添加弹窗提示,让用户直观知道程序出错,并引导查看日志:
except Exception as e: traceback.print_exc(file=log_file) # 添加弹窗提示 root = Tk() root.withdraw() messagebox.showerror("运行错误", f"程序执行出错,请查看项目文件夹中的error_log.txt获取详情:\n{str(e)}") sys.exit(1)
5. 正确使用Nuitka打包命令
打包时需确保依赖库和资源被正确包含,使用以下命令(替换your_script.py为实际脚本名):
nuitka --standalone --onefile --enable-plugin=tk-inter --include-data-dir="C:\Windows\Fonts"=./fonts your_script.py
--standalone:生成独立运行环境--onefile:生成单个exe文件(可选,若启动慢可去掉此参数生成文件夹)--enable-plugin=tk-inter:确保Tkinter组件被正确打包--include-data-dir:包含系统字体,解决reportlab的Helvetica字体依赖问题
内容的提问来源于stack exchange,提问作者Hitokiri
相关产品推荐
相关产品推荐

