#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import os from datetime import datetime # 添加项目根目录到 Python 路径 current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.dirname(current_dir) sys.path.append(project_root) from PyQt6.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTextEdit, QGroupBox, QCheckBox, QComboBox, QSpinBox, QFrame, QSplitter, QFileDialog, QMessageBox ) from PyQt6.QtCore import Qt, QTimer, pyqtSlot from PyQt6.QtGui import QFont, QTextCursor, QColor, QTextCharFormat from kodi_util.LoggerToolModule import LoggerTool class LogViewerWindow(QMainWindow): """日志查看器窗口""" def __init__(self, parent=None): super().__init__(parent) # 获取日志工具实例 self.logger = LoggerTool() # 日志级别颜色映射 self.level_colors = { 'DEBUG': QColor(128, 128, 128), # 灰色 'INFO': QColor(0, 0, 0), # 黑色 'WARNING': QColor(255, 165, 0), # 橙色 'ERROR': QColor(255, 0, 0), # 红色 'CRITICAL': QColor(139, 0, 0) # 深红色 } # 日志过滤设置 self.filter_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'} self.max_lines = 1000 # 最大显示行数 self.auto_scroll = True # 自动滚动 self.init_ui() self.connect_logger() def init_ui(self): """初始化用户界面""" self.setWindowTitle("日志查看器") self.setMinimumSize(800, 600) # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 创建主布局 main_layout = QVBoxLayout(central_widget) main_layout.setContentsMargins(5, 5, 5, 5) main_layout.setSpacing(5) # 创建控制面板 control_panel = self.create_control_panel() main_layout.addWidget(control_panel) # 创建分割器 splitter = QSplitter(Qt.Orientation.Vertical) # 创建日志显示区域 log_display_group = QGroupBox("日志显示") log_display_layout = QVBoxLayout(log_display_group) # 创建日志文本显示区域 self.log_text_edit = QTextEdit() self.log_text_edit.setReadOnly(True) self.log_text_edit.setFont(QFont("Consolas", 9)) self.log_text_edit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap) log_display_layout.addWidget(self.log_text_edit) # 创建状态信息区域 status_group = QGroupBox("状态信息") status_layout = QVBoxLayout(status_group) self.status_label = QLabel("就绪") self.status_label.setStyleSheet("QLabel { color: green; }") status_layout.addWidget(self.status_label) self.log_count_label = QLabel("日志条数: 0") status_layout.addWidget(self.log_count_label) # 添加到分割器 splitter.addWidget(log_display_group) splitter.addWidget(status_group) splitter.setStretchFactor(0, 4) # 日志显示区域占更多空间 splitter.setStretchFactor(1, 1) main_layout.addWidget(splitter) # 日志计数器 self.log_count = 0 def create_control_panel(self): """创建控制面板""" control_group = QGroupBox("控制面板") control_layout = QVBoxLayout(control_group) # 第一行:日志级别过滤 level_layout = QHBoxLayout() level_layout.addWidget(QLabel("日志级别过滤:")) self.level_checkboxes = {} for level in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']: checkbox = QCheckBox(level) checkbox.setChecked(True) checkbox.stateChanged.connect(self.update_level_filter) self.level_checkboxes[level] = checkbox level_layout.addWidget(checkbox) level_layout.addStretch() control_layout.addLayout(level_layout) # 第二行:显示设置 settings_layout = QHBoxLayout() # 最大行数设置 settings_layout.addWidget(QLabel("最大显示行数:")) self.max_lines_spinbox = QSpinBox() self.max_lines_spinbox.setMinimum(100) self.max_lines_spinbox.setMaximum(10000) self.max_lines_spinbox.setValue(self.max_lines) self.max_lines_spinbox.valueChanged.connect(self.update_max_lines) settings_layout.addWidget(self.max_lines_spinbox) # 自动滚动设置 self.auto_scroll_checkbox = QCheckBox("自动滚动") self.auto_scroll_checkbox.setChecked(self.auto_scroll) self.auto_scroll_checkbox.stateChanged.connect(self.update_auto_scroll) settings_layout.addWidget(self.auto_scroll_checkbox) settings_layout.addStretch() control_layout.addLayout(settings_layout) # 第三行:操作按钮 button_layout = QHBoxLayout() # 清空日志按钮 clear_btn = QPushButton("清空日志") clear_btn.clicked.connect(self.clear_logs) button_layout.addWidget(clear_btn) # 保存日志按钮 save_btn = QPushButton("保存日志") save_btn.clicked.connect(self.save_logs) button_layout.addWidget(save_btn) # 测试日志按钮 test_btn = QPushButton("测试日志") test_btn.clicked.connect(self.test_logs) button_layout.addWidget(test_btn) button_layout.addStretch() control_layout.addLayout(button_layout) return control_group def connect_logger(self): """连接日志信号""" try: ui_handler = self.logger.get_ui_handler() ui_handler.log_signal.connect(self.append_log) self.status_label.setText("已连接到日志系统") self.status_label.setStyleSheet("QLabel { color: green; }") except Exception as e: self.status_label.setText(f"连接日志系统失败: {str(e)}") self.status_label.setStyleSheet("QLabel { color: red; }") @pyqtSlot(str, str) def append_log(self, level, message): """添加日志消息""" # 检查级别过滤 if level not in self.filter_levels: return # 获取当前光标位置 cursor = self.log_text_edit.textCursor() cursor.movePosition(QTextCursor.MoveOperation.End) # 设置文本格式 format = QTextCharFormat() if level in self.level_colors: format.setForeground(self.level_colors[level]) # 插入日志消息 cursor.insertText(message + '\n', format) # 更新计数 self.log_count += 1 self.log_count_label.setText(f"日志条数: {self.log_count}") # 限制最大行数 self.limit_max_lines() # 自动滚动到底部 if self.auto_scroll: scrollbar = self.log_text_edit.verticalScrollBar() scrollbar.setValue(scrollbar.maximum()) def limit_max_lines(self): """限制最大显示行数""" document = self.log_text_edit.document() if document.blockCount() > self.max_lines: cursor = QTextCursor(document) cursor.movePosition(QTextCursor.MoveOperation.Start) # 删除超出的行数 lines_to_remove = document.blockCount() - self.max_lines for _ in range(lines_to_remove): cursor.select(QTextCursor.SelectionType.BlockUnderCursor) cursor.removeSelectedText() cursor.deleteChar() # 删除换行符 def update_level_filter(self): """更新日志级别过滤""" self.filter_levels.clear() for level, checkbox in self.level_checkboxes.items(): if checkbox.isChecked(): self.filter_levels.add(level) def update_max_lines(self, value): """更新最大行数设置""" self.max_lines = value self.limit_max_lines() def update_auto_scroll(self, state): """更新自动滚动设置""" self.auto_scroll = state == Qt.CheckState.Checked.value def clear_logs(self): """清空日志显示""" self.log_text_edit.clear() self.log_count = 0 self.log_count_label.setText("日志条数: 0") self.status_label.setText("日志已清空") self.status_label.setStyleSheet("QLabel { color: blue; }") def save_logs(self): """保存日志到文件""" try: file_path, _ = QFileDialog.getSaveFileName( self, "保存日志文件", f"logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt", "文本文件 (*.txt);;所有文件 (*)" ) if file_path: with open(file_path, 'w', encoding='utf-8') as f: f.write(self.log_text_edit.toPlainText()) QMessageBox.information(self, "保存成功", f"日志已保存到: {file_path}") self.status_label.setText("日志保存成功") self.status_label.setStyleSheet("QLabel { color: green; }") except Exception as e: QMessageBox.critical(self, "保存失败", f"保存日志文件失败: {str(e)}") self.status_label.setText(f"保存失败: {str(e)}") self.status_label.setStyleSheet("QLabel { color: red; }") def test_logs(self): """测试日志输出""" self.logger.debug("这是一条调试信息") self.logger.info("这是一条信息") self.logger.warning("这是一条警告信息") self.logger.error("这是一条错误信息") self.logger.critical("这是一条严重错误信息") self.status_label.setText("测试日志已发送") self.status_label.setStyleSheet("QLabel { color: blue; }") if __name__ == "__main__": from PyQt6.QtWidgets import QApplication app = QApplication(sys.argv) window = LogViewerWindow() window.show() sys.exit(app.exec())