浔川代码编辑器 v4.1.0 正式版重磅上线!AI 加持,轻量高效,开箱即用
·
浔川代码编辑器 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 核心优势
- 免安装单文件运行:双击主 Python 文件启动,低配电脑也流畅,无冗余占用
- 纯本地隐私安全:账号、配置、反馈全存在本地,不联网上传,数据自主掌控
- AI + 在线运行双加持:不用装环境、不用搜网址,写码→运行→问 AI 一站式完成
- 新手友好全功能:语法高亮、自动提示、配置记忆、协议规范,零基础也能快速上手
- 轻量化不臃肿:无多余插件、无广告干扰,专注编码,打开即用、用完即关
📢 四、即刻升级,开启高效编码
浔川代码编辑器 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()
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)