项目基本完成

This commit is contained in:
yovinchen 2025-06-30 21:00:51 +08:00
parent e66a6cec84
commit 0336d55682
2 changed files with 620 additions and 10 deletions

View File

@ -6,11 +6,91 @@
支持1-10人连坐需求能够处理不连续座位 支持1-10人连坐需求能够处理不连续座位
""" """
import pandas as pd import sys
import numpy as np import os
from pathlib import Path from pathlib import Path
import datetime import datetime
import sys
def check_dependencies():
"""检查并安装必要的依赖包"""
required_packages = {
'pandas': 'pandas>=1.3.0',
'numpy': 'numpy>=1.20.0',
'openpyxl': 'openpyxl>=3.0.0'
}
missing_packages = []
print("检查依赖包...")
for package_name, package_spec in required_packages.items():
try:
__import__(package_name)
print(f"{package_name} 已安装")
except ImportError:
missing_packages.append(package_spec)
print(f"{package_name} 未安装")
if missing_packages:
print(f"\n发现缺失依赖: {', '.join(missing_packages)}")
print("正在尝试自动安装...")
try:
import subprocess
for package in missing_packages:
print(f"安装 {package}...")
result = subprocess.run([sys.executable, '-m', 'pip', 'install', package],
capture_output=True, text=True)
if result.returncode == 0:
print(f"{package} 安装成功")
else:
print(f"{package} 安装失败: {result.stderr}")
return False
except Exception as e:
print(f"❌ 自动安装失败: {e}")
print("\n请手动安装以下依赖包:")
for package in missing_packages:
print(f" pip install {package}")
input("安装完成后按Enter键继续...")
return False
return True
def check_data_files():
"""检查必要的数据文件"""
required_files = ['人员信息.xlsx', '座位信息.xlsx']
missing_files = []
print("\n检查数据文件...")
for file_name in required_files:
if Path(file_name).exists():
print(f"{file_name} 存在")
else:
missing_files.append(file_name)
print(f"{file_name} 不存在")
if missing_files:
print(f"\n❌ 缺少必要文件: {', '.join(missing_files)}")
print("\n请确保以下文件存在于程序同一目录下:")
print("1. 人员信息.xlsx - 包含姓名、证件类型、证件号、手机号、备注等列")
print("2. 座位信息.xlsx - 包含区域、楼层、排号、座位号等列")
print("\n提示: 您可以参考示例文件来准备数据")
return False
return True
# 只有在依赖检查通过后才导入这些包
if check_dependencies():
try:
import pandas as pd
import numpy as np
except ImportError as e:
print(f"❌ 导入依赖包失败: {e}")
input("按Enter键退出...")
sys.exit(1)
else:
print("❌ 依赖检查失败")
input("按Enter键退出...")
sys.exit(1)
class Logger: class Logger:
"""日志记录器""" """日志记录器"""
@ -32,7 +112,7 @@ class Logger:
try: try:
with open(self.log_file, 'w', encoding='utf-8') as f: with open(self.log_file, 'w', encoding='utf-8') as f:
f.write('\n'.join(self.logs)) f.write('\n'.join(self.logs))
self.log(f"日志已保存到: {self.log_file}") print(f"日志已保存到: {self.log_file}")
return True return True
except Exception as e: except Exception as e:
print(f"保存日志失败: {e}") print(f"保存日志失败: {e}")
@ -879,20 +959,54 @@ class SeatAllocationSystem:
def main(): def main():
"""主函数""" """主函数"""
system = SeatAllocationSystem() print("=" * 60)
print("座位分配系统 v1.0")
print("=" * 60)
try: try:
# 检查数据文件
if not check_data_files():
input("\n按Enter键退出...")
return
print("\n开始运行座位分配系统...")
system = SeatAllocationSystem()
# 运行校验 # 运行校验
if system.run_validation(): if system.run_validation():
# 校验通过,运行分配 # 校验通过,询问是否继续分配
system.run_allocation() response = input("\n文件校验通过,是否开始座位分配? (Y/n): ")
if response.lower() in ['', 'y', 'yes']:
# 运行分配
if system.run_allocation():
print("\n🎉 座位分配完成!")
print("请查看以下输出文件:")
print("- 座位信息_最终分配.xlsx (分配结果)")
print("- 最终座位分配日志.xlsx (详细日志)")
print("- seat_allocation_log.txt (运行日志)")
else:
print("\n❌ 座位分配失败!")
else:
print("用户取消座位分配")
else:
print("\n❌ 文件校验失败,请修复问题后重试")
# 保存日志 # 保存日志
system.logger.save_logs() system.logger.save_logs()
except FileNotFoundError as e:
print(f"\n❌ 文件未找到: {e}")
print("请确保所有必要文件都在程序目录下")
except PermissionError as e:
print(f"\n❌ 文件权限错误: {e}")
print("请确保程序有读写文件的权限")
except Exception as e: except Exception as e:
system.logger.log(f"系统运行出错: {e}") print(f"\n❌ 程序运行出错: {e}")
system.logger.save_logs() print("请检查数据文件格式是否正确")
finally:
# 等待用户确认后退出
input("\n程序结束按Enter键退出...")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

