diff --git a/.gitignore b/.gitignore
index d46fb033f..c2fb389ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@ mongodb/
NapCat.Framework.Windows.Once/
log/
logs/
+run_ad.bat
+MaiBot-Napcat-Adapter-main
+MaiBot-Napcat-Adapter
/test
/src/test
nonebot-maibot-adapter/
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 000000000..02fe9f821
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,20 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Commands
+- **Run Bot**: `python bot.py`
+- **Lint**: `ruff check --fix .` or `ruff format .`
+- **Run Tests**: `python -m unittest discover -v`
+- **Run Single Test**: `python -m unittest src/plugins/message/test.py`
+
+## Code Style
+- **Formatting**: Line length 120 chars, use double quotes for strings
+- **Imports**: Group standard library, external packages, then internal imports
+- **Naming**: snake_case for functions/variables, PascalCase for classes
+- **Error Handling**: Use try/except blocks with specific exceptions
+- **Types**: Use type hints where possible
+- **Docstrings**: Document classes and complex functions
+- **Linting**: Follow ruff rules (E, F, B) with ignores E711, E501
+
+When making changes, run `ruff check --fix .` to ensure code follows style guidelines. The codebase uses Ruff for linting and formatting.
\ No newline at end of file
diff --git a/README.md b/README.md
index fa97fec14..46e1fb77d 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,66 @@
# 麦麦!MaiCore-MaiMBot (编辑中)
+
+
+
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+
+
+
+
+
+
+
+
+
+ 画师:略nd
+
+
+
MaiBot(麦麦)
+
+ 一款专注于 群组聊天 的赛博网友
+
+ 探索本项目的文档 »
+
+
+
+ 报告Bug
+ ·
+ 提出新特性
+
+
+
## 新版0.6.0部署前先阅读:https://docs.mai-mai.org/manual/usage/mmc_q_a
-
-
-
-
-
-
-
## 📝 项目简介
**🍔MaiCore是一个基于大语言模型的可交互智能体**
-- LLM 提供对话能力
-- 动态Prompt构建器
-- 实时的思维系统
-- MongoDB 提供数据持久化支持
-- 可扩展,可支持多种平台和多种功能
+
+- 💭 **智能对话系统**:基于LLM的自然语言交互
+- 🤔 **实时思维系统**:模拟人类思考过程
+- 💝 **情感表达系统**:丰富的表情包和情绪表达
+- 🧠 **持久记忆系统**:基于MongoDB的长期记忆存储
+- 🔄 **动态人格系统**:自适应的性格特征
+
+
+
+
+### 📢 版本信息
**最新版本: v0.6.0** ([查看更新日志](changelogs/changelog.md))
> [!WARNING]
@@ -28,19 +70,12 @@
> 次版本MaiBot将基于MaiCore运行,不再依赖于nonebot相关组件运行。
> MaiBot将通过nonebot的插件与nonebot建立联系,然后nonebot与QQ建立联系,实现MaiBot与QQ的交互
-**分支介绍:**
-- main 稳定版本
-- dev 开发版(不知道什么意思就别下)
-- classical 0.6.0以前的版本
+**分支说明:**
+- `main`: 稳定发布版本
+- `dev`: 开发测试版本(不知道什么意思就别下)
+- `classical`: 0.6.0之前的版本
-
> [!WARNING]
> - 项目处于活跃开发阶段,代码可能随时更改
@@ -49,6 +84,12 @@
> - 由于持续迭代,可能存在一些已知或未知的bug
> - 由于开发中,可能消耗较多token
+### ⚠️ 重要提示
+
+- 升级到v0.6.0版本前请务必阅读:[升级指南](https://docs.mai-mai.org/manual/usage/mmc_q_a)
+- 本版本基于MaiCore重构,通过nonebot插件与QQ平台交互
+- 项目处于活跃开发阶段,功能和API可能随时调整
+
### 💬交流群(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
- [五群](https://qm.qq.com/q/JxvHZnxyec) 1022489779
- [一群](https://qm.qq.com/q/VQ3XZrWgMs) 766798517 【已满】
@@ -72,55 +113,35 @@
## 🎯 功能介绍
-### 💬 聊天功能
-- 提供思维流(心流)聊天和推理聊天两种对话逻辑
-- 支持关键词检索主动发言:对消息的话题topic进行识别,如果检测到麦麦存储过的话题就会主动进行发言
-- 支持bot名字呼唤发言:检测到"麦麦"会主动发言,可配置
-- 支持多模型,多厂商自定义配置
-- 动态的prompt构建器,更拟人
-- 支持图片,转发消息,回复消息的识别
-- 支持私聊功能,可使用PFC模式的有目的多轮对话(实验性)
+| 模块 | 主要功能 | 特点 |
+|------|---------|------|
+| 💬 聊天系统 | • 思维流/推理聊天
• 关键词主动发言
• 多模型支持
• 动态prompt构建
• 私聊功能(PFC) | 拟人化交互 |
+| 🧠 思维流系统 | • 实时思考生成
• 自动启停机制
• 日程系统联动 | 智能化决策 |
+| 🧠 记忆系统 2.0 | • 优化记忆抽取
• 海马体记忆机制
• 聊天记录概括 | 持久化记忆 |
+| 😊 表情包系统 | • 情绪匹配发送
• GIF支持
• 自动收集与审查 | 丰富表达 |
+| 📅 日程系统 | • 动态日程生成
• 自定义想象力
• 思维流联动 | 智能规划 |
+| 👥 关系系统 2.0 | • 关系管理优化
• 丰富接口支持
• 个性化交互 | 深度社交 |
+| 📊 统计系统 | • 使用数据统计
• LLM调用记录
• 实时控制台显示 | 数据可视 |
+| 🔧 系统功能 | • 优雅关闭机制
• 自动数据保存
• 异常处理完善 | 稳定可靠 |
-### 🧠 思维流系统
-- 思维流能够在回复前后进行思考,生成实时想法
-- 思维流自动启停机制,提升资源利用效率
-- 思维流与日程系统联动,实现动态日程生成
+## 📐 项目架构
-### 🧠 记忆系统 2.0
-- 优化记忆抽取策略和prompt结构
-- 改进海马体记忆提取机制,提升自然度
-- 对聊天记录进行概括存储,在需要时调用
+```mermaid
+graph TD
+ A[MaiCore] --> B[对话系统]
+ A --> C[思维流系统]
+ A --> D[记忆系统]
+ A --> E[情感系统]
+ B --> F[多模型支持]
+ B --> G[动态Prompt]
+ C --> H[实时思考]
+ C --> I[日程联动]
+ D --> J[记忆存储]
+ D --> K[记忆检索]
+ E --> L[表情管理]
+ E --> M[情绪识别]
+```
-### 😊 表情包系统
-- 支持根据发言内容发送对应情绪的表情包
-- 支持识别和处理gif表情包
-- 会自动偷群友的表情包
-- 表情包审查功能
-- 表情包文件完整性自动检查
-- 自动清理缓存图片
-
-### 📅 日程系统
-- 动态更新的日程生成
-- 可自定义想象力程度
-- 与聊天情况交互(思维流模式下)
-
-### 👥 关系系统 2.0
-- 优化关系管理系统,适用于新版本
-- 提供更丰富的关系接口
-- 针对每个用户创建"关系",实现个性化回复
-
-### 📊 统计系统
-- 详细的使用数据统计
-- LLM调用统计
-- 在控制台显示统计信息
-
-### 🔧 系统功能
-- 支持优雅的shutdown机制
-- 自动保存功能,定期保存聊天记录和关系数据
-- 完善的异常处理机制
-- 可自定义时区设置
-- 优化的日志输出格式
-- 配置自动更新功能
## 开发计划TODO:LIST
@@ -157,7 +178,6 @@ MaiCore是一个开源项目,我们非常欢迎你的参与。你的贡献,
## 致谢
-- [nonebot2](https://github.com/nonebot/nonebot2): 跨平台 Python 异步聊天机器人框架
- [NapCat](https://github.com/NapNeko/NapCatQQ): 现代化的基于 NTQQ 的 Bot 协议端实现
### 贡献者
diff --git a/bot.py b/bot.py
index a0bf3a3cb..ca214967e 100644
--- a/bot.py
+++ b/bot.py
@@ -8,6 +8,7 @@ import time
import platform
from dotenv import load_dotenv
from src.common.logger import get_module_logger
+from src.common.crash_logger import install_crash_handler
from src.main import MainSystem
logger = get_module_logger("main_bot")
@@ -193,6 +194,9 @@ def raw_main():
if platform.system().lower() != "windows":
time.tzset()
+ # 安装崩溃日志处理器
+ install_crash_handler()
+
check_eula()
print("检查EULA和隐私条款完成")
easter_egg()
diff --git a/depends-data/maimai.png b/depends-data/maimai.png
new file mode 100644
index 000000000..faccb856b
Binary files /dev/null and b/depends-data/maimai.png differ
diff --git a/depends-data/video.png b/depends-data/video.png
new file mode 100644
index 000000000..84176b2d9
Binary files /dev/null and b/depends-data/video.png differ
diff --git a/src/common/crash_logger.py b/src/common/crash_logger.py
new file mode 100644
index 000000000..658e1bb02
--- /dev/null
+++ b/src/common/crash_logger.py
@@ -0,0 +1,72 @@
+import sys
+import traceback
+import logging
+from pathlib import Path
+from logging.handlers import RotatingFileHandler
+
+def setup_crash_logger():
+ """设置崩溃日志记录器"""
+ # 创建logs/crash目录(如果不存在)
+ crash_log_dir = Path("logs/crash")
+ crash_log_dir.mkdir(parents=True, exist_ok=True)
+
+ # 创建日志记录器
+ crash_logger = logging.getLogger('crash_logger')
+ crash_logger.setLevel(logging.ERROR)
+
+ # 设置日志格式
+ formatter = logging.Formatter(
+ '%(asctime)s - %(name)s - %(levelname)s\n'
+ '异常类型: %(exc_info)s\n'
+ '详细信息:\n%(message)s\n'
+ '-------------------\n'
+ )
+
+ # 创建按大小轮转的文件处理器(最大10MB,保留5个备份)
+ log_file = crash_log_dir / "crash.log"
+ file_handler = RotatingFileHandler(
+ log_file,
+ maxBytes=10*1024*1024, # 10MB
+ backupCount=5,
+ encoding='utf-8'
+ )
+ file_handler.setFormatter(formatter)
+ crash_logger.addHandler(file_handler)
+
+ return crash_logger
+
+def log_crash(exc_type, exc_value, exc_traceback):
+ """记录崩溃信息到日志文件"""
+ if exc_type is None:
+ return
+
+ # 获取崩溃日志记录器
+ crash_logger = logging.getLogger('crash_logger')
+
+ # 获取完整的异常堆栈信息
+ stack_trace = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
+
+ # 记录崩溃信息
+ crash_logger.error(
+ stack_trace,
+ exc_info=(exc_type, exc_value, exc_traceback)
+ )
+
+def install_crash_handler():
+ """安装全局异常处理器"""
+ # 设置崩溃日志记录器
+ setup_crash_logger()
+
+ # 保存原始的异常处理器
+ original_hook = sys.excepthook
+
+ def exception_handler(exc_type, exc_value, exc_traceback):
+ """全局异常处理器"""
+ # 记录崩溃信息
+ log_crash(exc_type, exc_value, exc_traceback)
+
+ # 调用原始的异常处理器
+ original_hook(exc_type, exc_value, exc_traceback)
+
+ # 设置全局异常处理器
+ sys.excepthook = exception_handler
\ No newline at end of file
diff --git a/src/gui/logger_gui.py b/src/gui/logger_gui.py
index f2dd698cd..ad6edafb8 100644
--- a/src/gui/logger_gui.py
+++ b/src/gui/logger_gui.py
@@ -1,347 +1,378 @@
-import customtkinter as ctk
-import subprocess
-import threading
-import queue
-import re
-import os
-import signal
-from collections import deque
+# import customtkinter as ctk
+# import subprocess
+# import threading
+# import queue
+# import re
+# import os
+# import signal
+# from collections import deque
+# import sys
-# 设置应用的外观模式和默认颜色主题
-ctk.set_appearance_mode("dark")
-ctk.set_default_color_theme("blue")
+# # 设置应用的外观模式和默认颜色主题
+# ctk.set_appearance_mode("dark")
+# ctk.set_default_color_theme("blue")
-class LogViewerApp(ctk.CTk):
- """日志查看器应用的主类,继承自customtkinter的CTk类"""
+# class LogViewerApp(ctk.CTk):
+# """日志查看器应用的主类,继承自customtkinter的CTk类"""
- def __init__(self):
- """初始化日志查看器应用的界面和状态"""
- super().__init__()
- self.title("日志查看器")
- self.geometry("1200x800")
+# def __init__(self):
+# """初始化日志查看器应用的界面和状态"""
+# super().__init__()
+# self.title("日志查看器")
+# self.geometry("1200x800")
- # 初始化进程、日志队列、日志数据等变量
- self.process = None
- self.log_queue = queue.Queue()
- self.log_data = deque(maxlen=10000) # 使用固定长度队列
- self.available_levels = set()
- self.available_modules = set()
- self.sorted_modules = []
- self.module_checkboxes = {} # 存储模块复选框的字典
+# # 标记GUI是否运行中
+# self.is_running = True
- # 日志颜色配置
- self.color_config = {
- "time": "#888888",
- "DEBUG": "#2196F3",
- "INFO": "#4CAF50",
- "WARNING": "#FF9800",
- "ERROR": "#F44336",
- "module": "#D4D0AB",
- "default": "#FFFFFF",
- }
+# # 程序关闭时的清理操作
+# self.protocol("WM_DELETE_WINDOW", self._on_closing)
- # 列可见性配置
- self.column_visibility = {"show_time": True, "show_level": True, "show_module": True}
+# # 初始化进程、日志队列、日志数据等变量
+# self.process = None
+# self.log_queue = queue.Queue()
+# self.log_data = deque(maxlen=10000) # 使用固定长度队列
+# self.available_levels = set()
+# self.available_modules = set()
+# self.sorted_modules = []
+# self.module_checkboxes = {} # 存储模块复选框的字典
- # 选中的日志等级和模块
- self.selected_levels = set()
- self.selected_modules = set()
+# # 日志颜色配置
+# self.color_config = {
+# "time": "#888888",
+# "DEBUG": "#2196F3",
+# "INFO": "#4CAF50",
+# "WARNING": "#FF9800",
+# "ERROR": "#F44336",
+# "module": "#D4D0AB",
+# "default": "#FFFFFF",
+# }
- # 创建界面组件并启动日志队列处理
- self.create_widgets()
- self.after(100, self.process_log_queue)
+# # 列可见性配置
+# self.column_visibility = {"show_time": True, "show_level": True, "show_module": True}
- def create_widgets(self):
- """创建应用界面的各个组件"""
- self.grid_columnconfigure(0, weight=1)
- self.grid_rowconfigure(1, weight=1)
+# # 选中的日志等级和模块
+# self.selected_levels = set()
+# self.selected_modules = set()
- # 控制面板
- control_frame = ctk.CTkFrame(self)
- control_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=5)
+# # 创建界面组件并启动日志队列处理
+# self.create_widgets()
+# self.after(100, self.process_log_queue)
- self.start_btn = ctk.CTkButton(control_frame, text="启动", command=self.start_process)
- self.start_btn.pack(side="left", padx=5)
+# def create_widgets(self):
+# """创建应用界面的各个组件"""
+# self.grid_columnconfigure(0, weight=1)
+# self.grid_rowconfigure(1, weight=1)
- self.stop_btn = ctk.CTkButton(control_frame, text="停止", command=self.stop_process, state="disabled")
- self.stop_btn.pack(side="left", padx=5)
+# # 控制面板
+# control_frame = ctk.CTkFrame(self)
+# control_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=5)
- self.clear_btn = ctk.CTkButton(control_frame, text="清屏", command=self.clear_logs)
- self.clear_btn.pack(side="left", padx=5)
+# self.start_btn = ctk.CTkButton(control_frame, text="启动", command=self.start_process)
+# self.start_btn.pack(side="left", padx=5)
- column_filter_frame = ctk.CTkFrame(control_frame)
- column_filter_frame.pack(side="left", padx=20)
+# self.stop_btn = ctk.CTkButton(control_frame, text="停止", command=self.stop_process, state="disabled")
+# self.stop_btn.pack(side="left", padx=5)
- self.time_check = ctk.CTkCheckBox(column_filter_frame, text="显示时间", command=self.refresh_logs)
- self.time_check.pack(side="left", padx=5)
- self.time_check.select()
+# self.clear_btn = ctk.CTkButton(control_frame, text="清屏", command=self.clear_logs)
+# self.clear_btn.pack(side="left", padx=5)
- self.level_check = ctk.CTkCheckBox(column_filter_frame, text="显示等级", command=self.refresh_logs)
- self.level_check.pack(side="left", padx=5)
- self.level_check.select()
+# column_filter_frame = ctk.CTkFrame(control_frame)
+# column_filter_frame.pack(side="left", padx=20)
- self.module_check = ctk.CTkCheckBox(column_filter_frame, text="显示模块", command=self.refresh_logs)
- self.module_check.pack(side="left", padx=5)
- self.module_check.select()
+# self.time_check = ctk.CTkCheckBox(column_filter_frame, text="显示时间", command=self.refresh_logs)
+# self.time_check.pack(side="left", padx=5)
+# self.time_check.select()
- # 筛选面板
- filter_frame = ctk.CTkFrame(self)
- filter_frame.grid(row=0, column=1, rowspan=2, sticky="ns", padx=5)
+# self.level_check = ctk.CTkCheckBox(column_filter_frame, text="显示等级", command=self.refresh_logs)
+# self.level_check.pack(side="left", padx=5)
+# self.level_check.select()
- ctk.CTkLabel(filter_frame, text="日志等级筛选").pack(pady=5)
- self.level_scroll = ctk.CTkScrollableFrame(filter_frame, width=150, height=200)
- self.level_scroll.pack(fill="both", expand=True, padx=5)
+# self.module_check = ctk.CTkCheckBox(column_filter_frame, text="显示模块", command=self.refresh_logs)
+# self.module_check.pack(side="left", padx=5)
+# self.module_check.select()
- ctk.CTkLabel(filter_frame, text="模块筛选").pack(pady=5)
- self.module_filter_entry = ctk.CTkEntry(filter_frame, placeholder_text="输入模块过滤词")
- self.module_filter_entry.pack(pady=5)
- self.module_filter_entry.bind("", self.update_module_filter)
+# # 筛选面板
+# filter_frame = ctk.CTkFrame(self)
+# filter_frame.grid(row=0, column=1, rowspan=2, sticky="ns", padx=5)
- self.module_scroll = ctk.CTkScrollableFrame(filter_frame, width=300, height=200)
- self.module_scroll.pack(fill="both", expand=True, padx=5)
+# ctk.CTkLabel(filter_frame, text="日志等级筛选").pack(pady=5)
+# self.level_scroll = ctk.CTkScrollableFrame(filter_frame, width=150, height=200)
+# self.level_scroll.pack(fill="both", expand=True, padx=5)
- self.log_text = ctk.CTkTextbox(self, wrap="word")
- self.log_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=5)
+# ctk.CTkLabel(filter_frame, text="模块筛选").pack(pady=5)
+# self.module_filter_entry = ctk.CTkEntry(filter_frame, placeholder_text="输入模块过滤词")
+# self.module_filter_entry.pack(pady=5)
+# self.module_filter_entry.bind("", self.update_module_filter)
- self.init_text_tags()
+# self.module_scroll = ctk.CTkScrollableFrame(filter_frame, width=300, height=200)
+# self.module_scroll.pack(fill="both", expand=True, padx=5)
- def update_module_filter(self, event):
- """根据模块过滤词更新模块复选框的显示"""
- filter_text = self.module_filter_entry.get().strip().lower()
- for module, checkbox in self.module_checkboxes.items():
- if filter_text in module.lower():
- checkbox.pack(anchor="w", padx=5, pady=2)
- else:
- checkbox.pack_forget()
+# self.log_text = ctk.CTkTextbox(self, wrap="word")
+# self.log_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=5)
- def update_filters(self, level, module):
- """更新日志等级和模块的筛选器"""
- if level not in self.available_levels:
- self.available_levels.add(level)
- self.add_checkbox(self.level_scroll, level, "level")
+# self.init_text_tags()
- module_key = self.get_module_key(module)
- if module_key not in self.available_modules:
- self.available_modules.add(module_key)
- self.sorted_modules = sorted(self.available_modules, key=lambda x: x.lower())
- self.rebuild_module_checkboxes()
+# def update_module_filter(self, event):
+# """根据模块过滤词更新模块复选框的显示"""
+# filter_text = self.module_filter_entry.get().strip().lower()
+# for module, checkbox in self.module_checkboxes.items():
+# if filter_text in module.lower():
+# checkbox.pack(anchor="w", padx=5, pady=2)
+# else:
+# checkbox.pack_forget()
- def rebuild_module_checkboxes(self):
- """重新构建模块复选框"""
- # 清空现有复选框
- for widget in self.module_scroll.winfo_children():
- widget.destroy()
- self.module_checkboxes.clear()
+# def update_filters(self, level, module):
+# """更新日志等级和模块的筛选器"""
+# if level not in self.available_levels:
+# self.available_levels.add(level)
+# self.add_checkbox(self.level_scroll, level, "level")
- # 重建排序后的复选框
- for module in self.sorted_modules:
- self.add_checkbox(self.module_scroll, module, "module")
+# module_key = self.get_module_key(module)
+# if module_key not in self.available_modules:
+# self.available_modules.add(module_key)
+# self.sorted_modules = sorted(self.available_modules, key=lambda x: x.lower())
+# self.rebuild_module_checkboxes()
- def add_checkbox(self, parent, text, type_):
- """在指定父组件中添加复选框"""
+# def rebuild_module_checkboxes(self):
+# """重新构建模块复选框"""
+# # 清空现有复选框
+# for widget in self.module_scroll.winfo_children():
+# widget.destroy()
+# self.module_checkboxes.clear()
- def update_filter():
- current = cb.get()
- if type_ == "level":
- (self.selected_levels.add if current else self.selected_levels.discard)(text)
- else:
- (self.selected_modules.add if current else self.selected_modules.discard)(text)
- self.refresh_logs()
+# # 重建排序后的复选框
+# for module in self.sorted_modules:
+# self.add_checkbox(self.module_scroll, module, "module")
- cb = ctk.CTkCheckBox(parent, text=text, command=update_filter)
- cb.select() # 初始选中
+# def add_checkbox(self, parent, text, type_):
+# """在指定父组件中添加复选框"""
- # 手动同步初始状态到集合(关键修复)
- if type_ == "level":
- self.selected_levels.add(text)
- else:
- self.selected_modules.add(text)
+# def update_filter():
+# current = cb.get()
+# if type_ == "level":
+# (self.selected_levels.add if current else self.selected_levels.discard)(text)
+# else:
+# (self.selected_modules.add if current else self.selected_modules.discard)(text)
+# self.refresh_logs()
- if type_ == "module":
- self.module_checkboxes[text] = cb
- cb.pack(anchor="w", padx=5, pady=2)
- return cb
+# cb = ctk.CTkCheckBox(parent, text=text, command=update_filter)
+# cb.select() # 初始选中
- def check_filter(self, entry):
- """检查日志条目是否符合当前筛选条件"""
- level_ok = not self.selected_levels or entry["level"] in self.selected_levels
- module_key = self.get_module_key(entry["module"])
- module_ok = not self.selected_modules or module_key in self.selected_modules
- return level_ok and module_ok
+# # 手动同步初始状态到集合(关键修复)
+# if type_ == "level":
+# self.selected_levels.add(text)
+# else:
+# self.selected_modules.add(text)
- def init_text_tags(self):
- """初始化日志文本的颜色标签"""
- for tag, color in self.color_config.items():
- self.log_text.tag_config(tag, foreground=color)
- self.log_text.tag_config("default", foreground=self.color_config["default"])
+# if type_ == "module":
+# self.module_checkboxes[text] = cb
+# cb.pack(anchor="w", padx=5, pady=2)
+# return cb
- def start_process(self):
- """启动日志进程并开始读取输出"""
- self.process = subprocess.Popen(
- ["nb", "run"],
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- text=True,
- bufsize=1,
- encoding="utf-8",
- errors="ignore",
- )
- self.start_btn.configure(state="disabled")
- self.stop_btn.configure(state="normal")
- threading.Thread(target=self.read_output, daemon=True).start()
+# def check_filter(self, entry):
+# """检查日志条目是否符合当前筛选条件"""
+# level_ok = not self.selected_levels or entry["level"] in self.selected_levels
+# module_key = self.get_module_key(entry["module"])
+# module_ok = not self.selected_modules or module_key in self.selected_modules
+# return level_ok and module_ok
- def stop_process(self):
- """停止日志进程并清理相关资源"""
- if self.process:
- try:
- if hasattr(self.process, "pid"):
- if os.name == "nt":
- subprocess.run(
- ["taskkill", "/F", "/T", "/PID", str(self.process.pid)], check=True, capture_output=True
- )
- else:
- os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
- except (subprocess.CalledProcessError, ProcessLookupError, OSError) as e:
- print(f"终止进程失败: {e}")
- finally:
- self.process = None
- self.log_queue.queue.clear()
- self.start_btn.configure(state="normal")
- self.stop_btn.configure(state="disabled")
- self.refresh_logs()
+# def init_text_tags(self):
+# """初始化日志文本的颜色标签"""
+# for tag, color in self.color_config.items():
+# self.log_text.tag_config(tag, foreground=color)
+# self.log_text.tag_config("default", foreground=self.color_config["default"])
- def read_output(self):
- """读取日志进程的输出并放入队列"""
- try:
- while self.process and self.process.poll() is None:
- line = self.process.stdout.readline()
- if line:
- self.log_queue.put(line)
- else:
- break # 避免空循环
- self.process.stdout.close() # 确保关闭文件描述符
- except ValueError: # 处理可能的I/O操作异常
- pass
+# def start_process(self):
+# """启动日志进程并开始读取输出"""
+# self.process = subprocess.Popen(
+# ["nb", "run"],
+# stdout=subprocess.PIPE,
+# stderr=subprocess.STDOUT,
+# text=True,
+# bufsize=1,
+# encoding="utf-8",
+# errors="ignore",
+# )
+# self.start_btn.configure(state="disabled")
+# self.stop_btn.configure(state="normal")
+# threading.Thread(target=self.read_output, daemon=True).start()
- def process_log_queue(self):
- """处理日志队列中的日志条目"""
- while not self.log_queue.empty():
- line = self.log_queue.get()
- self.process_log_line(line)
- self.after(100, self.process_log_queue)
+# def stop_process(self):
+# """停止日志进程并清理相关资源"""
+# if self.process:
+# try:
+# if hasattr(self.process, "pid"):
+# if os.name == "nt":
+# subprocess.run(
+# ["taskkill", "/F", "/T", "/PID", str(self.process.pid)], check=True, capture_output=True
+# )
+# else:
+# os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
+# except (subprocess.CalledProcessError, ProcessLookupError, OSError) as e:
+# print(f"终止进程失败: {e}")
+# finally:
+# self.process = None
+# self.log_queue.queue.clear()
+# self.start_btn.configure(state="normal")
+# self.stop_btn.configure(state="disabled")
+# self.refresh_logs()
- def process_log_line(self, line):
- """解析单行日志并更新日志数据和筛选器"""
- match = re.match(
- r"""^
- (?:(?P