From 0336d55682d2201abedf4dc9f0832324e74c24a3 Mon Sep 17 00:00:00 2001 From: yovinchen Date: Mon, 30 Jun 2025 21:00:51 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seat_allocation_system.py | 134 +++++++++- windows_build.py | 496 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 620 insertions(+), 10 deletions(-) create mode 100644 windows_build.py diff --git a/seat_allocation_system.py b/seat_allocation_system.py index fad598b..f908ed0 100644 --- a/seat_allocation_system.py +++ b/seat_allocation_system.py @@ -6,11 +6,91 @@ 支持1-10人连坐需求,能够处理不连续座位 """ -import pandas as pd -import numpy as np +import sys +import os from pathlib import Path 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: """日志记录器""" @@ -32,7 +112,7 @@ class Logger: try: with open(self.log_file, 'w', encoding='utf-8') as f: f.write('\n'.join(self.logs)) - self.log(f"日志已保存到: {self.log_file}") + print(f"日志已保存到: {self.log_file}") return True except Exception as e: print(f"保存日志失败: {e}") @@ -879,20 +959,54 @@ class SeatAllocationSystem: def main(): """主函数""" - system = SeatAllocationSystem() - + print("=" * 60) + print("座位分配系统 v1.0") + print("=" * 60) + try: + # 检查数据文件 + if not check_data_files(): + input("\n按Enter键退出...") + return + + print("\n开始运行座位分配系统...") + system = SeatAllocationSystem() + # 运行校验 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() + except FileNotFoundError as e: + print(f"\n❌ 文件未找到: {e}") + print("请确保所有必要文件都在程序目录下") + except PermissionError as e: + print(f"\n❌ 文件权限错误: {e}") + print("请确保程序有读写文件的权限") except Exception as e: - system.logger.log(f"系统运行出错: {e}") - system.logger.save_logs() + print(f"\n❌ 程序运行出错: {e}") + print("请检查数据文件格式是否正确") + + finally: + # 等待用户确认后退出 + input("\n程序结束,按Enter键退出...") if __name__ == "__main__": main() diff --git a/windows_build.py b/windows_build.py new file mode 100644 index 0000000..ae44227 --- /dev/null +++ b/windows_build.py @@ -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() \ No newline at end of file