#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ PDF书签合并工具 - 自动打包脚本 使用PyInstaller将Python程序打包成独立的exe文件 """ import os import sys import subprocess import shutil from pathlib import Path def check_pyinstaller(): """检查是否安装了PyInstaller""" try: subprocess.run([sys.executable, "-c", "import PyInstaller"], check=True, capture_output=True) print("✅ PyInstaller 已安装") return True except subprocess.CalledProcessError: print("❌ PyInstaller 未安装") return False def install_pyinstaller(): """安装PyInstaller""" print("正在安装 PyInstaller...") try: subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], check=True) print("✅ PyInstaller 安装成功") return True except subprocess.CalledProcessError as ex: print(f"❌ PyInstaller 安装失败: {ex}") return False def clean_build(): """清理之前的构建""" print("🧹 清理之前的构建...") folders_to_remove = ['build', 'dist', '__pycache__'] files_to_remove = ['SlideCombine.spec'] for folder in folders_to_remove: if os.path.exists(folder): shutil.rmtree(folder) print(f" 已删除文件夹: {folder}") for file in files_to_remove: if os.path.exists(file): os.remove(file) print(f" 已删除文件: {file}") def create_spec_file(): """创建PyInstaller配置文件""" spec_content = ''' # -*- mode: python ; coding: utf-8 -*- block_cipher = None a = Analysis( ['slide_combine.py'], pathex=[], binaries=[], datas=[], hiddenimports=[], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='SlideCombine', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=False, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, icon='app.ico', # 图标文件(如果存在) version='version_info.txt' # 版本信息(如果存在) ) ''' with open('SlideCombine.spec', 'w', encoding='utf-8') as f: f.write(spec_content) print("✅ 已创建 SlideCombine.spec 配置文件") def create_version_info(): """创建版本信息文件""" version_info = ''' # UTF-8 # # For more details about fixed file info 'ffi' see: # http://msdn.microsoft.com/en-us/library/ms646997.aspx VSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. filevers=(2,0,0,0), prodvers=(2,0,0,0), # Contains a bitmask that specifies the valid bits 'flags'r mask=0x3f, # Contains a bitmask that specifies the Boolean attributes of the file. flags=0x0, # The operating system for which this file was designed. # 0x4 - NT and there is no need to change it. OS=0x4, # The general type of file. # 0x1 - the file is an application. fileType=0x1, # The function of the file. # 0x0 - the function is not defined for this fileType subtype=0x0, # Creation date and time stamp. date=(0, 0) ), kids=[ StringFileInfo( [ StringTable( u'080404B0', [StringStruct(u'CompanyName', u'PDF书签合并工具'), StringStruct(u'FileDescription', u'PDF书签合并工具 - 用于从PDF文件夹中提取书签信息,与TXT元数据文件合并'), StringStruct(u'FileVersion', u'2.0.0.0'), StringStruct(u'InternalName', u'SlideCombine'), StringStruct(u'LegalCopyright', u'Copyright (C) 2024'), StringStruct(u'OriginalFilename', u'SlideCombine.exe'), StringStruct(u'ProductName', u'PDF书签合并工具'), StringStruct(u'ProductVersion', u'2.0.0.0')]) ]), VarFileInfo([VarStruct(u'Translation', [2052, 1200])]) ] ) ''' with open('version_info.txt', 'w', encoding='utf-8') as f: f.write(version_info) print("✅ 已创建 version_info.txt 版本信息文件") def build_exe(): """构建exe文件""" print("🔨 开始构建 exe 文件...") try: # 构建命令 cmd = [ sys.executable, '-m', 'PyInstaller', '--clean', '--onefile', '--windowed', '--name=SlideCombine', 'slide_combine.py' ] # 如果有图标文件,添加图标参数 if os.path.exists('app.ico'): cmd.insert(-1, '--icon=app.ico') print("✅ 已找到图标文件: app.ico") else: print("⚠️ 未找到图标文件,将使用默认图标") print(f"执行命令: {' '.join(cmd)}") result = subprocess.run(cmd, check=True, capture_output=True, text=True) print("✅ 构建成功!") return True except subprocess.CalledProcessError as ex: print(f"❌ 构建失败: {ex}") if ex.stderr: print(f"错误信息: {ex.stderr}") return False def create_release_package(): """创建发布包""" print("📦 创建发布包...") # 检查dist文件夹 dist_path = Path('dist') if not dist_path.exists(): print("❌ 未找到 dist 文件夹") return False # 查找生成的exe文件 exe_files = list(dist_path.glob('*.exe')) if not exe_files: print("❌ 未找到生成的 exe 文件") return False exe_file = exe_files[0] print(f"✅ 找到 exe 文件: {exe_file}") # 创建发布包文件夹 import datetime version = "2.0.0" date = datetime.datetime.now().strftime("%Y%m%d") package_name = f"SlideCombine_v{version}_{date}" package_path = Path(package_name) if package_path.exists(): shutil.rmtree(package_path) package_path.mkdir() print(f"✅ 创建发布包文件夹: {package_name}") # 复制exe文件 shutil.copy2(exe_file, package_path / 'SlideCombine.exe') print("✅ 复制主程序文件") # 创建使用说明 readme_content = f"""PDF书签合并工具 v{version} 使用说明 ===================================== 系统要求: - Windows 7 SP1 或更高版本 - 无需额外安装软件(绿色软件) 使用方法: 1. 双击运行 SlideCombine.exe 2. 选择三个路径: - PDF文件夹路径:包含 FreePic2Pdf_bkmk.txt 文件的文件夹 - TXT源文件路径:包含元数据 TXT 文件的路径 - 输出路径:合并后文件的保存位置 3. 点击"开始合并"按钮 4. 等待处理完成 示例目录结构: PDF文件夹/ ├─ CH-875 1-3/FreePic2Pdf_bkmk.txt ├─ CH-875 4-6/FreePic2Pdf_bkmk.txt TXT源文件/ ├─ CH-875 1-3.txt ├─ CH-875 4-6.txt 输出结果: 输出路径/CH-875.txt (合并后的文件) 特点: - 🚀 运行速度快 - 📦 绿色软件,无需安装 - 🎯 智能文件分组 - 📊 详细处理日志 - 🌍 支持多种文件编码 - 💾 独立程序,无依赖 故障排除: - 如果程序无法启动,请检查是否有杀毒软件阻止运行 - 确保输入的路径存在且有访问权限 - 查看日志输出了解详细的处理信息 版本信息: - 程序版本:v{version} - 构建日期:{date} - 开发语言:Python - 界面框架:Tkinter 技术支持: 这是一个绿色软件,解压即用,无需安装任何依赖项。 """ with open(package_path / '使用说明.txt', 'w', encoding='utf-8') as f: f.write(readme_content) print("✅ 创建使用说明") # 创建启动脚本 bat_content = '''@echo off title PDF书签合并工具 v2.0 echo 启动 PDF书签合并工具... echo. if exist "SlideCombine.exe" ( start "" "SlideCombine.exe" echo ✅ 程序已启动 ) else ( echo ❌ 错误:未找到 SlideCombine.exe echo 请确保在正确的目录中运行此脚本 pause ) timeout /t 2 >nul ''' with open(package_path / '启动程序.bat', 'w', encoding='gbk') as f: f.write(bat_content) print("✅ 创建启动脚本") # 获取文件大小 exe_size = (package_path / 'SlideCombine.exe').stat().st_size size_mb = exe_size / (1024 * 1024) size_kb = exe_size / 1024 print(f""" ✅ 发布包创建完成! 📁 发布包位置: {package_path.absolute()} 💾 主程序大小: {size_kb:.1f} KB ({size_mb:.1f} MB) 📋 包含内容: ├─ SlideCombine.exe (主程序) ├─ 使用说明.txt (用户指南) └─ 启动程序.bat (快捷启动) 🎉 部署说明: 1. 将整个 {package_name} 文件夹复制到目标电脑 2. 双击"启动程序.bat"或直接运行"SlideCombine.exe" 3. 无需安装任何软件,绿色环保 """) return True def main(): """主函数""" print("="*50) print(" PDF书签合并工具 - 自动打包脚本") print("="*50) print() # 检查Python版本 if sys.version_info < (3, 7): print("❌ 需要 Python 3.7 或更高版本") sys.exit(1) print(f"✅ Python版本: {sys.version}") # 检查主程序文件 if not os.path.exists('slide_combine.py'): print("❌ 未找到 slide_combine.py 文件") print("请确保在项目根目录下运行此脚本") sys.exit(1) # 检查/安装PyInstaller if not check_pyinstaller(): print("尝试自动安装 PyInstaller...") if not install_pyinstaller(): print("请手动安装 PyInstaller: pip install pyinstaller") sys.exit(1) # 清理之前的构建 clean_build() # 创建配置文件 create_spec_file() create_version_info() # 构建exe文件 if not build_exe(): print("❌ 构建失败,请检查错误信息") sys.exit(1) # 创建发布包 if not create_release_package(): print("❌ 创建发布包失败") sys.exit(1) print("🎉 打包完成!") if __name__ == "__main__": main()