feat: 知识管理平台精简版 - PyQt6桌面应用
主要功能: - 账号管理:添加/编辑/删除账号,测试登录 - 浏览任务:批量浏览应读/选读内容并标记已读 - 截图管理:wkhtmltoimage截图,查看历史 - 金山文档:扫码登录/微信快捷登录,自动上传截图 技术栈: - PyQt6 GUI框架 - Playwright 浏览器自动化 - SQLite 本地数据存储 - wkhtmltoimage 网页截图 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
193
utils/worker.py
Normal file
193
utils/worker.py
Normal file
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
后台线程管理模块
|
||||
使用QThread实现非阻塞的后台任务
|
||||
"""
|
||||
|
||||
from typing import Callable, Any, Optional
|
||||
from PyQt6.QtCore import QObject, QThread, pyqtSignal
|
||||
|
||||
|
||||
class WorkerSignals(QObject):
|
||||
"""工作线程信号类"""
|
||||
# 进度信号:(百分比, 消息)
|
||||
progress = pyqtSignal(int, str)
|
||||
# 日志信号:日志消息
|
||||
log = pyqtSignal(str)
|
||||
# 完成信号:(成功/失败, 结果/错误消息)
|
||||
finished = pyqtSignal(bool, str)
|
||||
# 截图完成信号:截图文件路径
|
||||
screenshot_ready = pyqtSignal(str)
|
||||
# 通用结果信号:任意结果对象
|
||||
result = pyqtSignal(object)
|
||||
# 错误信号
|
||||
error = pyqtSignal(str)
|
||||
|
||||
|
||||
class Worker(QThread):
|
||||
"""
|
||||
通用后台工作线程
|
||||
|
||||
用法:
|
||||
worker = Worker(some_function, arg1, arg2, kwarg1=value1)
|
||||
worker.signals.finished.connect(on_finished)
|
||||
worker.signals.progress.connect(on_progress)
|
||||
worker.start()
|
||||
"""
|
||||
|
||||
def __init__(self, fn: Callable, *args, **kwargs):
|
||||
super().__init__()
|
||||
self.fn = fn
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.signals = WorkerSignals()
|
||||
self._is_stopped = False
|
||||
|
||||
def run(self):
|
||||
"""执行后台任务"""
|
||||
try:
|
||||
# 把信号传递给任务函数,方便回调
|
||||
self.kwargs['_signals'] = self.signals
|
||||
self.kwargs['_should_stop'] = self.should_stop
|
||||
|
||||
result = self.fn(*self.args, **self.kwargs)
|
||||
|
||||
if not self._is_stopped:
|
||||
self.signals.result.emit(result)
|
||||
self.signals.finished.emit(True, "完成")
|
||||
except Exception as e:
|
||||
if not self._is_stopped:
|
||||
error_msg = str(e)
|
||||
self.signals.error.emit(error_msg)
|
||||
self.signals.finished.emit(False, error_msg)
|
||||
|
||||
def stop(self):
|
||||
"""停止线程"""
|
||||
self._is_stopped = True
|
||||
|
||||
def should_stop(self) -> bool:
|
||||
"""检查是否应该停止"""
|
||||
return self._is_stopped
|
||||
|
||||
|
||||
class TaskRunner:
|
||||
"""
|
||||
任务运行器
|
||||
管理多个后台任务,确保不会同时运行太多任务
|
||||
"""
|
||||
|
||||
def __init__(self, max_workers: int = 1):
|
||||
self.max_workers = max_workers
|
||||
self._workers: list[Worker] = []
|
||||
self._queue: list[tuple] = [] # (fn, args, kwargs, callbacks)
|
||||
|
||||
def submit(self, fn: Callable, *args,
|
||||
on_progress: Optional[Callable] = None,
|
||||
on_log: Optional[Callable] = None,
|
||||
on_finished: Optional[Callable] = None,
|
||||
on_result: Optional[Callable] = None,
|
||||
on_error: Optional[Callable] = None,
|
||||
**kwargs) -> Optional[Worker]:
|
||||
"""
|
||||
提交任务
|
||||
|
||||
Args:
|
||||
fn: 要执行的函数
|
||||
*args: 位置参数
|
||||
on_progress: 进度回调 (percent, message)
|
||||
on_log: 日志回调 (message)
|
||||
on_finished: 完成回调 (success, message)
|
||||
on_result: 结果回调 (result)
|
||||
on_error: 错误回调 (error_message)
|
||||
**kwargs: 关键字参数
|
||||
|
||||
Returns:
|
||||
Worker对象,如果队列满了返回None
|
||||
"""
|
||||
callbacks = {
|
||||
'on_progress': on_progress,
|
||||
'on_log': on_log,
|
||||
'on_finished': on_finished,
|
||||
'on_result': on_result,
|
||||
'on_error': on_error,
|
||||
}
|
||||
|
||||
# 清理已完成的worker
|
||||
self._workers = [w for w in self._workers if w.isRunning()]
|
||||
|
||||
if len(self._workers) >= self.max_workers:
|
||||
# 加入队列等待
|
||||
self._queue.append((fn, args, kwargs, callbacks))
|
||||
return None
|
||||
|
||||
return self._start_worker(fn, args, kwargs, callbacks)
|
||||
|
||||
def _start_worker(self, fn: Callable, args: tuple, kwargs: dict,
|
||||
callbacks: dict) -> Worker:
|
||||
"""启动一个worker"""
|
||||
worker = Worker(fn, *args, **kwargs)
|
||||
|
||||
# 连接信号
|
||||
if callbacks.get('on_progress'):
|
||||
worker.signals.progress.connect(callbacks['on_progress'])
|
||||
if callbacks.get('on_log'):
|
||||
worker.signals.log.connect(callbacks['on_log'])
|
||||
if callbacks.get('on_result'):
|
||||
worker.signals.result.connect(callbacks['on_result'])
|
||||
if callbacks.get('on_error'):
|
||||
worker.signals.error.connect(callbacks['on_error'])
|
||||
|
||||
# 完成时的处理
|
||||
def on_worker_finished(success, message):
|
||||
if callbacks.get('on_finished'):
|
||||
callbacks['on_finished'](success, message)
|
||||
# 处理队列中的下一个任务
|
||||
self._process_queue()
|
||||
|
||||
worker.signals.finished.connect(on_worker_finished)
|
||||
|
||||
self._workers.append(worker)
|
||||
worker.start()
|
||||
return worker
|
||||
|
||||
def _process_queue(self):
|
||||
"""处理队列中的任务"""
|
||||
# 清理已完成的worker
|
||||
self._workers = [w for w in self._workers if w.isRunning()]
|
||||
|
||||
if self._queue and len(self._workers) < self.max_workers:
|
||||
fn, args, kwargs, callbacks = self._queue.pop(0)
|
||||
self._start_worker(fn, args, kwargs, callbacks)
|
||||
|
||||
def stop_all(self):
|
||||
"""停止所有任务"""
|
||||
for worker in self._workers:
|
||||
worker.stop()
|
||||
self._queue.clear()
|
||||
|
||||
def is_running(self) -> bool:
|
||||
"""检查是否有任务在运行"""
|
||||
return any(w.isRunning() for w in self._workers)
|
||||
|
||||
@property
|
||||
def running_count(self) -> int:
|
||||
"""运行中的任务数"""
|
||||
return sum(1 for w in self._workers if w.isRunning())
|
||||
|
||||
@property
|
||||
def queue_size(self) -> int:
|
||||
"""队列中等待的任务数"""
|
||||
return len(self._queue)
|
||||
|
||||
|
||||
# 全局任务运行器
|
||||
_task_runner: Optional[TaskRunner] = None
|
||||
|
||||
|
||||
def get_task_runner() -> TaskRunner:
|
||||
"""获取全局任务运行器"""
|
||||
global _task_runner
|
||||
if _task_runner is None:
|
||||
_task_runner = TaskRunner(max_workers=1)
|
||||
return _task_runner
|
||||
Reference in New Issue
Block a user