"""
主 GUI 程序 Python 代码编辑器 + GPT 助手
"""
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, font
import subprocess
import tempfile
import os
import sys
from datetime import datetime
from database import db
from gpt_client import gpt_client
COLORS = {
"light": {
"bg": "#f5f5f5",
"fg": "#333333",
"editor_bg": "#f8f8f8",
"button_bg": "#007acc",
"button_fg": "white",
"tab_bg": "#ffffff",
"border": "#cccccc",
"highlight": "#e6f3ff"
},
"dark": {
"bg": "#2d2d2d",
"fg": "#e0e0e0",
"editor_bg": "#1e1e1e",
"button_bg": "#007acc",
"button_fg": "white",
"tab_bg": "#252526",
"border": "#404040",
"highlight": "#04395e"
}
}
class CodeEditor(tk.Frame):
"""代码编辑器组件"""
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.create_widgets()
def create_widgets(self):
"""创建编辑器组件"""
toolbar = tk.Frame(self)
toolbar.pack(fill=tk.X, padx=5, pady=2)
tk.Button(toolbar, text="新建", command=self.new_file, width=6).pack(side=tk.LEFT, padx=2)
tk.Button(toolbar, text="打开", command=self.open_file, width=6).pack(side=tk.LEFT, padx=2)
tk.Button(toolbar, text="保存", command=self.save_file, width=6).pack(side=tk.LEFT, padx=2)
tk.Label(toolbar, text="|").pack(side=tk.LEFT, padx=5)
tk.Button(toolbar, text="撤销", command=self.undo, width=6).pack(side=tk.LEFT, padx=2)
tk.Button(toolbar, text="重做", command=self.redo, width=6).pack(side=tk.LEFT, padx=2)
tk.Label(toolbar, text="|").pack(side=tk.LEFT, padx=5)
tk.Button(toolbar, text="查找", command=self.find_text, width=6).pack(side=tk.LEFT, padx=2)
tk.Button(toolbar, text="替换", command=self.replace_text, width=6).pack(side=tk.LEFT, padx=2)
self.text_widget = scrolledtext.ScrolledText(
self, wrap=tk.WORD, font=("Consolas", 12), bg=COLORS["light"]["editor_bg"], fg=COLORS["light"]["fg"], insertbackground="black", selectbackground="#d4d4d4", undo=True, maxundo=-1
)
self.text_widget.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.bind_shortcuts()
self.status_bar = tk.Label(self, text="就绪", bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
self.current_file = None
def bind_shortcuts(self):
"""绑定快捷键"""
self.text_widget.bind('<Control-s>', lambda e: self.save_file())
self.text_widget.bind('<Control-o>', lambda e: self.open_file())
self.text_widget.bind('<Control-n>', lambda e: self.new_file())
self.text_widget.bind('<Control-f>', lambda e: self.find_text())
self.text_widget.bind('<Control-h>', lambda e: self.replace_text())
self.text_widget.bind('<Control-z>', lambda e: self.undo())
self.text_widget.bind('<Control-y>', lambda e: self.redo())
def new_file(self):
"""新建文件"""
self.text_widget.delete(1.0, tk.END)
self.current_file = None
self.update_status("新建文件")
def open_file(self):
"""打开文件"""
from tkinter import filedialog
file_path = filedialog.askopenfilename(filetypes=[("Python files", "*.py"), ("Text files", "*.txt"), ("All files", "*.*")])
if file_path:
try:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
self.text_widget.delete(1.0, tk.END)
self.text_widget.insert(1.0, content)
self.current_file = file_path
self.update_status(f"已打开:{os.path.basename(file_path)}")
except Exception as e:
messagebox.showerror("错误", f"无法打开文件:{str(e)}")
def save_file(self):
"""保存文件"""
if not self.current_file:
self.save_as_file()
else:
try:
content = self.text_widget.get(1.0, tk.END)
with open(self.current_file, 'w', encoding='utf-8') as file:
file.write(content)
self.update_status(f"已保存:{os.path.basename(self.current_file)}")
except Exception as e:
messagebox.showerror("错误", f"无法保存文件:{str(e)}")
def save_as_file(self):
"""另存为文件"""
from tkinter import filedialog
file_path = filedialog.asksaveasfilename(defaultextension=".py", filetypes=[("Python files", "*.py"), ("Text files", "*.txt"), ("All files", "*.*")])
if file_path:
self.current_file = file_path
self.save_file()
def undo(self):
"""撤销"""
try:
self.text_widget.edit_undo()
except:
pass
def redo(self):
"""重做"""
try:
self.text_widget.edit_redo()
except:
pass
def find_text(self):
"""查找文本"""
self.show_search_dialog()
def replace_text(self):
"""替换文本"""
self.show_search_dialog(replace=True)
def show_search_dialog(self, replace=False):
"""显示查找/替换对话框"""
dialog = tk.Toplevel(self)
dialog.title("替换" if replace else "查找")
dialog.geometry("400x200")
dialog.transient(self)
dialog.grab_set()
tk.Label(dialog, text="查找内容:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
find_entry = tk.Entry(dialog, width=30)
find_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
if replace:
tk.Label(dialog, text="替换为:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
replace_entry = tk.Entry(dialog, width=30)
replace_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)
def do_find():
text = find_entry.get()
if text:
start_pos = self.text_widget.index(tk.INSERT)
pos = self.text_widget.search(text, start_pos, tk.END)
if pos:
end_pos = f"{pos}+{len(text)}c"
self.text_widget.tag_remove(tk.SEL, 1.0, tk.END)
self.text_widget.tag_add(tk.SEL, pos, end_pos)
self.text_widget.mark_set(tk.INSERT, pos)
self.text_widget.see(pos)
else:
messagebox.showinfo("查找", "未找到匹配项")
def do_replace():
find_text_val = find_entry.get()
replace_text_val = replace_entry.get()
if find_text_val:
content = self.text_widget.get(1.0, tk.END)
new_content = content.replace(find_text_val, replace_text_val)
self.text_widget.delete(1.0, tk.END)
self.text_widget.insert(1.0, new_content)
tk.Button(dialog, text="查找", command=do_find).grid(row=2, column=0, padx=5, pady=10)
if replace:
tk.Button(dialog, text="替换", command=do_replace).grid(row=2, column=1, padx=5, pady=10)
def update_status(self, message):
"""更新状态栏"""
self.status_bar.config(text=message)
def get_code(self):
"""获取当前代码"""
return self.text_widget.get(1.0, tk.END).strip()
def set_code(self, code):
"""设置代码"""
self.text_widget.delete(1.0, tk.END)
self.text_widget.insert(1.0, code)
def clear(self):
"""清空编辑器"""
self.text_widget.delete(1.0, tk.END)
class GPTAssistant(tk.Frame):
"""GPT 助手组件"""
def __init__(self, parent, code_editor, **kwargs):
super().__init__(parent, **kwargs)
self.code_editor = code_editor
self.create_widgets()
self.load_history()
def create_widgets(self):
"""创建 GPT 助手界面"""
config_frame = tk.Frame(self)
config_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Label(config_frame, text="API 密钥:").pack(side=tk.LEFT)
self.api_key_var = tk.StringVar(value=db.get_setting('api_key') or "")
self.api_entry = tk.Entry(config_frame, textvariable=self.api_key_var, width=30, show="*")
self.api_entry.pack(side=tk.LEFT, padx=5)
tk.Button(config_frame, text="保存", command=self.save_api_key).pack(side=tk.LEFT, padx=2)
tk.Button(config_frame, text="显示/隐藏", command=self.toggle_api_key).pack(side=tk.LEFT, padx=2)
tk.Label(self, text="问题:", font=("Arial", 10, "bold")).pack(anchor=tk.W, padx=10, pady=(10, 0))
self.question_text = scrolledtext.ScrolledText(self, height=8, wrap=tk.WORD, font=("Arial", 11))
self.question_text.pack(fill=tk.X, padx=10, pady=5)
button_frame = tk.Frame(self)
button_frame.pack(fill=tk.X, padx=10, pady=10)
self.ask_button = tk.Button(button_frame, text="获取答案", command=self.ask_gpt, bg="#007acc", fg="white", font=("Arial", 11, "bold"), height=2, width=12, cursor="hand2")
self.ask_button.pack(side=tk.LEFT, padx=5)
tk.Button(button_frame, text="清除问题", command=self.clear_question, bg="#6c757d", fg="white", height=2, width=10).pack(side=tk.LEFT, padx=5)
self.include_code_var = tk.BooleanVar(value=True)
tk.Checkbutton(button_frame, text="包含代码上下文", variable=self.include_code_var, font=("Arial", 9)).pack(side=tk.LEFT, padx=20)
self.notebook = ttk.Notebook(self)
self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
self.answer_frame = tk.Frame(self.notebook)
self.notebook.add(self.answer_frame, text="答案")
self.answer_text = scrolledtext.ScrolledText(self.answer_frame, wrap=tk.WORD, font=("Arial", 11))
self.answer_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.history_frame = tk.Frame(self.notebook)
self.notebook.add(self.history_frame, text="历史")
history_toolbar = tk.Frame(self.history_frame)
history_toolbar.pack(fill=tk.X, padx=5, pady=5)
tk.Button(history_toolbar, text="刷新", command=self.load_history).pack(side=tk.LEFT, padx=2)
tk.Button(history_toolbar, text="清空", command=self.clear_history).pack(side=tk.LEFT, padx=2)
tk.Label(history_toolbar, text="搜索:").pack(side=tk.LEFT, padx=(10, 2))
self.search_var = tk.StringVar()
self.search_entry = tk.Entry(history_toolbar, textvariable=self.search_var, width=20)
self.search_entry.pack(side=tk.LEFT, padx=2)
tk.Button(history_toolbar, text="搜索", command=self.search_history).pack(side=tk.LEFT, padx=2)
history_list_frame = tk.Frame(self.history_frame)
history_list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
columns = ("时间", "问题", "答案摘要")
self.history_tree = ttk.Treeview(history_list_frame, columns=columns, show="headings", height=15)
for col in columns:
self.history_tree.heading(col, text=col)
self.history_tree.column(col, width=150)
self.history_tree.column("问题", width=250)
self.history_tree.column("答案摘要", width=300)
scrollbar = ttk.Scrollbar(history_list_frame, orient="vertical", command=self.history_tree.yview)
self.history_tree.configure(yscrollcommand=scrollbar.set)
self.history_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.history_tree.bind("<Double-1>", self.show_history_detail)
self.status_label = tk.Label(self, text="就绪", bd=1, relief=tk.SUNKEN, anchor=tk.W)
self.status_label.pack(side=tk.BOTTOM, fill=tk.X)
def save_api_key(self):
"""保存 API 密钥"""
api_key = self.api_key_var.get().strip()
if api_key:
gpt_client.set_api_key(api_key)
messagebox.showinfo("成功", "API 密钥已保存")
else:
messagebox.showwarning("警告", "请输入 API 密钥")
def toggle_api_key(self):
"""切换 API 密钥显示/隐藏"""
current_show = self.api_entry.cget("show")
self.api_entry.config(show="" if current_show == "*" else "*")
def ask_gpt(self):
"""向 GPT 提问"""
question = self.question_text.get(1.0, tk.END).strip()
if not question:
messagebox.showwarning("警告", "请输入问题")
return
if not gpt_client.api_key:
messagebox.showwarning("警告", "请先设置 API 密钥")
return
code_context = self.code_editor.get_code() if self.include_code_var.get() else ""
self.ask_button.config(state=tk.DISABLED, text="处理中...")
self.status_label.config(text="正在向 GPT 发送请求...")
self.update()
def update_answer(answer):
self.answer_text.delete(1.0, tk.END)
self.answer_text.insert(1.0, answer)
db.save_query(question, answer, code_context)
self.ask_button.config(state=tk.NORMAL, text="获取答案")
self.status_label.config(text=f"完成 - {datetime.now().strftime('%H:%M:%S')}")
self.load_history()
gpt_client.ask_question(question=question, code_context=code_context, callback=update_answer)
def clear_question(self):
"""清除问题"""
self.question_text.delete(1.0, tk.END)
def load_history(self):
"""加载历史记录"""
for item in self.history_tree.get_children():
self.history_tree.delete(item)
history = db.get_query_history(limit=100)
for record in history:
created_at = datetime.strptime(record['created_at'], '%Y-%m-%d %H:%M:%S')
time_str = created_at.strftime('%m-%d %H:%M')
question_summary = record['question'][:50] + "..." if len(record['question']) > 50 else record['question']
answer_summary = record['answer'][:50] + "..." if len(record['answer']) > 50 else record['answer']
self.history_tree.insert("", tk.END, values=(time_str, question_summary, answer_summary), tags=(record['id'],))
def search_history(self):
"""搜索历史记录"""
keyword = self.search_var.get().strip()
if not keyword:
self.load_history()
return
for item in self.history_tree.get_children():
self.history_tree.delete(item)
results = db.search_history(keyword)
for record in results:
created_at = datetime.strptime(record['created_at'], '%Y-%m-%d %H:%M:%S')
time_str = created_at.strftime('%m-%d %H:%M')
question_summary = record['question'][:50] + "..." if len(record['question']) > 50 else record['question']
answer_summary = record['answer'][:50] + "..." if len(record['answer']) > 50 else record['answer']
self.history_tree.insert("", tk.END, values=(time_str, question_summary, answer_summary), tags=(record['id'],))
def clear_history(self):
"""清空历史记录"""
if messagebox.askyesno("确认", "确定要清空所有历史记录吗?"):
db.clear_history()
self.load_history()
def show_history_detail(self, event):
"""显示历史记录详情"""
selection = self.history_tree.selection()
if not selection:
return
item = self.history_tree.item(selection[0])
record_id = item['tags'][0]
history = db.get_query_history(limit=1000)
record = next((r for r in history if r['id'] == int(record_id)), None)
if record:
detail_window = tk.Toplevel(self)
detail_window.title(f"历史记录详情 - {record['created_at']}")
detail_window.geometry("800x600")
notebook = ttk.Notebook(detail_window)
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
question_frame = tk.Frame(notebook)
notebook.add(question_frame, text="问题")
question_text = scrolledtext.ScrolledText(question_frame, wrap=tk.WORD)
question_text.insert(1.0, record['question'])
question_text.config(state=tk.DISABLED)
question_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
answer_frame = tk.Frame(notebook)
notebook.add(answer_frame, text="答案")
answer_text = scrolledtext.ScrolledText(answer_frame, wrap=tk.WORD)
answer_text.insert(1.0, record['answer'])
answer_text.config(state=tk.DISABLED)
answer_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
if record['code_context']:
code_frame = tk.Frame(notebook)
notebook.add(code_frame, text="代码上下文")
code_text = scrolledtext.ScrolledText(code_frame, wrap=tk.WORD)
code_text.insert(1.0, record['code_context'])
code_text.config(state=tk.DISABLED)
code_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
button_frame = tk.Frame(detail_window)
button_frame.pack(fill=tk.X, padx=10, pady=5)
tk.Button(button_frame, text="复制答案", command=lambda: self.copy_to_clipboard(record['answer'])).pack(side=tk.LEFT, padx=5)
tk.Button(button_frame, text="重新提问", command=lambda: self.reuse_question(record['question'])).pack(side=tk.LEFT, padx=5)
tk.Button(button_frame, text="删除记录", command=lambda: self.delete_record(record_id, detail_window)).pack(side=tk.LEFT, padx=5)
def copy_to_clipboard(self, text):
"""复制文本到剪贴板"""
self.clipboard_clear()
self.clipboard_append(text)
messagebox.showinfo("成功", "已复制到剪贴板")
def reuse_question(self, question):
"""重新使用问题"""
self.question_text.delete(1.0, tk.END)
self.question_text.insert(1.0, question)
self.notebook.select(0)
def delete_record(self, record_id, window):
"""删除记录"""
if messagebox.askyesno("确认", "确定要删除这条记录吗?"):
db.delete_query(int(record_id))
self.load_history()
window.destroy()
class PythonIDE(tk.Tk):
"""Python IDE 主窗口"""
def __init__(self):
super().__init__()
self.title("Python IDE with GPT Assistant")
self.geometry("1400x800")
try:
self.iconbitmap('icon.ico')
except:
pass
self.current_theme = db.get_setting('theme') or 'light'
self.create_menu()
self.create_main_interface()
self.protocol("WM_DELETE_WINDOW", self.on_closing)
def create_menu(self):
"""创建菜单栏"""
menubar = tk.Menu(self)
self.config(menu=menubar)
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="文件", menu=file_menu)
file_menu.add_command(label="新建", command=self.new_file)
file_menu.add_command(label="打开", command=self.open_file)
file_menu.add_command(label="保存", command=self.save_file)
file_menu.add_command(label="另存为", command=self.save_as_file)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.quit)
edit_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="编辑", menu=edit_menu)
edit_menu.add_command(label="撤销", command=self.undo)
edit_menu.add_command(label="重做", command=self.redo)
edit_menu.add_separator()
edit_menu.add_command(label="剪切", command=self.cut)
edit_menu.add_command(label="复制", command=self.copy)
edit_menu.add_command(label="粘贴", command=self.paste)
edit_menu.add_separator()
edit_menu.add_command(label="全选", command=self.select_all)
run_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="运行", menu=run_menu)
run_menu.add_command(label="运行代码", command=self.run_code)
run_menu.add_command(label="调试", command=self.debug_code)
tools_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="工具", menu=tools_menu)
tools_menu.add_command(label="设置", command=self.open_settings)
tools_menu.add_command(label="代码格式化", command=self.format_code)
theme_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="主题", menu=theme_menu)
theme_menu.add_command(label="浅色主题", command=lambda: self.change_theme('light'))
theme_menu.add_command(label="深色主题", command=lambda: self.change_theme('dark'))
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="帮助", menu=help_menu)
help_menu.add_command(label="使用说明", command=self.show_help)
help_menu.add_command(label="关于", command=self.show_about)
def create_main_interface(self):
"""创建主界面"""
paned_window = tk.PanedWindow(self, orient=tk.HORIZONTAL, sashwidth=5, sashrelief=tk.RAISED)
paned_window.pack(fill=tk.BOTH, expand=True)
left_frame = tk.Frame(paned_window)
paned_window.add(left_frame, width=700)
self.code_editor = CodeEditor(left_frame)
self.code_editor.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
run_button = tk.Button(left_frame, text="运行代码 (F5)", command=self.run_code, bg="#28a745", fg="white", font=("Arial", 11, "bold"), height=2)
run_button.pack(fill=tk.X, padx=5, pady=5)
right_frame = tk.Frame(paned_window)
paned_window.add(right_frame, width=700)
self.gpt_assistant = GPTAssistant(right_frame, self.code_editor)
self.gpt_assistant.pack(fill=tk.BOTH, expand=True)
self.apply_theme()
def apply_theme(self):
"""应用主题"""
colors = COLORS[self.current_theme]
self.config(bg=colors['bg'])
self.code_editor.text_widget.config(bg=colors['editor_bg'], fg=colors['fg'], insertbackground=colors['fg'])
self.gpt_assistant.answer_text.config(bg=colors['tab_bg'], fg=colors['fg'])
self.gpt_assistant.question_text.config(bg=colors['tab_bg'], fg=colors['fg'])
def change_theme(self, theme):
"""切换主题"""
self.current_theme = theme
db.update_setting('theme', theme)
self.apply_theme()
def run_code(self):
"""运行代码"""
code = self.code_editor.get_code()
if not code.strip():
messagebox.showwarning("警告", "代码为空")
return
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as tmp:
tmp.write(code)
tmp_path = tmp.name
try:
result = subprocess.run([sys.executable, tmp_path], capture_output=True, text=True, timeout=10)
output = f"返回值:{result.returncode}\n\n"
if result.stdout:
output += f"标准输出:\n{result.stdout}\n"
if result.stderr:
output += f"标准错误:\n{result.stderr}\n"
self.gpt_assistant.answer_text.delete(1.0, tk.END)
self.gpt_assistant.answer_text.insert(1.0, output)
self.gpt_assistant.notebook.select(0)
except subprocess.TimeoutExpired:
messagebox.showerror("错误", "代码执行超时")
except Exception as e:
messagebox.showerror("错误", f"执行错误:{str(e)}")
finally:
try:
os.unlink(tmp_path)
except:
pass
def debug_code(self):
"""调试代码(简单实现)"""
code = self.code_editor.get_code()
debug_window = tk.Toplevel(self)
debug_window.title("调试输出")
debug_window.geometry("600x400")
text_widget = scrolledtext.ScrolledText(debug_window)
text_widget.pack(fill=tk.BOTH, expand=True)
import io
import contextlib
output = io.StringIO()
try:
with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output):
exec(code, {})
text_widget.insert(1.0, output.getvalue())
except Exception as e:
text_widget.insert(1.0, f"错误:{str(e)}\n")
def open_settings(self):
"""打开设置窗口"""
settings_window = tk.Toplevel(self)
settings_window.title("设置")
settings_window.geometry("500x400")
api_frame = tk.LabelFrame(settings_window, text="API 设置", padx=10, pady=10)
api_frame.pack(fill=tk.X, padx=10, pady=10)
tk.Label(api_frame, text="OpenAI API 密钥:").grid(row=0, column=0, sticky=tk.W, pady=5)
api_key_var = tk.StringVar(value=db.get_setting('api_key') or "")
api_entry = tk.Entry(api_frame, textvariable=api_key_var, width=40)
api_entry.grid(row=0, column=1, pady=5, padx=5)
tk.Label(api_frame, text="API URL:").grid(row=1, column=0, sticky=tk.W, pady=5)
api_url_var = tk.StringVar(value=db.get_setting('api_url') or "https://api.openai.com/v1/chat/completions")
tk.Entry(api_frame, textvariable=api_url_var, width=40).grid(row=1, column=1, pady=5, padx=5)
editor_frame = tk.LabelFrame(settings_window, text="编辑器设置", padx=10, pady=10)
editor_frame.pack(fill=tk.X, padx=10, pady=10)
tk.Label(editor_frame, text="字体大小:").grid(row=0, column=0, sticky=tk.W, pady=5)
font_size_var = tk.StringVar(value=db.get_setting('font_size') or "12")
tk.Spinbox(editor_frame, from_=8, to=24, textvariable=font_size_var, width=10).grid(row=0, column=1, pady=5, padx=5)
def save_settings():
db.update_setting('api_key', api_key_var.get())
db.update_setting('api_url', api_url_var.get())
db.update_setting('font_size', font_size_var.get())
gpt_client.set_api_key(api_key_var.get())
gpt_client.set_api_url(api_url_var.get())
self.code_editor.text_widget.config(font=("Consolas", int(font_size_var.get())))
messagebox.showinfo("成功", "设置已保存")
settings_window.destroy()
tk.Button(settings_window, text="保存设置", command=save_settings, bg="#007acc", fg="white", width=15).pack(pady=20)
def format_code(self):
"""格式化代码(简单实现)"""
code = self.code_editor.get_code()
try:
import autopep8
formatted_code = autopep8.fix_code(code)
self.code_editor.set_code(formatted_code)
except ImportError:
lines = code.split('\n')
formatted_lines = []
indent = 0
for line in lines:
line_stripped = line.strip()
if line_stripped.endswith(':'):
formatted_lines.append(' ' * indent + line_stripped)
indent += 4
elif line_stripped and line_stripped[0] != '#':
if any(line_stripped.startswith(keyword) for keyword in ['elif ', 'else:', 'except ', 'finally:']):
indent = max(0, indent - 4)
formatted_lines.append(' ' * indent + line_stripped)
else:
formatted_lines.append(' ' * indent + line_stripped)
self.code_editor.set_code('\n'.join(formatted_lines))
def new_file(self):
"""新建文件"""
self.code_editor.new_file()
def open_file(self):
"""打开文件"""
self.code_editor.open_file()
def save_file(self):
"""保存文件"""
self.code_editor.save_file()
def save_as_file(self):
"""另存为文件"""
self.code_editor.save_as_file()
def undo(self):
"""撤销"""
self.code_editor.undo()
def redo(self):
"""重做"""
self.code_editor.redo()
def cut(self):
"""剪切"""
self.code_editor.text_widget.event_generate("<<Cut>>")
def copy(self):
"""复制"""
self.code_editor.text_widget.event_generate("<<Copy>>")
def paste(self):
"""粘贴"""
self.code_editor.text_widget.event_generate("<<Paste>>")
def select_all(self):
"""全选"""
self.code_editor.text_widget.tag_add(tk.SEL, "1.0", tk.END)
self.code_editor.text_widget.mark_set(tk.INSERT, "1.0")
self.code_editor.text_widget.see(tk.INSERT)
return 'break'
def show_help(self):
"""显示使用说明"""
help_text = """使用说明:
1. 代码编辑区:
- 左侧是 Python 代码编辑器,支持语法高亮
- 使用菜单或快捷键进行文件操作
- 点击'运行代码'按钮执行当前代码
2. GPT 助手区:
- 右侧是 GPT 助手面板
- 在'问题'框中输入您的问题
- 点击'获取答案'按钮向 GPT 提问
- 答案会显示在'答案'标签页中
- 历史记录保存在'历史'标签页中
3. 设置:
- 在'工具'菜单中打开设置
- 配置 API 密钥和 URL
- 切换浅色/深色主题
4. 快捷键:
- Ctrl+S: 保存文件
- Ctrl+O: 打开文件
- Ctrl+N: 新建文件
- Ctrl+Z: 撤销
- Ctrl+Y: 重做
- F5: 运行代码"""
help_window = tk.Toplevel(self)
help_window.title("使用说明")
help_window.geometry("600x500")
text_widget = scrolledtext.ScrolledText(help_window, wrap=tk.WORD)
text_widget.insert(1.0, help_text)
text_widget.config(state=tk.DISABLED)
text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
def show_about(self):
"""显示关于信息"""
about_text = """Python IDE with GPT Assistant v1.0
功能特点:
- 集成 Python 代码编辑器
- GPT 智能助手
- 代码执行和调试
- 历史记录管理
- 多主题支持
作者:Your Name
联系:[email protected]"""
messagebox.showinfo("关于", about_text)
def on_closing(self):
"""窗口关闭事件"""
if messagebox.askokcancel("退出", "确定要退出程序吗?"):
db.update_setting('theme', self.current_theme)
self.destroy()
def main():
"""主函数"""
app = PythonIDE()
app.mainloop()
if __name__ == "__main__":
main()