浔川代码编辑器 v4.1.0 正式版重磅上线!AI 加持,轻量高效,开箱即用

基于 Python + Tkinter + SQLite3 深度打磨,浔川代码编辑器 v4.1.0 正式版今日全面发布!本次迭代全面整合内测优化成果,AI 助手、在线运行、菜单布局、登录体验四大核心升级,纯本地运行、免安装开箱即用,为 Python 初学者、脚本开发者、轻量化编程场景带来更稳定、更便捷、更智能的编码体验。


🎯 一、软件核心定位

浔川代码编辑器是轻量级桌面代码编辑工具,纯本地运行、数据本地存储、无需复杂环境配置,集代码编辑、语法高亮、在线运行、AI 快捷助手、账号管理、配置记忆、版本更新、用户反馈于一体,新手友好、老手高效,一站式满足日常代码编写与调试需求。

✨ 二、v4.1.0 正式版核心更新(全覆盖内测功能)

1. AI 助手全新上线,编程更智能

  • 新增顶部菜单栏「AI 对话」专属入口,内置三大 AI 快捷通道:豆包、DeepSeek、文心一言
  • 修复旧版弹窗空白 Bug,点击直接浏览器打开官网,无需额外弹窗,一键唤 AI 辅助编码、查问题、写思路

2. 菜单与布局重构,操作更清晰

  • 新增在线运行独立菜单栏,拆分「代码运行」「在线运行」双模块,功能分类更直观、查找更高效
  • 优化整体界面布局,行号实时显示、编辑区更清爽,编码视野无干扰

3. 在线代码运行升级,免环境写码

  • 新增一键在线运行 Python 跳转接口,无需本地配置 Python 环境,打开即用
  • 新增一键在线运行 C++ 跳转接口,多语言轻量开发全覆盖
  • 保留本地运行入口并标注版本提示,兼顾未来拓展与当前体验

4. 启动与登录优化,三种入口随心选

  • 账号注册:用户名≥3 位、密码≥6 位,密码 MD5 加密存储本地数据库,安全可靠
  • 账号登录:输入账号密码即可进入,首次登录必须同意用户协议,合规使用更放心
  • 游客模式:免注册免登录,一键「游客体验」快速上手,适合临时编写、快速测试

5. 核心编辑能力拉满,编码更流畅

  • 基础编辑:支持多行编辑、无限撤销 / 重做、左侧实时行号,基础操作丝滑不卡顿
  • Python 语法高亮:关键字(蓝)、字符串(红)、注释(绿)、数字(紫)自动识别标色,代码结构一目了然
  • 文件管理:支持新建 / 打开 / 保存 / 另存为,兼容 .py、.txt 及所有格式文件;切换 / 关闭自动检测未保存内容,防丢码更安心
  • 字体与视图:Ctrl++/Ctrl+- 快速缩放字体,最低 10 号防过小;登录用户自动记忆字体、窗口大小,下次打开还原偏好

6. 完整菜单功能,一站式操作

  • 文件:新建、打开、保存、另存为、退出软件
  • 编辑:撤销、重做、全选、复制、剪切、粘贴
  • 视图:增大 / 减小字体
  • 代码运行:本地运行(版本提示)、在线运行 Python、在线运行 C++
  • AI 对话:豆包、DeepSeek、文心一言一键直达
  • 帮助:检查更新、我要反馈、用户协议、关于软件

7. 实用快捷键大全,高效提速

表格

快捷键 功能
Ctrl+N 新建文件
Ctrl+O 打开文件
Ctrl+S 保存文件
Ctrl+Shift+S 另存为
Ctrl+Z 撤销
Ctrl+Y 重做
Ctrl+A 全选
Ctrl+C 复制
Ctrl+X 剪切
Ctrl+V 粘贴
Ctrl++ 增大字体
Ctrl+- 减小字体

8. 数据安全与配置持久化

  • 本地数据库 editor_user.db:存储账号信息、加密密码、个人配置、用户协议,所有数据本地留存,不上传云端,隐私更安全
  • 反馈文件夹 feedback:提交反馈自动保存版本、身份、时间、标题、内容,问题追踪更高效
  • 配置记忆:仅登录用户自动保存字体、字号、窗口尺寸,游客模式不保存配置,兼顾便捷与简洁

