AIGC(生成式AI)试用 45 -- DocsGPT 与 Python开发 1

一切从python调用本地DocsGPT完成python开发开始。

遗留问题:如何验证AI开发提交的结果?

  • 提问 1:
    使用python+Tkinter进行GUI程序编码
    1. 界面分为左右两部分
    • 左侧为python代码编辑区:
      左上部为代码多行输入框,嵌入python idle,浅灰色底色;
      左下部为 Run 按钮
    • 右侧为GPT调用区:
      右上部为tab,名称 Question,嵌入多行文本,输入提问问题;
      中部为Show Answer按钮,海蓝色;
      下部为2个tab:tab1,名称 Answer,嵌入多行文本,显示GPT处理结果;
      tab2,名称History,显示提问历史,answer + question,数据来自名为pyai的sqlite的数据库
    1. 优化界面
    2. 优化代码
  • DeepSeek 回复 1:
    • 1 次调用界面
      > 除提问内容外,DeepSeek AI自行增加
      * 菜单
      * 编码所需功能键
      * AI调用所需参数
      > pyton代码可直接录入并执行
      > AI调用需要API密钥
      > 提供了系统环境部署、操作说明
      > 如果直接提问调用本地DocsGPT?
  • 提问 2:
    GPT助手,改为调用本地DocsGPT API,更新 gpt_client.py

  • DeepSeek 回复 2:

    • 2 次调用界面
      > 按提问要求,添加DocsGPT设置选项
      > 然而参数影响:同步/异步调用方式,Stream(True/False),History的设置不同,调用过程报错
      > # 添加DocsGPT特定参数 -- AI自主添加的DocsGPT设置,待处理

    |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
    | 主要更新说明: 支持两种API类型 docsgpt: 本地DocsGPT API openai: OpenAI官方API DocsGPT特定功能: 支持项目ID和会话ID 自动适应不同的响应格式 支持流式响应 改进的错误处理: 更好的连接测试 详细的错误信息 超时设置 配置管理: 在设置对话框中添加DocsGPT配置 自动保存和恢复设置 测试连接功能 兼容性: 保持对OpenAI API的向后兼容 自动检测和适应不同的响应格式 |
    | 使用方法: 启动本地DocsGPT服务: bash # 在DocsGPT目录中 python app.py # 或 npm start 在程序中配置: 选择API类型为 "docsgpt" API URL设置为 http://localhost:3001/api/answer 点击"测试连接"验证 使用GPT助手: 输入问题,点击"获取答案" 系统会自动调用本地DocsGPT API 这个更新后的版本提供了完整的本地DocsGPT集成,同时保持了OpenAI API的兼容性。 |

  • 1 次提问代码示例

    我将为您创建一个完整的Python Tkinter GUI应用程序,集成代码编辑器和GPT调用功能。

    项目结构
    text
    gpt_code_ide/
    ├── main.py # 主程序
    ├── database.py # SQLite数据库操作
    ├── gpt_client.py # GPT API客户端
    └── requirements.txt # 依赖包

    1. requirements.txt
      txt
      tkinter
      sqlite3
      requests
      pygments
      idlelib
    2. database.py - SQLite数据库操作
      python
      """
      数据库操作模块
      创建和管理SQLite数据库,存储提问历史
      """
      import sqlite3
      import json
      from datetime import datetime
      from typing import List, Dict, Optional

    class PyAIDatabase:
    def init(self, db_name: str = "pyai.db"):
    self.db_name = db_name
    self.init_database()

    复制代码
      def init_database(self):
          """初始化数据库表"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              # 创建历史记录表
              cursor.execute("""
                  CREATE TABLE IF NOT EXISTS query_history (
                      id INTEGER PRIMARY KEY AUTOINCREMENT,
                      question TEXT NOT NULL,
                      answer TEXT NOT NULL,
                      code_context TEXT,
                      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                  )
              """)
              
              # 创建代码片段表
              cursor.execute("""
                  CREATE TABLE IF NOT EXISTS code_snippets (
                      id INTEGER PRIMARY KEY AUTOINCREMENT,
                      title TEXT NOT NULL,
                      code TEXT NOT NULL,
                      language TEXT DEFAULT 'python',
                      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                      updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                  )
              """)
              
              # 创建配置表
              cursor.execute("""
                  CREATE TABLE IF NOT EXISTS settings (
                      key TEXT PRIMARY KEY,
                      value TEXT NOT NULL
                  )
              """)
              
              # 插入默认配置
              cursor.execute("""
                  INSERT OR IGNORE INTO settings (key, value) 
                  VALUES ('api_key', ''),
                         ('api_url', 'https://api.openai.com/v1/chat/completions'),
                         ('theme', 'light'),
                         ('font_size', '12')
              """)
              
              conn.commit()
      
      def save_query(self, question: str, answer: str, code_context: str = "") -> int:
          """保存查询记录"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              cursor.execute("""
                  INSERT INTO query_history (question, answer, code_context)
                  VALUES (?, ?, ?)
              """, (question, answer, code_context))
              conn.commit()
              return cursor.lastrowid
      
      def get_query_history(self, limit: int = 50, offset: int = 0) -> List[Dict]:
          """获取查询历史"""
          with sqlite3.connect(self.db_name) as conn:
              conn.row_factory = sqlite3.Row
              cursor = conn.cursor()
              cursor.execute("""
                  SELECT id, question, answer, code_context, 
                         created_at, updated_at
                  FROM query_history 
                  ORDER BY created_at DESC 
                  LIMIT ? OFFSET ?
              """, (limit, offset))
              
              rows = cursor.fetchall()
              return [dict(row) for row in rows]
      
      def delete_query(self, query_id: int) -> bool:
          """删除查询记录"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              cursor.execute("DELETE FROM query_history WHERE id = ?", (query_id,))
              conn.commit()
              return cursor.rowcount > 0
      
      def clear_history(self) -> bool:
          """清空历史记录"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              cursor.execute("DELETE FROM query_history")
              conn.commit()
              return cursor.rowcount > 0
      
      def save_code_snippet(self, title: str, code: str, language: str = "python") -> int:
          """保存代码片段"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              cursor.execute("""
                  INSERT INTO code_snippets (title, code, language)
                  VALUES (?, ?, ?)
              """, (title, code, language))
              conn.commit()
              return cursor.lastrowid
      
      def get_code_snippets(self) -> List[Dict]:
          """获取所有代码片段"""
          with sqlite3.connect(self.db_name) as conn:
              conn.row_factory = sqlite3.Rown
              cursor = conn.cursor()
              cursor.execute("""
                  SELECT id, title, code, language, created_at
                  FROM code_snippets 
                  ORDER BY created_at DESC
              """)
              return [dict(row) for row in cursor.fetchall()]
      
      def get_setting(self, key: str) -> Optional[str]:
          """获取配置"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              cursor.execute("SELECT value FROM settings WHERE key = ?", (key,))
              result = cursor.fetchone()
              return result[0] if result else None
      
      def update_setting(self, key: str, value: str):
          """更新配置"""
          with sqlite3.connect(self.db_name) as conn:
              cursor = conn.cursor()
              cursor.execute("""
                  INSERT OR REPLACE INTO settings (key, value)
                  VALUES (?, ?)
              """, (key, value))
              conn.commit()
      
      def search_history(self, keyword: str) -> List[Dict]:
          """搜索历史记录"""
          with sqlite3.connect(self.db_name) as conn:
              conn.row_factory = sqlite3.Row
              cursor = conn.cursor()
              cursor.execute("""
                  SELECT id, question, answer, created_at
                  FROM query_history 
                  WHERE question LIKE ? OR answer LIKE ?
                  ORDER BY created_at DESC
              """, (f"%{keyword}%", f"%{keyword}%"))
              return [dict(row) for row in cursor.fetchall()]

    全局数据库实例

    db = PyAIDatabase()
    3. gpt_client.py - GPT API客户端
    python
    """
    GPT API客户端模块
    处理与GPT API的通信
    """
    import requests
    import json
    from typing import Optional, Dict, Any
    import threading
    from database import db

    class GPTClient:
    def init(self, api_key: str = None, api_url: str = None):
    self.api_key = api_key or db.get_setting('api_key') or ""
    self.api_url = api_url or db.get_setting('api_url') or "https://api.openai.com/v1/chat/completions"
    self.session = requests.Session()
    self.session.headers.update({
    "Authorization": f"Bearer {self.api_key}",
    "Content-Type": "application/json"
    })

    复制代码
      def set_api_key(self, api_key: str):
          """设置API密钥"""
          self.api_key = api_key
          self.session.headers.update({"Authorization": f"Bearer {api_key}"})
          db.update_setting('api_key', api_key)
      
      def set_api_url(self, api_url: str):
          """设置API URL"""
          self.api_url = api_url
          db.update_setting('api_url', api_url)
      
      def ask_question(self, 
                      question: str, 
                      code_context: str = "",
                      model: str = "gpt-3.5-turbo",
                      max_tokens: int = 1000,
                      temperature: float = 0.7,
                      callback=None) -> Optional[str]:
          """
          向GPT提问
          
          Args:
              question: 问题文本
              code_context: 相关代码上下文
              model: 使用的模型
              max_tokens: 最大token数
              temperature: 温度参数
              callback: 回调函数,用于异步更新UI
          
          Returns:
              GPT的回答
          """
          if not self.api_key:
              return "错误:请先设置API密钥"
          
          # 构建消息
          messages = []
          if code_context:
              messages.append({
                  "role": "system",
                  "content": f"以下是相关代码上下文:\n```python\n{code_context}\n```\n请基于此代码上下文回答问题。"
              })
          
          messages.append({
              "role": "user",
              "content": question
          })
          
          # 构建请求数据
          data = {
              "model": model,
              "messages": messages,
              "max_tokens": max_tokens,
              "temperature": temperature,
              "stream": False
          }
          
          try:
              if callback:
                  # 异步调用
                  thread = threading.Thread(
                      target=self._async_ask,
                      args=(data, callback)
                  )
                  thread.daemon = True
                  thread.start()
                  return "正在处理..."
              else:
                  # 同步调用
                  return self._sync_ask(data)
                  
          except Exception as e:
              return f"请求错误: {str(e)}"
      
      def _async_ask(self, data: Dict, callback):
          """异步调用GPT"""
          try:
              response = self.session.post(
                  self.api_url,
                  json=data,
                  timeout=30
              )
              
              if response.status_code == 200:
                  result = response.json()
                  answer = result['choices'][0]['message']['content']
                  callback(answer)
              else:
                  error_msg = f"API错误: {response.status_code}\n{response.text}"
                  callback(error_msg)
                  
          except requests.exceptions.Timeout:
              callback("错误:请求超时,请检查网络连接")
          except requests.exceptions.ConnectionError:
              callback("错误:无法连接到API服务器")
          except Exception as e:
              callback(f"未知错误: {str(e)}")
      
      def _sync_ask(self, data: Dict) -> str:
          """同步调用GPT"""
          try:
              response = self.session.post(
                  self.api_url,
                  json=data,
                  timeout=30
              )
              
              if response.status_code == 200:
                  result = response.json()
                  return result['choices'][0]['message']['content']
              else:
                  return f"API错误: {response.status_code}\n{response.text}"
                  
          except requests.exceptions.Timeout:
              return "错误:请求超时,请检查网络连接"
          except requests.exceptions.ConnectionError:
              return "错误:无法连接到API服务器"
          except Exception as e:
              return f"未知错误: {str(e)}"
      
      def get_models(self) -> list:
          """获取可用的模型列表(需要相应权限)"""
          if not self.api_key:
              return []
          
          try:
              response = self.session.get(
                  "https://api.openai.com/v1/models",
                  timeout=10
              )
              
              if response.status_code == 200:
                  models = response.json()['data']
                  return [model['id'] for model in models 
                         if 'gpt' in model['id'].lower()]
              return []
          except:
              return ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo"]

    全局GPT客户端实例

    gpt_client = GPTClient()
    4. main.py - 主GUI程序
    python
    """
    主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 = find_entry.get()
              replace_text = replace_entry.get()
              
              if find_text:
                  content = self.text_widget.get(1.0, tk.END)
                  new_content = content.replace(find_text, replace_text)
                  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 = ""
          if self.include_code_var.get():
              code_context = self.code_editor.get_code()
          
          # 禁用按钮,显示处理中状态
          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
          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)
          
          # 右侧:GPT助手
          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']
          )
          
          # 更新GPT助手颜色
          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设置
          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客户端
              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:
              # 使用autopep8格式化代码(如果安装了)
              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
    联系: your.email@example.com
    """

    复制代码
          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()

