2025-08-06 09:04:13 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
"""
|
2025-08-13 14:17:50 +08:00
|
|
|
|
身体平衡评估系统后端打包脚本
|
|
|
|
|
基于最小测试框架的成功经验,简化打包配置
|
2025-08-06 09:04:13 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import shutil
|
2025-08-13 14:17:50 +08:00
|
|
|
|
import subprocess
|
2025-08-06 09:04:13 +08:00
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
2025-08-13 14:17:50 +08:00
|
|
|
|
def check_dependencies():
|
|
|
|
|
"""检查必需的依赖"""
|
|
|
|
|
print("检查依赖模块...")
|
|
|
|
|
required_modules = [
|
|
|
|
|
'flask',
|
|
|
|
|
'flask_socketio',
|
|
|
|
|
'flask_cors',
|
|
|
|
|
'socketio',
|
|
|
|
|
'engineio',
|
|
|
|
|
'numpy',
|
|
|
|
|
'cv2',
|
|
|
|
|
'psutil'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
missing_modules = []
|
|
|
|
|
for module in required_modules:
|
|
|
|
|
try:
|
|
|
|
|
__import__(module)
|
|
|
|
|
print(f"✓ {module}")
|
|
|
|
|
except ImportError:
|
|
|
|
|
print(f"✗ {module} (缺失)")
|
|
|
|
|
missing_modules.append(module)
|
|
|
|
|
|
|
|
|
|
if missing_modules:
|
|
|
|
|
print(f"\n缺失模块: {', '.join(missing_modules)}")
|
|
|
|
|
print("请运行: pip install -r requirements_build.txt")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
print("✓ 所有依赖模块检查通过")
|
|
|
|
|
return True
|
|
|
|
|
|
2025-08-06 09:04:13 +08:00
|
|
|
|
def create_spec_file():
|
|
|
|
|
"""创建PyInstaller spec文件"""
|
|
|
|
|
spec_content = '''
|
|
|
|
|
# -*- mode: python ; coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
a = Analysis(
|
2025-08-13 14:17:50 +08:00
|
|
|
|
['main_exe.py'],
|
2025-08-06 09:04:13 +08:00
|
|
|
|
pathex=[],
|
|
|
|
|
binaries=[
|
2025-08-13 14:17:50 +08:00
|
|
|
|
# 只包含确实存在的DLL文件
|
2025-08-06 09:04:13 +08:00
|
|
|
|
],
|
|
|
|
|
datas=[
|
2025-08-13 14:17:50 +08:00
|
|
|
|
('config.ini', '.'), # 配置文件
|
2025-08-06 09:04:13 +08:00
|
|
|
|
('data', 'data'), # 数据文件夹
|
2025-08-13 14:17:50 +08:00
|
|
|
|
('tests', 'tests'), # 测试文件夹
|
|
|
|
|
('app_minimal.py', '.'), # 应用入口文件
|
2025-08-06 09:04:13 +08:00
|
|
|
|
],
|
|
|
|
|
hiddenimports=[
|
|
|
|
|
'flask',
|
|
|
|
|
'flask_socketio',
|
|
|
|
|
'flask_cors',
|
2025-08-13 14:17:50 +08:00
|
|
|
|
'socketio',
|
|
|
|
|
'engineio',
|
|
|
|
|
'engineio.async_drivers.threading',
|
|
|
|
|
'socketio.namespace',
|
2025-08-06 09:04:13 +08:00
|
|
|
|
'cv2',
|
|
|
|
|
'numpy',
|
2025-08-13 14:17:50 +08:00
|
|
|
|
'psutil',
|
2025-08-06 09:04:13 +08:00
|
|
|
|
'sqlite3',
|
|
|
|
|
'configparser',
|
|
|
|
|
'logging',
|
|
|
|
|
'threading',
|
|
|
|
|
'queue',
|
|
|
|
|
'base64',
|
2025-08-13 14:17:50 +08:00
|
|
|
|
'requests',
|
|
|
|
|
'click',
|
|
|
|
|
'colorama',
|
2025-08-06 09:04:13 +08:00
|
|
|
|
'database',
|
|
|
|
|
'device_manager',
|
|
|
|
|
'utils',
|
2025-08-13 14:17:50 +08:00
|
|
|
|
'dns',
|
|
|
|
|
'dns.resolver',
|
|
|
|
|
'dns.asyncresolver'
|
2025-08-06 09:04:13 +08:00
|
|
|
|
],
|
|
|
|
|
hookspath=[],
|
|
|
|
|
hooksconfig={},
|
|
|
|
|
runtime_hooks=[],
|
2025-08-13 14:17:50 +08:00
|
|
|
|
excludes=[
|
|
|
|
|
'eventlet',
|
|
|
|
|
'gevent',
|
|
|
|
|
'gevent_uwsgi'
|
|
|
|
|
],
|
2025-08-06 09:04:13 +08:00
|
|
|
|
noarchive=False,
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-13 14:17:50 +08:00
|
|
|
|
pyz = PYZ(a.pure)
|
2025-08-06 09:04:13 +08:00
|
|
|
|
|
|
|
|
|
exe = EXE(
|
|
|
|
|
pyz,
|
|
|
|
|
a.scripts,
|
|
|
|
|
a.binaries,
|
|
|
|
|
a.datas,
|
|
|
|
|
[],
|
|
|
|
|
name='BodyBalanceBackend',
|
|
|
|
|
debug=False,
|
|
|
|
|
bootloader_ignore_signals=False,
|
|
|
|
|
strip=False,
|
|
|
|
|
upx=True,
|
|
|
|
|
upx_exclude=[],
|
|
|
|
|
runtime_tmpdir=None,
|
|
|
|
|
console=True,
|
|
|
|
|
disable_windowed_traceback=False,
|
|
|
|
|
argv_emulation=False,
|
|
|
|
|
target_arch=None,
|
|
|
|
|
codesign_identity=None,
|
|
|
|
|
entitlements_file=None,
|
2025-08-13 14:17:50 +08:00
|
|
|
|
icon=None
|
2025-08-06 09:04:13 +08:00
|
|
|
|
)
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
with open('backend.spec', 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(spec_content)
|
|
|
|
|
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("✓ 已创建 backend.spec 文件")
|
2025-08-06 09:04:13 +08:00
|
|
|
|
|
|
|
|
|
def build_exe():
|
|
|
|
|
"""构建exe文件"""
|
|
|
|
|
print("开始构建exe文件...")
|
|
|
|
|
|
2025-08-13 14:17:50 +08:00
|
|
|
|
# 检查依赖模块
|
|
|
|
|
print("\n检查依赖模块...")
|
|
|
|
|
if not check_dependencies():
|
|
|
|
|
print("\n❌ 依赖检查失败,请先安装缺失的模块")
|
|
|
|
|
return False
|
|
|
|
|
|
2025-08-06 09:04:13 +08:00
|
|
|
|
# 检查PyInstaller是否安装
|
|
|
|
|
try:
|
|
|
|
|
import PyInstaller
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print(f"\n✓ PyInstaller版本: {PyInstaller.__version__}")
|
2025-08-06 09:04:13 +08:00
|
|
|
|
except ImportError:
|
|
|
|
|
print("❌ PyInstaller未安装,正在安装...")
|
2025-08-13 14:17:50 +08:00
|
|
|
|
subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyinstaller>=6.10.0'])
|
2025-08-06 09:04:13 +08:00
|
|
|
|
|
|
|
|
|
# 清理之前的构建
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("\n清理构建目录...")
|
2025-08-06 09:04:13 +08:00
|
|
|
|
if os.path.exists('build'):
|
|
|
|
|
shutil.rmtree('build')
|
|
|
|
|
print("✓ 清理build目录")
|
|
|
|
|
|
|
|
|
|
if os.path.exists('dist'):
|
|
|
|
|
shutil.rmtree('dist')
|
|
|
|
|
print("✓ 清理dist目录")
|
|
|
|
|
|
2025-08-13 14:17:50 +08:00
|
|
|
|
# 创建spec文件
|
|
|
|
|
create_spec_file()
|
|
|
|
|
|
2025-08-06 09:04:13 +08:00
|
|
|
|
# 使用spec文件构建
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("\n开始打包...")
|
|
|
|
|
cmd = [sys.executable, '-m', 'PyInstaller', 'backend.spec', '--clean', '--noconfirm']
|
|
|
|
|
print(f"执行命令: {' '.join(cmd)}")
|
|
|
|
|
|
|
|
|
|
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
2025-08-06 09:04:13 +08:00
|
|
|
|
|
2025-08-13 14:17:50 +08:00
|
|
|
|
if result.returncode == 0:
|
2025-08-06 09:04:13 +08:00
|
|
|
|
print("\n✓ 构建成功!")
|
|
|
|
|
print("exe文件位置: dist/BodyBalanceBackend.exe")
|
|
|
|
|
|
|
|
|
|
# 复制额外的文件
|
|
|
|
|
dist_path = Path('dist')
|
|
|
|
|
if dist_path.exists():
|
2025-08-13 14:17:50 +08:00
|
|
|
|
# 复制配置文件(如果存在)
|
|
|
|
|
config_files = ['config.ini', 'config.json']
|
2025-08-06 09:04:13 +08:00
|
|
|
|
for config_file in config_files:
|
|
|
|
|
if os.path.exists(config_file):
|
|
|
|
|
shutil.copy2(config_file, dist_path)
|
|
|
|
|
print(f"✓ 复制配置文件: {config_file}")
|
|
|
|
|
|
|
|
|
|
# 创建启动脚本
|
|
|
|
|
start_script = dist_path / 'start_backend.bat'
|
|
|
|
|
with open(start_script, 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write('@echo off\n')
|
2025-08-13 14:17:50 +08:00
|
|
|
|
f.write('chcp 65001 >nul\n')
|
|
|
|
|
f.write('echo Starting Body Balance Backend...\n')
|
2025-08-06 09:04:13 +08:00
|
|
|
|
f.write('BodyBalanceBackend.exe\n')
|
|
|
|
|
f.write('pause\n')
|
|
|
|
|
print("✓ 创建启动脚本: start_backend.bat")
|
|
|
|
|
|
|
|
|
|
print("\n🎉 打包完成!")
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("\n测试方法:")
|
2025-08-06 09:04:13 +08:00
|
|
|
|
print("1. 直接运行: dist/BodyBalanceBackend.exe")
|
|
|
|
|
print("2. 使用脚本: dist/start_backend.bat")
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("3. 在浏览器中访问: http://localhost:5000")
|
|
|
|
|
|
|
|
|
|
return True
|
2025-08-06 09:04:13 +08:00
|
|
|
|
else:
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("\n❌ 构建失败")
|
|
|
|
|
print(f"错误信息: {result.stderr}")
|
2025-08-06 09:04:13 +08:00
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""主函数"""
|
2025-08-13 14:17:50 +08:00
|
|
|
|
print("================================================")
|
|
|
|
|
print("身体平衡评估系统 - 后端打包工具 (简化版)")
|
|
|
|
|
print("基于最小测试框架的成功经验")
|
|
|
|
|
print("================================================")
|
2025-08-06 09:04:13 +08:00
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
try:
|
2025-08-13 14:17:50 +08:00
|
|
|
|
# 检查是否在正确的目录
|
|
|
|
|
if not os.path.exists('main_exe.py'):
|
|
|
|
|
print("❌ 错误:请在backend目录下运行此脚本")
|
|
|
|
|
print("当前目录应包含 main_exe.py 文件")
|
|
|
|
|
return
|
2025-08-06 09:04:13 +08:00
|
|
|
|
|
|
|
|
|
# 构建exe
|
|
|
|
|
if build_exe():
|
|
|
|
|
print("\n✅ 所有操作完成!")
|
|
|
|
|
else:
|
|
|
|
|
print("\n❌ 构建过程中出现错误")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ 错误: {e}")
|
2025-08-13 14:17:50 +08:00
|
|
|
|
import traceback
|
|
|
|
|
traceback.print_exc()
|
2025-08-06 09:04:13 +08:00
|
|
|
|
|
|
|
|
|
input("\n按回车键退出...")
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
main()
|