9. 版本更新与合规机制

  • 自动联网检测最新版,对比弹窗提示,展示更新日志,一键跳转下载地址,更新更省心
  • 内置用户协议,新用户首次登录必阅同意,随时可在帮助菜单查看,合规使用有保障

🚀 三、v4.1.0 核心优势

  1. 免安装单文件运行:双击主 Python 文件启动,低配电脑也流畅,无冗余占用
  2. 纯本地隐私安全:账号、配置、反馈全存在本地,不联网上传,数据自主掌控
  3. AI + 在线运行双加持:不用装环境、不用搜网址,写码→运行→问 AI 一站式完成
  4. 新手友好全功能:语法高亮、自动提示、配置记忆、协议规范,零基础也能快速上手
  5. 轻量化不臃肿:无多余插件、无广告干扰,专注编码,打开即用、用完即关

📢 四、即刻升级,开启高效编码

浔川代码编辑器 v4.1.0 正式版已全面上线,修复所有已知问题、优化性能体验、完善功能细节,无论是日常练习 Python、编写小脚本,还是快速调试代码,都是你的贴心编程搭子!

立即更新,体验AI 辅助 + 免环境运行 + 本地安全的轻量编码新方式,让写代码更简单、更高效、更安心!

import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog, simpledialog
import sqlite3
import hashlib
import os
import sys
import requests
import json
from datetime import datetime
import webbrowser

# -------------------------- 全局配置与常量 --------------------------
SOFT_NAME = "浔川代码编辑器"
SOFT_VERSION = "4.1.0"
UPDATE_API = "https://jsonplaceholder.typicode.com/posts/1"
DB_FILE = "editor_user.db"
FEEDBACK_DIR = "feedback"
DEFAULT_FONT = ("Consolas", 12)
HIGHLIGHT_COLORS = {
    "keyword": "#0000FF",
    "string": "#A31515",
    "comment": "#008000",
    "number": "#FF00FF"
}
PYTHON_KEYWORDS = {
    'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await',
    'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except',
    'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
    'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return',
    'try', 'while', 'with', 'yield'
}
LOGGED_USER = None
PROTOCOL_VERSION = "4.1.0"
IS_GUEST = False