496
windows_build.py Normal file
View File

@ -0,0 +1,496 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Windows专用构建脚本
自动检测依赖构建独立exe文件并创建分发包
适用于Windows 7/10/11 (x64)
"""
import os
import sys
import subprocess
import platform
import shutil
import time
from pathlib import Path
class WindowsBuilder:
"""Windows构建器"""
def __init__(self):
self.project_root = Path.cwd()
self.dist_dir = self.project_root / 'dist'
self.build_dir = self.project_root / 'build'
self.package_dir = self.project_root / '座位分配系统_Windows_分发包'
def check_environment(self):
"""检查构建环境"""
print("=" * 60)
print("Windows构建环境检查")
print("=" * 60)
# 检查操作系统
if platform.system() != 'Windows':
print(f"❌ 当前系统: {platform.system()}")
print("此脚本仅适用于Windows系统")
return False
print(f"✅ 操作系统: {platform.system()} {platform.release()}")
print(f"✅ 架构: {platform.machine()}")
print(f"✅ Python版本: {sys.version}")
# 检查主程序文件
main_script = self.project_root / 'seat_allocation_system.py'
if not main_script.exists():
print("❌ 未找到主程序文件: seat_allocation_system.py")
return False
print(f"✅ 主程序文件存在: {main_script}")
return True
def install_dependencies(self):
"""安装构建依赖"""
print("\n" + "=" * 60)
print("安装构建依赖")
print("=" * 60)
required_packages = [
'pandas>=1.3.0',
'openpyxl>=3.0.0',
'numpy>=1.20.0',
'pyinstaller>=4.0'
]
for package in required_packages:
print(f"\n检查 {package}...")
package_name = package.split('>=')[0]
try:
__import__(package_name)
print(f"{package_name} 已安装")
except ImportError:
print(f"📦 安装 {package}...")
try:
cmd = [sys.executable, '-m', 'pip', 'install', package, '--user']
result = subprocess.run(cmd, capture_output=True, text=True,
encoding='utf-8', errors='ignore')
if result.returncode == 0:
print(f"{package} 安装成功")
else:
print(f"{package} 安装失败")
print(f"错误信息: {result.stderr}")
return False
except Exception as e:
print(f"❌ 安装过程出错: {e}")
return False
return True
def clean_build_dirs(self):
"""清理构建目录"""
print("\n清理构建目录...")
for dir_path in [self.dist_dir, self.build_dir]:
if dir_path.exists():
try:
shutil.rmtree(dir_path)
print(f"✅ 清理目录: {dir_path}")
except Exception as e:
print(f"⚠️ 清理目录失败 {dir_path}: {e}")
# 清理spec文件
for spec_file in self.project_root.glob('*.spec'):
try:
spec_file.unlink()
print(f"✅ 清理spec文件: {spec_file}")
except Exception as e:
print(f"⚠️ 清理spec文件失败 {spec_file}: {e}")
def create_spec_file(self):
"""创建PyInstaller配置文件"""
print("\n创建PyInstaller配置文件...")
spec_content = '''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
'pandas',
'openpyxl',
'numpy',
'xlsxwriter',
'xlrd',
'datetime',
'pathlib',
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles'
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'notebook',
'tkinter',
'PyQt5',
'PyQt6',
'PySide2',
'PySide6'
],
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='座位分配系统',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
'''
spec_file = self.project_root / 'seat_allocation.spec'
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"✅ 配置文件已创建: {spec_file}")
return spec_file
def build_executable(self, spec_file):
"""构建可执行文件"""
print("\n" + "=" * 60)
print("开始构建可执行文件")
print("=" * 60)
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
'--noconfirm',
str(spec_file)
]
print(f"执行命令: {' '.join(cmd)}")
print("这可能需要几分钟时间,请耐心等待...")
start_time = time.time()
try:
# 使用Popen来实时显示输出
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
encoding='utf-8',
errors='ignore'
)
# 实时显示输出
for line in process.stdout:
line = line.strip()
if line:
if 'WARNING' in line:
print(f"⚠️ {line}")
elif 'ERROR' in line:
print(f"{line}")
elif 'Building' in line or 'Analyzing' in line:
print(f"🔄 {line}")
elif 'completed successfully' in line:
print(f"{line}")
# 等待进程完成
return_code = process.wait()
build_time = time.time() - start_time
if return_code == 0:
print(f"\n✅ 构建成功! 耗时: {build_time:.1f}")
# 检查生成的文件
exe_path = self.dist_dir / '座位分配系统.exe'
if exe_path.exists():
file_size = exe_path.stat().st_size / (1024 * 1024) # MB
print(f"✅ 生成文件: {exe_path}")
print(f"✅ 文件大小: {file_size:.1f} MB")
return True, exe_path
else:
print("❌ 未找到生成的exe文件")
return False, None
else:
print(f"\n❌ 构建失败! 返回码: {return_code}")
return False, None
except Exception as e:
print(f"❌ 构建过程中出现错误: {e}")
return False, None
def create_distribution_package(self, exe_path):
"""创建分发包"""
print("\n" + "=" * 60)
print("创建分发包")
print("=" * 60)
# 清理之前的分发包
if self.package_dir.exists():
shutil.rmtree(self.package_dir)
self.package_dir.mkdir()
print(f"✅ 创建分发目录: {self.package_dir}")
# 复制可执行文件
dest_exe = self.package_dir / exe_path.name
shutil.copy2(exe_path, dest_exe)
print(f"✅ 复制可执行文件: {exe_path.name}")
# 复制示例文件(如果存在)
example_files = [
('人员信息.xlsx', '人员信息_示例.xlsx'),
('座位信息.xlsx', '座位信息_示例.xlsx')
]
for src_name, dest_name in example_files:
src_path = self.project_root / src_name
if src_path.exists():
dest_path = self.package_dir / dest_name
shutil.copy2(src_path, dest_path)
print(f"✅ 复制示例文件: {dest_name}")
# 创建启动脚本
self.create_startup_script()
# 创建使用说明
self.create_readme()
print(f"\n🎉 分发包创建完成: {self.package_dir}")
return True
def create_startup_script(self):
"""创建启动脚本"""
bat_content = '''@echo off
chcp 65001 >nul
title 座位分配系统
echo ==========================================
echo 座位分配系统 v1.0
echo ==========================================
echo.
:: 检查数据文件
if not exist "人员信息.xlsx" (
echo 缺少文件: 人员信息.xlsx
echo.
echo 请将 人员信息_示例.xlsx 重命名为 人员信息.xlsx
echo 并按照格式填入您的数据
echo.
pause
exit /b 1
)
if not exist "座位信息.xlsx" (
echo 缺少文件: 座位信息.xlsx
echo.
echo 请将 座位信息_示例.xlsx 重命名为 座位信息.xlsx
echo 并按照格式填入您的数据
echo.
pause
exit /b 1
)
echo 数据文件检查通过
echo.
echo 正在启动座位分配系统...
echo.
:: 运行程序
"座位分配系统.exe"
echo.
echo 程序运行完毕
pause
'''
bat_file = self.package_dir / '运行座位分配系统.bat'
with open(bat_file, 'w', encoding='gbk') as f:
f.write(bat_content)
print(f"✅ 创建启动脚本: {bat_file.name}")
def create_readme(self):
"""创建使用说明"""
readme_content = f"""座位分配系统 使用说明
版本: v1.0
构建时间: {time.strftime('%Y-%m-%d %H:%M:%S')}
适用系统: Windows 7/10/11 (64)
====================
快速开始
====================
1. 准备数据文件
- "人员信息_示例.xlsx" 重命名为 "人员信息.xlsx"
- "座位信息_示例.xlsx" 重命名为 "座位信息.xlsx"
- 按照示例格式填入您的实际数据
2. 运行程序
- 双击 "运行座位分配系统.bat" 启动程序
- 或者直接双击 "座位分配系统.exe"
3. 查看结果
- 座位信息_最终分配.xlsx (最终分配结果)
- 最终座位分配日志.xlsx (详细分配记录)
- seat_allocation_log.txt (运行日志)
====================
数据文件格式要求
====================
人员信息.xlsx 必需列:
- 姓名: 人员姓名
- 证件类型: 身份证/护照等
- 证件号: 证件号码
- 手机号: 联系电话
- 备注: 连坐人数(留空表示单独坐)
座位信息.xlsx 必需列:
- 区域: 座位区域
- 楼层: 楼层信息
- 排号: 排号
- 座位号: 具体座位号
====================
连坐规则说明
====================
1. 单人坐位: 备注列留空
2. 连坐组合: 第一人在备注列填写总人数其他人备注留空
例如: 张三(备注:3)李四(备注:)王五(备注:)
表示这3人需要连坐
3. 支持1-10人连坐
4. 系统自动寻找连续座位进行分配
====================
常见问题
====================
Q: 提示缺少依赖包怎么办?
A: 程序会自动尝试安装如果失败请确保网络连接正常
Q: 提示文件权限错误?
A: 请确保程序有读写当前目录的权限
Q: 无法找到连续座位?
A: 请检查座位信息是否完整或调整连坐组大小
Q: Excel文件打不开?
A: 请使用Microsoft Excel 2010或更高版本
====================
技术支持
====================
如果遇到问题请检查以下内容:
1. 数据文件格式是否正确
2. 文件是否存在读写权限
3. 系统是否为Windows 7以上版本
4. 是否有足够的磁盘空间
详细错误信息请查看 seat_allocation_log.txt 文件
"""
readme_file = self.package_dir / '使用说明.txt'
with open(readme_file, 'w', encoding='utf-8') as f:
f.write(readme_content)
print(f"✅ 创建使用说明: {readme_file.name}")
def build(self):
"""执行完整构建流程"""
print("开始Windows构建流程...\n")
# 1. 检查环境
if not self.check_environment():
return False
# 2. 安装依赖
if not self.install_dependencies():
return False
# 3. 清理构建目录
self.clean_build_dirs()
# 4. 创建配置文件
spec_file = self.create_spec_file()
# 5. 构建可执行文件
success, exe_path = self.build_executable(spec_file)
if not success:
return False
# 6. 创建分发包
if not self.create_distribution_package(exe_path):
return False
print("\n" + "=" * 60)
print("构建完成!")
print("=" * 60)
print(f"✅ 分发包位置: {self.package_dir}")
print(f"✅ 可执行文件: {exe_path}")
print("\n使用方法:")
print("1. 将整个分发包复制到目标电脑")
print("2. 准备好人员信息.xlsx和座位信息.xlsx文件")
print("3. 双击运行 '运行座位分配系统.bat'")
return True
def main():
"""主函数"""
builder = WindowsBuilder()
try:
success = builder.build()
if success:
print("\n🎉 构建成功!")
else:
print("\n❌ 构建失败!")
except KeyboardInterrupt:
print("\n\n用户中断构建过程")
except Exception as e:
print(f"\n❌ 构建过程中出现未知错误: {e}")
finally:
input("\n按Enter键退出...")
if __name__ == "__main__":
main()