111/gui_app.py
2026-01-08 21:48:24 +08:00

353 lines
13 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="<EFBFBD> 代码解释",
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="<EFBFBD> 代码修复",
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="<EFBFBD> 开始分析",
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="<EFBFBD> 清空",
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()