Add PyInstaller configuration and enhance resource path handling

- Introduced PyInstaller configuration files for packaging: `TomatoPick.spec`, `build_windows_exe.bat`, and `build_linux.sh`.
- Updated resource paths in `control.py` and `control_core.py` to ensure compatibility with packaged applications.
- Enhanced `main.py` to manage runtime resource directories and added a startup animation.
- Improved UI elements and color themes for better user experience.
This commit is contained in:
Brunsmeier
2026-05-21 11:26:31 +08:00
parent 02657c18f8
commit cf8908fcc1
5 changed files with 569 additions and 78 deletions

View File

@ -6,6 +6,7 @@ import os
import sys
import threading
import time
from pathlib import Path
import cv2
import numpy as np
@ -105,6 +106,61 @@ ui_callback: Callable[[np.ndarray], None] = lambda frame: None
robot_rpc_client = pyaubo_sdk.RpcClient()
def get_runtime_resource_dir() -> Path:
"""返回运行时资源目录,兼容 PyInstaller 解包目录。"""
if hasattr(sys, "_MEIPASS"):
return Path(sys._MEIPASS).resolve()
return Path(__file__).resolve().parent
def get_runtime_app_dir() -> Path:
"""返回应用目录,打包后优先使用可执行文件所在目录。"""
if getattr(sys, "frozen", False):
return Path(sys.executable).resolve().parent
return Path(__file__).resolve().parent
def get_resource_candidates(filename: str) -> List[Path]:
"""返回模型/图片等资源的候选路径。"""
resource_dir = get_runtime_resource_dir()
app_dir = get_runtime_app_dir()
return [
resource_dir / filename,
resource_dir / "models" / filename,
resource_dir / "tools" / filename,
app_dir / filename,
app_dir / "models" / filename,
app_dir / "tools" / filename,
Path.cwd() / filename,
Path.cwd() / "models" / filename,
Path.cwd() / "tools" / filename,
]
def resolve_resource_path(path_value: Optional[str], fallback_name: Optional[str] = None) -> Optional[Path]:
"""解析运行时资源路径,兼容源码运行与打包运行。"""
if path_value:
candidate = Path(path_value).expanduser()
if candidate.is_absolute():
candidate = candidate.resolve()
if candidate.exists():
return candidate
else:
for relative_candidate in (
get_runtime_app_dir() / candidate,
get_runtime_resource_dir() / candidate,
Path.cwd() / candidate,
):
resolved = relative_candidate.resolve()
if resolved.exists():
return resolved
if fallback_name:
for candidate in get_resource_candidates(fallback_name):
if candidate.exists():
return candidate.resolve()
return None
def configure(config: Dict[str, Any]) -> None:
"""接收 control.py 同步过来的可调参数。"""
_runtime_config.update(config)
@ -798,7 +854,9 @@ def init_camera() -> Tuple[Any, Any, Any]:
def init_tomato_detector(model_path: Optional[str] = None) -> Any:
"""初始化 YOLO 模型。"""
use_path = model_path or _cfg("YOLO_MODEL_PATH") or "best.pt"
configured_path = model_path or _cfg("YOLO_MODEL_PATH") or "models/best.pt"
resolved_path = resolve_resource_path(configured_path, fallback_name="best.pt")
use_path = str(resolved_path) if resolved_path is not None else configured_path
print(f"加载 YOLO 模型 | 路径: {use_path}")
if not os.path.exists(use_path):