- 新增依赖检测模块:启动时自动检测wkhtmltoimage和Playwright Chromium - 新增依赖安装对话框:缺失时提示用户一键下载安装 - 修复选项记忆功能:浏览类型、自动截图、自动上传选项现在会保存 - 优化KDocs登录检测:未登录时自动切换到金山文档页面并显示二维码 - 简化日志输出:移除debug信息,保留用户友好的状态提示 - 新增账号变化信号:账号管理页面的修改会自动同步到浏览任务页面 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
202 lines
6.6 KiB
Python
202 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
依赖安装对话框
|
||
检测到缺失依赖时显示,让用户选择是否下载安装
|
||
"""
|
||
|
||
from PyQt6.QtWidgets import (
|
||
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||
QProgressBar, QCheckBox, QGroupBox, QMessageBox
|
||
)
|
||
from PyQt6.QtCore import Qt, QThread, pyqtSignal
|
||
|
||
|
||
class DownloadThread(QThread):
|
||
"""下载线程"""
|
||
progress = pyqtSignal(int, int) # downloaded, total
|
||
log = pyqtSignal(str)
|
||
finished = pyqtSignal(bool, str) # success, message
|
||
|
||
def __init__(self, dep_name: str):
|
||
super().__init__()
|
||
self.dep_name = dep_name
|
||
|
||
def run(self):
|
||
from utils.dependency_installer import download_and_install_dependency
|
||
|
||
def on_progress(downloaded, total):
|
||
self.progress.emit(downloaded, total)
|
||
|
||
def on_log(msg):
|
||
self.log.emit(msg)
|
||
|
||
success, message = download_and_install_dependency(
|
||
self.dep_name,
|
||
progress_callback=on_progress,
|
||
log_callback=on_log
|
||
)
|
||
self.finished.emit(success, message)
|
||
|
||
|
||
class DependencyDialog(QDialog):
|
||
"""依赖安装对话框"""
|
||
|
||
def __init__(self, missing_deps: dict, parent=None):
|
||
super().__init__(parent)
|
||
self.missing_deps = missing_deps
|
||
self.download_threads = []
|
||
self.setWindowTitle("环境检测")
|
||
self.setMinimumWidth(450)
|
||
self.setModal(True)
|
||
self._setup_ui()
|
||
|
||
def _setup_ui(self):
|
||
layout = QVBoxLayout(self)
|
||
layout.setSpacing(16)
|
||
layout.setContentsMargins(20, 20, 20, 20)
|
||
|
||
# 标题
|
||
title = QLabel("⚠️ 检测到缺少必要的运行环境")
|
||
title.setStyleSheet("font-size: 16px; font-weight: bold; color: #fa8c16;")
|
||
layout.addWidget(title)
|
||
|
||
# 说明
|
||
desc = QLabel("以下组件缺失,部分功能可能无法正常使用:")
|
||
desc.setStyleSheet("color: #666;")
|
||
layout.addWidget(desc)
|
||
|
||
# 缺失组件列表
|
||
deps_group = QGroupBox("缺失组件")
|
||
deps_layout = QVBoxLayout(deps_group)
|
||
|
||
self.checkboxes = {}
|
||
|
||
if self.missing_deps.get("wkhtmltoimage"):
|
||
cb = QCheckBox("wkhtmltoimage(截图功能需要)")
|
||
cb.setChecked(True)
|
||
self.checkboxes["wkhtmltoimage"] = cb
|
||
deps_layout.addWidget(cb)
|
||
|
||
if self.missing_deps.get("chromium"):
|
||
cb = QCheckBox("Playwright Chromium(金山文档上传需要)")
|
||
cb.setChecked(True)
|
||
self.checkboxes["chromium"] = cb
|
||
deps_layout.addWidget(cb)
|
||
|
||
layout.addWidget(deps_group)
|
||
|
||
# 进度区域
|
||
self.progress_group = QGroupBox("安装进度")
|
||
progress_layout = QVBoxLayout(self.progress_group)
|
||
|
||
self.status_label = QLabel("等待开始...")
|
||
self.status_label.setStyleSheet("color: #666;")
|
||
progress_layout.addWidget(self.status_label)
|
||
|
||
self.progress_bar = QProgressBar()
|
||
self.progress_bar.setRange(0, 100)
|
||
self.progress_bar.setValue(0)
|
||
progress_layout.addWidget(self.progress_bar)
|
||
|
||
self.progress_group.setVisible(False)
|
||
layout.addWidget(self.progress_group)
|
||
|
||
# 按钮
|
||
btn_layout = QHBoxLayout()
|
||
btn_layout.addStretch()
|
||
|
||
self.skip_btn = QPushButton("跳过")
|
||
self.skip_btn.setMinimumWidth(80)
|
||
self.skip_btn.clicked.connect(self.reject)
|
||
btn_layout.addWidget(self.skip_btn)
|
||
|
||
self.install_btn = QPushButton("立即安装")
|
||
self.install_btn.setObjectName("primary")
|
||
self.install_btn.setMinimumWidth(100)
|
||
self.install_btn.clicked.connect(self._start_install)
|
||
btn_layout.addWidget(self.install_btn)
|
||
|
||
layout.addLayout(btn_layout)
|
||
|
||
def _start_install(self):
|
||
"""开始安装"""
|
||
# 获取选中的组件
|
||
selected = [name for name, cb in self.checkboxes.items() if cb.isChecked()]
|
||
|
||
if not selected:
|
||
QMessageBox.information(self, "提示", "请选择要安装的组件")
|
||
return
|
||
|
||
# 禁用按钮和复选框
|
||
self.install_btn.setEnabled(False)
|
||
self.skip_btn.setText("取消")
|
||
for cb in self.checkboxes.values():
|
||
cb.setEnabled(False)
|
||
|
||
# 显示进度
|
||
self.progress_group.setVisible(True)
|
||
|
||
# 开始安装
|
||
self._install_queue = selected.copy()
|
||
self._install_next()
|
||
|
||
def _install_next(self):
|
||
"""安装下一个组件"""
|
||
if not self._install_queue:
|
||
# 全部完成
|
||
self.status_label.setText("✅ 安装完成!")
|
||
self.status_label.setStyleSheet("color: #52c41a; font-weight: bold;")
|
||
self.skip_btn.setText("完成")
|
||
self.skip_btn.clicked.disconnect()
|
||
self.skip_btn.clicked.connect(self.accept)
|
||
return
|
||
|
||
dep_name = self._install_queue.pop(0)
|
||
display_name = "wkhtmltoimage" if dep_name == "wkhtmltoimage" else "Chromium 浏览器"
|
||
self.status_label.setText(f"正在下载 {display_name}...")
|
||
self.progress_bar.setValue(0)
|
||
|
||
# 启动下载线程
|
||
thread = DownloadThread(dep_name)
|
||
thread.progress.connect(self._on_progress)
|
||
thread.log.connect(self._on_log)
|
||
thread.finished.connect(self._on_finished)
|
||
thread.start()
|
||
self.download_threads.append(thread)
|
||
|
||
def _on_progress(self, downloaded: int, total: int):
|
||
"""下载进度更新"""
|
||
if total > 0:
|
||
percent = int(downloaded * 100 / total)
|
||
self.progress_bar.setValue(percent)
|
||
mb_downloaded = downloaded / 1024 / 1024
|
||
mb_total = total / 1024 / 1024
|
||
self.status_label.setText(f"下载中... {mb_downloaded:.1f}MB / {mb_total:.1f}MB")
|
||
|
||
def _on_log(self, msg: str):
|
||
"""日志更新"""
|
||
self.status_label.setText(msg)
|
||
|
||
def _on_finished(self, success: bool, message: str):
|
||
"""安装完成"""
|
||
if success:
|
||
if message:
|
||
# wkhtmltoimage 需要手动完成安装向导
|
||
QMessageBox.information(self, "提示", message)
|
||
# 继续安装下一个
|
||
self._install_next()
|
||
else:
|
||
self.status_label.setText(f"❌ 安装失败: {message}")
|
||
self.status_label.setStyleSheet("color: #ff4d4f;")
|
||
self.install_btn.setEnabled(True)
|
||
self.skip_btn.setText("跳过")
|
||
|
||
def closeEvent(self, event):
|
||
"""关闭时停止所有线程"""
|
||
for thread in self.download_threads:
|
||
if thread.isRunning():
|
||
thread.terminate()
|
||
thread.wait()
|
||
event.accept()
|