feat: 添加依赖自动检测与安装、选项记忆、KDocs登录优化

- 新增依赖检测模块:启动时自动检测wkhtmltoimage和Playwright Chromium
- 新增依赖安装对话框:缺失时提示用户一键下载安装
- 修复选项记忆功能:浏览类型、自动截图、自动上传选项现在会保存
- 优化KDocs登录检测:未登录时自动切换到金山文档页面并显示二维码
- 简化日志输出:移除debug信息,保留用户友好的状态提示
- 新增账号变化信号:账号管理页面的修改会自动同步到浏览任务页面

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 01:28:06 +08:00
parent 83fef6dff2
commit 9743186a9e
11 changed files with 713 additions and 506 deletions

201
ui/dependency_dialog.py Normal file
View File

@@ -0,0 +1,201 @@
#!/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()