Skip to content
text
# Related Code
src/ui.py                    - MainWindow 类 (28-464行)
src/unified_downloader.py    - 下载引擎 (426-777行)
src/resource_utils.py        - 资源路径工具
src/utils.py                 - 清理工具

核心组件概览

组件关系图

组件职责

组件文件职责
MainWindowsrc/ui.py:28GUI 主窗口,处理用户交互
UnifiedDownloadWorkersrc/unified_downloader.py:426QThread 子类,管理下载进程
ThreadSafeSignalEmittersrc/unified_downloader.py:388跨线程安全的信号发射
LoggerManagersrc/unified_downloader.py:41单例日志管理器
SafePipeWritersrc/unified_downloader.py:156进程安全的管道写入

MainWindow

UI 入口,负责:

  1. 平台选择(HuggingFace / ModelScope)
  2. 下载参数配置
  3. 启动/停止下载
  4. 显示日志和进度

关键属性:

python
# src/ui.py
class MainWindow(QMainWindow):
    platform_button_group  # 平台切换按钮组
    repo_input            # 仓库 ID 输入框
    path_input            # 保存路径
    token_input           # Token 输入
    endpoint_input        # 自定义 Endpoint
    download_worker       # 当前下载 Worker 实例

UnifiedDownloadWorker

下载操作的核心,继承自 QThread

为什么不直接用 multiprocessing?

PyQt 要求 UI 更新必须在主线程。Worker 作为 QThread 桥接下载进程和 UI。

关键信号:

信号说明
finished下载完成
error(str)下载出错
status(str)状态更新
log(str)日志输出

ThreadSafeSignalEmitter

解决跨线程信号发射的线程安全问题。

python
# src/unified_downloader.py:388-423
class ThreadSafeSignalEmitter(QObject):
    finished = pyqtSignal()
    error = pyqtSignal(str)
    status = pyqtSignal(str)
    log = pyqtSignal(str)

    def __init__(self):
        self._mutex = QMutex()  # 保护信号发射

为什么需要这个类?

下载进程的输出处理在独立线程中,直接发射 PyQt 信号可能导致竞态条件。

LoggerManager

单例模式,管理所有日志处理器的生命周期。

python
# src/unified_downloader.py:41-73
class LoggerManager:
    _instance = None
    _handlers = {}

    def get_handler(self, signal): ...
    def cleanup_handler(self, signal): ...

为什么是单例?

防止每次下载创建新的日志处理器导致内存泄漏。下载完成后必须清理处理器。

工具模块

resource_utils

处理 PyInstaller 打包后的资源路径问题:

python
# src/resource_utils.py
def get_resource_path(relative_path):
    """开发环境和打包环境统一的资源路径"""
    if getattr(sys, 'frozen', False):
        # PyInstaller 环境
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.dirname(__file__), '..', relative_path)

utils

清理工具:

python
# src/utils.py
def cleanup_lock_files(directory): ...   # 清理 SDK 锁文件
def cleanup_environment(): ...           # 清理环境变量

为什么需要清理锁文件?

huggingface_hub 下载时会创建 .lock 文件,异常退出后可能残留,阻止后续下载。