import customtkinter as ctk from tkinter import scrolledtext import threading import queue import time from openai import OpenAI import os from dotenv import load_dotenv load_dotenv() class Config: SILICONFLOW_API_KEY = os.getenv("SILICONFLOW_API_KEY") SILICONFLOW_BASE_URL = os.getenv("SILICONFLOW_BASE_URL", "https://api.siliconflow.cn/v1") SILICONFLOW_MODEL = os.getenv("SILICONFLOW_MODEL", "deepseek-ai/DeepSeek-V2.5") config = Config() class CodeExplanationApp(ctk.CTk): def __init__(self): super().__init__() self.title("代码解释与修复助手") self.geometry("1100x750") self.resizable(True, True) ctk.set_appearance_mode("System") ctk.set_default_color_theme("blue") self.setup_ui() self.message_queue = queue.Queue() self.check_queue() def setup_ui(self): self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.main_frame = ctk.CTkFrame(self, corner_radius=0) self.main_frame.grid(row=0, column=0, sticky="nsew") self.main_frame.grid_columnconfigure(0, weight=1) self.main_frame.grid_rowconfigure(0, weight=1) self.header_frame = ctk.CTkFrame(self.main_frame, height=50, corner_radius=0) self.header_frame.grid(row=0, column=0, sticky="ew", padx=0, pady=0) self.header_frame.grid_columnconfigure(0, weight=1) self.title_label = ctk.CTkLabel( self.header_frame, text="🤖 代码解释与修复助手", font=("Microsoft YaHei", 20, "bold") ) self.title_label.grid(row=0, column=0, padx=15, pady=12) self.content_frame = ctk.CTkFrame(self.main_frame, corner_radius=0) self.content_frame.grid(row=1, column=0, sticky="nsew", padx=10, pady=10) self.content_frame.grid_columnconfigure(0, weight=2) self.content_frame.grid_columnconfigure(1, weight=3) self.content_frame.grid_rowconfigure(0, weight=1) self.left_panel = ctk.CTkFrame(self.content_frame, corner_radius=8) self.left_panel.grid(row=0, column=0, sticky="nsew", padx=(0, 8)) self.left_panel.grid_columnconfigure(0, weight=1) self.left_panel.grid_rowconfigure(0, weight=1) self.left_panel.grid_rowconfigure(1, weight=1) self.input_frame = ctk.CTkFrame(self.left_panel, corner_radius=6) self.input_frame.grid(row=0, column=0, sticky="nsew", padx=8, pady=8) self.input_frame.grid_columnconfigure(0, weight=1) self.input_frame.grid_rowconfigure(1, weight=1) self.input_label = ctk.CTkLabel( self.input_frame, text="📝 输入您的代码:", font=("Microsoft YaHei", 13, "bold") ) self.input_label.grid(row=0, column=0, sticky="w", padx=10, pady=(8, 4)) self.code_input = ctk.CTkTextbox( self.input_frame, font=("Consolas", 13), corner_radius=6 ) self.code_input.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 8)) self.button_frame = ctk.CTkFrame(self.left_panel, corner_radius=6) self.button_frame.grid(row=1, column=0, sticky="ew", padx=8, pady=(0, 8)) self.button_frame.grid_columnconfigure(0, weight=1) self.options_frame = ctk.CTkFrame(self.button_frame, corner_radius=6) self.options_frame.grid(row=0, column=0, sticky="ew", padx=8, pady=8) self.options_frame.grid_columnconfigure((0, 1, 2), weight=1) self.task_type = ctk.StringVar(value="explain") ctk.CTkRadioButton( self.options_frame, text="� 代码解释", variable=self.task_type, value="explain", font=("Microsoft YaHei", 11) ).grid(row=0, column=0, padx=15, pady=6) ctk.CTkRadioButton( self.options_frame, text="� 代码修复", variable=self.task_type, value="fix", font=("Microsoft YaHei", 11) ).grid(row=0, column=1, padx=15, pady=6) self.language_var = ctk.StringVar(value="auto") self.language_combo = ctk.CTkComboBox( self.options_frame, values=["auto", "python", "javascript", "java", "cpp", "c", "go", "rust", "ruby", "php"], variable=self.language_var, font=("Microsoft YaHei", 11) ) self.language_combo.grid(row=0, column=2, padx=15, pady=6) self.button_inner_frame = ctk.CTkFrame(self.button_frame, corner_radius=0, fg_color="transparent") self.button_inner_frame.grid(row=1, column=0, sticky="e", padx=8, pady=(0, 8)) self.submit_button = ctk.CTkButton( self.button_inner_frame, text="� 开始分析", command=self.on_submit_clicked, font=("Microsoft YaHei", 12, "bold"), height=30, corner_radius=6 ) self.submit_button.grid(row=0, column=0, padx=4) self.clear_button = ctk.CTkButton( self.button_inner_frame, text="�️ 清空", command=self.on_clear_clicked, font=("Microsoft YaHei", 11), height=30, corner_radius=6, fg_color="gray", hover_color="darkgray" ) self.clear_button.grid(row=0, column=1, padx=4) self.right_panel = ctk.CTkFrame(self.content_frame, corner_radius=8) self.right_panel.grid(row=0, column=1, sticky="nsew", padx=(8, 0)) self.right_panel.grid_columnconfigure(0, weight=1) self.right_panel.grid_rowconfigure(0, weight=1) self.right_panel.grid_rowconfigure(1, weight=2) self.process_frame = ctk.CTkFrame(self.right_panel, corner_radius=6) self.process_frame.grid(row=0, column=0, sticky="nsew", padx=8, pady=8) self.process_frame.grid_columnconfigure(0, weight=1) self.process_frame.grid_rowconfigure(1, weight=1) self.process_label = ctk.CTkLabel( self.process_frame, text="⚙️ 进程状态", font=("Microsoft YaHei", 13, "bold") ) self.process_label.grid(row=0, column=0, sticky="w", padx=10, pady=(8, 4)) self.process_text = ctk.CTkTextbox( self.process_frame, font=("Consolas", 11), corner_radius=6, state="disabled" ) self.process_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 8)) self.result_frame = ctk.CTkFrame(self.right_panel, corner_radius=6) self.result_frame.grid(row=1, column=0, sticky="nsew", padx=8, pady=(0, 8)) self.result_frame.grid_columnconfigure(0, weight=1) self.result_frame.grid_rowconfigure(1, weight=1) self.result_label = ctk.CTkLabel( self.result_frame, text="💡 AI 分析结果", font=("Microsoft YaHei", 13, "bold") ) self.result_label.grid(row=0, column=0, sticky="w", padx=10, pady=(8, 4)) self.result_text = ctk.CTkTextbox( self.result_frame, font=("Microsoft YaHei", 12), corner_radius=6, state="disabled" ) self.result_text.grid(row=1, column=0, sticky="nsew", padx=10, pady=(0, 8)) self.status_label = ctk.CTkLabel( self, text="就绪 - 请输入代码后点击「🚀 开始分析」按钮", font=("Microsoft YaHei", 10) ) self.status_label.grid(row=2, column=0, sticky="ew", padx=15, pady=(0, 8)) def log_process(self, message): self.process_text.configure(state="normal") timestamp = time.strftime("%H:%M:%S", time.localtime()) self.process_text.insert("end", f"[{timestamp}] {message}\n") self.process_text.see("end") self.process_text.configure(state="disabled") def on_submit_clicked(self): code = self.code_input.get("0.0", "end").strip() if code: self.process_code(code) else: self.status_label.configure(text="请先输入代码!") def on_clear_clicked(self): self.code_input.delete("0.0", "end") self.result_text.configure(state="normal") self.result_text.delete("0.0", "end") self.result_text.configure(state="disabled") self.process_text.configure(state="normal") self.process_text.delete("0.0", "end") self.process_text.configure(state="disabled") self.status_label.configure(text="已清空,请输入新的代码") def process_code(self, code): task = self.task_type.get() language = self.language_var.get() self.process_text.configure(state="normal") self.process_text.delete("0.0", "end") self.process_text.configure(state="disabled") self.log_process("🚀 任务已启动") self.log_process(f"📋 任务类型: {'代码解释' if task == 'explain' else '代码修复'}") self.log_process(f"🌐 检测语言: {language}") self.log_process(f"📏 代码长度: {len(code)} 字符") self.status_label.configure(text="正在处理...") self.result_text.configure(state="normal") self.result_text.delete("0.0", "end") self.result_text.insert("0.0", "🚀 正在实时生成响应...\n\n") self.result_text.configure(state="disabled") threading.Thread(target=self.call_ai, args=(code, task, language), daemon=True).start() def call_ai(self, code, task, language): try: api_key = config.SILICONFLOW_API_KEY if not api_key: self.message_queue.put(("error", "请在 .env 文件中设置 SILICONFLOW_API_KEY")) return self.log_process("🔌 正在连接 SiliconFlow API...") self.log_process(f"🤖 使用模型: {config.SILICONFLOW_MODEL}") client = OpenAI(api_key=api_key, base_url=config.SILICONFLOW_BASE_URL) if task == "explain": prompt = f""" 请作为一位耐心的编程导师,解释以下{language}代码。 请提供: 1. 代码整体功能的概述 2. 逐行或分段的详细解释 3. 关键概念和语法的说明 4. 相关的最佳实践建议 代码: ```{language} {code} ``` """ else: prompt = f""" 请作为一位经验丰富的开发者,分析并修复以下{language}代码中的问题。 请提供: 1. 发现的问题列表 2. 修复后的完整代码 3. 每个修复的详细说明 4. 相关的最佳实践建议 代码: ```{language} {code} ``` """ self.log_process("📤 正在发送请求...") start_time = time.time() stream = client.chat.completions.create( model=config.SILICONFLOW_MODEL, messages=[{"role": "user", "content": prompt}], temperature=0.7 if task == "explain" else 0.5, stream=True ) self.log_process("📥 正在接收响应...") full_response = "" chunk_count = 0 for chunk in stream: if chunk.choices[0].delta.content: content = chunk.choices[0].delta.content full_response += content chunk_count += 1 self.message_queue.put(("stream", content)) elapsed_time = time.time() - start_time self.log_process(f"✅ 响应接收完成") self.log_process(f"📊 收到 {chunk_count} 个数据块") self.log_process(f"⏱️ 耗时: {elapsed_time:.2f} 秒") self.log_process(f"📏 结果长度: {len(full_response)} 字符") self.message_queue.put(("success", full_response)) except Exception as e: self.log_process(f"❌ 发生错误: {str(e)}") self.message_queue.put(("error", f"调用 AI 服务失败:{str(e)}")) def check_queue(self): try: while not self.message_queue.empty(): msg_type, content = self.message_queue.get_nowait() if msg_type == "stream": self.result_text.configure(state="normal") self.result_text.insert("end", content) self.result_text.see("end") self.result_text.configure(state="disabled") self.status_label.configure(text="正在生成响应中...") else: self.result_text.configure(state="normal") if msg_type == "success": if content and not content.startswith("✅"): self.result_text.insert("end", f"\n\n{content}") self.status_label.configure(text="✅ 处理完成") self.log_process("🎉 任务完成!") else: self.result_text.insert("end", f"\n\n❌ 错误:{content}") self.status_label.configure(text="❌ 发生错误") self.result_text.configure(state="disabled") except: pass self.after(50, self.check_queue) def main(): app = CodeExplanationApp() app.mainloop() if __name__ == "__main__": main()