主要功能: - 账号管理:添加/编辑/删除账号,测试登录 - 浏览任务:批量浏览应读/选读内容并标记已读 - 截图管理:wkhtmltoimage截图,查看历史 - 金山文档:扫码登录/微信快捷登录,自动上传截图 技术栈: - PyQt6 GUI框架 - Playwright 浏览器自动化 - SQLite 本地数据存储 - wkhtmltoimage 网页截图 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
121 lines
3.8 KiB
Python
121 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Log display panel - collapsible log output area
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from PyQt6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QPlainTextEdit,
|
|
QPushButton, QFrame, QLabel
|
|
)
|
|
from PyQt6.QtCore import Qt, pyqtSlot
|
|
from PyQt6.QtGui import QTextCursor
|
|
|
|
from .constants import LOG_MIN_HEIGHT, BUTTON_HEIGHT_SMALL, BUTTON_SPACING
|
|
|
|
|
|
class LogWidget(QWidget):
|
|
"""Log display panel"""
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setObjectName("log_widget")
|
|
self._collapsed = False
|
|
self._max_lines = 500
|
|
self._setup_ui()
|
|
|
|
def _setup_ui(self):
|
|
"""Setup UI"""
|
|
layout = QVBoxLayout(self)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
layout.setSpacing(0)
|
|
|
|
# Header bar
|
|
header = QFrame()
|
|
header.setFixedHeight(36)
|
|
header.setStyleSheet("background-color: rgba(0,0,0,0.03); border-bottom: 1px solid rgba(0,0,0,0.08);")
|
|
header_layout = QHBoxLayout(header)
|
|
header_layout.setContentsMargins(16, 0, 16, 0)
|
|
header_layout.setSpacing(BUTTON_SPACING)
|
|
|
|
# Title
|
|
title = QLabel("📜 日志输出")
|
|
title.setStyleSheet("font-weight: bold; font-size: 13px;")
|
|
header_layout.addWidget(title)
|
|
|
|
header_layout.addStretch()
|
|
|
|
# Clear button
|
|
self._clear_btn = QPushButton("清空")
|
|
self._clear_btn.setFixedWidth(60)
|
|
self._clear_btn.setFixedHeight(BUTTON_HEIGHT_SMALL)
|
|
self._clear_btn.clicked.connect(self.clear)
|
|
header_layout.addWidget(self._clear_btn)
|
|
|
|
# Toggle button
|
|
self._toggle_btn = QPushButton("▼")
|
|
self._toggle_btn.setFixedWidth(36)
|
|
self._toggle_btn.setFixedHeight(BUTTON_HEIGHT_SMALL)
|
|
self._toggle_btn.clicked.connect(self._toggle_collapse)
|
|
header_layout.addWidget(self._toggle_btn)
|
|
|
|
layout.addWidget(header)
|
|
|
|
# Log text area
|
|
self._text_edit = QPlainTextEdit()
|
|
self._text_edit.setReadOnly(True)
|
|
self._text_edit.setMinimumHeight(LOG_MIN_HEIGHT)
|
|
self._text_edit.setMaximumHeight(250)
|
|
self._text_edit.setStyleSheet("""
|
|
QPlainTextEdit {
|
|
font-family: "Consolas", "Monaco", "Courier New", monospace;
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
padding: 8px 12px;
|
|
}
|
|
""")
|
|
layout.addWidget(self._text_edit)
|
|
|
|
def _toggle_collapse(self):
|
|
"""Toggle collapse state"""
|
|
self._collapsed = not self._collapsed
|
|
self._text_edit.setVisible(not self._collapsed)
|
|
self._toggle_btn.setText("▲" if self._collapsed else "▼")
|
|
|
|
@pyqtSlot(str)
|
|
def append_log(self, message: str):
|
|
"""Append log message"""
|
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
formatted = f"[{timestamp}] {message}"
|
|
|
|
self._text_edit.appendPlainText(formatted)
|
|
|
|
# Limit lines
|
|
doc = self._text_edit.document()
|
|
if doc.blockCount() > self._max_lines:
|
|
cursor = QTextCursor(doc)
|
|
cursor.movePosition(QTextCursor.MoveOperation.Start)
|
|
cursor.select(QTextCursor.SelectionType.BlockUnderCursor)
|
|
cursor.movePosition(
|
|
QTextCursor.MoveOperation.Down,
|
|
QTextCursor.MoveMode.KeepAnchor,
|
|
doc.blockCount() - self._max_lines
|
|
)
|
|
cursor.removeSelectedText()
|
|
|
|
# Scroll to bottom
|
|
self._text_edit.moveCursor(QTextCursor.MoveOperation.End)
|
|
|
|
def log(self, message: str):
|
|
"""Log alias"""
|
|
self.append_log(message)
|
|
|
|
def clear(self):
|
|
"""Clear log"""
|
|
self._text_edit.clear()
|
|
|
|
def get_text(self) -> str:
|
|
"""Get all log text"""
|
|
return self._text_edit.toPlainText()
|