1. 为什么选择Python开发记事本?
用Python开发一个基础记事本应用,可能是大多数初学者接触GUI编程的第一个实战项目。作为一个从2008年就开始使用Python的老鸟,我依然记得当年用Tkinter写出第一个能保存文件的简陋编辑器时的那种兴奋感。时至今日,Python生态已经涌现出PyQt、wxPython等更强大的GUI框架,但Tkinter因其内置标准库的天然优势,仍然是快速开发小型桌面应用的首选。
从技术角度看,一个基础记事本需要实现的核心功能其实非常明确:文本编辑区域、文件操作(新建/打开/保存)、简单的格式设置。这些需求与Tkinter的Text组件、filedialog模块以及Menu组件的功能完美匹配。实测在MacBook Pro M1上,从零开始到完整可用的记事本应用,代码量不超过150行,开发时间完全可以控制在30分钟内——前提是你已经掌握了几个关键组件的用法。
提示:虽然PyQt等第三方库功能更强大,但考虑到依赖管理和打包体积,Tkinter仍然是轻量级应用的最佳选择。它的另一个优势是跨平台表现一致,不需要处理不同系统下的UI适配问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备与基础框架搭建
2.1 确保Python环境就绪
首先确认你的Python版本——建议使用3.8及以上版本。在终端运行:
bash复制python --version
如果没有安装,直接从官网下载安装包。Windows用户记得勾选"Add Python to PATH"选项,这样才能在命令行直接调用python命令。
2.2 初始化项目结构
创建一个空文件夹作为项目根目录,建议命名为py_notepad。里面只需要两个文件:
code复制py_notepad/
├── main.py # 主程序入口
└── requirements.txt # 依赖声明文件
在requirements.txt中写入:
code复制# 本项目实际上不需要额外依赖
# 这个文件是为后续可能的扩展保留
2.3 构建基础窗口框架
在main.py中导入Tkinter并创建主窗口:
python复制import tkinter as tk
from tkinter import filedialog, messagebox
class NotepadApp:
def __init__(self, root):
self.root = root
self.root.title("Python记事本")
self.root.geometry("800x600")
# 初始化文本编辑区域
self.text_area = tk.Text(self.root, wrap="word", undo=True)
self.text_area.pack(expand=True, fill="both")
# 初始化菜单栏
self.init_menu()
# 当前打开的文件路径
self.current_file = None
if __name__ == "__main__":
root = tk.Tk()
app = NotepadApp(root)
root.mainloop()
这段代码已经创建了一个带文本区域的基础窗口。关键点说明:
wrap="word"确保文本按单词边界换行undo=True启用撤销功能geometry("800x600")设置初始窗口大小
3. 实现核心文件操作功能
3.1 文件菜单功能实现
在init_menu方法中添加文件操作菜单:
python复制def init_menu(self):
menubar = tk.Menu(self.root)
# 文件菜单
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_separator()
file_menu.add_command(label="退出", command=self.exit_app)
menubar.add_cascade(label="文件", menu=file_menu)
self.root.config(menu=menubar)
# 绑定快捷键
self.root.bind_all("<Control-n>", lambda event: self.new_file())
self.root.bind_all("<Control-o>", lambda event: self.open_file())
self.root.bind_all("<Control-s>", lambda event: self.save_file())
3.2 文件操作具体实现
添加以下方法来实现具体的文件操作:
python复制def new_file(self, *args):
self.text_area.delete(1.0, tk.END)
self.current_file = None
self.root.title("Python记事本 - 未命名")
def open_file(self, *args):
file_path = filedialog.askopenfilename(
filetypes=[("文本文档", "*.txt"), ("所有文件", "*.*")]
)
if not file_path:
return
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
self.text_area.delete(1.0, tk.END)
self.text_area.insert(1.0, content)
self.current_file = file_path
self.root.title(f"Python记事本 - {file_path}")
except Exception as e:
messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")
def save_file(self, *args):
if not self.current_file:
self.save_as_file()
return
try:
content = self.text_area.get(1.0, tk.END)
with open(self.current_file, "w", encoding="utf-8") as f:
f.write(content)
except Exception as e:
messagebox.showerror("错误", f"保存文件失败:\n{str(e)}")
def save_as_file(self):
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("文本文档", "*.txt"), ("所有文件", "*.*")]
)
if not file_path:
return
self.current_file = file_path
self.save_file()
self.root.title(f"Python记事本 - {file_path}")
def exit_app(self):
if messagebox.askokcancel("退出", "确定要退出吗?"):
self.root.destroy()
注意:文件操作中一定要指定encoding="utf-8",这是实际开发中最容易忽略但会导致最多问题的细节。特别是在Windows系统上,默认编码可能是gbk,会导致中文内容乱码。
4. 添加编辑与格式功能
4.1 编辑菜单实现
扩展init_menu方法,添加编辑菜单:
python复制def init_menu(self):
# ...文件菜单代码...
# 编辑菜单
edit_menu = tk.Menu(menubar, tearoff=0)
edit_menu.add_command(label="撤销", command=self.text_area.edit_undo, accelerator="Ctrl+Z")
edit_menu.add_command(label="重做", command=self.text_area.edit_redo, accelerator="Ctrl+Y")
edit_menu.add_separator()
edit_menu.add_command(label="剪切", command=self.cut_text, accelerator="Ctrl+X")
edit_menu.add_command(label="复制", command=self.copy_text, accelerator="Ctrl+C")
edit_menu.add_command(label="粘贴", command=self.paste_text, accelerator="Ctrl+V")
edit_menu.add_separator()
edit_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
menubar.add_cascade(label="编辑", menu=edit_menu)
# 绑定快捷键
self.root.bind_all("<Control-z>", lambda event: self.text_area.edit_undo())
self.root.bind_all("<Control-y>", lambda event: self.text_area.edit_redo())
self.root.bind_all("<Control-x>", lambda event: self.cut_text())
self.root.bind_all("<Control-c>", lambda event: self.copy_text())
self.root.bind_all("<Control-v>", lambda event: self.paste_text())
self.root.bind_all("<Control-a>", lambda event: self.select_all())
4.2 编辑操作具体实现
添加以下编辑操作方法:
python复制def cut_text(self):
self.text_area.event_generate("<<Cut>>")
def copy_text(self):
self.text_area.event_generate("<<Copy>>")
def paste_text(self):
self.text_area.event_generate("<<Paste>>")
def select_all(self):
self.text_area.tag_add("sel", "1.0", "end")
return "break" # 阻止默认绑定
4.3 添加简单的格式设置
虽然基础记事本不需要复杂的格式功能,但我们可以添加一些基本设置:
python复制def init_menu(self):
# ...文件、编辑菜单代码...
# 格式菜单
format_menu = tk.Menu(menubar, tearoff=0)
format_menu.add_command(label="字体", command=self.change_font)
menubar.add_cascade(label="格式", menu=format_menu)
def change_font(self):
from tkinter import font
# 创建字体选择对话框
font_window = tk.Toplevel(self.root)
font_window.title("选择字体")
# 获取系统可用字体
families = list(font.families())
families.sort()
# 字体选择列表
font_list = tk.Listbox(font_window, width=30, height=15)
for f in families:
font_list.insert(tk.END, f)
font_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 应用按钮
apply_btn = tk.Button(
font_window,
text="应用",
command=lambda: self.apply_font(font_list.get(tk.ACTIVE))
)
apply_btn.pack(side=tk.BOTTOM, fill=tk.X)
def apply_font(self, font_family):
current_tags = self.text_area.tag_names()
if "custom_font" in current_tags:
self.text_area.tag_configure("custom_font", font=(font_family, 12))
else:
self.text_area.tag_add("custom_font", "1.0", "end")
self.text_area.tag_configure("custom_font", font=(font_family, 12))
5. 完善与优化
5.1 添加状态栏
在__init__方法中添加状态栏:
python复制def __init__(self, root):
# ...原有代码...
# 添加状态栏
self.status_var = tk.StringVar()
self.status_var.set("就绪")
status_bar = tk.Label(
self.root,
textvariable=self.status_var,
bd=1,
relief=tk.SUNKEN,
anchor=tk.W
)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# 绑定文本修改事件
self.text_area.bind("<<Modified>>", self.on_text_modified)
def on_text_modified(self, event):
if self.text_area.edit_modified():
self.status_var.set("已修改")
self.text_area.edit_modified(False)
5.2 退出时确认保存
修改exit_app方法:
python复制def exit_app(self):
if self.text_area.edit_modified():
response = messagebox.askyesnocancel(
"退出",
"文档已修改,是否保存?"
)
if response is None: # 取消
return
if response: # 是
self.save_file()
self.root.destroy()
5.3 添加右键上下文菜单
在__init__中添加:
python复制def __init__(self, root):
# ...原有代码...
# 右键菜单
self.context_menu = tk.Menu(self.root, tearoff=0)
self.context_menu.add_command(label="剪切", command=self.cut_text)
self.context_menu.add_command(label="复制", command=self.copy_text)
self.context_menu.add_command(label="粘贴", command=self.paste_text)
self.text_area.bind("<Button-3>", self.show_context_menu)
def show_context_menu(self, event):
try:
self.context_menu.tk_popup(event.x_root, event.y_root)
finally:
self.context_menu.grab_release()
6. 打包为可执行文件
虽然这不是记事本功能的一部分,但将Python脚本打包成exe可以让应用更方便地分享和使用。推荐使用PyInstaller:
- 首先安装PyInstaller:
bash复制pip install pyinstaller
- 在项目目录下运行:
bash复制pyinstaller --onefile --windowed main.py
- 生成的exe文件会在dist目录下。可以添加图标:
bash复制pyinstaller --onefile --windowed --icon=notepad.ico main.py
实际开发中发现的一个坑:PyInstaller打包时可能会遗漏一些资源文件。如果遇到问题,可以使用
--add-data选项显式包含所需文件。
7. 扩展思路与进阶功能
虽然我们已经实现了一个基础记事本,但还有很多可以扩展的方向:
- 多标签支持:使用ttk.Notebook组件实现多文档界面
- 语法高亮:通过tag配置实现简单的语法高亮
- 搜索替换:添加查找/替换对话框
- 主题切换:实现暗黑/明亮模式切换
- 自动保存:定时自动保存功能
- 版本历史:记录文件修改历史
以搜索功能为例,可以这样实现:
python复制def init_menu(self):
# ...原有菜单代码...
# 添加搜索菜单
edit_menu.add_separator()
edit_menu.add_command(label="查找", command=self.show_find_dialog, accelerator="Ctrl+F")
def show_find_dialog(self):
find_window = tk.Toplevel(self.root)
find_window.title("查找")
tk.Label(find_window, text="查找内容:").pack()
find_entry = tk.Entry(find_window)
find_entry.pack(fill=tk.X, padx=5, pady=5)
def do_find():
text = self.text_area.get(1.0, tk.END)
pattern = find_entry.get()
if pattern in text:
start_pos = self.text_area.search(pattern, 1.0, stopindex=tk.END)
if start_pos:
end_pos = f"{start_pos}+{len(pattern)}c"
self.text_area.tag_remove("found", 1.0, tk.END)
self.text_area.tag_add("found", start_pos, end_pos)
self.text_area.tag_config("found", background="yellow")
self.text_area.see(start_pos)
find_btn = tk.Button(find_window, text="查找", command=do_find)
find_btn.pack(side=tk.BOTTOM, fill=tk.X)
这个记事本项目虽然简单,但涵盖了GUI开发的核心概念:组件布局、事件处理、文件操作等。我在实际教学中发现,通过这样一个完整的小项目,初学者能够快速建立起对Python GUI编程的直观认识,为后续更复杂的应用开发打下基础。