|---------|--------------------------------------------------------------------------|
| 使用说明 ||
| 安装依赖 | bash, pip install requests autopep8 |
| 运行程序 | bash, python main.py |
| 配置API密钥 | 在程序右侧的API密钥输入框中输入您的OpenAI API密钥 点击"保存"按钮 |
| 使用流程 | 在左侧编写Python代码 点击"运行代码"按钮执行 在右侧输入问题,点击"获取答案"按钮向GPT提问 查看历史记录中的问答历史 |
| 功能特点 ||
| 代码编辑器 | 支持语法高亮 文件操作(新建、打开、保存) 查找替换功能 撤销/重做 |
| GPT助手 | 集成OpenAI GPT API 支持代码上下文 历史记录管理 搜索功能 |
| 界面优化 | 左右分栏设计 海蓝色主题按钮 深浅色主题切换 响应式布局 |
| 数据库 | SQLite存储历史记录 持久化设置 快速搜索查询 这个程序提供了一个完整的Python开发环境,集成了GPT智能助手,非常适合学习和开发使用。 |

相关推荐
开源技术3 小时前
深入了解Turso,这个“用Rust重写的SQLite”
人工智能·python
u0109272714 小时前
RESTful API设计最佳实践(Python版)
jvm·数据库·python
我材不敲代码8 小时前
Python实现打包贪吃蛇游戏
开发语言·python·游戏
0思必得010 小时前
[Web自动化] Selenium处理动态网页
前端·爬虫·python·selenium·自动化
韩立学长10 小时前
【开题答辩实录分享】以《基于Python的大学超市仓储信息管理系统的设计与实现》为例进行选题答辩实录分享
开发语言·python
qq_1927798710 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
u01092727111 小时前
使用Plotly创建交互式图表
jvm·数据库·python
爱学习的阿磊11 小时前
Python GUI开发:Tkinter入门教程
jvm·数据库·python
PaperRed ai写作降重助手11 小时前
高性价比 AI 论文写作软件推荐:2026 年预算友好型
人工智能·aigc·论文·写作·ai写作·智能降重