ZHZ.YHY/gui_app.py

353 lines
13 KiB
Python
Raw Normal View History

2026-01-08 21:48:24 +08:00
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()