BodyBalanceEvaluation/backend/build_exe_original.py
2025-08-13 14:17:50 +08:00

524 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

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 -*-
"""
后端应用程序打包脚本
使用PyInstaller将Flask应用程序打包成独立的exe文件
"""
import os
import sys
import shutil
import subprocess
from pathlib import Path
def create_spec_file():
"""创建PyInstaller spec文件"""
spec_content = '''
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main_exe.py'],
pathex=[],
binaries=[
('dll/femtobolt/bin/k4a.dll', '.'), # K4A动态库
('dll/femtobolt/bin/k4arecord.dll', '.'), # K4A录制库
('dll/femtobolt/bin/depthengine_2_0.dll', '.'), # 深度引擎
('dll/femtobolt/bin/OrbbecSDK.dll', '.'), # Orbbec SDK
('dll/femtobolt/bin/ob_usb.dll', '.'), # Orbbec USB库
('dll/femtobolt/bin/live555.dll', '.'), # Live555库
('dll/smitsense/SMiTSenseUsb-F3.0.dll', '.'), # SMiTSense传感器库
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', '.'), # Orbbec配置文件
],
datas=[
('..\\config.ini', '.'), # 配置文件
('..\\config.json', '.'), # JSON配置文件
('tests', 'tests'), # 测试文件夹
('data', 'data'), # 数据文件夹
],
hiddenimports=[
'flask',
'flask_socketio',
'flask_cors',
'cv2',
'numpy',
'pandas',
'scipy',
'matplotlib',
'seaborn',
'sklearn',
'PIL',
'reportlab',
'sqlite3',
'configparser',
'logging',
'threading',
'queue',
'base64',
'psutil',
'pykinect_azure',
'pyserial',
'requests',
'yaml',
'click',
'colorama',
'tqdm',
'database',
'device_manager',
'utils',
],
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='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,
icon=None, # 图标文件不存在,暂时禁用
)
'''
with open('backend.spec', 'w', encoding='utf-8') as f:
f.write(spec_content)
print("✓ 已创建 backend.spec 文件")
def create_debug_spec():
"""创建诊断脚本的spec文件"""
import subprocess
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['test_socketio_debug.py'],
pathex=['{os.getcwd()}'],
binaries=[],
datas=[],
hiddenimports=[
'flask',
'flask_socketio',
'eventlet',
'threading',
'engineio.async_drivers.threading',
'engineio.async_drivers.eventlet',
'engineio.async_eventlet',
'socketio.async_eventlet',
'greenlet',
],
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='test_socketio_debug',
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,
)
'''
with open('debug.spec', 'w', encoding='utf-8') as f:
f.write(spec_content)
print("✓ 已创建 debug.spec 文件")
# 直接构建诊断exe
print("开始构建诊断exe文件...")
result = subprocess.run([sys.executable, '-m', 'PyInstaller', 'debug.spec'],
capture_output=True, text=True, encoding='utf-8')
if result.returncode == 0:
print("✓ 诊断exe构建成功")
print("运行诊断: dist/test_socketio_debug.exe")
else:
print(f"❌ 诊断exe构建失败: {result.stderr}")
def create_minimal_spec():
"""创建最小化测试脚本的spec文件"""
print("创建最小化测试脚本的spec文件...")
spec_content = '''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['test_socketio_minimal.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
'flask',
'flask_socketio',
'eventlet',
'eventlet.hubs',
'eventlet.hubs.epolls',
'eventlet.hubs.kqueue',
'eventlet.hubs.selects',
'threading',
'engineio',
'engineio.async_drivers',
'engineio.async_drivers.threading',
'engineio.async_drivers.eventlet',
'engineio.async_drivers.gevent',
'socketio',
'socketio.namespace'
],
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='test_socketio_minimal',
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,
)
'''
with open('minimal.spec', 'w', encoding='utf-8') as f:
f.write(spec_content)
print("✓ 已创建 minimal.spec 文件")
# 直接构建
print("开始构建最小化测试exe...")
result = subprocess.run([sys.executable, '-m', 'PyInstaller', 'minimal.spec', '--clean'],
capture_output=True, text=True, encoding='utf-8')
if result.returncode == 0:
print("✓ 最小化测试exe构建完成: dist/test_socketio_minimal.exe")
else:
print(f"❌ 最小化测试exe构建失败: {result.stderr}")
def create_main_entry():
"""创建主入口文件"""
main_content = '''
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
身体平衡评估系统 - 后端服务入口
独立运行的exe版本
"""
import os
import sys
import logging
from pathlib import Path
# 设置工作目录
if getattr(sys, 'frozen', False):
# 如果是打包后的exe
application_path = os.path.dirname(sys.executable)
else:
# 如果是开发环境
application_path = os.path.dirname(os.path.abspath(__file__))
os.chdir(application_path)
sys.path.insert(0, application_path)
# 创建必要的目录
os.makedirs('logs', exist_ok=True)
os.makedirs('data', exist_ok=True)
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('logs/backend.log', encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
def main():
"""主函数"""
try:
logger.info("启动身体平衡评估系统后端服务...")
logger.info(f"工作目录: {os.getcwd()}")
# 导入并启动Flask应用
from app import app, socketio, init_app
# 初始化应用
init_app()
# 启动服务器
logger.info("后端服务器启动在 http://localhost:5000")
socketio.run(
app,
host='0.0.0.0',
port=5000,
debug=False
)
except KeyboardInterrupt:
logger.info("用户中断,正在关闭服务器...")
except Exception as e:
logger.error(f"启动失败: {e}")
input("按回车键退出...")
sys.exit(1)
if __name__ == '__main__':
main()
'''
with open('main_exe.py', 'w', encoding='utf-8') as f:
f.write(main_content)
print("✓ 已创建 main_exe.py 入口文件")
def check_dependencies():
"""检查必需的依赖模块"""
required_modules = [
'flask', 'flask_cors', 'flask_socketio', 'numpy', 'pandas',
'cv2', 'matplotlib', 'seaborn', 'PIL', 'reportlab',
'serial', 'requests', 'sqlite3', 'configparser', 'scipy', 'eventlet'
]
missing_modules = []
for module in required_modules:
try:
__import__(module)
print(f"{module}")
except ImportError:
missing_modules.append(module)
print(f"{module} - 未安装")
if missing_modules:
print(f"\n警告: 发现 {len(missing_modules)} 个缺失的依赖模块")
print("建议运行: pip install -r requirements.txt")
return False
print("✓ 所有必需依赖模块检查通过")
return True
def build_exe():
"""构建exe文件"""
print("开始构建exe文件...")
# 检查依赖模块
print("\n检查依赖模块...")
if not check_dependencies():
print("\n❌ 依赖检查失败,请先安装缺失的模块")
return False
# 检查PyInstaller是否安装
try:
import PyInstaller
print(f"\n✓ PyInstaller版本: {PyInstaller.__version__}")
except ImportError:
print("❌ PyInstaller未安装正在安装...")
os.system('pip install pyinstaller')
# 清理之前的构建
if os.path.exists('build'):
shutil.rmtree('build')
print("✓ 清理build目录")
if os.path.exists('dist'):
shutil.rmtree('dist')
print("✓ 清理dist目录")
# 使用spec文件构建
cmd = 'python -m PyInstaller backend.spec --clean --noconfirm'
print(f"执行命令: {cmd}")
result = os.system(cmd)
if result == 0:
print("\n✓ 构建成功!")
print("exe文件位置: dist/BodyBalanceBackend.exe")
# 复制额外的文件
dist_path = Path('dist')
if dist_path.exists():
# 复制配置文件
config_files = ['../config.ini', '../config.json']
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')
f.write('echo 启动身体平衡评估系统后端服务...\n')
f.write('BodyBalanceBackend.exe\n')
f.write('pause\n')
print("✓ 创建启动脚本: start_backend.bat")
print("\n🎉 打包完成!")
print("运行方式:")
print("1. 直接运行: dist/BodyBalanceBackend.exe")
print("2. 使用脚本: dist/start_backend.bat")
else:
print("❌ 构建失败")
return False
return True
def check_required_files():
"""检查必需的文件是否存在"""
required_files = {
'app.py': '主应用文件',
'database.py': '数据库模块',
'device_manager.py': '设备管理模块',
'utils.py': '工具模块',
'../config.ini': '配置文件',
'../config.json': 'JSON配置文件'
}
required_dlls = {
'dll/femtobolt/bin/k4a.dll': 'K4A动态库',
'dll/femtobolt/bin/k4arecord.dll': 'K4A录制库',
'dll/femtobolt/bin/depthengine_2_0.dll': '深度引擎',
'dll/femtobolt/bin/OrbbecSDK.dll': 'Orbbec SDK',
'dll/femtobolt/bin/ob_usb.dll': 'Orbbec USB库',
'dll/femtobolt/bin/live555.dll': 'Live555库',
'dll/smitsense/SMiTSenseUsb-F3.0.dll': 'SMiTSense传感器库',
'dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml': 'Orbbec配置文件'
}
missing_files = []
print("检查必需文件...")
for file_path, description in required_files.items():
if os.path.exists(file_path):
print(f"{description}: {file_path}")
else:
missing_files.append((file_path, description))
print(f"{description}: {file_path} - 文件不存在")
print("\n检查DLL文件...")
for dll_path, description in required_dlls.items():
if os.path.exists(dll_path):
print(f"{description}: {dll_path}")
else:
missing_files.append((dll_path, description))
print(f"{description}: {dll_path} - 文件不存在")
if missing_files:
print(f"\n警告: 发现 {len(missing_files)} 个缺失文件")
print("缺失的文件可能导致打包失败或运行时错误")
return False
print("\n✓ 所有必需文件检查通过")
return True
def main():
"""主函数"""
print("=" * 50)
print("身体平衡评估系统 - 后端打包工具")
print("=" * 50)
print()
# 检查当前目录
if not os.path.exists('app.py'):
print("❌ 请在backend目录下运行此脚本")
return
# 检查必需文件
print("\n检查必需文件...")
if not check_required_files():
print("\n⚠️ 文件检查发现问题,但将继续执行打包")
print("如果打包失败,请检查上述缺失的文件")
input("按回车键继续...")
try:
# 创建配置文件
create_spec_file()
# create_main_entry() # 注释掉以保留现有的main_exe.py修改
# 临时为诊断脚本创建spec文件
if len(sys.argv) > 1 and sys.argv[1] == 'debug':
create_debug_spec()
return
elif len(sys.argv) > 1 and sys.argv[1] == 'minimal':
create_minimal_spec()
return
# 构建exe
if build_exe():
print("\n✅ 所有操作完成!")
else:
print("\n❌ 构建过程中出现错误")
except Exception as e:
print(f"❌ 错误: {e}")
input("\n按回车键退出...")
if __name__ == '__main__':
main()