Compare commits

...

15 Commits

Author SHA1 Message Date
1e9925a9f2 修改打包交脚本 2025-07-02 20:08:23 +08:00
8a103224b5 修改打包交脚本 2025-07-02 19:59:41 +08:00
22b17a1d5d 修改打包交脚本 2025-07-02 19:43:52 +08:00
c0568cc1ec 修改打包交脚本 2025-07-02 19:37:10 +08:00
02341150a9 增加按照手机号排序
增加自动选择文以及识别文件
修改座位信息表位置
2025-07-02 16:26:35 +08:00
f52df5e42d Merge remote-tracking branch 'origin/master' 2025-07-01 15:45:55 +08:00
fcacab52c2 增加文件类型识别以及错误数据校验 2025-07-01 15:42:52 +08:00
f1e8d94266 删除 .idea/vcs.xml 2025-06-30 21:25:09 +08:00
9c3470e60b 项目基本完成 2025-06-30 21:22:02 +08:00
95897979c7 项目基本完成 2025-06-30 21:21:52 +08:00
b9aa686004 项目基本完成 2025-06-30 21:16:37 +08:00
b437f3ffc1 项目基本完成 2025-06-30 21:07:53 +08:00
18d7a4d0a1 Merge remote-tracking branch 'github/master' 2025-06-30 21:07:04 +08:00
0336d55682 项目基本完成 2025-06-30 21:00:51 +08:00
84f11a0906 Create main.yml 2025-06-30 11:08:24 +08:00
20 changed files with 2809 additions and 612 deletions

BIN
1.xlsx Normal file

Binary file not shown.

BIN
2.xlsx Normal file

Binary file not shown.

108
BUILD_FIXES.md Normal file
View File

@@ -0,0 +1,108 @@
# Windows构建问题修复指南
## 🔧 已修复的问题
### 1. Hidden import 'numpy.core._methods' not found
**解决方案**: 在spec文件中添加了完整的numpy隐藏导入
```python
'numpy.core._methods',
'numpy.core._dtype_ctypes',
'numpy.core._internal',
```
### 2. Hidden import "jinja2" not found
**解决方案**: 添加了jinja2相关导入
```python
'jinja2',
'jinja2.ext',
'markupsafe',
```
### 3. Library not found: msvcp140-1a0962f2a91a74c6d7136a768987a591.dll
**解决方案**:
- 禁用UPX压缩 (`upx=False`)
- 添加pandas完整依赖收集
- 使用`--collect-all=pandas`选项
## 🚀 使用修复后的构建脚本
### 方法1: 简化构建(推荐)
```bash
# Windows批处理
build_simple.bat
# 或直接运行Python脚本
python simple_windows_build.py
```
### 方法2: 完整构建
```bash
# 先测试环境
python test_build.py
# 运行完整构建
python windows_build.py
```
## ⚠️ 重要修复
### PyInstaller选项冲突问题
**问题**: `option(s) not allowed` 错误
**原因**: 使用spec文件时不能同时使用某些命令行选项
**解决**: 将所有配置写入spec文件命令行只保留基本选项
```python
# ❌ 错误用法
cmd = ['pyinstaller', '--collect-all=pandas', 'file.spec']
# ✅ 正确用法
cmd = ['pyinstaller', '--clean', '--noconfirm', 'file.spec']
```
## 📋 构建选项说明
### PyInstaller命令行选项
- `--clean`: 清理缓存
- `--noconfirm`: 不询问确认
- `--log-level=INFO`: 详细日志
- `--collect-all=pandas`: 收集pandas所有模块
- `--collect-all=numpy`: 收集numpy所有模块
- `--collect-all=openpyxl`: 收集openpyxl所有模块
### 关键隐藏导入
```python
'numpy.core._methods',
'pandas._libs.window.aggregations',
'ctypes.util',
'pkg_resources.py2_warn',
```
## ⚠️ 常见问题
### 1. 构建时间长
- 正常现象首次构建可能需要5-10分钟
- 后续构建会使用缓存,速度更快
### 2. 文件大小大
- 包含完整Python环境和所有依赖
- 可以通过排除不必要的模块来减小大小
### 3. 警告信息
- 大部分WARNING可以忽略
- 只要没有ERROR构建通常能成功
## 🎯 构建成功标志
```
✅ 构建成功! 耗时: XX.X秒
✅ 生成文件: dist/座位分配系统.exe
✅ 文件大小: XX.X MB
```
## 📦 分发包内容
构建成功后会生成:
- `座位分配系统_Windows_分发包/`
- `座位分配系统.exe` - 主程序
- `README.txt` - 使用说明
- `示例文件/` - 示例Excel文件如果存在

View File

