Files
shuidrop_gui/quick_build.py

366 lines
12 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
快速打包脚本 - 避免spec文件缩进问题
"""
import os
import subprocess
import shutil
from pathlib import Path
def clean_build():
"""清理构建目录"""
print("🧹 清理旧的构建文件...")
# 跳过进程结束步骤,避免影响当前脚本运行
print("🔄 跳过进程结束步骤(避免脚本自终止)...")
# 等待少量时间确保文件句柄释放
import time
print("⏳ 等待文件句柄释放...")
time.sleep(0.5)
dirs_to_clean = ['dist', 'build']
for dir_name in dirs_to_clean:
print(f"🔍 检查目录: {dir_name}")
if os.path.exists(dir_name):
try:
print(f"🗑️ 正在删除: {dir_name}")
shutil.rmtree(dir_name)
print(f"✅ 已删除: {dir_name}")
except PermissionError as e:
print(f"⚠️ {dir_name} 被占用,尝试强制删除... 错误: {e}")
try:
subprocess.run(['rd', '/s', '/q', dir_name],
shell=True, check=True)
print(f"✅ 强制删除成功: {dir_name}")
except Exception as e2:
print(f"❌ 无法删除 {dir_name}: {e2}")
print("💡 请手动删除或运行 force_clean_dist.bat")
except Exception as e:
print(f"❌ 删除 {dir_name} 时出错: {e}")
else:
print(f" {dir_name} 不存在,跳过")
print("✅ 清理阶段完成")
def build_with_command():
"""使用命令行参数直接打包"""
print("🚀 开始打包...")
cmd = [
'pyinstaller',
'--name=main',
'--onedir', # 相当于 --exclude-binaries
'--windowed', # 相当于 -w
'--icon=static/ai_assistant_icon_64.png', # 添加主程序图标
'--add-data=config.py;.',
'--add-data=exe_file_logger.py;.',
'--add-data=windows_taskbar_fix.py;.',
'--add-data=Utils/PythonNew32;Utils/PythonNew32',
'--add-data=Utils/JD;Utils/JD',
'--add-data=Utils/Dy;Utils/Dy',
'--add-data=Utils/Pdd;Utils/Pdd',
'--add-data=Utils/QianNiu;Utils/QianNiu',
'--add-data=Utils/message_models.py;Utils',
'--add-data=Utils/__init__.py;Utils',
'--add-data=WebSocket;WebSocket',
'--add-data=static;static',
'--add-binary=Utils/PythonNew32/SaiNiuApi.dll;Utils/PythonNew32',
'--add-binary=Utils/PythonNew32/SaiNiuSys.dll;Utils/PythonNew32',
'--add-binary=Utils/PythonNew32/SaiNiuServer.dll;Utils/PythonNew32',
'--add-binary=Utils/PythonNew32/python32.exe;Utils/PythonNew32',
'--add-binary=Utils/PythonNew32/python313.dll;Utils/PythonNew32',
'--add-binary=Utils/PythonNew32/vcruntime140.dll;Utils/PythonNew32',
'--hidden-import=PyQt5.QtCore',
'--hidden-import=PyQt5.QtGui',
'--hidden-import=PyQt5.QtWidgets',
'--hidden-import=websockets',
'--hidden-import=asyncio',
'--hidden-import=Utils.QianNiu.QianNiuUtils',
'--hidden-import=Utils.JD.JdUtils',
'--hidden-import=Utils.Dy.DyUtils',
'--hidden-import=Utils.Pdd.PddUtils',
'--hidden-import=WebSocket.backend_singleton',
'--hidden-import=WebSocket.BackendClient',
'--hidden-import=windows_taskbar_fix',
'main.py'
]
try:
print(f"执行命令: {' '.join(cmd[:5])}... (共{len(cmd)}个参数)")
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
if result.returncode == 0:
print("✅ 打包成功!")
print("📁 打包结果: dist/main/")
# 重命名目录为统一的输出路径
if os.path.exists('dist/main'):
try:
# 如果目标目录已存在,先删除
if os.path.exists('dist/MultiPlatformGUI'):
print("🗑️ 删除旧的 MultiPlatformGUI 目录...")
shutil.rmtree('dist/MultiPlatformGUI')
# 重命名
os.rename('dist/main', 'dist/MultiPlatformGUI')
print("✅ 已重命名为: dist/MultiPlatformGUI/")
# 验证重命名结果
if os.path.exists('dist/MultiPlatformGUI/main.exe'):
exe_size = os.path.getsize('dist/MultiPlatformGUI/main.exe') / 1024 / 1024
print(f"✅ 主程序验证通过: main.exe ({exe_size:.1f} MB)")
else:
print("⚠️ 重命名后主程序文件验证失败")
except Exception as e:
print(f"⚠️ 重命名失败: {e}")
print("💡 可以手动重命名: dist/main -> dist/MultiPlatformGUI")
print("📁 当前可用路径: dist/main/")
else:
print("⚠️ 未找到 dist/main 目录,重命名跳过")
# 创建使用说明
try:
create_usage_guide()
except:
create_usage_guide_fallback()
return True
else:
print("❌ 打包失败")
if result.stderr:
print("错误信息:")
print(result.stderr)
return False
except Exception as e:
print(f"❌ 打包过程出错: {e}")
return False
def create_usage_guide():
"""创建使用说明"""
guide_content = r"""# 多平台客服GUI使用说明
## 🚀 运行方式
```
cd dist/MultiPlatformGUI
.\main.exe
```
## 📝 支持平台
- 千牛 (淘宝)
- 京东 (JD)
- 抖音 (DY)
- 拼多多 (PDD)
## 🎯 特点
- 支持多平台同时监听
- 无平台间冲突
- 自动生成日志文件
- 美观的系统托盘图标
- 现代化UI界面
## 📁 文件结构
```
MultiPlatformGUI/
├── main.exe # 主程序
└── _internal/ # 程序依赖文件
├── static/ # 图标资源
│ ├── ai_assistant_icon_16.png
│ ├── ai_assistant_icon_32.png
│ └── ai_assistant_icon_64.png
├── Utils/
│ ├── PythonNew32/ # 千牛DLL文件
│ ├── JD/ # 京东模块
│ ├── Dy/ # 抖音模块
│ └── Pdd/ # 拼多多模块
└── ...
```
## 🔧 故障排除
1. **图标不显示**:检查 _internal/static/ 目录下的图标文件是否存在
2. **连接失败**:检查网络连接和令牌是否正确
3. **平台无响应**:查看日志文件了解详细错误信息
## 📞 技术支持
如有问题,请提供:
- 错误截图
- 具体的错误描述
- 操作步骤
"""
try:
with open('dist/MultiPlatformGUI/使用说明.txt', 'w', encoding='utf-8') as f:
f.write(guide_content)
print("✅ 已生成使用说明文件")
except:
print("⚠️ 使用说明生成失败")
def create_usage_guide_fallback():
"""备用使用说明生成"""
guide_content = r"""# 多平台客服GUI使用说明
## 🚀 运行方式
```
cd dist/main
.\main.exe
```
## 📝 支持平台
- 千牛 (淘宝)
- 京东 (JD)
- 抖音 (DY)
- 拼多多 (PDD)
## 🎯 特点
- 支持多平台同时监听
- 无平台间冲突
- 自动生成日志文件
"""
try:
with open('dist/main/使用说明.txt', 'w', encoding='utf-8') as f:
f.write(guide_content)
print("✅ 已生成使用说明文件 (备用路径)")
except:
print("⚠️ 使用说明生成失败")
def verify_result():
"""验证打包结果"""
print("\n🔍 验证打包结果...")
# 优先检查MultiPlatformGUI标准输出目录
target_dir = "dist/MultiPlatformGUI"
fallback_dir = "dist/main"
if os.path.exists(target_dir):
return _verify_directory(target_dir)
elif os.path.exists(fallback_dir):
print(f"⚠️ 发现备用目录: {fallback_dir}")
print("💡 建议重命名为: dist/MultiPlatformGUI")
return _verify_directory(fallback_dir)
else:
print("❌ 未找到任何打包输出目录")
return False
def _verify_directory(base_dir):
"""验证指定目录的打包结果"""
exe_path = f"{base_dir}/main.exe"
internal_dir = f"{base_dir}/_internal"
dll_path = f"{base_dir}/_internal/Utils/PythonNew32/SaiNiuApi.dll"
static_dir = f"{base_dir}/_internal/static"
print(f"📁 验证目录: {base_dir}")
# 检查主程序
if os.path.exists(exe_path):
size = os.path.getsize(exe_path) / 1024 / 1024 # MB
print(f"✅ 主程序: main.exe ({size:.1f} MB)")
else:
print("❌ 主程序 main.exe 不存在")
return False
# 检查_internal目录
if os.path.exists(internal_dir):
file_count = len([f for f in os.listdir(internal_dir) if os.path.isfile(os.path.join(internal_dir, f))])
dir_count = len([d for d in os.listdir(internal_dir) if os.path.isdir(os.path.join(internal_dir, d))])
print(f"✅ 依赖目录: _internal/ ({file_count} 个文件, {dir_count} 个子目录)")
else:
print("❌ _internal 目录不存在")
return False
# 检查关键DLL可选
if os.path.exists(dll_path):
dll_size = os.path.getsize(dll_path) / 1024 # KB
print(f"✅ 千牛DLL: SaiNiuApi.dll ({dll_size:.1f} KB)")
else:
print("⚠️ 千牛DLL不存在可能正常")
# 检查静态资源
if os.path.exists(static_dir):
icon_files = list(Path(static_dir).glob("*.png"))
print(f"✅ 静态资源: static/ ({len(icon_files)} 个图标文件)")
else:
print("⚠️ static 目录不存在")
print(f"✅ 目录 {base_dir} 验证通过")
return True
def main():
"""主函数"""
print("🔥 多平台客服GUI快速打包工具")
print("=" * 60)
try:
# 检查依赖
print("🔍 检查打包依赖...")
try:
import subprocess
result = subprocess.run(['pyinstaller', '--version'],
capture_output=True, text=True, check=False)
if result.returncode == 0:
print(f"✅ PyInstaller 版本: {result.stdout.strip()}")
else:
print("❌ PyInstaller 未安装或不可用")
print("💡 请运行: pip install pyinstaller")
return False
except Exception as e:
print(f"❌ 检查PyInstaller时出错: {e}")
return False
# 清理
print("\n📍 开始清理阶段...")
clean_build()
print("📍 清理阶段完成")
# 打包
print("\n📍 开始打包阶段...")
if not build_with_command():
print("❌ 打包阶段失败")
return False
print("📍 打包阶段完成")
# 验证
print("\n📍 开始验证阶段...")
if not verify_result():
print("❌ 验证阶段失败")
return False
print("📍 验证阶段完成")
print("\n" + "=" * 60)
print("🎉 打包完成!")
# 智能显示可用路径
if os.path.exists("dist/MultiPlatformGUI"):
print("📁 打包结果: dist/MultiPlatformGUI/")
print("🚀 运行方式: cd dist/MultiPlatformGUI && .\\main.exe")
print("📦 安装包构建: python installer/build_installer.py")
elif os.path.exists("dist/main"):
print("📁 打包结果: dist/main/")
print("🚀 运行方式: cd dist/main && .\\main.exe")
print("⚠️ 建议重命名: dist/main -> dist/MultiPlatformGUI")
print("=" * 60)
return True
except Exception as e:
print(f"❌ 打包失败: {e}")
return False
if __name__ == "__main__":
success = main()
if success:
print("\n✅ 可以开始测试了!")
else:
print("\n❌ 请检查错误信息并重试")