log_viewer_window.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import sys
  4. import os
  5. from datetime import datetime
  6. # 添加项目根目录到 Python 路径
  7. current_dir = os.path.dirname(os.path.abspath(__file__))
  8. project_root = os.path.dirname(current_dir)
  9. sys.path.append(project_root)
  10. from PyQt6.QtWidgets import (
  11. QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
  12. QPushButton, QLabel, QTextEdit, QGroupBox, QCheckBox,
  13. QComboBox, QSpinBox, QFrame, QSplitter, QFileDialog,
  14. QMessageBox
  15. )
  16. from PyQt6.QtCore import Qt, QTimer, pyqtSlot
  17. from PyQt6.QtGui import QFont, QTextCursor, QColor, QTextCharFormat
  18. from kodi_util.LoggerToolModule import LoggerTool
  19. class LogViewerWindow(QMainWindow):
  20. """日志查看器窗口"""
  21. def __init__(self, parent=None):
  22. super().__init__(parent)
  23. # 获取日志工具实例
  24. self.logger = LoggerTool()
  25. # 日志级别颜色映射
  26. self.level_colors = {
  27. 'DEBUG': QColor(128, 128, 128), # 灰色
  28. 'INFO': QColor(0, 0, 0), # 黑色
  29. 'WARNING': QColor(255, 165, 0), # 橙色
  30. 'ERROR': QColor(255, 0, 0), # 红色
  31. 'CRITICAL': QColor(139, 0, 0) # 深红色
  32. }
  33. # 日志过滤设置
  34. self.filter_levels = {'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'}
  35. self.max_lines = 1000 # 最大显示行数
  36. self.auto_scroll = True # 自动滚动
  37. self.init_ui()
  38. self.connect_logger()
  39. def init_ui(self):
  40. """初始化用户界面"""
  41. self.setWindowTitle("日志查看器")
  42. self.setMinimumSize(800, 600)
  43. # 创建中央部件
  44. central_widget = QWidget()
  45. self.setCentralWidget(central_widget)
  46. # 创建主布局
  47. main_layout = QVBoxLayout(central_widget)
  48. main_layout.setContentsMargins(5, 5, 5, 5)
  49. main_layout.setSpacing(5)
  50. # 创建控制面板
  51. control_panel = self.create_control_panel()
  52. main_layout.addWidget(control_panel)
  53. # 创建分割器
  54. splitter = QSplitter(Qt.Orientation.Vertical)
  55. # 创建日志显示区域
  56. log_display_group = QGroupBox("日志显示")
  57. log_display_layout = QVBoxLayout(log_display_group)
  58. # 创建日志文本显示区域
  59. self.log_text_edit = QTextEdit()
  60. self.log_text_edit.setReadOnly(True)
  61. self.log_text_edit.setFont(QFont("Consolas", 9))
  62. self.log_text_edit.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
  63. log_display_layout.addWidget(self.log_text_edit)
  64. # 创建状态信息区域
  65. status_group = QGroupBox("状态信息")
  66. status_layout = QVBoxLayout(status_group)
  67. self.status_label = QLabel("就绪")
  68. self.status_label.setStyleSheet("QLabel { color: green; }")
  69. status_layout.addWidget(self.status_label)
  70. self.log_count_label = QLabel("日志条数: 0")
  71. status_layout.addWidget(self.log_count_label)
  72. # 添加到分割器
  73. splitter.addWidget(log_display_group)
  74. splitter.addWidget(status_group)
  75. splitter.setStretchFactor(0, 4) # 日志显示区域占更多空间
  76. splitter.setStretchFactor(1, 1)
  77. main_layout.addWidget(splitter)
  78. # 日志计数器
  79. self.log_count = 0
  80. def create_control_panel(self):
  81. """创建控制面板"""
  82. control_group = QGroupBox("控制面板")
  83. control_layout = QVBoxLayout(control_group)
  84. # 第一行:日志级别过滤
  85. level_layout = QHBoxLayout()
  86. level_layout.addWidget(QLabel("日志级别过滤:"))
  87. self.level_checkboxes = {}
  88. for level in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
  89. checkbox = QCheckBox(level)
  90. checkbox.setChecked(True)
  91. checkbox.stateChanged.connect(self.update_level_filter)
  92. self.level_checkboxes[level] = checkbox
  93. level_layout.addWidget(checkbox)
  94. level_layout.addStretch()
  95. control_layout.addLayout(level_layout)
  96. # 第二行:显示设置
  97. settings_layout = QHBoxLayout()
  98. # 最大行数设置
  99. settings_layout.addWidget(QLabel("最大显示行数:"))
  100. self.max_lines_spinbox = QSpinBox()
  101. self.max_lines_spinbox.setMinimum(100)
  102. self.max_lines_spinbox.setMaximum(10000)
  103. self.max_lines_spinbox.setValue(self.max_lines)
  104. self.max_lines_spinbox.valueChanged.connect(self.update_max_lines)
  105. settings_layout.addWidget(self.max_lines_spinbox)
  106. # 自动滚动设置
  107. self.auto_scroll_checkbox = QCheckBox("自动滚动")
  108. self.auto_scroll_checkbox.setChecked(self.auto_scroll)
  109. self.auto_scroll_checkbox.stateChanged.connect(self.update_auto_scroll)
  110. settings_layout.addWidget(self.auto_scroll_checkbox)
  111. settings_layout.addStretch()
  112. control_layout.addLayout(settings_layout)
  113. # 第三行:操作按钮
  114. button_layout = QHBoxLayout()
  115. # 清空日志按钮
  116. clear_btn = QPushButton("清空日志")
  117. clear_btn.clicked.connect(self.clear_logs)
  118. button_layout.addWidget(clear_btn)
  119. # 保存日志按钮
  120. save_btn = QPushButton("保存日志")
  121. save_btn.clicked.connect(self.save_logs)
  122. button_layout.addWidget(save_btn)
  123. # 测试日志按钮
  124. test_btn = QPushButton("测试日志")
  125. test_btn.clicked.connect(self.test_logs)
  126. button_layout.addWidget(test_btn)
  127. button_layout.addStretch()
  128. control_layout.addLayout(button_layout)
  129. return control_group
  130. def connect_logger(self):
  131. """连接日志信号"""
  132. try:
  133. ui_handler = self.logger.get_ui_handler()
  134. ui_handler.log_signal.connect(self.append_log)
  135. self.status_label.setText("已连接到日志系统")
  136. self.status_label.setStyleSheet("QLabel { color: green; }")
  137. except Exception as e:
  138. self.status_label.setText(f"连接日志系统失败: {str(e)}")
  139. self.status_label.setStyleSheet("QLabel { color: red; }")
  140. @pyqtSlot(str, str)
  141. def append_log(self, level, message):
  142. """添加日志消息"""
  143. # 检查级别过滤
  144. if level not in self.filter_levels:
  145. return
  146. # 获取当前光标位置
  147. cursor = self.log_text_edit.textCursor()
  148. cursor.movePosition(QTextCursor.MoveOperation.End)
  149. # 设置文本格式
  150. format = QTextCharFormat()
  151. if level in self.level_colors:
  152. format.setForeground(self.level_colors[level])
  153. # 插入日志消息
  154. cursor.insertText(message + '\n', format)
  155. # 更新计数
  156. self.log_count += 1
  157. self.log_count_label.setText(f"日志条数: {self.log_count}")
  158. # 限制最大行数
  159. self.limit_max_lines()
  160. # 自动滚动到底部
  161. if self.auto_scroll:
  162. scrollbar = self.log_text_edit.verticalScrollBar()
  163. scrollbar.setValue(scrollbar.maximum())
  164. def limit_max_lines(self):
  165. """限制最大显示行数"""
  166. document = self.log_text_edit.document()
  167. if document.blockCount() > self.max_lines:
  168. cursor = QTextCursor(document)
  169. cursor.movePosition(QTextCursor.MoveOperation.Start)
  170. # 删除超出的行数
  171. lines_to_remove = document.blockCount() - self.max_lines
  172. for _ in range(lines_to_remove):
  173. cursor.select(QTextCursor.SelectionType.BlockUnderCursor)
  174. cursor.removeSelectedText()
  175. cursor.deleteChar() # 删除换行符
  176. def update_level_filter(self):
  177. """更新日志级别过滤"""
  178. self.filter_levels.clear()
  179. for level, checkbox in self.level_checkboxes.items():
  180. if checkbox.isChecked():
  181. self.filter_levels.add(level)
  182. def update_max_lines(self, value):
  183. """更新最大行数设置"""
  184. self.max_lines = value
  185. self.limit_max_lines()
  186. def update_auto_scroll(self, state):
  187. """更新自动滚动设置"""
  188. self.auto_scroll = state == Qt.CheckState.Checked.value
  189. def clear_logs(self):
  190. """清空日志显示"""
  191. self.log_text_edit.clear()
  192. self.log_count = 0
  193. self.log_count_label.setText("日志条数: 0")
  194. self.status_label.setText("日志已清空")
  195. self.status_label.setStyleSheet("QLabel { color: blue; }")
  196. def save_logs(self):
  197. """保存日志到文件"""
  198. try:
  199. file_path, _ = QFileDialog.getSaveFileName(
  200. self,
  201. "保存日志文件",
  202. f"logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
  203. "文本文件 (*.txt);;所有文件 (*)"
  204. )
  205. if file_path:
  206. with open(file_path, 'w', encoding='utf-8') as f:
  207. f.write(self.log_text_edit.toPlainText())
  208. QMessageBox.information(self, "保存成功", f"日志已保存到: {file_path}")
  209. self.status_label.setText("日志保存成功")
  210. self.status_label.setStyleSheet("QLabel { color: green; }")
  211. except Exception as e:
  212. QMessageBox.critical(self, "保存失败", f"保存日志文件失败: {str(e)}")
  213. self.status_label.setText(f"保存失败: {str(e)}")
  214. self.status_label.setStyleSheet("QLabel { color: red; }")
  215. def test_logs(self):
  216. """测试日志输出"""
  217. self.logger.debug("这是一条调试信息")
  218. self.logger.info("这是一条信息")
  219. self.logger.warning("这是一条警告信息")
  220. self.logger.error("这是一条错误信息")
  221. self.logger.critical("这是一条严重错误信息")
  222. self.status_label.setText("测试日志已发送")
  223. self.status_label.setStyleSheet("QLabel { color: blue; }")
  224. if __name__ == "__main__":
  225. from PyQt6.QtWidgets import QApplication
  226. app = QApplication(sys.argv)
  227. window = LogViewerWindow()
  228. window.show()
  229. sys.exit(app.exec())