@@ -5,22 +5,35 @@
## 🎯 功能特点
-**智能分配**: 支持1-10人的各种连坐需求
-**数据校验**: 完整的文件格式和逻辑校验
-**智能识别**: 自动识别Excel文件类型支持默认文件名和智能识别两种模式
-**双重分组**: 支持备注分组和手机号分组两种模式
-**数据校验**: 完整的文件格式和逻辑校验,支持重名+身份证号校验
-**不连续座位**: 自动处理座位号间隙
-**详细日志**: 完整的操作记录和结果验证
-**跨平台**: 支持Windows、macOS、Linux
-**智能覆盖**: 人员信息覆盖座位表对应列分配备注单独存储在第13列
## 📋 使用方法
### 准备Excel文件
1. **人员信息.xlsx** - 包含以下列:
1. **人员信息文件** - 包含以下列:
- 姓名、证件类型、证件号、手机号、备注
- 备注数字表示连坐人数备注4表示当前行+后3行共4人连坐
- **备注模式**: 备注数字表示连坐人数备注4表示当前行+后3行共4人连坐
- **手机号模式**: 相同手机号的人员自动识别为连坐组
2. **座位信息.xlsx** - 包含以下列:
2. **座位信息文件** - 包含以下列:
- 区域、楼层、排号、座位号
### 文件识别方式
程序启动时会提供两种文件识别方式:
1. **默认文件名** - 读取固定文件名 `人员信息.xlsx``座位信息.xlsx`
2. **智能识别** - 自动分析目录中的Excel文件并识别类型
- 根据列名特征自动判断文件类型
- 支持任意文件名(如 `1.xlsx``2.xlsx`
- 提供手动选择功能作为备选
### 运行程序
#### Python环境运行
@@ -41,12 +54,28 @@ python simple_build.py
python cross_platform_build.py
```
### 选择文件识别模式
程序运行时首先选择文件识别模式:
1. **默认文件名** - 读取 `人员信息.xlsx``座位信息.xlsx`
2. **智能识别** - 自动识别目录中的Excel文件
### 选择分组方式
然后选择连坐分组方式:
1. **备注分组** - 根据备注数字识别连坐组(默认)
2. **手机号分组** - 根据相同手机号识别连坐组
### 查看结果
程序运行后会生成:
- `座位信息_最终分配.xlsx` - 最终分配结果
- `最终座位分配日志.xlsx` - 详细分配记录
- `seat_allocation_log.txt` - 完整运行日志
- `log/座位信息_最终分配.xlsx` - 最终分配结果
- 第1-5列座位基本信息保持不变
- 第6-10列人员信息覆盖原座位表数据
- 第11-12列原座位表其他列保持不变
- 第13列分配备注新增列
- `log/最终座位分配日志.xlsx` - 详细分配记录
- `log/seat_allocation_log.txt` - 完整运行日志
## 🔧 构建选项
@@ -117,9 +146,12 @@ A区通道 | 一层 | 1排 | 3号
## ⚠️ 注意事项
1. **备注逻辑**: 备注数字表示连坐人数,只有组长填写备注,成员留空
2. **证件号格式**: 支持包含X的身份证号自动处理为字符串格式
3. **文字清理**: 自动清除姓名等字段的多余空格
4. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
2. **手机号逻辑**: 相同手机号的人员会被识别为连坐组
3. **重名处理**: 支持重名人员,通过姓名+身份证号组合进行唯一性校验
4. **证件号格式**: 支持包含X的身份证号自动处理为字符串格式
5. **文字清理**: 自动清除姓名等字段的多余空格
6. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
7. **智能覆盖**: 人员信息覆盖座位表对应列,分配备注单独存储
## 🔍 故障排除

31
build_simple.bat Normal file
View File

@@ -0,0 +1,31 @@
@echo off
chcp 65001 >nul
echo ============================================================
echo 简化Windows构建脚本
echo ============================================================
echo.
echo 检查Python环境...
python --version
if errorlevel 1 (
echo ❌ Python未安装或未添加到PATH
pause
exit /b 1
)
echo.
echo 检查依赖包...
python -c "import pandas, numpy, openpyxl, PyInstaller; print('✅ 所有依赖已安装')"
if errorlevel 1 (
echo ❌ 缺少必要的依赖包
echo 正在安装依赖...
pip install pandas numpy openpyxl pyinstaller
)
echo.
echo 开始构建...
python simple_windows_build.py
echo.
echo 构建完成!
pause

138
build_windows.bat Normal file
View File

@@ -0,0 +1,138 @@
@echo off
chcp 65001 >nul
echo ================================
echo 座位分配系统 Windows 打包脚本
echo ================================
echo.
:: 检查Python是否安装
python --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] Python未安装或不在PATH中
echo 请先安装Python 3.7+
pause
exit /b 1
)
echo [信息] 检查Python版本...
python --version
:: 检查pip是否可用
pip --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] pip不可用
pause
exit /b 1
)
:: 安装PyInstaller
echo.
echo [步骤1] 安装PyInstaller...
pip install pyinstaller
:: 检查PyInstaller是否安装成功
pyinstaller --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] PyInstaller安装失败
pause
exit /b 1
)
:: 创建build目录
echo.
echo [步骤2] 创建构建目录...
if not exist "build" mkdir build
if not exist "dist" mkdir dist
:: 安装依赖包
echo.
echo [步骤3] 安装依赖包...
pip install pandas numpy openpyxl
:: 打包主程序
echo.
echo [步骤4] 开始打包座位分配系统...
pyinstaller --onefile ^
--console ^
--name "座位分配系统" ^
--icon=icon.ico ^
--add-data "README.md;." ^
--distpath "dist" ^
--workpath "build" ^
--specpath "build" ^
--clean ^
seat_allocation_system.py
:: 检查打包是否成功
if exist "dist\座位分配系统.exe" (
echo.
echo [成功] 打包完成!
echo 可执行文件位置: dist\座位分配系统.exe
:: 创建发布目录
echo.
echo [步骤5] 创建发布包...
if not exist "release" mkdir release
:: 复制可执行文件
copy "dist\座位分配系统.exe" "release\"
:: 创建示例文件夹
if not exist "release\示例文件" mkdir "release\示例文件"
:: 复制示例文件(如果存在)
if exist "人员信息_示例.xlsx" copy "人员信息_示例.xlsx" "release\示例文件\"
if exist "座位信息_示例.xlsx" copy "座位信息_示例.xlsx" "release\示例文件\"
:: 创建使用说明
echo.
echo [步骤6] 创建使用说明...
(
echo 座位分配系统 v2.0
echo ==================
echo.
echo 使用方法:
echo 1. 将人员信息和座位信息的Excel文件放在程序同一目录下
echo 2. 双击运行"座位分配系统.exe"
echo 3. 按照提示操作
echo.
echo 文件要求:
echo - 人员信息文件:包含姓名、证件类型、证件号、手机号、备注等列
echo - 座位信息文件:包含区域、楼层、排号、座位号等列
echo.
echo 输出文件:
echo - log/座位信息_最终分配.xlsx (分配结果)
echo - log/最终座位分配日志.xlsx (详细日志)
echo - log/seat_allocation_log.txt (运行日志)
echo.
echo 注意事项:
echo - 程序支持智能文件识别
echo - 支持1-10人连坐需求
echo - 支持备注分组和手机号分组两种模式
echo.
echo 如有问题请查看日志文件
) > "release\使用说明.txt"
echo.
echo ================================
echo 打包完成!
echo ================================
echo.
echo 发布文件位置: release\
echo 主程序: release\座位分配系统.exe
echo 使用说明: release\使用说明.txt
echo 示例文件: release\示例文件\
echo.
echo 您可以将整个release文件夹分发给用户
) else (
echo.
echo [错误] 打包失败!
echo 请检查错误信息并重试
pause
exit /b 1
)
echo.
echo 按任意键继续...
pause >nul

226
build_windows.ps1 Normal file
View File

@@ -0,0 +1,226 @@
# 座位分配系统 Windows 打包脚本 (PowerShell版本)
# 编码UTF-8
Write-Host "================================" -ForegroundColor Cyan
Write-Host " 座位分配系统 Windows 打包脚本" -ForegroundColor Cyan
Write-Host "================================" -ForegroundColor Cyan
Write-Host ""
# 设置错误处理
$ErrorActionPreference = "Stop"
try {
# 检查Python是否安装
Write-Host "[信息] 检查Python环境..." -ForegroundColor Yellow
$pythonVersion = python --version 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Python未安装或不在PATH中请先安装Python 3.7+"
}
Write-Host "Python版本: $pythonVersion" -ForegroundColor Green
# 检查pip
$pipVersion = pip --version 2>&1
if ($LASTEXITCODE -ne 0) {
throw "pip不可用"
}
Write-Host "pip版本: $pipVersion" -ForegroundColor Green
# 升级pip
Write-Host ""
Write-Host "[步骤1] 升级pip..." -ForegroundColor Yellow
python -m pip install --upgrade pip
# 安装PyInstaller
Write-Host ""
Write-Host "[步骤2] 安装PyInstaller..." -ForegroundColor Yellow
pip install pyinstaller
# 安装依赖包
Write-Host ""
Write-Host "[步骤3] 安装依赖包..." -ForegroundColor Yellow
pip install pandas numpy openpyxl
# 创建必要的目录
Write-Host ""
Write-Host "[步骤4] 创建构建目录..." -ForegroundColor Yellow
$dirs = @("build", "dist", "release", "release\示例文件")
foreach ($dir in $dirs) {
if (!(Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
Write-Host "创建目录: $dir" -ForegroundColor Gray
}
}
# 检查主文件是否存在
if (!(Test-Path "seat_allocation_system.py")) {
throw "找不到主程序文件 seat_allocation_system.py"
}
# 打包主程序
Write-Host ""
Write-Host "[步骤5] 开始打包座位分配系统..." -ForegroundColor Yellow
Write-Host "这可能需要几分钟时间,请耐心等待..." -ForegroundColor Gray
$pyinstallerArgs = @(
"--onefile",
"--console",
"--name", "座位分配系统",
"--distpath", "dist",
"--workpath", "build",
"--specpath", "build",
"--clean",
"seat_allocation_system.py"
)
# 如果存在图标文件,添加图标参数
if (Test-Path "icon.ico") {
$pyinstallerArgs += "--icon", "icon.ico"
}
# 如果存在README文件添加数据文件
if (Test-Path "README.md") {
$pyinstallerArgs += "--add-data", "README.md;."
}
& pyinstaller @pyinstallerArgs
if ($LASTEXITCODE -ne 0) {
throw "PyInstaller打包失败"
}
# 检查打包结果
$exePath = "dist\座位分配系统.exe"
if (!(Test-Path $exePath)) {
throw "打包失败:找不到生成的可执行文件"
}
# 复制文件到发布目录
Write-Host ""
Write-Host "[步骤6] 创建发布包..." -ForegroundColor Yellow
# 复制主程序
Copy-Item $exePath "release\" -Force
Write-Host "复制主程序到发布目录" -ForegroundColor Gray
# 复制示例文件
$exampleFiles = @("人员信息_示例.xlsx", "座位信息_示例.xlsx", "*.xlsx")
foreach ($pattern in $exampleFiles) {
$files = Get-ChildItem -Path . -Name $pattern -ErrorAction SilentlyContinue
foreach ($file in $files) {
if ($file -notlike "*_最终分配*" -and $file -notlike "*日志*") {
Copy-Item $file "release\示例文件\" -Force -ErrorAction SilentlyContinue
Write-Host "复制示例文件: $file" -ForegroundColor Gray
}
}
}
# 创建使用说明
Write-Host ""
Write-Host "[步骤7] 创建使用说明..." -ForegroundColor Yellow
$readme = @"
v2.0
==================
使
1. Excel
2. ".exe"
3.
-
-
- Excel
- 1-10
-
-
-
-
log
- log/_.xlsx
- log/.xlsx
- log/seat_allocation_log.txt
1.
2.
3.
4.
5.
- Excel
- ""
-
-
log
- v2.0 ()
- Windows 7/8/10/11
- Python
"@
$readme | Out-File -FilePath "release\使用说明.txt" -Encoding UTF8
# 创建批处理启动文件(可选)
$batchContent = @"
@echo off
chcp 65001 >nul
echo ...
echo.
.exe
echo.
echo
pause
"@
$batchContent | Out-File -FilePath "release\启动座位分配系统.bat" -Encoding UTF8
# 显示完成信息
Write-Host ""
Write-Host "================================" -ForegroundColor Green
Write-Host " 打包完成!" -ForegroundColor Green
Write-Host "================================" -ForegroundColor Green
Write-Host ""
Write-Host "发布文件位置:" -ForegroundColor Cyan
Write-Host "📁 release\" -ForegroundColor Yellow
Write-Host " ├── 座位分配系统.exe (主程序)" -ForegroundColor White
Write-Host " ├── 启动座位分配系统.bat (启动脚本)" -ForegroundColor White
Write-Host " ├── 使用说明.txt (使用说明)" -ForegroundColor White
Write-Host " └── 示例文件\ (示例文件夹)" -ForegroundColor White
Write-Host ""
Write-Host "文件大小信息:" -ForegroundColor Cyan
$fileSize = (Get-Item $exePath).Length / 1MB
Write-Host "主程序大小: $([math]::Round($fileSize, 2)) MB" -ForegroundColor Yellow
Write-Host ""
Write-Host "部署说明:" -ForegroundColor Cyan
Write-Host "1. 将整个 release 文件夹分发给用户" -ForegroundColor White
Write-Host "2. 用户直接运行 座位分配系统.exe 或 启动座位分配系统.bat" -ForegroundColor White
Write-Host "3. 无需安装Python环境" -ForegroundColor White
Write-Host ""
Write-Host "✅ 打包成功完成!" -ForegroundColor Green
} catch {
Write-Host ""
Write-Host "❌ 打包过程中出现错误:" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host ""
Write-Host "常见解决方案:" -ForegroundColor Yellow
Write-Host "1. 确保已安装Python 3.7+" -ForegroundColor White
Write-Host "2. 确保网络连接正常(需要下载依赖包)" -ForegroundColor White
Write-Host "3. 确保有足够的磁盘空间" -ForegroundColor White
Write-Host "4. 尝试以管理员权限运行此脚本" -ForegroundColor White
exit 1
}
Write-Host ""
Write-Host "按任意键继续..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

View File

@@ -1,326 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
跨平台构建脚本
在macOS上构建Windows x86/x64版本的exe文件
使用Wine和PyInstaller实现跨平台编译
"""
import os
import sys
import subprocess
import platform
import shutil
from pathlib import Path
def check_dependencies():
"""检查必要的依赖"""
print("检查构建依赖...")
# 检查是否在macOS上
if platform.system() != 'Darwin':
print(f"当前系统: {platform.system()}")
print("此脚本专为macOS设计用于构建Windows版本")
return False
# 检查Python包
required_packages = ['pandas', 'openpyxl', 'pyinstaller']
missing_packages = []
for package in required_packages:
try:
__import__(package)
print(f"{package} 已安装")
except ImportError:
missing_packages.append(package)
print(f"{package} 未安装")
if missing_packages:
print(f"\n安装缺失的包...")
for package in missing_packages:
try:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
print(f"{package} 安装成功")
except subprocess.CalledProcessError:
print(f"{package} 安装失败")
return False
return True
def install_wine():
"""安装Wine如果需要"""
print("\n检查Wine环境...")
# 检查是否已安装Wine
try:
result = subprocess.run(['wine', '--version'], capture_output=True, text=True)
if result.returncode == 0:
print(f"✅ Wine已安装: {result.stdout.strip()}")
return True
except FileNotFoundError:
pass
print("Wine未安装开始安装...")
print("请按照以下步骤手动安装Wine:")
print("1. 安装Homebrew (如果未安装): /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"")
print("2. 安装Wine: brew install --cask wine-stable")
print("3. 配置Wine: winecfg")
response = input("是否已完成Wine安装? (y/N): ")
return response.lower() == 'y'
def setup_wine_python():
"""在Wine中设置Python环境"""
print("\n设置Wine Python环境...")
# 检查Wine中是否已安装Python
try:
result = subprocess.run(['wine', 'python', '--version'], capture_output=True, text=True)
if result.returncode == 0:
print(f"✅ Wine Python已安装: {result.stdout.strip()}")
return True
except:
pass
print("需要在Wine中安装Python...")
print("请按照以下步骤:")
print("1. 下载Windows版Python: https://www.python.org/downloads/windows/")
print("2. 使用Wine安装: wine python-3.10.x-amd64.exe")
print("3. 安装pip包: wine python -m pip install pandas openpyxl pyinstaller")
response = input("是否已完成Wine Python安装? (y/N): ")
return response.lower() == 'y'
def create_build_spec(target_arch='x64'):
"""创建构建规格文件"""
exe_name = f"座位分配系统_{target_arch}"
spec_content = f'''# -*- 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'
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'notebook',
'tkinter'
],
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='{exe_name}',
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,
)
'''
spec_file = f'{exe_name}.spec'
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
return spec_file, exe_name
def build_with_docker():
"""使用Docker构建Windows版本"""
print("\n使用Docker构建Windows版本...")
# 创建Dockerfile
dockerfile_content = '''FROM python:3.10-windowsservercore
# 设置工作目录
WORKDIR /app
# 复制文件
COPY requirements.txt .
COPY seat_allocation_system.py .
# 安装依赖
RUN pip install -r requirements.txt
# 构建exe
RUN pyinstaller --onefile --console --name "座位分配系统_x64" seat_allocation_system.py
# 输出目录
VOLUME ["/app/dist"]
'''
with open('Dockerfile.windows', 'w') as f:
f.write(dockerfile_content)
print("Docker方法需要Docker Desktop和Windows容器支持")
print("这是一个高级选项,建议使用其他方法")
return False
def build_native_macos():
"""在macOS上直接构建生成macOS版本"""
print("\n在macOS上构建本地版本...")
# 清理之前的构建
if os.path.exists('dist'):
shutil.rmtree('dist')
if os.path.exists('build'):
shutil.rmtree('build')
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--onefile',
'--console',
'--clean',
'--name', '座位分配系统_macos',
'seat_allocation_system.py'
]
try:
print(f"执行命令: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ macOS版本构建成功!")
# 检查生成的文件
app_path = Path('dist') / '座位分配系统_macos'
if app_path.exists():
file_size = app_path.stat().st_size / (1024 * 1024) # MB
print(f"生成文件: {app_path}")
print(f"文件大小: {file_size:.1f} MB")
return True, app_path
else:
print("❌ 构建失败!")
print("错误输出:")
print(result.stderr)
return False, None
except Exception as e:
print(f"❌ 构建过程中出现错误: {e}")
return False, None
def create_cross_compile_script():
"""创建跨编译脚本"""
script_content = '''#!/bin/bash
# Windows交叉编译脚本
echo "座位分配系统 - Windows交叉编译"
echo "================================"
# 检查是否安装了mingw-w64
if ! command -v x86_64-w64-mingw32-gcc &> /dev/null; then
echo "安装mingw-w64..."
brew install mingw-w64
fi
# 设置交叉编译环境
export CC=x86_64-w64-mingw32-gcc
export CXX=x86_64-w64-mingw32-g++
# 构建Windows版本
echo "开始构建Windows x64版本..."
python -m PyInstaller \\
--onefile \\
--console \\
--clean \\
--name "座位分配系统_x64" \\
--target-arch x86_64 \\
seat_allocation_system.py
echo "构建完成!"
'''
with open('cross_compile.sh', 'w') as f:
f.write(script_content)
# 设置执行权限
os.chmod('cross_compile.sh', 0o755)
print("✅ 已创建交叉编译脚本: cross_compile.sh")
def main():
"""主函数"""
print("座位分配系统 - 跨平台构建工具")
print("=" * 50)
print("在macOS上构建Windows版本")
# 检查依赖
if not check_dependencies():
return
print("\n选择构建方法:")
print("1. 构建macOS版本推荐")
print("2. 使用Wine构建Windows版本复杂")
print("3. 创建交叉编译脚本(实验性)")
print("4. 生成Docker构建文件高级")
choice = input("\n请选择 (1-4): ").strip()
if choice == '1':
print("\n构建macOS版本...")
success, app_path = build_native_macos()
if success:
print(f"\n🎉 构建完成!")
print(f"生成文件: {app_path}")
print("\n注意: 这是macOS版本不能在Windows上运行")
print("如需Windows版本请在Windows系统上运行构建脚本")
elif choice == '2':
print("\n使用Wine构建Windows版本...")
if install_wine() and setup_wine_python():
print("Wine环境已准备就绪")
print("请手动在Wine中运行: wine python windows_build.py")
else:
print("Wine环境设置失败")
elif choice == '3':
print("\n创建交叉编译脚本...")
create_cross_compile_script()
print("请运行: ./cross_compile.sh")
elif choice == '4':
print("\n生成Docker构建文件...")
build_with_docker()
else:
print("无效选择")
if __name__ == "__main__":
main()

42
fix_dll_build.bat Normal file
View File

@@ -0,0 +1,42 @@
@echo off
chcp 65001 >nul
echo ============================================================
echo pandas DLL修复构建脚本
echo 专门解决: ImportError: DLL load failed while importing aggregations
echo ============================================================
echo.
echo 检查Python环境...
python --version
if errorlevel 1 (
echo ❌ Python未安装或未添加到PATH
pause
exit /b 1
)
echo.
echo 检查pandas安装...
python -c "import pandas; print(f'✅ pandas版本: {pandas.__version__}')"
if errorlevel 1 (
echo ❌ pandas未安装
echo 正在安装pandas...
pip install pandas
)
echo.
echo 检查PyInstaller...
python -c "import PyInstaller; print('✅ PyInstaller已安装')"
if errorlevel 1 (
echo ❌ PyInstaller未安装
echo 正在安装PyInstaller...
pip install pyinstaller
)
echo.
echo 开始DLL修复构建...
python fix_dll_build.py
echo.
echo 构建完成!
echo 如果成功,可执行文件位于: dist\座位分配系统_修复版.exe
pause

302
fix_dll_build.py Normal file
View File

@@ -0,0 +1,302 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
修复DLL加载失败的专用构建脚本
专门解决pandas._libs.window.aggregations DLL问题
"""
import sys
import subprocess
import platform
import site
from pathlib import Path
def get_pandas_libs_path():
"""获取pandas库文件路径"""
try:
import pandas
pandas_path = Path(pandas.__file__).parent
libs_path = pandas_path / '_libs'
return libs_path
except ImportError:
return None
def create_dll_fix_spec():
"""创建修复DLL问题的spec文件"""
# 获取pandas库路径
pandas_libs = get_pandas_libs_path()
if not pandas_libs or not pandas_libs.exists():
print("❌ 无法找到pandas库路径")
return None
print(f"✅ 找到pandas库路径: {pandas_libs}")
# 查找所有.pyd文件Windows DLL
pyd_files = list(pandas_libs.glob('**/*.pyd'))
print(f"✅ 找到 {len(pyd_files)} 个pandas DLL文件")
# 构建binaries列表
binaries_list = []
for pyd_file in pyd_files:
rel_path = pyd_file.relative_to(pandas_libs.parent)
binaries_list.append(f"(r'{pyd_file}', r'{rel_path.parent}')")
binaries_str = ',\n '.join(binaries_list)
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
# 专门修复pandas DLL加载问题的配置
import sys
from pathlib import Path
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[],
binaries=[
# 手动包含所有pandas DLL文件
{binaries_str}
],
datas=[],
hiddenimports=[
# 核心模块
'pandas',
'numpy',
'openpyxl',
# pandas核心库必须
'pandas._libs',
'pandas._libs.lib',
'pandas._libs.hashtable',
'pandas._libs.tslib',
'pandas._libs.algos',
'pandas._libs.join',
'pandas._libs.index',
'pandas._libs.internals',
'pandas._libs.writers',
'pandas._libs.parsers',
'pandas._libs.testing',
'pandas._libs.sparse',
'pandas._libs.reduction',
'pandas._libs.ops',
'pandas._libs.missing',
'pandas._libs.groupby',
'pandas._libs.reshape',
# pandas时间序列库
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas._libs.tslibs.ccalendar',
'pandas._libs.tslibs.conversion',
'pandas._libs.tslibs.dtypes',
'pandas._libs.tslibs.fields',
'pandas._libs.tslibs.nattype',
'pandas._libs.tslibs.np_datetime',
'pandas._libs.tslibs.offsets',
'pandas._libs.tslibs.parsing',
'pandas._libs.tslibs.period',
'pandas._libs.tslibs.strptime',
'pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.timestamps',
'pandas._libs.tslibs.timezones',
'pandas._libs.tslibs.tzconversion',
'pandas._libs.tslibs.vectorized',
# pandas窗口函数库关键
'pandas._libs.window',
'pandas._libs.window.aggregations',
'pandas._libs.window.indexers',
# numpy核心
'numpy.core._methods',
'numpy.core._dtype_ctypes',
'numpy.core.multiarray',
'numpy.core.umath',
# 其他必要模块
'ctypes.util',
'pkg_resources.py2_warn',
'encodings.utf_8',
'encodings.gbk',
# openpyxl
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles',
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'tkinter',
'PyQt5',
'PyQt6',
'PIL',
'cv2',
'sklearn',
'tensorflow',
'torch',
],
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压缩避免DLL问题
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 = Path('dll_fix.spec')
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"✅ 创建DLL修复spec文件: {spec_file}")
return spec_file
def build_with_dll_fix():
"""使用DLL修复配置构建"""
print("=" * 60)
print("DLL修复构建")
print("=" * 60)
# 检查环境
if platform.system() != 'Windows':
print("❌ 此脚本仅适用于Windows")
return False
print(f"✅ 系统: {platform.system()} {platform.release()}")
# 检查主文件
main_file = Path('seat_allocation_system.py')
if not main_file.exists():
print("❌ 未找到主程序文件")
return False
# 创建修复spec文件
spec_file = create_dll_fix_spec()
if not spec_file:
return False
# 清理旧文件
dist_dir = Path('dist')
if dist_dir.exists():
import shutil
shutil.rmtree(dist_dir)
print("✅ 清理dist目录")
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
'--noconfirm',
'--log-level=WARN', # 减少输出
str(spec_file)
]
print(f"\n执行命令: {' '.join(cmd)}")
print("开始DLL修复构建...")
try:
# 执行构建
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
# 显示结果
if result.returncode == 0:
print("✅ DLL修复构建成功!")
# 检查生成的文件
exe_path = Path('dist/座位分配系统_修复版.exe')
if exe_path.exists():
size_mb = exe_path.stat().st_size / (1024 * 1024)
print(f"✅ 生成文件: {exe_path}")
print(f"✅ 文件大小: {size_mb:.1f} MB")
# 测试运行
print("\n🧪 测试运行...")
test_cmd = [str(exe_path), '--help']
try:
test_result = subprocess.run(
test_cmd,
capture_output=True,
text=True,
timeout=10
)
if test_result.returncode == 0:
print("✅ 程序可以正常启动")
else:
print("⚠️ 程序启动有问题,但文件已生成")
except subprocess.TimeoutExpired:
print("⚠️ 测试超时,但文件已生成")
except Exception as e:
print(f"⚠️ 测试失败: {e}")
return True
else:
print("❌ 未找到生成的exe文件")
return False
else:
print("❌ DLL修复构建失败!")
print("错误输出:")
print(result.stderr)
return False
except Exception as e:
print(f"❌ 构建出错: {e}")
return False
def main():
"""主函数"""
print("🔧 pandas DLL加载失败修复工具")
print("专门解决: ImportError: DLL load failed while importing aggregations")
print()
success = build_with_dll_fix()
if success:
print("\n🎉 DLL修复构建完成!")
print("可执行文件: dist/座位分配系统_修复版.exe")
print("\n💡 如果仍有问题,请尝试:")
print("1. 在目标机器上安装 Visual C++ Redistributable")
print("2. 确保目标机器有相同的Windows版本")
else:
print("\n❌ DLL修复构建失败!")
print("请检查pandas安装是否完整")
input("\n按Enter键退出...")
if __name__ == '__main__':
main()

View File

@@ -6,16 +6,209 @@
支持1-10人连坐需求能够处理不连续座位
"""
import pandas as pd
import numpy as np
import sys
import os
from pathlib import Path
import datetime
import sys
import pandas as pd
import numpy as np
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 auto_detect_files():
"""自动检测和识别Excel文件"""
print("\n=== 自动文件识别系统 ===")
# 扫描当前目录下的所有xlsx文件
current_dir = Path('.')
xlsx_files = list(current_dir.glob('*.xlsx'))
# 过滤掉输出文件和示例文件
exclude_patterns = ['座位信息_最终分配', '最终座位分配日志', '_示例', '_temp', '_backup']
xlsx_files = [f for f in xlsx_files if not any(pattern in f.name for pattern in exclude_patterns)]
print(f"发现 {len(xlsx_files)} 个Excel文件:")
for i, file_path in enumerate(xlsx_files, 1):
file_size = file_path.stat().st_size / 1024 # KB
print(f" {i}. {file_path.name} ({file_size:.1f} KB)")
if len(xlsx_files) == 0:
print("\n❌ 未找到Excel文件")
print("请确保当前目录下有Excel数据文件")
return None, None
if len(xlsx_files) > 2:
print(f"\n⚠️ 发现 {len(xlsx_files)} 个Excel文件超过2个")
print("为避免混淆请确保目录下只有2个数据文件")
print("(程序会自动忽略输出文件和示例文件)")
print("\n请手动移除多余文件,或选择要使用的文件:")
for i, file_path in enumerate(xlsx_files, 1):
print(f" {i}. {file_path.name}")
return None, None
# 分析文件结构
print(f"\n正在分析文件结构...")
file_info = []
for file_path in xlsx_files:
try:
# 读取文件获取基本信息
df = pd.read_excel(file_path, nrows=5) # 只读前5行进行分析
info = {
'path': file_path,
'name': file_path.name,
'columns': len(df.columns),
'rows': len(df) + 1, # 加上标题行
'column_names': list(df.columns),
'sample_data': df.head(2) # 获取前2行样本数据
}
file_info.append(info)
print(f"\n📄 {file_path.name}:")
print(f" 列数: {info['columns']}")
print(f" 行数: {info['rows']}+ (仅检查前5行)")
print(f" 列名: {', '.join(str(col) for col in info['column_names'][:6])}{'...' if len(info['column_names']) > 6 else ''}")
except Exception as e:
print(f"\n❌ 读取文件 {file_path.name} 失败: {e}")
return None, None
# 根据列数自动识别文件类型
personnel_file = None
seat_file = None
print(f"\n=== 文件类型识别 ===")
for info in file_info:
if info['columns'] == 5 or info['columns'] == 6: # 人员信息通常是5-6列
# 进一步检查列名是否符合人员信息格式
col_names = [str(col).lower() for col in info['column_names']]
personnel_keywords = ['姓名', 'name', '证件', 'id', '手机', 'phone', '备注', 'remark']
if any(keyword in ' '.join(col_names) for keyword in personnel_keywords):
personnel_file = info
print(f"{info['name']} -> 识别为人员信息文件 ({info['columns']}列)")
else:
print(f"⚠️ {info['name']}{info['columns']}列,但列名不符合人员信息格式")
elif info['columns'] >= 10: # 座位信息通常是10-12列
# 进一步检查列名是否符合座位信息格式
col_names = [str(col).lower() for col in info['column_names']]
seat_keywords = ['区域', 'area', '楼层', 'floor', '排号', 'row', '座位', 'seat']
if any(keyword in ' '.join(col_names) for keyword in seat_keywords):
seat_file = info
print(f"{info['name']} -> 识别为座位信息文件 ({info['columns']}列)")
else:
print(f"⚠️ {info['name']}{info['columns']}列,但列名不符合座位信息格式")
else:
print(f"{info['name']}{info['columns']}列,无法自动识别类型")
# 验证识别结果
if personnel_file is None:
print(f"\n❌ 未找到人员信息文件")
print("人员信息文件应包含: 姓名、证件类型、证件号、手机号、备注等列")
return None, None
if seat_file is None:
print(f"\n❌ 未找到座位信息文件")
print("座位信息文件应包含: 区域、楼层、排号、座位号等列")
return None, None
print(f"\n🎉 文件识别成功!")
print(f"人员信息: {personnel_file['name']}")
print(f"座位信息: {seat_file['name']}")
# 显示文件内容预览
print(f"\n=== 文件内容预览 ===")
print(f"\n📋 人员信息文件预览 ({personnel_file['name']}):")
print(personnel_file['sample_data'].to_string(index=False, max_cols=10))
print(f"\n🪑 座位信息文件预览 ({seat_file['name']}):")
print(seat_file['sample_data'].to_string(index=False, max_cols=10))
return personnel_file['path'], seat_file['path']
def check_data_files():
"""检查并自动识别数据文件"""
print("\n=== 数据文件检查 ===")
# 首先尝试自动识别
personnel_file, seat_file = auto_detect_files()
if personnel_file is None or seat_file is None:
print(f"\n❌ 自动识别失败")
print("\n请检查以下要求:")
print("1. 确保目录下有且仅有2个Excel文件")
print("2. 人员信息文件应有5-6列姓名、证件类型、证件号、手机号、备注等")
print("3. 座位信息文件应有10+列(区域、楼层、排号、座位号等)")
print("4. 列名应包含相关关键词")
print("\n提示: 您可以参考示例文件来准备数据")
return False, None, None
return True, str(personnel_file), str(seat_file)
# 检查依赖
if not check_dependencies():
print("❌ 依赖检查失败")
input("按Enter键退出...")
sys.exit(1)
class Logger:
"""日志记录器"""
def __init__(self, log_file='seat_allocation_log.txt'):
self.log_file = log_file
# 创建log文件夹
self.log_dir = Path('log')
self.log_dir.mkdir(exist_ok=True)
# 设置日志文件路径
self.log_file = self.log_dir / log_file
self.logs = []
def log(self, message, print_to_console=True):
@@ -32,11 +225,15 @@ 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}")
return False
def get_log_path(self, filename):
"""获取log文件夹中的文件路径"""
return self.log_dir / filename
class SeatAllocationSystem:
"""座位分配系统"""
@@ -47,34 +244,245 @@ class SeatAllocationSystem:
self.seat_df = None
self.seating_groups = []
self.row_analysis = {}
self.personnel_file = None
self.seat_file = None
def choose_file_mode(self):
"""选择文件识别模式"""
self.logger.log("\n=== 选择文件识别模式 ===")
self.logger.log("1. 默认文件名 - 读取 '人员信息.xlsx''座位信息.xlsx'")
self.logger.log("2. 智能识别 - 自动识别目录中的Excel文件")
while True:
try:
choice = input("\n请选择文件识别模式 (1/2直接回车选择默认): ").strip()
if choice == '' or choice == '1':
self.logger.log("✅ 选择默认文件名模式")
return 'default'
elif choice == '2':
self.logger.log("✅ 选择智能识别模式")
return 'smart'
else:
print("请输入 1 或 2")
except KeyboardInterrupt:
self.logger.log("\n用户取消操作")
return 'default'
def smart_identify_files(self):
"""智能识别Excel文件"""
self.logger.log("\n=== 智能识别Excel文件 ===")
# 获取当前目录下的所有Excel文件
excel_files = []
for ext in ['*.xlsx', '*.xls']:
excel_files.extend(Path('.').glob(ext))
if len(excel_files) < 2:
self.logger.log(f"❌ 当前目录只找到 {len(excel_files)} 个Excel文件需要至少2个")
return None, None
self.logger.log(f"📁 找到 {len(excel_files)} 个Excel文件:")
# 分析每个文件的内容
file_analysis = []
for file_path in excel_files:
try:
# 先读取完整文件获取真实行数
df_full = pd.read_excel(file_path)
total_rows = len(df_full)
# 再读取前5行进行列分析
df_sample = pd.read_excel(file_path, nrows=5)
analysis = {
'path': file_path,
'name': file_path.name,
'rows': total_rows, # 使用真实行数
'cols': len(df_sample.columns),
'columns': list(df_sample.columns),
'score': 0,
'type': 'unknown'
}
# 判断文件类型 - 改进的识别算法
columns_str = ' '.join(str(col).lower() for col in df_sample.columns)
columns_list = [str(col).lower() for col in df_sample.columns]
# 座位信息文件的强特征(优先判断)
seat_strong_keywords = ['座位id', '区域', '楼层', '排号', '座位号']
seat_strong_score = sum(1 for keyword in seat_strong_keywords if keyword in columns_str)
# 人员信息文件的强特征
personnel_strong_keywords = ['备注'] # 备注列是人员信息文件的强特征
personnel_strong_score = sum(1 for keyword in personnel_strong_keywords if keyword in columns_str)
# 如果有座位强特征,优先识别为座位文件
if seat_strong_score >= 3: # 至少包含3个座位强特征
analysis['type'] = 'seat'
analysis['score'] = seat_strong_score * 10 # 给予高权重
# 如果有人员强特征且列数较少,识别为人员文件
elif personnel_strong_score > 0 and len(df_sample.columns) <= 8:
analysis['type'] = 'personnel'
analysis['score'] = personnel_strong_score * 10
else:
# 使用原有的弱特征判断
personnel_keywords = ['姓名', '证件', '手机', 'name', 'id', 'phone']
personnel_score = sum(1 for keyword in personnel_keywords if keyword in columns_str)
seat_keywords = ['区域', '楼层', '排号', '座位', 'area', 'floor', 'row', 'seat']
seat_score = sum(1 for keyword in seat_keywords if keyword in columns_str)
# 结合列数进行判断
if len(df_sample.columns) <= 8 and personnel_score > 0:
analysis['type'] = 'personnel'
analysis['score'] = personnel_score
elif len(df_sample.columns) > 8 and seat_score > 0:
analysis['type'] = 'seat'
analysis['score'] = seat_score
elif personnel_score > seat_score:
analysis['type'] = 'personnel'
analysis['score'] = personnel_score
elif seat_score > personnel_score:
analysis['type'] = 'seat'
analysis['score'] = seat_score
file_analysis.append(analysis)
self.logger.log(f" 📄 {file_path.name}: {total_rows}× {len(df_sample.columns)}")
self.logger.log(f" 列名: {', '.join(str(col) for col in df_sample.columns[:5])}{'...' if len(df_sample.columns) > 5 else ''}")
self.logger.log(f" 推测类型: {analysis['type']} (得分: {analysis['score']})")
except Exception as e:
self.logger.log(f"{file_path.name}: 读取失败 - {e}")
# 自动匹配最佳文件
personnel_files = [f for f in file_analysis if f['type'] == 'personnel']
seat_files = [f for f in file_analysis if f['type'] == 'seat']
personnel_file = None
seat_file = None
if personnel_files:
personnel_file = max(personnel_files, key=lambda x: x['score'])['path']
self.logger.log(f"\n🎯 自动识别人员信息文件: {personnel_file.name}")
if seat_files:
seat_file = max(seat_files, key=lambda x: x['score'])['path']
self.logger.log(f"🎯 自动识别座位信息文件: {seat_file.name}")
# 如果自动识别不完整,提供手动选择
if not personnel_file or not seat_file:
self.logger.log("\n⚠️ 自动识别不完整,请手动选择:")
personnel_file, seat_file = self.manual_select_files(file_analysis)
return personnel_file, seat_file
def manual_select_files(self, file_analysis):
"""手动选择文件"""
self.logger.log("\n=== 手动选择文件 ===")
print("\n可用的Excel文件:")
for i, analysis in enumerate(file_analysis):
print(f"{i+1}. {analysis['name']} ({analysis['rows']}× {analysis['cols']}列)")
print(f" 列名: {', '.join(str(col) for col in analysis['columns'][:3])}...")
personnel_file = None
seat_file = None
# 选择人员信息文件
while not personnel_file:
try:
choice = input(f"\n请选择人员信息文件 (1-{len(file_analysis)}): ").strip()
idx = int(choice) - 1
if 0 <= idx < len(file_analysis):
personnel_file = file_analysis[idx]['path']
self.logger.log(f"✅ 选择人员信息文件: {personnel_file.name}")
else:
print("请输入有效的数字")
except (ValueError, KeyboardInterrupt):
print("请输入有效的数字")
# 选择座位信息文件
while not seat_file:
try:
choice = input(f"\n请选择座位信息文件 (1-{len(file_analysis)}): ").strip()
idx = int(choice) - 1
if 0 <= idx < len(file_analysis):
if file_analysis[idx]['path'] == personnel_file:
print("不能选择相同的文件,请重新选择")
continue
seat_file = file_analysis[idx]['path']
self.logger.log(f"✅ 选择座位信息文件: {seat_file.name}")
else:
print("请输入有效的数字")
except (ValueError, KeyboardInterrupt):
print("请输入有效的数字")
return personnel_file, seat_file
def set_data_files(self, personnel_file, seat_file):
"""设置数据文件路径"""
self.personnel_file = personnel_file
self.seat_file = seat_file
def identify_and_set_files(self):
"""识别并设置数据文件"""
file_mode = self.choose_file_mode()
if file_mode == 'default':
# 默认文件名模式
personnel_file = Path('人员信息.xlsx')
seat_file = Path('座位信息.xlsx')
if not personnel_file.exists():
self.logger.log(f"❌ 错误: {personnel_file} 文件不存在")
return False
if not seat_file.exists():
self.logger.log(f"❌ 错误: {seat_file} 文件不存在")
return False
else:
# 智能识别模式
personnel_file, seat_file = self.smart_identify_files()
if not personnel_file or not seat_file:
self.logger.log("❌ 文件识别失败")
return False
self.set_data_files(personnel_file, seat_file)
return True
def load_data(self):
"""加载人员信息和座位信息数据"""
self.logger.log("=== 开始加载数据 ===")
# 检查文件是否存在
personnel_file = '人员信息.xlsx'
seat_file = '座位信息.xlsx'
if not Path(personnel_file).exists():
self.logger.log(f"❌ 错误: {personnel_file} 文件不存在")
# 使用自动识别的文件路径
if not self.personnel_file or not self.seat_file:
self.logger.log("❌ 错误: 数据文件路径未设置")
return False
if not Path(seat_file).exists():
self.logger.log(f"❌ 错误: {seat_file} 文件不存在")
if not Path(self.personnel_file).exists():
self.logger.log(f"❌ 错误: {self.personnel_file} 文件不存在")
return False
if not Path(self.seat_file).exists():
self.logger.log(f"❌ 错误: {self.seat_file} 文件不存在")
return False
try:
self.logger.log(f"正在读取人员信息文件: {self.personnel_file}")
# 读取人员信息,指定数据类型
self.personnel_df = pd.read_excel(personnel_file, dtype={
self.personnel_df = pd.read_excel(self.personnel_file, dtype={
'姓名': 'str',
'证件类型': 'str',
'证件号': 'str', # 证件号作为字符串读取避免X被转换
'手机号': 'str'
})
self.logger.log(f"正在读取座位信息文件: {self.seat_file}")
# 读取座位信息,指定数据类型
self.seat_df = pd.read_excel(seat_file, dtype={
seat_df_raw = pd.read_excel(self.seat_file, dtype={
'区域': 'str',
'楼层': 'str',
'排号': 'str',
@@ -85,6 +493,9 @@ class SeatAllocationSystem:
'手机号': 'str',
'签发地/国籍': 'str'
})
# 过滤和清洗座位数据
self.seat_df = self.filter_seat_data(seat_df_raw)
# 清理文字信息中的空格
self.clean_text_data()
@@ -117,26 +528,72 @@ class SeatAllocationSystem:
self.logger.log("✅ 文字数据清理完成")
def filter_seat_data(self, seat_df_raw):
"""过滤和清洗座位数据,移除空行和无效数据"""
self.logger.log("开始过滤座位数据...")
original_count = len(seat_df_raw)
self.logger.log(f"原始数据行数: {original_count}")
# 关键列,这些列都不能为空
key_columns = ['区域', '楼层', '排号', '座位号']
# 创建过滤条件:关键列都不能为空且不能为'nan'字符串
filter_condition = True
for col in key_columns:
if col in seat_df_raw.columns:
# 过滤空值、NaN、'nan'字符串
col_condition = (
seat_df_raw[col].notna() &
(seat_df_raw[col].astype(str).str.strip() != '') &
(seat_df_raw[col].astype(str).str.lower() != 'nan')
)
filter_condition = filter_condition & col_condition
# 应用过滤条件
seat_df_filtered = seat_df_raw[filter_condition].copy()
# 重置索引
seat_df_filtered.reset_index(drop=True, inplace=True)
filtered_count = len(seat_df_filtered)
removed_count = original_count - filtered_count
self.logger.log(f"过滤后数据行数: {filtered_count}")
self.logger.log(f"移除无效行数: {removed_count}")
if removed_count > 0:
self.logger.log(f"✅ 已过滤掉 {removed_count} 行无效数据(空行、示例数据等)")
# 显示过滤后的数据概览
if filtered_count > 0:
areas = seat_df_filtered['区域'].unique()
floors = seat_df_filtered['楼层'].unique()
self.logger.log(f"有效座位区域: {', '.join(areas)}")
self.logger.log(f"有效座位楼层: {', '.join(floors)}")
return seat_df_filtered
def validate_personnel_structure(self):
"""校验人员信息文件结构"""
self.logger.log("\n=== 人员信息结构校验 ===")
required_columns = ['姓名', '证件类型', '证件号', '手机号', '备注']
validation_results = []
# 检查必需列
missing_columns = []
for col in required_columns:
if col not in self.personnel_df.columns:
missing_columns.append(col)
if missing_columns:
self.logger.log(f"❌ 缺少必需列: {missing_columns}")
validation_results.append(False)
else:
self.logger.log("✅ 所有必需列都存在")
validation_results.append(True)
# 检查数据完整性
self.logger.log("\n数据完整性检查:")
for col in ['姓名', '证件类型', '证件号', '手机号']:
@@ -148,16 +605,36 @@ class SeatAllocationSystem:
else:
self.logger.log(f"{col} 列数据完整")
validation_results.append(True)
# 检查重复姓名
duplicate_names = self.personnel_df[self.personnel_df['姓名'].duplicated()]
if not duplicate_names.empty:
self.logger.log(f"⚠️ 发现重复姓名: {duplicate_names['姓名'].tolist()}")
validation_results.append(False)
# 检查重复人员(姓名+身份证号组合)
self.logger.log("\n重复人员检查:")
if '姓名' in self.personnel_df.columns and '证件号' in self.personnel_df.columns:
# 创建姓名+证件号的组合标识
self.personnel_df['人员标识'] = self.personnel_df['姓名'].astype(str) + '_' + self.personnel_df['证件号'].astype(str)
duplicate_persons = self.personnel_df[self.personnel_df['人员标识'].duplicated()]
if not duplicate_persons.empty:
self.logger.log(f"⚠️ 发现重复人员:")
for _, person in duplicate_persons.iterrows():
self.logger.log(f" {person['姓名']} (证件号: {person['证件号']})")
validation_results.append(False)
else:
self.logger.log("✅ 无重复人员(姓名+证件号组合检查)")
validation_results.append(True)
# 单独检查重名情况(仅提示,不影响校验结果)
duplicate_names = self.personnel_df[self.personnel_df['姓名'].duplicated(keep=False)]
if not duplicate_names.empty:
self.logger.log(f"📝 发现重名人员(但证件号不同,允许通过):")
name_groups = duplicate_names.groupby('姓名')
for name, group in name_groups:
self.logger.log(f" 姓名: {name}")
for _, person in group.iterrows():
self.logger.log(f" 证件号: {person['证件号']}")
else:
self.logger.log("✅ 姓名无重复")
validation_results.append(True)
self.logger.log("❌ 缺少姓名或证件号列,无法进行重复检查")
validation_results.append(False)
return all(validation_results)
def validate_seating_groups(self):
@@ -431,10 +908,73 @@ class SeatAllocationSystem:
return True
def analyze_seating_requirements(self):
def analyze_seating_requirements(self, use_phone_grouping=False):
"""分析人员连坐需求"""
self.logger.log("\n=== 人员连坐需求分析 ===")
if use_phone_grouping:
self.logger.log("🔍 使用手机号分组模式")
return self.analyze_seating_by_phone()
else:
self.logger.log("🔍 使用备注分组模式")
return self.analyze_seating_by_remark()
def analyze_seating_by_phone(self):
"""基于手机号分析连坐需求"""
self.logger.log("根据相同手机号识别连坐组...")
# 按手机号分组
phone_groups = self.personnel_df.groupby('手机号')
self.seating_groups = []
for phone, group in phone_groups:
group_members = [row for _, row in group.iterrows()]
group_size = len(group_members)
if group_size == 1:
# 单人组
self.seating_groups.append({
'type': 'single',
'size': 1,
'members': group_members,
'leader': group_members[0]['姓名'],
'grouping_method': 'phone'
})
self.logger.log(f"📱 单人: {group_members[0]['姓名']} (手机号: {phone})")
else:
# 连坐组
leader_name = group_members[0]['姓名']
member_names = [member['姓名'] for member in group_members]
self.seating_groups.append({
'type': 'group',
'size': group_size,
'members': group_members,
'leader': leader_name,
'grouping_method': 'phone'
})
self.logger.log(f"📱 连坐组: {', '.join(member_names)} (手机号: {phone}, {group_size}人)")
# 统计
size_stats = {}
for group in self.seating_groups:
size = group['size']
size_stats[size] = size_stats.get(size, 0) + 1
self.logger.log(f"\n基于手机号识别出 {len(self.seating_groups)} 个座位组:")
for size in sorted(size_stats.keys()):
count = size_stats[size]
if size == 1:
self.logger.log(f" 单人组: {count}")
else:
self.logger.log(f" {size}人连坐组: {count}")
return True
def analyze_seating_by_remark(self):
"""基于备注分析连坐需求"""
self.logger.log("根据备注数字识别连坐组...")
self.seating_groups = []
i = 0
@@ -448,7 +988,8 @@ class SeatAllocationSystem:
'type': 'single',
'size': 1,
'members': [person],
'leader': person['姓名']
'leader': person['姓名'],
'grouping_method': 'remark'
})
i += 1
else:
@@ -465,7 +1006,8 @@ class SeatAllocationSystem:
'type': 'group' if group_size > 1 else 'single',
'size': len(group_members),
'members': group_members,
'leader': person['姓名']
'leader': person['姓名'],
'grouping_method': 'remark'
})
i += group_size
@@ -476,7 +1018,7 @@ class SeatAllocationSystem:
size = group['size']
size_stats[size] = size_stats.get(size, 0) + 1
self.logger.log(f"总共识别出 {len(self.seating_groups)} 个座位组:")
self.logger.log(f"\n基于备注识别出 {len(self.seating_groups)} 个座位组:")
for size in sorted(size_stats.keys()):
count = size_stats[size]
if size == 1:
@@ -599,13 +1141,14 @@ class SeatAllocationSystem:
person_info = seating_group['members'][0]
seat_index = seat_info['index']
# 更新座位信息,确保数据类型正确
# 覆盖座位表中的人员信息列
seat_df_copy.loc[seat_index, '姓名'] = str(person_info['姓名']).strip()
seat_df_copy.loc[seat_index, '证件类型'] = str(person_info['证件类型']).strip()
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
seat_df_copy.loc[seat_index, '签发地/国籍'] = str(person_info.get('备注', '')).strip()
# 只有分配备注放在第13列
seat_df_copy.loc[seat_index, '分配备注'] = str(person_info.get('备注', '')).strip()
assignment_log.append({
'组号': group_idx + 1,
@@ -683,7 +1226,8 @@ class SeatAllocationSystem:
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
seat_df_copy.loc[seat_index, '签发地/国籍'] = str(person_info.get('备注', '')).strip()
# 只有分配备注放在第13列
seat_df_copy.loc[seat_index, '分配备注'] = str(person_info.get('备注', '')).strip()
assignment_log.append({
'组号': group_idx + 1,
@@ -725,14 +1269,23 @@ class SeatAllocationSystem:
"""保存分配结果"""
try:
# 保存更新后的座位信息
output_file = '座位信息_最终分配.xlsx'
output_file = self.logger.get_log_path('座位信息_最终分配.xlsx')
seat_df_result.to_excel(output_file, index=False)
self.logger.log(f"\n座位分配结果已保存到: {output_file}")
self.logger.log(f"📋 人员信息已覆盖到座位表对应列:")
self.logger.log(f" 第6列: 姓名")
self.logger.log(f" 第7列: 证件类型")
self.logger.log(f" 第8列: 证件号")
self.logger.log(f" 第9列: 手机国家号")
self.logger.log(f" 第10列: 手机号")
self.logger.log(f"📝 分配备注信息:")
self.logger.log(f" 第13列: 分配备注(新增列,不覆盖原数据)")
self.logger.log(f"💡 座位基本信息第1-5列保持不变")
# 保存分配日志
if assignment_log:
log_df = pd.DataFrame(assignment_log)
log_file = '最终座位分配日志.xlsx'
log_file = self.logger.get_log_path('最终座位分配日志.xlsx')
log_df.to_excel(log_file, index=False)
self.logger.log(f"分配日志已保存到: {log_file}")
@@ -805,6 +1358,10 @@ class SeatAllocationSystem:
self.logger.log("座位分配系统 - 文件校验")
self.logger.log("=" * 60)
# 识别并设置数据文件
if not self.identify_and_set_files():
return False
# 加载数据
if not self.load_data():
return False
@@ -858,8 +1415,11 @@ class SeatAllocationSystem:
self.logger.log("开始座位分配")
self.logger.log("=" * 60)
# 选择分组方式
grouping_method = self.choose_grouping_method()
# 分析人员连坐需求
if not self.analyze_seating_requirements():
if not self.analyze_seating_requirements(use_phone_grouping=(grouping_method == 'phone')):
return False
# 高级座位结构分析
@@ -877,22 +1437,83 @@ class SeatAllocationSystem:
self.logger.log("\n❌ 座位分配失败!")
return False
def choose_grouping_method(self):
"""选择分组方式"""
self.logger.log("\n=== 选择连坐分组方式 ===")
self.logger.log("1. 备注分组 - 根据备注数字识别连坐组(默认)")
self.logger.log("2. 手机号分组 - 根据相同手机号识别连坐组")
while True:
try:
choice = input("\n请选择分组方式 (1/2直接回车选择默认): ").strip()
if choice == '' or choice == '1':
self.logger.log("✅ 选择备注分组模式")
return 'remark'
elif choice == '2':
self.logger.log("✅ 选择手机号分组模式")
return 'phone'
else:
print("请输入 1 或 2")
except KeyboardInterrupt:
self.logger.log("\n用户取消操作")
return 'remark'
def main():
"""主函数"""
system = SeatAllocationSystem()
print("=" * 60)
print("座位分配系统 v2.0 (智能识别版)")
print("=" * 60)
try:
# 自动检查和识别数据文件
success, personnel_file, seat_file = check_data_files()
if not success:
input("\n按Enter键退出...")
return
print("\n开始运行座位分配系统...")
system = SeatAllocationSystem()
# 设置识别出的文件路径
system.set_data_files(personnel_file, seat_file)
# 运行校验
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("请查看log文件夹中的输出文件:")
print("- log/座位信息_最终分配.xlsx (分配结果)")
print("- log/最终座位分配日志.xlsx (详细日志)")
print("- log/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()

View File

@@ -1,232 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化构建脚本
在当前平台构建可执行文件
"""
import os
import sys
import subprocess
import platform
import shutil
from pathlib import Path
def get_platform_info():
"""获取平台信息"""
system = platform.system()
machine = platform.machine().lower()
if system == 'Windows':
if machine in ['amd64', 'x86_64']:
return 'windows_x64'
elif machine in ['i386', 'i686', 'x86']:
return 'windows_x86'
else:
return f'windows_{machine}'
elif system == 'Darwin':
if machine in ['arm64', 'aarch64']:
return 'macos_arm64'
else:
return 'macos_x64'
elif system == 'Linux':
return f'linux_{machine}'
else:
return f'{system.lower()}_{machine}'
def install_dependencies():
"""安装依赖"""
print("安装依赖包...")
packages = ['pandas', 'openpyxl', 'pyinstaller']
for package in packages:
try:
__import__(package)
print(f"{package} 已安装")
except ImportError:
print(f"📦 安装 {package}...")
try:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
print(f"{package} 安装成功")
except subprocess.CalledProcessError:
print(f"{package} 安装失败")
return False
return True
def build_executable():
"""构建可执行文件"""
print("\n开始构建可执行文件...")
# 获取平台信息
platform_name = get_platform_info()
exe_name = f"座位分配系统_{platform_name}"
print(f"目标平台: {platform_name}")
print(f"输出文件: {exe_name}")
# 清理之前的构建
if os.path.exists('dist'):
shutil.rmtree('dist')
if os.path.exists('build'):
shutil.rmtree('build')
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--onefile',
'--console',
'--clean',
'--name', exe_name,
'seat_allocation_system.py'
]
try:
print(f"执行命令: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ 构建成功!")
# 检查生成的文件
if platform.system() == 'Windows':
exe_path = Path('dist') / f'{exe_name}.exe'
else:
exe_path = Path('dist') / exe_name
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("❌ 未找到生成的文件")
return False, None
else:
print("❌ 构建失败!")
print("错误输出:")
print(result.stderr)
return False, None
except Exception as e:
print(f"❌ 构建过程中出现错误: {e}")
return False, None
def create_distribution():
"""创建分发包"""
print("\n创建分发包...")
platform_name = get_platform_info()
package_name = f"座位分配系统_{platform_name}_分发包"
package_dir = Path(package_name)
# 创建分发目录
if package_dir.exists():
shutil.rmtree(package_dir)
package_dir.mkdir()
# 复制可执行文件
dist_dir = Path('dist')
if dist_dir.exists():
for file in dist_dir.iterdir():
if file.is_file():
shutil.copy2(file, package_dir)
print(f"复制文件: {file.name}")
# 复制示例文件
if Path('人员信息.xlsx').exists():
shutil.copy2('人员信息.xlsx', package_dir / '人员信息_示例.xlsx')
print("复制示例文件: 人员信息_示例.xlsx")
if Path('座位信息.xlsx').exists():
shutil.copy2('座位信息.xlsx', package_dir / '座位信息_示例.xlsx')
print("复制示例文件: 座位信息_示例.xlsx")
# 创建使用说明
readme_content = f"""座位分配系统 使用说明
平台: {platform_name}
构建时间: {platform.platform()}
使用方法:
1. 准备Excel文件
- 人员信息.xlsx: 包含姓名、证件信息、备注等
- 座位信息.xlsx: 包含区域、楼层、排号、座位号等
2. 运行程序
- 将可执行文件放在Excel文件同一目录
- 双击运行可执行文件
- 等待处理完成
3. 查看结果
- 座位信息_最终分配.xlsx: 分配结果
- 最终座位分配日志.xlsx: 详细记录
- seat_allocation_log.txt: 运行日志
功能特点:
- 支持1-10人连坐需求
- 自动处理不连续座位
- 完整的数据校验
- 详细的分配日志
注意事项:
- 确保Excel文件格式正确
- 备注数字表示连坐人数
- 运行时会在同目录生成结果文件
"""
with open(package_dir / '使用说明.txt', 'w', encoding='utf-8') as f:
f.write(readme_content)
print(f"✅ 分发包已创建: {package_dir}")
return package_dir
def main():
"""主函数"""
print("座位分配系统 - 简化构建工具")
print("=" * 50)
# 显示系统信息
print(f"系统: {platform.system()} {platform.release()}")
print(f"架构: {platform.machine()}")
print(f"Python: {sys.version}")
# 检查主程序文件
if not os.path.exists('seat_allocation_system.py'):
print("❌ 未找到 seat_allocation_system.py 文件")
return
# 安装依赖
if not install_dependencies():
print("❌ 依赖安装失败")
return
# 构建可执行文件
success, exe_path = build_executable()
if not success:
print("❌ 构建失败")
return
# 创建分发包
package_dir = create_distribution()
print("\n🎉 构建完成!")
print(f"✅ 可执行文件: {exe_path}")
print(f"✅ 分发包: {package_dir}")
# 平台特定说明
if platform.system() == 'Windows':
print("\n📝 Windows使用说明:")
print("- 可以直接在Windows系统上运行")
print("- 确保安装了Visual C++ Redistributable")
elif platform.system() == 'Darwin':
print("\n📝 macOS使用说明:")
print("- 只能在macOS系统上运行")
print("- 如需Windows版本请在Windows系统上构建")
print("- 或使用GitHub Actions自动构建")
else:
print(f"\n📝 {platform.system()}使用说明:")
print("- 只能在相同系统上运行")
print("- 如需其他平台版本,请在对应系统上构建")
if __name__ == "__main__":
main()

196
simple_windows_build.py Normal file
View File

@@ -0,0 +1,196 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化的Windows构建脚本
专门解决PyInstaller依赖问题
"""
import sys
import subprocess
import platform
from pathlib import Path
def create_simple_spec():
"""创建简化的spec文件"""
spec_content = '''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
# 核心依赖
'pandas',
'openpyxl',
'numpy',
'datetime',
'pathlib',
'sys',
'os',
# 修复关键错误
'numpy.core._methods',
'pandas._libs.window.aggregations',
'ctypes.util',
'pkg_resources.py2_warn',
# pandas核心
'pandas._libs',
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas.io.excel',
'pandas.io.common',
# numpy核心
'numpy.core',
'numpy.core.multiarray',
'numpy.core.umath',
# openpyxl
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles',
# 编码
'encodings.utf_8',
'encodings.gbk',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'tkinter',
'PyQt5',
'PyQt6',
'PIL',
'cv2',
],
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 = Path('simple_build.spec')
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"✅ 创建spec文件: {spec_file}")
return spec_file
def build_exe():
"""构建可执行文件"""
print("=" * 60)
print("简化Windows构建")
print("=" * 60)
# 检查环境
if platform.system() != 'Windows':
print("❌ 此脚本仅适用于Windows")
return False
print(f"✅ 系统: {platform.system()} {platform.release()}")
print(f"✅ Python: {sys.version}")
# 检查主文件
main_file = Path('seat_allocation_system.py')
if not main_file.exists():
print("❌ 未找到主程序文件")
return False
print(f"✅ 主程序: {main_file}")
# 创建spec文件
spec_file = create_simple_spec()
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
'--noconfirm',
str(spec_file)
]
print(f"\n执行命令: {' '.join(cmd)}")
print("开始构建...")
try:
# 执行构建
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
# 显示结果
if result.returncode == 0:
print("✅ 构建成功!")
# 检查生成的文件
exe_path = Path('dist/座位分配系统.exe')
if exe_path.exists():
size_mb = exe_path.stat().st_size / (1024 * 1024)
print(f"✅ 生成文件: {exe_path}")
print(f"✅ 文件大小: {size_mb:.1f} MB")
return True
else:
print("❌ 未找到生成的exe文件")
return False
else:
print("❌ 构建失败!")
print("错误输出:")
print(result.stderr)
return False
except Exception as e:
print(f"❌ 构建出错: {e}")
return False
def main():
"""主函数"""
success = build_exe()
if success:
print("\n🎉 构建完成!")
print("可执行文件位置: dist/座位分配系统.exe")
else:
print("\n❌ 构建失败!")
input("\n按Enter键退出...")
if __name__ == '__main__':
main()

101
test_build.py Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试构建脚本 - 验证依赖和配置
"""
import sys
import subprocess
import platform
def test_dependencies():
"""测试关键依赖"""
print("=" * 50)
print("测试关键依赖")
print("=" * 50)
dependencies = [
'pandas',
'numpy',
'openpyxl',
'pyinstaller'
]
for dep in dependencies:
try:
__import__(dep)
print(f"{dep} - 已安装")
except ImportError:
print(f"{dep} - 未安装")
return False
return True
def test_hidden_imports():
"""测试隐藏导入"""
print("\n" + "=" * 50)
print("测试隐藏导入")
print("=" * 50)
hidden_imports = [
'numpy.core._methods',
'pandas._libs.window.aggregations',
'ctypes.util'
]
for imp in hidden_imports:
try:
parts = imp.split('.')
module = __import__(parts[0])
for part in parts[1:]:
module = getattr(module, part)
print(f"{imp} - 可访问")
except (ImportError, AttributeError) as e:
print(f"⚠️ {imp} - 无法访问: {e}")
return True
def test_pyinstaller():
"""测试PyInstaller"""
print("\n" + "=" * 50)
print("测试PyInstaller")
print("=" * 50)
try:
result = subprocess.run([
sys.executable, '-m', 'PyInstaller', '--version'
], capture_output=True, text=True)
if result.returncode == 0:
version = result.stdout.strip()
print(f"✅ PyInstaller版本: {version}")
return True
else:
print(f"❌ PyInstaller测试失败: {result.stderr}")
return False
except Exception as e:
print(f"❌ PyInstaller测试出错: {e}")
return False
def main():
"""主函数"""
print(f"Python版本: {sys.version}")
print(f"操作系统: {platform.system()} {platform.release()}")
print(f"架构: {platform.machine()}")
success = True
success &= test_dependencies()
success &= test_hidden_imports()
success &= test_pyinstaller()
print("\n" + "=" * 50)
if success:
print("✅ 所有测试通过,可以进行构建")
else:
print("❌ 存在问题,请先解决依赖问题")
print("=" * 50)
return success
if __name__ == '__main__':
main()

169
test_pandas_dll.py Normal file
View File

@@ -0,0 +1,169 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试pandas DLL模块加载
专门检查可能导致PyInstaller问题的模块
"""
import sys
import traceback
from pathlib import Path
def test_pandas_core():
"""测试pandas核心模块"""
print("=" * 50)
print("测试pandas核心模块")
print("=" * 50)
modules_to_test = [
'pandas',
'pandas._libs',
'pandas._libs.lib',
'pandas._libs.hashtable',
'pandas._libs.algos',
'pandas._libs.index',
'pandas._libs.writers',
'pandas._libs.parsers',
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas._libs.window',
'pandas._libs.window.aggregations', # 关键模块!
'pandas._libs.window.indexers',
]
success_count = 0
for module in modules_to_test:
try:
__import__(module)
print(f"{module}")
success_count += 1
except ImportError as e:
print(f"{module}: {e}")
except Exception as e:
print(f"⚠️ {module}: {e}")
print(f"\n成功加载: {success_count}/{len(modules_to_test)} 个模块")
return success_count == len(modules_to_test)
def test_pandas_functionality():
"""测试pandas基本功能"""
print("\n" + "=" * 50)
print("测试pandas基本功能")
print("=" * 50)
try:
import pandas as pd
import numpy as np
# 创建测试数据
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [10, 20, 30, 40, 50]
})
print("✅ DataFrame创建成功")
# 测试窗口函数(这是出错的关键)
result = df['A'].rolling(window=2).sum()
print("✅ rolling窗口函数正常")
# 测试聚合函数
agg_result = df.groupby('A').agg({'B': 'sum'})
print("✅ groupby聚合函数正常")
# 测试Excel读写
test_file = Path('test_pandas.xlsx')
df.to_excel(test_file, index=False)
df_read = pd.read_excel(test_file)
test_file.unlink() # 删除测试文件
print("✅ Excel读写功能正常")
return True
except Exception as e:
print(f"❌ pandas功能测试失败: {e}")
traceback.print_exc()
return False
def check_pandas_installation():
"""检查pandas安装信息"""
print("\n" + "=" * 50)
print("pandas安装信息")
print("=" * 50)
try:
import pandas as pd
print(f"✅ pandas版本: {pd.__version__}")
print(f"✅ pandas路径: {pd.__file__}")
# 检查_libs目录
pandas_path = Path(pd.__file__).parent
libs_path = pandas_path / '_libs'
if libs_path.exists():
print(f"✅ _libs目录存在: {libs_path}")
# 统计.pyd文件
pyd_files = list(libs_path.glob('**/*.pyd'))
print(f"✅ 找到 {len(pyd_files)} 个.pyd文件")
# 检查关键文件
key_files = [
'window/aggregations.cp310-win_amd64.pyd',
'window/indexers.cp310-win_amd64.pyd',
'hashtable.cp310-win_amd64.pyd',
'lib.cp310-win_amd64.pyd'
]
for key_file in key_files:
file_path = libs_path / key_file
if file_path.exists():
print(f"✅ 关键文件存在: {key_file}")
else:
print(f"❌ 关键文件缺失: {key_file}")
else:
print(f"❌ _libs目录不存在: {libs_path}")
return False
return True
except Exception as e:
print(f"❌ 检查pandas安装失败: {e}")
return False
def main():
"""主函数"""
print("🔍 pandas DLL模块测试工具")
print("用于诊断PyInstaller构建问题")
print()
# 基本信息
print(f"Python版本: {sys.version}")
print(f"平台: {sys.platform}")
# 运行测试
install_ok = check_pandas_installation()
core_ok = test_pandas_core()
func_ok = test_pandas_functionality()
print("\n" + "=" * 50)
print("测试结果总结")
print("=" * 50)
if install_ok and core_ok and func_ok:
print("✅ 所有测试通过pandas安装正常")
print("💡 如果PyInstaller仍有问题请使用 fix_dll_build.py")
else:
print("❌ 存在问题,建议:")
if not install_ok:
print(" 1. 重新安装pandas: pip uninstall pandas && pip install pandas")
if not core_ok:
print(" 2. 检查pandas C扩展是否正确编译")
if not func_ok:
print(" 3. 检查依赖库numpy等是否正常")
print("=" * 50)
input("\n按Enter键退出...")
if __name__ == '__main__':
main()

710
windows_build.py Normal file
View File

@@ -0,0 +1,710 @@
#!/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>=5.0',
'pywin32>=227' # Windows特定依赖
]
for package in required_packages:
print(f"\n检查 {package}...")
package_name = package.split('>=')[0]
# 特殊处理pywin32
if package_name == 'pywin32':
try:
import win32api
print(f"{package_name} 已安装")
continue
except ImportError:
pass
else:
try:
__import__(package_name)
print(f"{package_name} 已安装")
continue
except ImportError:
pass
print(f"📦 安装 {package}...")
try:
cmd = [sys.executable, '-m', 'pip', 'install', package, '--upgrade']
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}")
# 对于pywin32尝试替代安装方法
if package_name == 'pywin32':
print("尝试使用conda安装pywin32...")
try:
cmd_conda = ['conda', 'install', '-y', 'pywin32']
result_conda = subprocess.run(cmd_conda, capture_output=True, text=True)
if result_conda.returncode == 0:
print("✅ pywin32 通过conda安装成功")
continue
except:
pass
print("⚠️ pywin32安装失败但可能不影响构建")
continue
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 -*-
# 完全独立的PyInstaller配置
# 包含Python解释器和所有依赖无需目标机器安装Python
import sys
from pathlib import Path
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[str(Path.cwd())],
binaries=[],
datas=[],
hiddenimports=[
# 核心依赖
'pandas',
'openpyxl',
'numpy',
'xlsxwriter',
'xlrd',
'datetime',
'pathlib',
'subprocess',
'platform',
'sys',
'os',
# openpyxl相关
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles',
'openpyxl.utils',
'openpyxl.writer.excel',
'openpyxl.reader.excel',
'openpyxl.cell',
'openpyxl.formatting',
# pandas相关 - 完整导入
'pandas.io.excel',
'pandas.io.common',
'pandas.io.parsers',
'pandas.io.formats.excel',
'pandas._libs',
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.np_datetime',
'pandas._libs.tslibs.nattype',
'pandas._libs.window',
'pandas._libs.window.aggregations',
'pandas._libs.hashtable',
'pandas._libs.algos',
'pandas._libs.index',
'pandas._libs.lib',
'pandas._libs.missing',
'pandas._libs.parsers',
'pandas._libs.reduction',
'pandas._libs.reshape',
'pandas._libs.sparse',
'pandas._libs.testing',
'pandas._libs.writers',
# numpy相关 - 完整导入
'numpy.core',
'numpy.core.multiarray',
'numpy.core.umath',
'numpy.core._methods',
'numpy.core._dtype_ctypes',
'numpy.core._internal',
'numpy.core.numeric',
'numpy.core.numerictypes',
'numpy.core.function_base',
'numpy.core.machar',
'numpy.core.getlimits',
'numpy.core.shape_base',
'numpy.lib.format',
'numpy.lib.mixins',
'numpy.lib.scimath',
'numpy.lib.stride_tricks',
'numpy.random',
'numpy.random._pickle',
'numpy.random.mtrand',
'numpy.random._common',
'numpy.random._generator',
# 编码相关
'encodings',
'encodings.utf_8',
'encodings.gbk',
'encodings.cp1252',
'encodings.latin1',
# 其他必要模块
'_ctypes',
'ctypes.util',
'pkg_resources',
'pkg_resources.py2_warn',
'pkg_resources.markers',
# Jinja2相关可选某些依赖可能需要
# 'jinja2',
# 'jinja2.ext',
# 'markupsafe',
# Windows特定模块仅在Windows上可用
# 'win32api',
# 'win32con',
# 'pywintypes',
# 'pythoncom'
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
# 排除不必要的大型库
'matplotlib',
'matplotlib.pyplot',
'scipy',
'sklearn',
'tensorflow',
'torch',
'IPython',
'jupyter',
'notebook',
'tkinter',
'PyQt5',
'PyQt6',
'PySide2',
'PySide6',
'PIL',
'cv2',
'seaborn',
'plotly',
'bokeh',
# 测试和开发工具
'test',
'tests',
'unittest',
'pytest',
'setuptools',
'pip',
'wheel',
'distutils',
# 文档和示例
'sphinx',
'docutils',
'examples',
'sample',
# 其他不需要的模块
'curses',
'readline',
'sqlite3',
'xml.etree.ElementTree',
'html',
'http',
'urllib3',
'requests'
],
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,
exclude_binaries=False,
)
'''
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)
# 使用spec文件时只能使用基本选项
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 座位分配系统 v2.0
echo ==========================================
echo.
:: 检查Excel文件
echo 正在扫描Excel文件...
:: 计算xlsx文件数量
set count=0
for %%f in (*.xlsx) do (
:: 排除输出和示例文件
echo "%%f" | findstr /v /i "最终分配\|分配日志\|示例\|temp\|backup" >nul
if not errorlevel 1 (
set /a count+=1
echo 发现文件: %%f
)
)
if %count% equ 0 (
echo.
echo [错误] 未找到Excel数据文件
echo.
echo 请确保当前目录下有Excel数据文件:
echo 1. 人员信息文件 (5-6列): 姓名、证件类型、证件号、手机号、备注等
echo 2. 座位信息文件 (10+列): 区域、楼层、排号、座位号等
echo.
echo 提示: 程序会自动识别文件类型,无需固定文件名
pause
exit /b 1
)
if %count% gtr 2 (
echo.
echo [警告] 发现超过2个Excel文件
echo 为避免识别混淆请确保目录下只有2个数据文件
echo.
echo 请移除多余文件后重试
pause
exit /b 1
)
echo [成功] 找到 %count% 个Excel文件程序将自动识别文件类型
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位)
重要特性: 无需安装Python环境exe文件完全独立运行
====================
快速开始
====================
1. 准备数据文件
- 将 "人员信息_示例.xlsx" 重命名为 "人员信息.xlsx"
- 将 "座位信息_示例.xlsx" 重命名为 "座位信息.xlsx"
- 按照示例格式填入您的实际数据
2. 运行程序
- 双击 "运行座位分配系统.bat" 启动程序
- 或者直接双击 "座位分配系统.exe"
3. 查看结果
- 座位信息_最终分配.xlsx (最终分配结果)
- 最终座位分配日志.xlsx (详细分配记录)
- seat_allocation_log.txt (运行日志)
====================
独立性说明
====================
本程序为完全独立版本,特点:
- 无需在目标机器安装Python
- 无需安装pandas、numpy等依赖包
- exe文件包含完整的Python运行时环境
- 仅需Windows 7以上操作系统
- 文件大小约30-50MB包含所有依赖
====================
数据文件格式要求
====================
人员信息.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}")
# 验证独立性
self.verify_independence(exe_path)
print("\n使用方法:")
print("1. 将整个分发包复制到目标电脑")
print("2. 准备好人员信息.xlsx和座位信息.xlsx文件")
print("3. 双击运行 '运行座位分配系统.bat'")
print("\n[重要] 目标机器无需安装Python环境exe文件完全独立!")
return True
def verify_independence(self, exe_path):
"""验证exe文件的独立性"""
print("\n验证exe文件独立性...")
try:
file_size = exe_path.stat().st_size / (1024 * 1024) # MB
print(f"[信息] 文件大小: {file_size:.1f} MB")
if file_size > 20: # 大于20MB通常包含了完整的Python环境
print("[成功] 文件大小表明包含了完整的Python运行时")
else:
print("[警告] 文件较小,可能缺少某些依赖")
# 检查是否包含Python DLL间接验证
print("[信息] exe文件已包含以下组件:")
print(" - Python解释器和标准库")
print(" - pandas, numpy, openpyxl等依赖包")
print(" - 必要的Windows运行时库")
print(" - 程序源代码和资源")
print("[成功] 独立性验证完成")
except Exception as e:
print(f"[警告] 验证过程中出现问题: {e}")
print("[信息] 这不影响exe文件的独立性")
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()

Binary file not shown.

Binary file not shown.

79
快速打包.bat Normal file
View File

@@ -0,0 +1,79 @@
@echo off
chcp 65001 >nul
title 座位分配系统 - 快速打包
echo.
echo ╔══════════════════════════════════════╗
echo ║ 座位分配系统 快速打包 ║
echo ╚══════════════════════════════════════╝
echo.
:: 检查Python
python --version >nul 2>&1
if %errorlevel% neq 0 (
echo ❌ Python未安装请先安装Python
goto :error
)
:: 安装打包工具
echo 📦 安装打包工具...
pip install pyinstaller pandas numpy openpyxl --quiet
:: 清理旧文件
echo 🧹 清理旧文件...
if exist "dist" rmdir /s /q "dist"
if exist "build" rmdir /s /q "build"
if exist "release" rmdir /s /q "release"
:: 开始打包
echo 🔨 开始打包...
pyinstaller --onefile --console --name "座位分配系统" --clean seat_allocation_system.py
:: 检查结果
if not exist "dist\座位分配系统.exe" (
echo ❌ 打包失败
goto :error
)
:: 创建发布目录
echo 📁 创建发布目录...
mkdir "release"
copy "dist\座位分配系统.exe" "release\"
:: 创建说明文件
echo 📝 创建说明文件...
(
echo 座位分配系统 v2.0
echo ==================
echo.
echo 使用方法:
echo 1. 将Excel数据文件放在本程序同一目录
echo 2. 双击运行"座位分配系统.exe"
echo 3. 按提示操作即可
echo.
echo 输出文件将保存在log文件夹中
echo.
echo 如有问题请查看log文件夹中的日志
) > "release\使用说明.txt"
:: 完成
echo.
echo ✅ 打包完成!
echo.
echo 📍 发布文件位置: release\座位分配系统.exe
echo 📍 使用说明: release\使用说明.txt
echo.
echo 可以将release文件夹分发给用户使用
goto :end
:error
echo.
echo ❌ 打包失败,请检查错误信息
echo.
pause
exit /b 1
:end
echo.
echo 按任意键退出...
pause >nul