界面调整,功能定稿
parent
e2eaf3cbe5
commit
daeef833f8
|
|
@ -1 +1,3 @@
|
|||
__pycache__/
|
||||
backend/build/
|
||||
backend/dist/
|
||||
|
|
|
|||
|
|
@ -197,3 +197,5 @@ chmod +x start_frontend.sh
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -111,3 +111,5 @@ simulator = WrenchSimulator(
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -248,3 +248,5 @@ ssh -p 2222 root@localhost
|
|||
如有问题,请检查日志或联系技术支持。
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['app.py'],
|
||||
pathex=['..'],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=['wrench_controller'],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='backend_api',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='backend_api',
|
||||
)
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
@echo off
|
||||
REM Simple backend build script (ASCII only, avoid encoding issues)
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ========================================
|
||||
echo Building backend_api with PyInstaller...
|
||||
echo ========================================
|
||||
|
||||
echo Checking Python environment...
|
||||
python -c "import PyInstaller" 2>nul
|
||||
if errorlevel 1 (
|
||||
echo PyInstaller not found, installing...
|
||||
pip install --upgrade setuptools
|
||||
pip install pyinstaller
|
||||
if errorlevel 1 (
|
||||
echo Failed to install PyInstaller. Please run: pip install pyinstaller
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
) else (
|
||||
echo Checking PyInstaller dependencies...
|
||||
python -c "import jaraco" 2>nul
|
||||
if errorlevel 1 (
|
||||
echo Missing dependencies detected, fixing...
|
||||
pip install --upgrade setuptools
|
||||
pip install --upgrade --force-reinstall pyinstaller
|
||||
)
|
||||
)
|
||||
|
||||
echo Cleaning old build/dist...
|
||||
if exist build (
|
||||
echo Removing build directory...
|
||||
rmdir /s /q build 2>nul
|
||||
timeout /t 1 /nobreak >nul
|
||||
)
|
||||
if exist dist (
|
||||
echo Removing dist directory...
|
||||
rmdir /s /q dist 2>nul
|
||||
timeout /t 1 /nobreak >nul
|
||||
)
|
||||
if exist backend_api.spec (
|
||||
echo Removing old spec file...
|
||||
del /q backend_api.spec 2>nul
|
||||
)
|
||||
|
||||
echo Running PyInstaller with --clean flag...
|
||||
python -m PyInstaller --clean --name backend_api --console --paths .. --hidden-import wrench_controller app.py
|
||||
|
||||
if errorlevel 1 (
|
||||
echo Build failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Build success!
|
||||
echo Executable: dist\backend_api\backend_api.exe
|
||||
echo.
|
||||
echo If you have an existing wrench.db, copy it to:
|
||||
echo dist\backend_api\wrench.db
|
||||
echo.
|
||||
echo Then run:
|
||||
echo dist\backend_api\backend_api.exe
|
||||
echo.
|
||||
pause
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/bash
|
||||
# 电动扳手后端 API 打包脚本(Linux/Mac)
|
||||
|
||||
echo "========================================"
|
||||
echo "电动扳手后端 API 打包脚本"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 确保在 backend 目录下执行
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 检查 PyInstaller 是否已安装
|
||||
if ! python3 -c "import PyInstaller" 2>/dev/null; then
|
||||
echo "[错误] PyInstaller 未安装"
|
||||
echo "正在安装 PyInstaller..."
|
||||
pip3 install pyinstaller
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "[错误] PyInstaller 安装失败,请手动执行: pip3 install pyinstaller"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[信息] PyInstaller 已安装"
|
||||
echo ""
|
||||
|
||||
# 清理之前的构建文件
|
||||
echo "[信息] 清理之前的构建文件..."
|
||||
rm -rf build dist
|
||||
|
||||
echo ""
|
||||
echo "[信息] 当前目录: $(pwd)"
|
||||
echo "[信息] 开始打包后端 API 服务..."
|
||||
echo ""
|
||||
|
||||
# 使用 onedir 模式打包,并显式包含父目录中的 wrench_controller 模块
|
||||
# --paths .. 把项目根目录加入模块搜索路径
|
||||
# --hidden-import ... 确保 wrench_controller 被打包进可执行文件
|
||||
pyinstaller --name backend_api --console --paths .. --hidden-import wrench_controller app.py
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "[错误] 打包失败!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "后端打包完成!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "可执行文件位置: dist/backend_api/backend_api"
|
||||
echo ""
|
||||
echo "下一步操作:"
|
||||
echo "1. 如果已有生产用数据库,可将当前目录的 wrench.db 复制到 dist/backend_api/ 目录:"
|
||||
echo " cp wrench.db dist/backend_api/wrench.db"
|
||||
echo ""
|
||||
echo "2. 运行后端服务:"
|
||||
echo " ./dist/backend_api/backend_api"
|
||||
echo ""
|
||||
echo "3. 确保前端或第三方程序访问的地址仍为:"
|
||||
echo " http://localhost:5000"
|
||||
echo ""
|
||||
|
||||
|
||||
|
|
@ -77,3 +77,5 @@ print("\n" + "="*60)
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,3 +28,5 @@ conn.close()
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -68,3 +68,5 @@ print("="*60)
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -113,3 +113,5 @@ if __name__ == "__main__":
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -55,3 +55,5 @@ print("="*60)
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ flask-cors==4.0.0
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,5 @@ pause
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,3 +8,5 @@ python3 app.py
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -42,3 +42,5 @@ if __name__ == "__main__":
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
## 后端(Flask API)打包与运行说明
|
||||
|
||||
### 1. 是否可以用和前端相同的方式打包?
|
||||
|
||||
**可以。**
|
||||
后端同样可以用 **PyInstaller** 打包成一个可执行文件,只是:
|
||||
- 前端是 **GUI 程序**(`console=False`),
|
||||
- 后端是 **控制台服务程序**(`console=True`,启动后常驻,监听 5000 端口)。
|
||||
|
||||
本项目已经在 `backend` 目录下生成了两个打包脚本:
|
||||
- `build_backend.bat`(Windows)
|
||||
- `build_backend.sh`(Linux/Mac)
|
||||
|
||||
---
|
||||
|
||||
### 2. 打包前准备
|
||||
|
||||
在打包前,确保:
|
||||
|
||||
- 已安装 Python 3 环境
|
||||
- 已安装后端依赖:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
(如果使用虚拟环境,请先激活虚拟环境再执行上面的命令)
|
||||
|
||||
---
|
||||
|
||||
### 3. Windows 下打包后端
|
||||
|
||||
1. 打开命令提示符或 PowerShell
|
||||
|
||||
2. 进入 `backend` 目录:
|
||||
|
||||
```cmd
|
||||
cd F:\PyPro\TorqueWrench\backend
|
||||
```
|
||||
|
||||
3. 执行打包脚本:
|
||||
|
||||
```cmd
|
||||
build_backend.bat
|
||||
```
|
||||
|
||||
脚本会自动:
|
||||
- 检查并安装 PyInstaller
|
||||
- 清理旧的 `build/`、`dist/` 目录
|
||||
- 调用:
|
||||
|
||||
```cmd
|
||||
pyinstaller --name backend_api --console --paths .. --hidden-import wrench_controller app.py
|
||||
```
|
||||
|
||||
4. 打包完成后,可执行文件位置:
|
||||
|
||||
```text
|
||||
backend\dist\backend_api\backend_api.exe
|
||||
```
|
||||
|
||||
5. 如果你已经有一个正在使用的数据库文件 `wrench.db`,可以复制过去:
|
||||
|
||||
```cmd
|
||||
copy wrench.db dist\backend_api\wrench.db
|
||||
```
|
||||
|
||||
6. 启动后端服务:
|
||||
|
||||
```cmd
|
||||
dist\backend_api\backend_api.exe
|
||||
```
|
||||
|
||||
启动后,后端会监听 `http://localhost:5000`,前端照常访问。
|
||||
|
||||
---
|
||||
|
||||
### 4. Linux / Mac 下打包后端
|
||||
|
||||
1. 打开终端
|
||||
|
||||
2. 进入 `backend` 目录:
|
||||
|
||||
```bash
|
||||
cd /path/to/TorqueWrench/backend
|
||||
```
|
||||
|
||||
3. 赋予脚本执行权限(首次):
|
||||
|
||||
```bash
|
||||
chmod +x build_backend.sh
|
||||
```
|
||||
|
||||
4. 执行打包脚本:
|
||||
|
||||
```bash
|
||||
./build_backend.sh
|
||||
```
|
||||
|
||||
脚本会自动:
|
||||
- 检查并安装 PyInstaller
|
||||
- 清理旧的 `build/`、`dist/` 目录
|
||||
- 调用:
|
||||
|
||||
```bash
|
||||
pyinstaller --name backend_api --console --paths .. --hidden-import wrench_controller app.py
|
||||
```
|
||||
|
||||
5. 打包完成后,可执行文件位置:
|
||||
|
||||
```text
|
||||
backend/dist/backend_api/backend_api
|
||||
```
|
||||
|
||||
6. 如果已有生产数据库,复制到打包目录:
|
||||
|
||||
```bash
|
||||
cp wrench.db dist/backend_api/wrench.db
|
||||
```
|
||||
|
||||
7. 启动后端服务:
|
||||
|
||||
```bash
|
||||
./dist/backend_api/backend_api
|
||||
```
|
||||
|
||||
同样会监听 `http://localhost:5000`。
|
||||
|
||||
---
|
||||
|
||||
### 5. 和直接用 Python 运行的对比
|
||||
|
||||
- **直接运行(开发环境常用):**
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
python app.py
|
||||
```
|
||||
|
||||
- **打包运行(部署 / 发给其他机器):**
|
||||
- 不需要安装源码,只要有打包后的目录和配置/数据库文件即可。
|
||||
- 更适合发给不熟悉 Python 环境的用户。
|
||||
|
||||
两种方式使用的是同一套代码逻辑,差别只是启动方式。
|
||||
|
||||
---
|
||||
|
||||
### 6. 注意事项
|
||||
|
||||
- **端口**:打包后默认仍然是监听 5000 端口,如果需要改端口,需要改 `app.py` 启动代码(或用环境变量/参数)。
|
||||
- **数据库文件**:
|
||||
- 代码中使用的是:
|
||||
- `db_path = os.path.join(os.path.dirname(__file__), "wrench.db")`
|
||||
- 打包后的 `__file__` 会指向打包目录内的脚本位置,所以:
|
||||
- 你只要把 `wrench.db` 放到 `dist/backend_api/` 目录下,程序就能正常使用。
|
||||
- **日志输出**:后端是控制台应用,会在命令行窗口打印日志(方便排查问题)。
|
||||
|
||||
---
|
||||
|
||||
### 7. 简短回答你的问题
|
||||
|
||||
- **问**:后端服务要如何打包?也用同样方式可以吗?
|
||||
- **答**:可以。后端已经提供了和前端类似的一键打包脚本:
|
||||
- Windows:`backend/build_backend.bat`
|
||||
- Linux/Mac:`backend/build_backend.sh`
|
||||
|
||||
你只需要:
|
||||
|
||||
1. 进入 `backend` 目录
|
||||
2. 安装依赖:`pip install -r requirements.txt`
|
||||
3. 运行打包脚本(Windows 用 `.bat`,Linux/Mac 用 `.sh`)
|
||||
4. 把 `wrench.db` 复制到 `dist/backend_api/`,运行生成的 `backend_api` 即可。
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
{
|
||||
"api": {
|
||||
"base_url": "http://localhost:5000",
|
||||
"description": "后端API服务地址,例如:http://localhost:5000 或 http://192.168.1.100:5000"
|
||||
},
|
||||
"wrench": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 7888,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
@echo off
|
||||
REM Simple frontend build script (ASCII only, avoid encoding issues)
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
echo ========================================
|
||||
echo Building wrench_gui with PyInstaller...
|
||||
echo ========================================
|
||||
|
||||
python -c "import PyInstaller" 2>nul
|
||||
if errorlevel 1 (
|
||||
echo PyInstaller not found, installing...
|
||||
pip install pyinstaller
|
||||
if errorlevel 1 (
|
||||
echo Failed to install PyInstaller. Please run: pip install pyinstaller
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
echo Cleaning old build/dist...
|
||||
if exist build rmdir /s /q build
|
||||
if exist dist rmdir /s /q dist
|
||||
|
||||
echo Running PyInstaller...
|
||||
python -m PyInstaller wrench_gui.spec
|
||||
|
||||
if errorlevel 1 (
|
||||
echo Build failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Build success!
|
||||
echo Executable: dist\wrench_gui\wrench_gui.exe
|
||||
echo.
|
||||
echo Next steps:
|
||||
echo 1. Copy config.json to executable directory:
|
||||
echo copy ..\config.json dist\wrench_gui\config.json
|
||||
echo.
|
||||
echo 2. Make sure backend API is running (http://localhost:5000)
|
||||
echo.
|
||||
echo 3. Run the program:
|
||||
echo dist\wrench_gui\wrench_gui.exe
|
||||
echo.
|
||||
pause
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/bash
|
||||
# 电动扳手GUI打包脚本(Linux/Mac)
|
||||
|
||||
echo "========================================"
|
||||
echo "电动扳手GUI打包脚本"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 检查 PyInstaller 是否已安装
|
||||
if ! python3 -c "import PyInstaller" 2>/dev/null; then
|
||||
echo "[错误] PyInstaller 未安装"
|
||||
echo "正在安装 PyInstaller..."
|
||||
pip3 install pyinstaller
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "[错误] PyInstaller 安装失败,请手动执行: pip3 install pyinstaller"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[信息] PyInstaller 已安装"
|
||||
echo ""
|
||||
|
||||
# 确保在 frontend 目录下执行
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 清理之前的构建文件
|
||||
echo "[信息] 清理之前的构建文件..."
|
||||
rm -rf build dist
|
||||
|
||||
echo ""
|
||||
echo "[信息] 当前目录: $(pwd)"
|
||||
echo "[信息] 开始打包..."
|
||||
echo ""
|
||||
|
||||
# 执行打包
|
||||
pyinstaller wrench_gui.spec
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ""
|
||||
echo "[错误] 打包失败!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "打包完成!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "可执行文件位置: dist/wrench_gui/wrench_gui"
|
||||
echo ""
|
||||
echo "下一步操作:"
|
||||
echo "1. 复制配置文件到可执行文件目录:"
|
||||
echo " cp ../config.json dist/wrench_gui/config.json"
|
||||
echo ""
|
||||
echo "2. 确保后端API服务已启动(http://localhost:5000)"
|
||||
echo ""
|
||||
echo "3. 运行程序:"
|
||||
echo " ./dist/wrench_gui/wrench_gui"
|
||||
echo ""
|
||||
|
||||
|
|
@ -2,3 +2,5 @@ requests==2.31.0
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,5 @@ pause
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,3 +8,5 @@ python3 wrench_gui.py
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ class WrenchGUI:
|
|||
screen_height = self.root.winfo_screenheight()
|
||||
self.root.geometry(f"{screen_width}x{screen_height}")
|
||||
|
||||
# API配置
|
||||
self.api_base_url = "http://localhost:5000/api"
|
||||
# API配置(从配置文件读取,如果没有则使用默认值)
|
||||
self.api_base_url = self.load_api_config()
|
||||
self.poll_interval = 3 # 轮询间隔(秒)
|
||||
|
||||
# 数据
|
||||
|
|
@ -82,6 +82,9 @@ class WrenchGUI:
|
|||
# 检查测试模式
|
||||
self.check_test_mode()
|
||||
|
||||
# 初始化螺栓列表显示(确保始终可见)
|
||||
self.update_bolt_list()
|
||||
|
||||
# 加载设备列表
|
||||
self.load_wrench_devices()
|
||||
|
||||
|
|
@ -90,10 +93,21 @@ class WrenchGUI:
|
|||
|
||||
def _create_widgets(self):
|
||||
"""创建界面组件"""
|
||||
# 标题
|
||||
# 使用左右布局:左侧工单列表,右侧其他内容
|
||||
self.root.update_idletasks()
|
||||
screen_height = self.root.winfo_screenheight()
|
||||
|
||||
# 配置列权重:左侧40%,右侧60%,支持1920和1600分辨率
|
||||
# 使用相对权重,自适应不同分辨率
|
||||
self.root.grid_columnconfigure(0, weight=2, minsize=350) # 左侧工单列表和日志,最小350像素
|
||||
self.root.grid_columnconfigure(1, weight=3, minsize=450) # 右侧内容区域,最小450像素
|
||||
# 配置行权重:从row=2开始的内容区域可扩展
|
||||
self.root.grid_rowconfigure(2, weight=1)
|
||||
|
||||
# 标题(横跨两列)
|
||||
title_frame = tk.Frame(self.root, bg="#2c3e50", height=60)
|
||||
title_frame.pack(fill=tk.X)
|
||||
title_frame.pack_propagate(False)
|
||||
title_frame.grid(row=0, column=0, columnspan=2, sticky="ew")
|
||||
title_frame.grid_propagate(False)
|
||||
|
||||
title_label = tk.Label(
|
||||
title_frame,
|
||||
|
|
@ -114,9 +128,9 @@ class WrenchGUI:
|
|||
)
|
||||
self.test_mode_label.place(relx=1.0, rely=0.5, anchor=tk.E, x=-20)
|
||||
|
||||
# 工单列表状态区域
|
||||
# 工单列表状态区域(横跨两列)
|
||||
status_frame = tk.Frame(self.root, padx=10, pady=5)
|
||||
status_frame.pack(fill=tk.X)
|
||||
status_frame.grid(row=1, column=0, columnspan=2, sticky="ew")
|
||||
|
||||
self.poll_status_label = tk.Label(
|
||||
status_frame,
|
||||
|
|
@ -146,13 +160,24 @@ class WrenchGUI:
|
|||
)
|
||||
self.refresh_button.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
# 工单列表区域
|
||||
order_list_frame = tk.LabelFrame(self.root, text="可用工单列表", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
order_list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
# ========== 左侧:工单列表和操作控制 ==========
|
||||
# 创建左侧主容器(上下布局:工单列表 + 操作控制)
|
||||
left_container = tk.Frame(self.root)
|
||||
left_container.grid(row=2, column=0, sticky="nsew", padx=(10, 5), pady=5)
|
||||
# 配置行权重:工单列表可扩展,操作控制固定不压缩
|
||||
left_container.grid_rowconfigure(0, weight=3) # 工单列表权重较大
|
||||
left_container.grid_rowconfigure(1, weight=0, minsize=120) # 操作控制固定,最小120像素,不压缩
|
||||
left_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 创建工单列表表格
|
||||
# 工单列表区域
|
||||
order_list_frame = tk.LabelFrame(left_container, text="可用工单列表", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
order_list_frame.grid(row=0, column=0, sticky="nsew", padx=0, pady=(0, 5))
|
||||
order_list_frame.grid_rowconfigure(0, weight=1)
|
||||
order_list_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 创建工单列表表格(使用相对高度,不固定)
|
||||
order_columns = ("追溯号", "工序号", "工序名称", "产品", "螺栓数", "状态")
|
||||
self.order_tree = ttk.Treeview(order_list_frame, columns=order_columns, show="headings", height=5)
|
||||
self.order_tree = ttk.Treeview(order_list_frame, columns=order_columns, show="headings")
|
||||
|
||||
for col in order_columns:
|
||||
self.order_tree.heading(col, text=col)
|
||||
|
|
@ -164,9 +189,119 @@ class WrenchGUI:
|
|||
self.order_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
order_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
# 工单信息区域
|
||||
info_frame = tk.LabelFrame(self.root, text="当前工单信息", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
info_frame.pack(fill=tk.X, padx=10, pady=10)
|
||||
# 操作控制区域(移到左侧,放在工单列表下方)
|
||||
button_frame = tk.LabelFrame(left_container, text="操作控制", font=("微软雅黑", 12), padx=10, pady=8)
|
||||
button_frame.grid(row=1, column=0, sticky="ew", padx=0, pady=0)
|
||||
|
||||
# 使用grid布局,将所有按钮放在同一行确保可见
|
||||
# 第一列:工单操作
|
||||
col1_frame = tk.Frame(button_frame)
|
||||
col1_frame.grid(row=0, column=0, padx=(0, 20), sticky=tk.W)
|
||||
|
||||
tk.Label(col1_frame, text="工单操作", font=("微软雅黑", 9, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 3))
|
||||
self.claim_button = tk.Button(
|
||||
col1_frame,
|
||||
text="认领工单",
|
||||
font=("微软雅黑", 10),
|
||||
bg="#f39c12",
|
||||
fg="white",
|
||||
width=10,
|
||||
height=1,
|
||||
cursor="hand2",
|
||||
command=self.claim_work_order
|
||||
)
|
||||
self.claim_button.pack()
|
||||
|
||||
# 第二列:设备选择
|
||||
col2_frame = tk.Frame(button_frame)
|
||||
col2_frame.grid(row=0, column=1, padx=(0, 20), sticky=tk.W)
|
||||
|
||||
tk.Label(col2_frame, text="设备选择", font=("微软雅黑", 9, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 3))
|
||||
device_select_inner = tk.Frame(col2_frame)
|
||||
device_select_inner.pack()
|
||||
|
||||
tk.Label(device_select_inner, text="选择扳手:", font=("微软雅黑", 9)).pack(side=tk.LEFT, padx=(0, 5))
|
||||
|
||||
# 设备状态指示器(圆点)
|
||||
self.device_status_indicator = tk.Label(
|
||||
device_select_inner,
|
||||
text="●",
|
||||
font=("微软雅黑", 12),
|
||||
fg="#7f8c8d" # 默认灰色
|
||||
)
|
||||
self.device_status_indicator.pack(side=tk.LEFT, padx=(0, 3))
|
||||
|
||||
self.device_combo = ttk.Combobox(device_select_inner, width=18, state="readonly", font=("微软雅黑", 9))
|
||||
self.device_combo.pack(side=tk.LEFT, padx=(0, 5))
|
||||
self.device_combo.bind("<<ComboboxSelected>>", self.on_device_selected)
|
||||
|
||||
self.device_manage_button = tk.Button(
|
||||
device_select_inner,
|
||||
text="设备管理",
|
||||
font=("微软雅黑", 8),
|
||||
bg="#3498db",
|
||||
fg="white",
|
||||
width=8,
|
||||
height=1,
|
||||
command=self.open_device_manager
|
||||
)
|
||||
self.device_manage_button.pack(side=tk.LEFT)
|
||||
|
||||
# 第三列:执行控制(放在同一行,确保可见)
|
||||
col3_frame = tk.Frame(button_frame)
|
||||
col3_frame.grid(row=0, column=2, padx=(0, 0), sticky=tk.W)
|
||||
|
||||
tk.Label(col3_frame, text="执行控制", font=("微软雅黑", 9, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 3))
|
||||
exec_control_inner = tk.Frame(col3_frame)
|
||||
exec_control_inner.pack()
|
||||
|
||||
self.start_button = tk.Button(
|
||||
exec_control_inner,
|
||||
text="开始拧紧",
|
||||
font=("微软雅黑", 10),
|
||||
bg="#27ae60",
|
||||
fg="white",
|
||||
width=10,
|
||||
height=1,
|
||||
cursor="hand2",
|
||||
command=self.start_process,
|
||||
state=tk.DISABLED
|
||||
)
|
||||
self.start_button.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
self.stop_button = tk.Button(
|
||||
exec_control_inner,
|
||||
text="停止",
|
||||
font=("微软雅黑", 10),
|
||||
bg="#e74c3c",
|
||||
fg="white",
|
||||
width=10,
|
||||
height=1,
|
||||
cursor="hand2",
|
||||
state=tk.DISABLED,
|
||||
command=self.stop_process
|
||||
)
|
||||
self.stop_button.pack(side=tk.LEFT)
|
||||
|
||||
# 配置grid列权重
|
||||
button_frame.grid_columnconfigure(0, weight=0)
|
||||
button_frame.grid_columnconfigure(1, weight=1)
|
||||
button_frame.grid_columnconfigure(2, weight=0)
|
||||
|
||||
# ========== 右侧:内容区域 ==========
|
||||
# 创建右侧主容器(上下布局)
|
||||
right_container = tk.Frame(self.root)
|
||||
right_container.grid(row=2, column=1, sticky="nsew", padx=(5, 10), pady=5)
|
||||
# 配置行权重:日志区域可压缩,螺栓列表可压缩
|
||||
right_container.grid_rowconfigure(0, weight=0) # 工单信息不扩展
|
||||
right_container.grid_rowconfigure(1, weight=0) # 当前螺栓不扩展
|
||||
right_container.grid_rowconfigure(2, weight=3) # 螺栓列表可扩展可压缩(主要区域)
|
||||
right_container.grid_rowconfigure(3, weight=1, minsize=100) # 日志区域可压缩,最小100像素
|
||||
right_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 工单信息区域(上下布局,第一行)
|
||||
info_frame = tk.LabelFrame(right_container, text="当前工单信息", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
info_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
|
||||
|
||||
self.trace_id_label = tk.Label(info_frame, text="追溯号: --", font=("微软雅黑", 10))
|
||||
self.trace_id_label.grid(row=0, column=0, sticky=tk.W, padx=5, pady=2)
|
||||
|
|
@ -186,9 +321,9 @@ class WrenchGUI:
|
|||
self.device_label = tk.Label(info_frame, text="扳手设备: --", font=("微软雅黑", 10), fg="#2c3e50")
|
||||
self.device_label.grid(row=1, column=2, sticky=tk.W, padx=5, pady=2)
|
||||
|
||||
# 当前螺栓信息
|
||||
current_frame = tk.LabelFrame(self.root, text="当前螺栓", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
current_frame.pack(fill=tk.X, padx=10, pady=10)
|
||||
# 当前螺栓信息(上下布局,第二行)
|
||||
current_frame = tk.LabelFrame(right_container, text="当前螺栓", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
current_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=5)
|
||||
|
||||
self.current_bolt_label = tk.Label(
|
||||
current_frame,
|
||||
|
|
@ -213,9 +348,9 @@ class WrenchGUI:
|
|||
)
|
||||
self.current_status_label.pack(pady=2)
|
||||
|
||||
# 进度条
|
||||
progress_frame = tk.Frame(self.root, padx=10, pady=5)
|
||||
progress_frame.pack(fill=tk.X, padx=10)
|
||||
# 进度条(放在current_frame内部)
|
||||
progress_frame = tk.Frame(current_frame, padx=10, pady=5)
|
||||
progress_frame.pack(fill=tk.X, pady=5)
|
||||
|
||||
self.progress_label = tk.Label(progress_frame, text="总进度: 0/0", font=("微软雅黑", 10))
|
||||
self.progress_label.pack(anchor=tk.W)
|
||||
|
|
@ -223,13 +358,29 @@ class WrenchGUI:
|
|||
self.progress_bar = ttk.Progressbar(progress_frame, mode='determinate', length=400)
|
||||
self.progress_bar.pack(fill=tk.X, pady=5)
|
||||
|
||||
# 螺栓列表
|
||||
list_frame = tk.LabelFrame(self.root, text="螺栓列表", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
list_frame.pack(fill=tk.BOTH, expand=False, padx=10, pady=10)
|
||||
# 螺栓列表(固定在"当前螺栓"下方、"操作控制"上方,可压缩但始终可见)
|
||||
# 创建螺栓列表框架
|
||||
list_frame = tk.LabelFrame(right_container, text="螺栓列表", font=("微软雅黑", 12), padx=10, pady=10)
|
||||
list_frame.grid(row=2, column=0, sticky="nsew", padx=5, pady=5)
|
||||
# 螺栓列表通过right_container的row=2的weight=1来控制,可以压缩
|
||||
# 设置最小高度,支持1920和1600分辨率
|
||||
self.root.update_idletasks()
|
||||
screen_height = self.root.winfo_screenheight()
|
||||
# 根据分辨率动态调整最小高度:1920约180像素,1600约150像素
|
||||
if screen_height >= 1920:
|
||||
min_list_height = max(180, int(screen_height * 0.15))
|
||||
elif screen_height >= 1600:
|
||||
min_list_height = max(150, int(screen_height * 0.15))
|
||||
else:
|
||||
min_list_height = max(120, int(screen_height * 0.15))
|
||||
right_container.grid_rowconfigure(2, weight=1, minsize=min_list_height)
|
||||
|
||||
# 创建表格(减少高度,确保按钮区域可见)
|
||||
# 保存list_frame引用,确保始终可见
|
||||
self.list_frame = list_frame
|
||||
|
||||
# 创建表格(使用相对布局,不固定高度)
|
||||
columns = ("序号", "名称", "扭矩(Nm)", "状态", "实际扭矩", "时间")
|
||||
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=4)
|
||||
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings")
|
||||
|
||||
# 设置列
|
||||
for col in columns:
|
||||
|
|
@ -255,129 +406,62 @@ class WrenchGUI:
|
|||
self.tree.tag_configure("success", foreground="#27ae60", font=("微软雅黑", 10, "bold"))
|
||||
self.tree.tag_configure("failed", foreground="#e74c3c")
|
||||
|
||||
# 控制按钮区域(放在螺栓列表之后,确保可见)
|
||||
button_frame = tk.LabelFrame(self.root, text="操作控制", font=("微软雅黑", 12), padx=15, pady=15)
|
||||
button_frame.pack(fill=tk.X, padx=10, pady=10)
|
||||
# 操作日志区域(移到右侧,放在螺栓列表下方)
|
||||
log_frame = tk.LabelFrame(right_container, text="操作日志", font=("微软雅黑", 9), padx=5, pady=3)
|
||||
log_frame.grid(row=3, column=0, sticky="nsew", padx=5, pady=(0, 5))
|
||||
log_frame.grid_rowconfigure(0, weight=1)
|
||||
log_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# 使用grid布局,将所有按钮放在同一行确保可见
|
||||
# 第一列:工单操作
|
||||
col1_frame = tk.Frame(button_frame)
|
||||
col1_frame.grid(row=0, column=0, padx=(0, 20), sticky=tk.W)
|
||||
|
||||
tk.Label(col1_frame, text="工单操作", font=("微软雅黑", 10, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 5))
|
||||
self.claim_button = tk.Button(
|
||||
col1_frame,
|
||||
text="认领工单",
|
||||
font=("微软雅黑", 11, "bold"),
|
||||
bg="#f39c12",
|
||||
fg="white",
|
||||
width=12,
|
||||
height=2,
|
||||
cursor="hand2",
|
||||
command=self.claim_work_order
|
||||
)
|
||||
self.claim_button.pack()
|
||||
|
||||
# 第二列:设备选择
|
||||
col2_frame = tk.Frame(button_frame)
|
||||
col2_frame.grid(row=0, column=1, padx=(0, 20), sticky=tk.W)
|
||||
|
||||
tk.Label(col2_frame, text="设备选择", font=("微软雅黑", 10, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 5))
|
||||
device_select_inner = tk.Frame(col2_frame)
|
||||
device_select_inner.pack()
|
||||
|
||||
tk.Label(device_select_inner, text="选择扳手:", font=("微软雅黑", 10)).pack(side=tk.LEFT, padx=(0, 5))
|
||||
|
||||
# 设备状态指示器(圆点)
|
||||
self.device_status_indicator = tk.Label(
|
||||
device_select_inner,
|
||||
text="●",
|
||||
font=("微软雅黑", 12),
|
||||
fg="#7f8c8d" # 默认灰色
|
||||
)
|
||||
self.device_status_indicator.pack(side=tk.LEFT, padx=(0, 3))
|
||||
|
||||
self.device_combo = ttk.Combobox(device_select_inner, width=20, state="readonly", font=("微软雅黑", 10))
|
||||
self.device_combo.pack(side=tk.LEFT, padx=(0, 5))
|
||||
self.device_combo.bind("<<ComboboxSelected>>", self.on_device_selected)
|
||||
|
||||
self.device_manage_button = tk.Button(
|
||||
device_select_inner,
|
||||
text="设备管理",
|
||||
font=("微软雅黑", 9),
|
||||
bg="#3498db",
|
||||
fg="white",
|
||||
width=10,
|
||||
height=1,
|
||||
command=self.open_device_manager
|
||||
)
|
||||
self.device_manage_button.pack(side=tk.LEFT)
|
||||
|
||||
# 第三列:执行控制(放在同一行,确保可见)
|
||||
col3_frame = tk.Frame(button_frame)
|
||||
col3_frame.grid(row=0, column=2, padx=(0, 0), sticky=tk.W)
|
||||
|
||||
tk.Label(col3_frame, text="执行控制", font=("微软雅黑", 10, "bold"), fg="#2c3e50").pack(anchor=tk.W, pady=(0, 5))
|
||||
exec_control_inner = tk.Frame(col3_frame)
|
||||
exec_control_inner.pack()
|
||||
|
||||
self.start_button = tk.Button(
|
||||
exec_control_inner,
|
||||
text="开始拧紧",
|
||||
font=("微软雅黑", 11, "bold"),
|
||||
bg="#27ae60",
|
||||
fg="white",
|
||||
width=12,
|
||||
height=2,
|
||||
cursor="hand2",
|
||||
command=self.start_process,
|
||||
state=tk.DISABLED
|
||||
)
|
||||
self.start_button.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
self.stop_button = tk.Button(
|
||||
exec_control_inner,
|
||||
text="停止",
|
||||
font=("微软雅黑", 11, "bold"),
|
||||
bg="#e74c3c",
|
||||
fg="white",
|
||||
width=12,
|
||||
height=2,
|
||||
cursor="hand2",
|
||||
state=tk.DISABLED,
|
||||
command=self.stop_process
|
||||
)
|
||||
self.stop_button.pack(side=tk.LEFT)
|
||||
|
||||
# 配置grid列权重
|
||||
button_frame.grid_columnconfigure(0, weight=0)
|
||||
button_frame.grid_columnconfigure(1, weight=1)
|
||||
button_frame.grid_columnconfigure(2, weight=0)
|
||||
|
||||
# 日志区域
|
||||
log_frame = tk.LabelFrame(self.root, text="操作日志", font=("微软雅黑", 10), padx=5, pady=5)
|
||||
log_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
|
||||
|
||||
self.log_text = tk.Text(log_frame, height=5, font=("Consolas", 9))
|
||||
self.log_text = tk.Text(log_frame, font=("Consolas", 9))
|
||||
self.log_text.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
log_scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
|
||||
self.log_text.configure(yscrollcommand=log_scrollbar.set)
|
||||
log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def get_config_path(self):
|
||||
"""获取配置文件路径(支持开发环境和打包后的环境)"""
|
||||
# 如果是打包后的可执行文件,config.json 应该在可执行文件同一目录
|
||||
if getattr(sys, 'frozen', False):
|
||||
# 打包后的环境
|
||||
base_path = os.path.dirname(sys.executable)
|
||||
else:
|
||||
# 开发环境,在项目根目录
|
||||
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.join(base_path, "config.json")
|
||||
|
||||
def load_api_config(self):
|
||||
"""从配置文件读取API地址,如果没有则使用默认值"""
|
||||
default_url = "http://localhost:5000/api"
|
||||
try:
|
||||
config_path = self.get_config_path()
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
api_config = config.get("api", {})
|
||||
api_url = api_config.get("base_url", default_url)
|
||||
# 确保URL以/api结尾
|
||||
if not api_url.endswith("/api"):
|
||||
api_url = api_url.rstrip("/") + "/api"
|
||||
return api_url
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to load API config: {e}, using default: {default_url}")
|
||||
return default_url
|
||||
|
||||
def check_test_mode(self):
|
||||
"""检查测试模式"""
|
||||
try:
|
||||
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json")
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
self.test_mode = config.get("test_mode", {}).get("enabled", False)
|
||||
|
||||
if self.test_mode:
|
||||
self.test_mode_label.config(text="⚠️ 测试模式")
|
||||
self.log("⚠️ 测试模式已启用:失败也算成功", "WARN")
|
||||
else:
|
||||
self.test_mode_label.config(text="")
|
||||
config_path = self.get_config_path()
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
self.test_mode = config.get("test_mode", {}).get("enabled", False)
|
||||
|
||||
if self.test_mode:
|
||||
self.test_mode_label.config(text="⚠️ 测试模式")
|
||||
self.log("⚠️ 测试模式已启用:失败也算成功", "WARN")
|
||||
else:
|
||||
self.test_mode_label.config(text="")
|
||||
except Exception as e:
|
||||
self.log(f"读取配置失败: {e}", "ERROR")
|
||||
|
||||
|
|
@ -605,11 +689,19 @@ class WrenchGUI:
|
|||
self.device_label.config(text="扳手设备: 未选择", foreground="#2c3e50")
|
||||
|
||||
def update_bolt_list(self):
|
||||
"""更新螺栓列表"""
|
||||
"""更新螺栓列表(始终显示,即使没有工单)"""
|
||||
# 确保螺栓列表框架始终显示,无论连接状态如何
|
||||
if hasattr(self, 'list_frame'):
|
||||
self.list_frame.grid() # 强制显示,防止被隐藏
|
||||
|
||||
# 始终清空表格,确保表格框架始终可见
|
||||
self.tree.delete(*self.tree.get_children())
|
||||
|
||||
if not self.work_order:
|
||||
# 没有工单时,显示空表格,但表格框架保持可见
|
||||
return
|
||||
|
||||
self.tree.delete(*self.tree.get_children())
|
||||
# 有工单时,显示螺栓数据
|
||||
bolts = self.work_order.get('bolts', [])
|
||||
for bolt in bolts:
|
||||
self.tree.insert("", tk.END, values=(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
"""
|
||||
PyInstaller 打包配置文件
|
||||
用于打包 wrench_gui.py 为可执行文件
|
||||
"""
|
||||
|
||||
block_cipher = None
|
||||
|
||||
import os
|
||||
|
||||
# 获取当前脚本所在目录的父目录(项目根目录)
|
||||
# 这样 PyInstaller 可以找到 wrench_controller.py
|
||||
current_dir = os.path.dirname(os.path.abspath('wrench_gui.spec'))
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
|
||||
a = Analysis(
|
||||
['wrench_gui.py'],
|
||||
pathex=[
|
||||
parent_dir, # 添加父目录到搜索路径,让 PyInstaller 能找到 wrench_controller.py
|
||||
current_dir, # 当前目录
|
||||
],
|
||||
binaries=[],
|
||||
datas=[
|
||||
# 包含配置文件(如果程序需要读取)
|
||||
(os.path.join(parent_dir, 'config.json'), '.'), # 将 config.json 复制到可执行文件目录
|
||||
],
|
||||
hiddenimports=[
|
||||
'wrench_controller',
|
||||
'device_manager',
|
||||
'tkinter',
|
||||
'tkinter.ttk',
|
||||
'tkinter.messagebox',
|
||||
'requests',
|
||||
'json',
|
||||
'threading',
|
||||
'socket',
|
||||
'struct',
|
||||
'datetime',
|
||||
'pathlib',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='wrench_gui',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False, # 不显示控制台窗口(GUI应用)
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=None, # 可以添加图标文件路径,如 'icon.ico'
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
========================================
|
||||
wrench_gui.py 快速打包指南
|
||||
========================================
|
||||
|
||||
【Windows 系统】
|
||||
|
||||
1. 打开命令提示符(CMD)或 PowerShell
|
||||
|
||||
2. 进入 frontend 目录:
|
||||
cd F:\PyPro\TorqueWrench\frontend
|
||||
|
||||
3. 执行打包脚本:
|
||||
build_exe.bat
|
||||
|
||||
4. 等待打包完成(约 1-3 分钟)
|
||||
|
||||
5. 打包完成后,可执行文件在:
|
||||
dist\wrench_gui\wrench_gui.exe
|
||||
|
||||
6. 复制配置文件:
|
||||
copy ..\config.json dist\wrench_gui\config.json
|
||||
|
||||
7. 运行程序:
|
||||
dist\wrench_gui\wrench_gui.exe
|
||||
|
||||
|
||||
【Linux/Mac 系统】
|
||||
|
||||
1. 打开终端
|
||||
|
||||
2. 进入 frontend 目录:
|
||||
cd /path/to/TorqueWrench/frontend
|
||||
|
||||
3. 给脚本添加执行权限:
|
||||
chmod +x build_exe.sh
|
||||
|
||||
4. 执行打包脚本:
|
||||
./build_exe.sh
|
||||
|
||||
5. 等待打包完成(约 1-3 分钟)
|
||||
|
||||
6. 打包完成后,可执行文件在:
|
||||
dist/wrench_gui/wrench_gui
|
||||
|
||||
7. 复制配置文件:
|
||||
cp ../config.json dist/wrench_gui/config.json
|
||||
|
||||
8. 运行程序:
|
||||
./dist/wrench_gui/wrench_gui
|
||||
|
||||
|
||||
【注意事项】
|
||||
|
||||
1. 首次打包前,确保已安装 PyInstaller:
|
||||
pip install pyinstaller
|
||||
|
||||
2. 确保已安装所有依赖:
|
||||
pip install -r requirements.txt
|
||||
|
||||
3. 打包前确保后端服务可以正常启动
|
||||
|
||||
4. 打包后的程序需要 config.json 配置文件才能运行
|
||||
|
||||
5. 如果打包失败,查看错误信息,通常是缺少依赖包
|
||||
|
||||
|
||||
【常见问题】
|
||||
|
||||
Q: 打包失败,提示找不到模块?
|
||||
A: 检查是否已安装所有依赖:pip install -r requirements.txt
|
||||
|
||||
Q: 打包后的程序无法启动?
|
||||
A: 确保 config.json 在可执行文件同一目录下
|
||||
|
||||
Q: 打包后的程序很大?
|
||||
A: 这是正常的,PyInstaller 会打包所有依赖,通常 15-30MB
|
||||
|
||||
Q: 如何减小打包后的文件大小?
|
||||
A: 使用虚拟环境,只安装必需的包,或在 spec 文件中排除不需要的模块
|
||||
|
||||
|
||||
【技术支持】
|
||||
|
||||
详细说明请查看:打包说明.md
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
# wrench_gui.py 打包说明
|
||||
|
||||
## 一、环境准备
|
||||
|
||||
### 1. 安装 Python 依赖
|
||||
|
||||
确保已安装所有必需的 Python 包:
|
||||
|
||||
```bash
|
||||
# 进入 frontend 目录
|
||||
cd frontend
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 安装 PyInstaller(如果未安装)
|
||||
pip install pyinstaller
|
||||
```
|
||||
|
||||
### 2. 检查文件结构
|
||||
|
||||
确保以下文件存在:
|
||||
|
||||
```
|
||||
TorqueWrench/
|
||||
├── frontend/
|
||||
│ ├── wrench_gui.py # 主程序
|
||||
│ ├── device_manager.py # 设备管理模块
|
||||
│ ├── wrench_gui.spec # PyInstaller 配置文件
|
||||
│ ├── build_exe.bat # Windows 打包脚本
|
||||
│ └── build_exe.sh # Linux/Mac 打包脚本
|
||||
├── wrench_controller.py # 扳手控制模块(父目录)
|
||||
└── config.json # 配置文件(父目录)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、打包步骤
|
||||
|
||||
### Windows 系统
|
||||
|
||||
1. **打开命令提示符或 PowerShell**
|
||||
|
||||
2. **进入 frontend 目录**
|
||||
```cmd
|
||||
cd F:\PyPro\TorqueWrench\frontend
|
||||
```
|
||||
|
||||
3. **执行打包脚本**
|
||||
```cmd
|
||||
build_exe.bat
|
||||
```
|
||||
|
||||
或者手动执行:
|
||||
```cmd
|
||||
pyinstaller wrench_gui.spec
|
||||
```
|
||||
|
||||
### Linux/Mac 系统
|
||||
|
||||
1. **打开终端**
|
||||
|
||||
2. **进入 frontend 目录**
|
||||
```bash
|
||||
cd /path/to/TorqueWrench/frontend
|
||||
```
|
||||
|
||||
3. **给脚本添加执行权限**
|
||||
```bash
|
||||
chmod +x build_exe.sh
|
||||
```
|
||||
|
||||
4. **执行打包脚本**
|
||||
```bash
|
||||
./build_exe.sh
|
||||
```
|
||||
|
||||
或者手动执行:
|
||||
```bash
|
||||
pyinstaller wrench_gui.spec
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、打包输出
|
||||
|
||||
打包完成后,会在 `frontend/dist/` 目录下生成可执行文件:
|
||||
|
||||
### Windows
|
||||
- **位置**: `frontend/dist/wrench_gui/wrench_gui.exe`
|
||||
- **大小**: 约 15-30 MB(取决于依赖)
|
||||
|
||||
### Linux/Mac
|
||||
- **位置**: `frontend/dist/wrench_gui/wrench_gui`
|
||||
- **大小**: 约 15-30 MB
|
||||
|
||||
---
|
||||
|
||||
## 四、运行打包后的程序
|
||||
|
||||
### 1. 准备配置文件
|
||||
|
||||
将 `config.json` 复制到可执行文件所在目录:
|
||||
|
||||
**Windows:**
|
||||
```cmd
|
||||
copy ..\config.json dist\wrench_gui\config.json
|
||||
```
|
||||
|
||||
**Linux/Mac:**
|
||||
```bash
|
||||
cp ../config.json dist/wrench_gui/config.json
|
||||
```
|
||||
|
||||
### 2. 启动后端服务
|
||||
|
||||
确保后端 API 服务已启动:
|
||||
|
||||
```bash
|
||||
# 进入 backend 目录
|
||||
cd ../backend
|
||||
|
||||
# 启动后端(Windows)
|
||||
start_backend.bat
|
||||
|
||||
# 或启动后端(Linux/Mac)
|
||||
./start_backend.sh
|
||||
```
|
||||
|
||||
### 3. 运行可执行文件
|
||||
|
||||
**Windows:**
|
||||
- 双击 `dist/wrench_gui/wrench_gui.exe`
|
||||
- 或在命令行执行:
|
||||
```cmd
|
||||
dist\wrench_gui\wrench_gui.exe
|
||||
```
|
||||
|
||||
**Linux/Mac:**
|
||||
```bash
|
||||
./dist/wrench_gui/wrench_gui
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、常见问题
|
||||
|
||||
### 1. 打包失败:找不到模块
|
||||
|
||||
**问题**: `ModuleNotFoundError: No module named 'xxx'`
|
||||
|
||||
**解决**:
|
||||
- 检查是否已安装所有依赖:`pip install -r requirements.txt`
|
||||
- 检查 `wrench_gui.spec` 中的 `hiddenimports` 是否包含缺失的模块
|
||||
|
||||
### 2. 运行时报错:找不到 config.json
|
||||
|
||||
**问题**: 程序启动时报错找不到配置文件
|
||||
|
||||
**解决**:
|
||||
- 确保 `config.json` 在可执行文件同一目录下
|
||||
- 检查 `wrench_gui.spec` 中的 `datas` 配置是否正确
|
||||
|
||||
### 3. 打包后的程序很大(>50MB)
|
||||
|
||||
**原因**: PyInstaller 会打包所有依赖库
|
||||
|
||||
**优化**:
|
||||
- 使用虚拟环境,只安装必需的包
|
||||
- 在 `wrench_gui.spec` 的 `excludes` 中添加不需要的模块
|
||||
|
||||
### 4. 控制台窗口也显示(Windows)
|
||||
|
||||
**问题**: 打包后的程序同时显示 GUI 和控制台窗口
|
||||
|
||||
**解决**:
|
||||
- 检查 `wrench_gui.spec` 中 `console=False` 是否设置正确
|
||||
|
||||
### 5. 图标设置
|
||||
|
||||
如果需要自定义程序图标:
|
||||
|
||||
1. 准备图标文件(`.ico` 格式,Windows)
|
||||
2. 修改 `wrench_gui.spec`:
|
||||
```python
|
||||
icon='icon.ico', # 图标文件路径
|
||||
```
|
||||
3. 重新打包
|
||||
|
||||
---
|
||||
|
||||
## 六、打包选项说明
|
||||
|
||||
### wrench_gui.spec 关键配置
|
||||
|
||||
- **`console=False`**: 不显示控制台窗口(GUI应用)
|
||||
- **`upx=True`**: 使用 UPX 压缩(减小文件大小)
|
||||
- **`datas`**: 包含的数据文件(如配置文件)
|
||||
- **`hiddenimports`**: 需要显式导入的隐藏模块
|
||||
|
||||
### 单文件打包(可选)
|
||||
|
||||
如果需要打包成单个 `.exe` 文件,修改 `wrench_gui.spec`:
|
||||
|
||||
```python
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True, # 改为 True
|
||||
name='wrench_gui',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
)
|
||||
```
|
||||
|
||||
然后添加 `COLLECT` 部分(PyInstaller 会自动处理)。
|
||||
|
||||
---
|
||||
|
||||
## 七、分发打包后的程序
|
||||
|
||||
### 1. 最小分发包
|
||||
|
||||
需要包含的文件:
|
||||
|
||||
```
|
||||
wrench_gui/
|
||||
├── wrench_gui.exe # 可执行文件
|
||||
├── config.json # 配置文件
|
||||
└── README.txt # 使用说明(可选)
|
||||
```
|
||||
|
||||
### 2. 完整分发包(推荐)
|
||||
|
||||
```
|
||||
wrench_gui/
|
||||
├── wrench_gui.exe
|
||||
├── config.json
|
||||
├── README.txt
|
||||
└── 使用说明.pdf # 用户手册
|
||||
```
|
||||
|
||||
### 3. 注意事项
|
||||
|
||||
- 确保目标机器已安装必要的运行时库(如 Visual C++ Redistributable,Windows)
|
||||
- 如果使用了 UPX 压缩,某些杀毒软件可能会误报,建议关闭 UPX 或添加白名单
|
||||
|
||||
---
|
||||
|
||||
## 八、快速打包命令(一键打包)
|
||||
|
||||
### Windows
|
||||
```cmd
|
||||
cd frontend && build_exe.bat
|
||||
```
|
||||
|
||||
### Linux/Mac
|
||||
```bash
|
||||
cd frontend && chmod +x build_exe.sh && ./build_exe.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、验证打包结果
|
||||
|
||||
打包完成后,建议测试:
|
||||
|
||||
1. **运行可执行文件**
|
||||
- 检查是否能正常启动
|
||||
- 检查是否能连接到后端 API
|
||||
- 检查设备管理功能是否正常
|
||||
|
||||
2. **检查文件大小**
|
||||
- 正常大小:15-30 MB
|
||||
- 如果过大(>50MB),检查依赖
|
||||
|
||||
3. **测试功能**
|
||||
- 工单列表加载
|
||||
- 设备选择
|
||||
- 拧紧操作
|
||||
- 数据提交
|
||||
|
||||
---
|
||||
|
||||
## 十、技术支持
|
||||
|
||||
如果遇到打包问题:
|
||||
|
||||
1. 查看 PyInstaller 官方文档:https://pyinstaller.org/
|
||||
2. 检查错误日志:`build/wrench_gui/warn-wrench_gui.txt`
|
||||
3. 使用调试模式:在 `wrench_gui.spec` 中设置 `debug=True`
|
||||
|
||||
|
||||
|
||||
|
|
@ -79,3 +79,5 @@ while true; do
|
|||
done
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -72,3 +72,5 @@ echo " 查看日志: sudo journalctl -u ssh-tunnel -f"
|
|||
echo " 禁用自启: sudo systemctl disable ssh-tunnel"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -105,3 +105,5 @@ echo ""
|
|||
echo " 或者使用自动重连脚本(见下方)"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,3 +9,5 @@ pause
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@ python3 wrench_simulator.py
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81,3 +81,5 @@ if __name__ == "__main__":
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -72,3 +72,5 @@ if __name__ == "__main__":
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue