"""
主 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):
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_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):
current_show = self.api_entry.cget("show")
self.api_entry.config(show="" if current_show == "*" else "*")
def ask_gpt(self):
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 智能助手
- 代码执行和调试
- 历史记录管理
- 多主题支持"""
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()