# -------------------------- 数据库初始化 --------------------------
def init_database():
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS users
                 (id INTEGER PRIMARY KEY AUTOINCREMENT,
                  username TEXT UNIQUE NOT NULL,
                  password TEXT NOT NULL,
                  reg_time TEXT NOT NULL,
                  agree_protocol INTEGER DEFAULT 0)''')
    c.execute('''CREATE TABLE IF NOT EXISTS editor_config
                 (user_id INTEGER PRIMARY KEY,
                  font TEXT NOT NULL,
                  font_size INTEGER NOT NULL,
                  win_size TEXT DEFAULT "800x600",
                  FOREIGN KEY(user_id) REFERENCES users(id))''')
    c.execute('''CREATE TABLE IF NOT EXISTS user_protocol
                 (version TEXT PRIMARY KEY,
                  content TEXT NOT NULL,
                  update_time TEXT NOT NULL)''')
    c.execute("SELECT * FROM user_protocol WHERE version=?", (PROTOCOL_VERSION,))
    if not c.fetchone():
        default_protocol = """
[浔川代码编辑器用户协议]
版本:4.1.0
更新时间:2026-05-02

1. 本软件为免费开源工具,仅供个人学习和非商业使用;
2. 用户需妥善保管自己的账号密码,因密码泄露造成的损失由用户自行承担;
3. 软件支持在线资源更新,更新过程中请保证网络通畅;
4. 禁止使用本软件编写、运行违法违规、危害网络安全的代码;
5. 如需使用旧版本(未下架),需联系浔川社团官方联合会;
6. 浔川社团官方联合会网址:https://xunchuanshetuan.blog.csdn.net/
7. 本协议的最终解释权归软件开发者所有,如有更新将在启动时提示.
        """
        c.execute("INSERT INTO user_protocol VALUES (?, ?, ?)",
                  (PROTOCOL_VERSION, default_protocol.strip(), datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
    conn.commit()
    conn.close()

# -------------------------- 工具函数 --------------------------
def md5_encrypt(s):
    m = hashlib.md5()
    m.update(s.encode("utf-8"))
    return m.hexdigest()

def db_query(sql, params=()):
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    c.execute(sql, params)
    result = c.fetchall()
    conn.close()
    return result

def db_execute(sql, params=()):
    conn = sqlite3.connect(DB_FILE)
    c = conn.cursor()
    try:
        c.execute(sql, params)
        conn.commit()
        return True
    except sqlite3.IntegrityError:
        return False
    finally:
        conn.close()

# -------------------------- 反馈功能 --------------------------
def save_feedback(title, content):
    try:
        if not os.path.exists(FEEDBACK_DIR):
            os.makedirs(FEEDBACK_DIR)
        user = LOGGED_USER if LOGGED_USER else "游客"
        now = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{FEEDBACK_DIR}/反馈_{user}_{now}.txt"
        with open(filename, "w", encoding="utf-8") as f:
            f.write(f"软件版本:{SOFT_VERSION}\n")
            f.write(f"用户身份:{user}\n")
            f.write(f"反馈时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"反馈标题:{title}\n\n")
            f.write(f"反馈内容:\n{content}\n")
        return True
    except:
        return False

def show_feedback_window(parent):
    win = tk.Toplevel(parent)
    win.title("用户反馈")
    win.geometry("500x400")
    win.resizable(False, False)
    win.transient(parent)
    win.grab_set()

    ttk.Label(win, text="反馈标题", font=("SimHei", 12)).pack(pady=5)
    title_entry = ttk.Entry(win, width=50, font=("", 11))
    title_entry.pack(pady=2)

    ttk.Label(win, text="反馈内容(bug、建议、需求均可)", font=("SimHei", 12)).pack(pady=5)
    content_text = scrolledtext.ScrolledText(win, width=60, height=15, font=("", 10))
    content_text.pack(padx=10, pady=5)

    def submit():
        title = title_entry.get().strip()
        content = content_text.get("1.0", tk.END).strip()
        if not title or not content:
            messagebox.showwarning("提示", "标题和内容不能为空!")
            return
        if save_feedback(title, content):
            messagebox.showinfo("成功", "反馈已提交,感谢你的支持!")
            win.destroy()
        else:
            messagebox.showerror("失败", "反馈保存失败,请重试!")

    ttk.Button(win, text="提交反馈", command=submit, width=15).pack(pady=10)

# -------------------------- 资源更新 --------------------------
def check_update():
    try:
        response = requests.get(UPDATE_API, timeout=5)
        if response.status_code == 200:
            update_data = {
                "latest_version": "1.1.0",
                "current_version": SOFT_VERSION,
                "update_content": "1. 新增Python语法高亮优化;2. 修复保存文件乱码问题;3. 新增字体大小一键调整;4. 优化登录状态持久化;5. 新增在线运行及代码运行功能.",
                "download_url": "https://github.com/xxx/python-editor/archive/refs/tags/v1.1.0.zip"
            }
            return update_data
        else:
            return None
    except Exception as e:
        return None

def update_resource():
    messagebox.showinfo("检查更新", "正在检测最新版本,请稍候...")
    update_info = check_update()
    if not update_info:
        messagebox.showinfo("检查更新", "当前已是最新版本,或网络异常无法检测更新!")
        return
    def version2num(v):
        return list(map(int, v.split(".")))
    if version2num(update_info["latest_version"]) <= version2num(SOFT_VERSION):
        messagebox.showinfo("检查更新", f"当前已是最新版本(v{SOFT_VERSION}),无需更新!")
        return
    confirm = messagebox.askyesno(
        "发现新版本",
        f"检测到新版本:v{update_info['latest_version']}\n"
        f"当前版本:v{SOFT_VERSION}\n\n"
        f"更新内容:\n{update_info['update_content']}\n\n"
        "是否前往下载更新包?(下载后请解压替换原文件并重启)"
    )
    if confirm:
        webbrowser.open(update_info["download_url"])

# -------------------------- 用户协议 --------------------------
def show_protocol_window(parent, must_agree=False):
    protocol_win = tk.Toplevel(parent)
    protocol_win.title("用户协议")
    protocol_win.geometry("600x500")
    protocol_win.resizable(False, False)
    protocol_win.transient(parent)
    protocol_win.grab_set()

    protocol_data = db_query("SELECT content, version FROM user_protocol WHERE version=?", (PROTOCOL_VERSION,))
    if protocol_data:
        protocol_content = protocol_data[0][0]
        protocol_version = protocol_data[0][1]
    else:
        protocol_content = "暂无协议内容"
        protocol_version = "未知版本"

    ttk.Label(protocol_win, text=f"用户协议(版本:{protocol_version})",
              font=("SimHei", 14, "bold")).pack(pady=10)

    protocol_text = scrolledtext.ScrolledText(protocol_win, font=("SimSun", 10),
                                              wrap=tk.WORD, state=tk.NORMAL)
    protocol_text.insert(tk.END, protocol_content)
    protocol_text.config(state=tk.DISABLED)
    protocol_text.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)

    btn_frame = ttk.Frame(protocol_win)
    btn_frame.pack(pady=10)

    def agree_action():
        if must_agree and LOGGED_USER:
            user_id = db_query("SELECT id FROM users WHERE username=?", (LOGGED_USER,))[0][0]
            db_execute("UPDATE users SET agree_protocol=1 WHERE id=?", (user_id,))
        protocol_win.destroy()

    def cancel_action():
        protocol_win.destroy()
        if must_agree:
            global LOGGED_USER
            LOGGED_USER = None
            parent.quit()
            messagebox.showwarning("提示", "您必须同意用户协议才能使用本软件!")

    ttk.Button(btn_frame, text="同意", command=agree_action, width=10).grid(row=0, column=0, padx=20)
    if must_agree:
        ttk.Button(btn_frame, text="拒绝", command=cancel_action, width=10).grid(row=0, column=1)
    else:
        ttk.Button(btn_frame, text="关闭", command=cancel_action, width=10).grid(row=0, column=1)

    protocol_win.update_idletasks()
    parent_x = parent.winfo_x()
    parent_y = parent.winfo_y()
    parent_w = parent.winfo_width()
    parent_h = parent.winfo_height()
    win_w = protocol_win.winfo_width()
    win_h = protocol_win.winfo_height()
    x = parent_x + (parent_w - win_w) // 2
    y = parent_y + (parent_h - win_h) // 2
    protocol_win.geometry(f"+{x}+{y}")

# -------------------------- 注册登录 --------------------------
class LoginRegisterWindow:
    def __init__(self, root):
        self.root = root
        self.root.title(f"{SOFT_NAME} - 登录/注册")
        self.root.geometry("400x380")
        self.root.resizable(False, False)
        self.current_frame = None
        self.create_widgets()
        self.show_login_frame()

    def create_widgets(self):
        self.main_frame = ttk.Frame(self.root, padding="20")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        ttk.Label(self.main_frame, text=SOFT_NAME, font=("SimHei", 18, "bold")).pack(pady=10)
        ttk.Label(self.main_frame, text=f"版本:v{SOFT_VERSION}", font=("SimHei", 10)).pack(pady=5)

    def clear_frame(self):
        if self.current_frame:
            self.current_frame.destroy()
        self.current_frame = ttk.Frame(self.main_frame)
        self.current_frame.pack(fill=tk.BOTH, expand=True, pady=20)

    def show_login_frame(self):
        self.clear_frame()
        ttk.Label(self.current_frame, text="用户名:").grid(row=0, column=0, sticky=tk.W, pady=10)
        self.login_user = ttk.Entry(self.current_frame, width=25)
        self.login_user.grid(row=0, column=1, pady=10)
        ttk.Label(self.current_frame, text="密  码:").grid(row=1, column=0, sticky=tk.W, pady=10)
        self.login_pwd = ttk.Entry(self.current_frame, width=25, show="*")
        self.login_pwd.grid(row=1, column=1, pady=10)
        ttk.Button(self.current_frame, text="登录", command=self.login, width=15).grid(row=2, column=0, columnspan=2, pady=8)
        ttk.Button(self.current_frame, text="游客体验", command=self.guest_mode, width=15).grid(row=3, column=0, columnspan=2, pady=8)
        ttk.Label(self.current_frame, text="还没有账号?").grid(row=4, column=0, sticky=tk.E)
        ttk.Button(self.current_frame, text="立即注册", command=self.show_register_frame, style="Link.TButton").grid(row=4, column=1, sticky=tk.W)
        self.root.style = ttk.Style()
        self.root.style.configure("Link.TButton", borderwidth=0, foreground="#0000FF")

    def show_register_frame(self):
        self.clear_frame()
        ttk.Label(self.current_frame, text="用户名:").grid(row=0, column=0, sticky=tk.W, pady=8)
        self.reg_user = ttk.Entry(self.current_frame, width=25)
        self.reg_user.grid(row=0, column=1, pady=8)
        ttk.Label(self.current_frame, text="密  码:").grid(row=1, column=0, sticky=tk.W, pady=8)
        self.reg_pwd = ttk.Entry(self.current_frame, width=25, show="*")
        self.reg_pwd.grid(row=1, column=1, pady=8)
        ttk.Label(self.current_frame, text="确认密码:").grid(row=2, column=0, sticky=tk.W, pady=8)
        self.reg_pwd2 = ttk.Entry(self.current_frame, width=25, show="*")
        self.reg_pwd2.grid(row=2, column=1, pady=8)
        ttk.Button(self.current_frame, text="注册", command=self.register, width=15).grid(row=3, column=0, columnspan=2, pady=15)
        ttk.Label(self.current_frame, text="已有账号?").grid(row=4, column=0, sticky=tk.E)
        ttk.Button(self.current_frame, text="立即登录", command=self.show_login_frame, style="Link.TButton").grid(row=4, column=1, sticky=tk.W)

    def login(self):
        username = self.login_user.get().strip()
        password = self.login_pwd.get().strip()
        if not username or not password:
            messagebox.showwarning("提示", "用户名和密码不能为空!")
            return
        pwd_md5 = md5_encrypt(password)
        user_data = db_query("SELECT id, agree_protocol FROM users WHERE username=? AND password=?", (username, pwd_md5))
        if not user_data:
            messagebox.showerror("错误", "用户名或密码错误!")
            return
        global LOGGED_USER, IS_GUEST
        LOGGED_USER = username
        IS_GUEST = False
        user_id, agree_protocol = user_data[0]
        if not db_query("SELECT * FROM editor_config WHERE user_id=?", (user_id,)):
            db_execute("INSERT INTO editor_config (user_id, font, font_size) VALUES (?, ?, ?)",
                      (user_id, DEFAULT_FONT[0], DEFAULT_FONT[1]))
        if agree_protocol == 0:
            show_protocol_window(self.root, must_agree=True)
        self.root.destroy()
        main_editor = tk.Tk()
        CodeEditor(main_editor)
        main_editor.mainloop()

    def register(self):
        username = self.reg_user.get().strip()
        password = self.reg_pwd.get().strip()
        password2 = self.reg_pwd2.get().strip()
        if not username or not password:
            messagebox.showwarning("提示", "用户名和密码不能为空!")
            return
        if len(username) < 3 or len(password) < 6:
            messagebox.showwarning("提示", "用户名至少3位,密码至少6位!")
            return
        if password != password2:
            messagebox.showwarning("提示", "两次输入的密码不一致!")
            return
        pwd_md5 = md5_encrypt(password)
        reg_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        success = db_execute(
            "INSERT INTO users (username, password, reg_time) VALUES (?, ?, ?)",
            (username, pwd_md5, reg_time)
        )
        if success:
            messagebox.showinfo("成功", "注册成功!请登录使用~")
            self.show_login_frame()
        else:
            messagebox.showerror("错误", "用户名已存在!")

    def guest_mode(self):
        global IS_GUEST, LOGGED_USER
        IS_GUEST = True
        LOGGED_USER = None
        self.root.destroy()
        main_editor = tk.Tk()
        CodeEditor(main_editor)
        main_editor.mainloop()

# -------------------------- 代码编辑器 --------------------------
class CodeEditor:
    def __init__(self, root):
        self.root = root
        self.file_path = None
        self.user_id = None
        self.is_guest = IS_GUEST
        if self.is_guest:
            self.root.title(f"{SOFT_NAME} - 未命名文件 - 游客模式")
        else:
            self.root.title(f"{SOFT_NAME} - 未命名文件 - 登录用户:{LOGGED_USER}")
        self.root.geometry("800x600")
        self.font = DEFAULT_FONT
        if not self.is_guest:
            self.user_id = db_query("SELECT id FROM users WHERE username=?", (LOGGED_USER,))[0][0]
            self.load_user_config()
        self.create_menu()
        self.create_editor()
        self.bind_shortcuts()
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

    def load_user_config(self):
        if self.is_guest:
            return
        config = db_query("SELECT font, font_size, win_size FROM editor_config WHERE user_id=?", (self.user_id,))[0]
        self.font = (config[0], config[1])
        self.win_size = config[2]
        self.root.geometry(self.win_size)

    def save_user_config(self):
        if self.is_guest:
            return
        win_size = f"{self.root.winfo_width()}x{self.root.winfo_height()}"
        db_execute(
            "UPDATE editor_config SET font=?, font_size=?, win_size=? WHERE user_id=?",
            (self.font[0], self.font[1], win_size, self.user_id)
        )

    def create_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # 文件
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
        file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="另存", command=self.save_as_file, accelerator="Ctrl+Shift+S")
        file_menu.add_separator()
        file_menu.add_command(label="安全退出", command=self.on_closing)
        menubar.add_cascade(label="文件", menu=file_menu)

        # 编辑
        edit_menu = tk.Menu(menubar, tearoff=0)
        edit_menu.add_command(label="撤销", command=lambda: self.text_editor.edit_undo(), accelerator="Ctrl+Z")
        edit_menu.add_command(label="重做", command=lambda: self.text_editor.edit_redo(), accelerator="Ctrl+Y")
        edit_menu.add_separator()
        edit_menu.add_command(label="全选", command=lambda: self.text_editor.tag_add(tk.SEL, "1.0", tk.END), accelerator="Ctrl+A")
        edit_menu.add_command(label="复制", command=lambda: self.text_editor.event_generate("<<Copy>>"), accelerator="Ctrl+C")
        edit_menu.add_command(label="剪切", command=lambda: self.text_editor.event_generate("<<Cut>>"), accelerator="Ctrl+X")
        edit_menu.add_command(label="粘贴", command=lambda: self.text_editor.event_generate("<<Paste>>"), accelerator="Ctrl+V")
        menubar.add_cascade(label="编辑", menu=edit_menu)

        # 视图
        view_menu = tk.Menu(menubar, tearoff=0)
        view_menu.add_command(label="增大字体", command=self.increase_font, accelerator="Ctrl++")
        view_menu.add_command(label="减小字体", command=self.decrease_font, accelerator="Ctrl+-")
        menubar.add_cascade(label="视图", menu=view_menu)

        # 代码运行
        run_menu = tk.Menu(menubar, tearoff=0)
        run_menu.add_command(label="运行代码", command=self.run_code)
        menubar.add_cascade(label="代码运行", menu=run_menu)

        # 在线运行
        online_run_menu = tk.Menu(menubar, tearoff=0)
        online_run_menu.add_command(label="运行Python代码", command=self.run_python_online)
        online_run_menu.add_command(label="运行C++代码", command=self.run_cpp_online)
        menubar.add_cascade(label="在线运行", menu=online_run_menu)

        # ====================== AI 对话(直接菜单打开,100%可用)======================
        ai_menu = tk.Menu(menubar, tearoff=0)
        ai_menu.add_command(label="豆包", command=lambda: webbrowser.open("https://www.doubao.com/"))
        ai_menu.add_command(label="DeepSeek", command=lambda: webbrowser.open("https://www.deepseek.com/"))
        ai_menu.add_command(label="文心一言", command=lambda: webbrowser.open("https://yiyan.baidu.com/welcome"))
        menubar.add_cascade(label="AI对话", menu=ai_menu)
        # ============================================================================

        # 帮助
        help_menu = tk.Menu(menubar, tearoff=0)
        help_menu.add_command(label="检查更新", command=update_resource)
        help_menu.add_command(label="我要反馈", command=lambda: show_feedback_window(self.root))
        help_menu.add_command(label="用户协议", command=lambda: show_protocol_window(self.root, must_agree=False))
        help_menu.add_command(label="关于软件", command=self.show_about)
        menubar.add_cascade(label="帮助", menu=help_menu)

    def run_code(self):
        messagebox.showinfo("提示", "本代码编辑器版本暂不支持运行功能,请选择在线运行功能!")

    def run_python_online(self):
        webbrowser.open("https://www.jyshare.com/compile/9/")

    def run_cpp_online(self):
        webbrowser.open("https://www.jyshare.com/compile/12/")

    def create_editor(self):
        editor_frame = ttk.Frame(self.root)
        editor_frame.pack(fill=tk.BOTH, expand=True)
        self.line_num = tk.Text(editor_frame, width=4, font=self.font, state=tk.NORMAL, bg="#F0F0F0", wrap=tk.NONE)
        self.line_num.pack(side=tk.LEFT, fill=tk.Y)
        self.line_num.config(state=tk.DISABLED)
        self.text_editor = scrolledtext.ScrolledText(editor_frame, font=self.font, wrap=tk.NONE, undo=True, maxundo=-1)
        self.text_editor.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.text_editor.focus_set()
        for tag, color in HIGHLIGHT_COLORS.items():
            self.text_editor.tag_configure(tag, foreground=color)
        self.text_editor.bind("<KeyRelease>", self.update_editor)
        self.text_editor.bind("<MouseRelease>", self.update_editor)
        self.text_editor.bind("<Configure>", self.update_editor)
        self.update_line_num()

    def bind_shortcuts(self):
        self.root.bind("<Control-n>", lambda e: self.new_file())
        self.root.bind("<Control-o>", lambda e: self.open_file())
        self.root.bind("<Control-s>", lambda e: self.save_file())
        self.root.bind("<Control-Shift-S>", lambda e: self.save_as_file())
        self.root.bind("<Control-plus>", lambda e: self.increase_font())
        self.root.bind("<Control-minus>", lambda e: self.decrease_font())

    def update_line_num(self):
        line_count = int(self.text_editor.index(tk.END).split(".")[0]) - 1
        self.line_num.config(state=tk.NORMAL)
        self.line_num.delete("1.0", tk.END)
        self.line_num.insert(tk.END, "\n".join(map(str, range(1, line_count + 1))))
        self.line_num.config(state=tk.DISABLED)

    def syntax_highlight(self):
        for tag in HIGHLIGHT_COLORS.keys():
            self.text_editor.tag_remove(tag, "1.0", tk.END)
        content = self.text_editor.get("1.0", tk.END)
        lines = content.split("\n")
        for line_idx, line in enumerate(lines):
            line_num = line_idx + 1
            pos = 0
            while pos < len(line):
                if line[pos] == "#":
                    self.text_editor.tag_add("comment", f"{line_num}.{pos}", f"{line_num}.end")
                    break
                elif line[pos] in ("'", '"'):
                    quote = line[pos]
                    end_pos = line.find(quote, pos + 1)
                    if end_pos != -1:
                        self.text_editor.tag_add("string", f"{line_num}.{pos}", f"{line_num}.{end_pos+1}")
                        pos = end_pos + 1
                    else:
                        pos += 1
                elif line[pos].isdigit() or (line[pos] == "-" and pos+1 < len(line) and line[pos+1].isdigit()):
                    end_pos = pos
                    while end_pos < len(line) and (line[end_pos].isdigit() or line[end_pos] == "."):
                        end_pos += 1
                    self.text_editor.tag_add("number", f"{line_num}.{pos}", f"{line_num}.{end_pos}")
                    pos = end_pos
                elif line[pos].isalpha() or line[pos] == "_":
                    end_pos = pos
                    while end_pos < len(line) and (line[end_pos].isalnum() or line[end_pos] == "_"):
                        end_pos += 1
                    word = line[pos:end_pos]
                    if word in PYTHON_KEYWORDS:
                        self.text_editor.tag_add("keyword", f"{line_num}.{pos}", f"{line_num}.{end_pos}")
                    pos = end_pos
                else:
                    pos += 1

    def update_editor(self, event=None):
        self.update_line_num()
        self.syntax_highlight()

    def new_file(self):
        if self.check_unsaved():
            self.text_editor.delete("1.0", tk.END)
            self.file_path = None
            if self.is_guest:
                self.root.title(f"{SOFT_NAME} - 未命名文件 - 游客模式")
            else:
                self.root.title(f"{SOFT_NAME} - 未命名文件 - 登录用户:{LOGGED_USER}")

    def open_file(self):
        if self.check_unsaved():
            file_path = filedialog.askopenfilename(
                title="打开文件",
                filetypes=[("Python文件", "*.py"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
            )
            if file_path:
                self.file_path = file_path
                with open(file_path, "r", encoding="utf-8") as f:
                    content = f.read()
                self.text_editor.delete("1.0", tk.END)
                self.text_editor.insert("1.0", content)
                if self.is_guest:
                    self.root.title(f"{SOFT_NAME} - {os.path.basename(file_path)} - 游客模式")
                else:
                    self.root.title(f"{SOFT_NAME} - {os.path.basename(file_path)} - 登录用户:{LOGGED_USER}")

    def save_file(self):
        if not self.file_path:
            self.save_as_file()
        else:
            content = self.text_editor.get("1.0", tk.END)
            with open(self.file_path, "w", encoding="utf-8") as f:
                f.write(content)
            messagebox.showinfo("成功", "文件保存成功!")

    def save_as_file(self):
        file_path = filedialog.asksaveasfilename(
            title="另存为",
            defaultextension=".py",
            filetypes=[("Python文件", "*.py"), ("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        if file_path:
            self.file_path = file_path
            self.save_file()
            if self.is_guest:
                self.root.title(f"{SOFT_NAME} - {os.path.basename(file_path)} - 游客模式")
            else:
                self.root.title(f"{SOFT_NAME} - {os.path.basename(file_path)} - 登录用户:{LOGGED_USER}")

    def increase_font(self):
        new_size = self.font[1] + 2
        self.font = (self.font[0], new_size)
        self.text_editor.config(font=self.font)
        self.line_num.config(font=self.font)
        self.save_user_config()

    def decrease_font(self):
        if self.font[1] > 10:
            new_size = self.font[1] - 2
            self.font = (self.font[0], new_size)
            self.text_editor.config(font=self.font)
            self.line_num.config(font=self.font)
            self.save_user_config()

    def check_unsaved(self):
        content = self.text_editor.get("1.0", tk.END).strip()
        if content and not self.file_path:
            confirm = messagebox.askyesno("提示", "当前有未保存的内容,是否放弃?")
            return confirm
        return True

    def show_about(self):
        about_info = f"""
{SOFT_NAME}
版本:v{SOFT_VERSION}
开发语言:Python + Tkinter
数据库:SQLite3
功能:代码编辑(运行)/AI对话/资源更新/AI辅助/用户反馈

本软件为免费开源工具,仅供学习使用!
更新时间:2026-05-02
        """
        messagebox.showinfo("关于软件", about_info.strip())

    def on_closing(self):
        if self.check_unsaved():
            self.save_user_config()
            self.root.destroy()
            sys.exit()

# -------------------------- 程序入口 --------------------------
if __name__ == "__main__":
    init_database()
    login_root = tk.Tk()
    LoginRegisterWindow(login_root)
    login_root.mainloop()

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