阶段一实战项目:仿照记事本界面开发简易文本编辑器
哈喽~ 欢迎来到PyQt5系列的第5篇——阶段一实战项目!经过前4篇的学习,我们已经掌握了QWidget基础窗口、线性布局(QVBoxLayout/QHBoxLayout)、核心基础控件(标签、按钮、输入框、复选框等)以及信号与槽的基础用法。今天我们将把这些知识点整合起来,仿照Windows记事本的核心界面与基础功能,开发一个简易文本编辑器,实现“新建、打开、保存文本”“文本编辑”“字体加粗”等核心功能,让你快速掌握知识点的综合应用!
一、项目需求分析:仿照记事本核心功能
我们聚焦Windows记事本的核心功能,本次项目实现以下需求:
- 界面需求:仿照记事本布局,包含“功能按钮区”(新建、打开、保存、字体加粗)和“文本编辑区”(多行文本输入/显示);
- 核心功能:新建空白文本、打开本地文本文件、保存文本到本地、文本加粗编辑;
- 交互需求:按钮点击反馈、打开/保存文件弹窗提示、文本编辑实时响应;
- 适配需求:窗口缩放时,文本编辑区自适应调整大小。
【界面参考】Windows记事本核心布局:顶部功能按钮区 + 中间大面积文本编辑区,我们简化实现核心按钮,保证界面简洁且功能完整。
二、技术选型:贴合阶段一知识点
本次项目严格基于阶段一所学知识点,不引入新的复杂组件,技术栈如下:
- 窗口组件:QWidget(主窗口);
- 布局管理器:QVBoxLayout(垂直布局,管理按钮区和编辑区)、QHBoxLayout(水平布局,排列功能按钮);
- 核心控件:QPushButton(功能按钮)、QTextEdit(多行文本编辑区)、QCheckBox(字体加粗选择框);
- 交互核心:信号与槽(按钮点击、复选框状态变化绑定对应功能);
- 文件操作:基础文件读写(结合Python内置open函数)。
三、界面设计与实现步骤
我们采用“先搭框架,再填功能”的思路,分3步实现:
- 搭建主窗口与布局(垂直布局+水平布局组合);
- 添加控件(功能按钮、复选框、文本编辑区)并绑定布局;
- 实现控件信号与槽绑定,编写功能逻辑。
四、完整代码实现(可直接运行)

import sys
import os
from PyQt5.QtWidgets import (
QApplication, QWidget, QPushButton, QTextEdit,
QCheckBox, QVBoxLayout, QHBoxLayout, QFileDialog
)
from PyQt5.QtGui import QFont
from PyQt5.QtCore import Qt
class SimpleNotepad(QWidget):
def __init__(self):
super().__init__()
# 初始化窗口基础属性
self.init_window()
# 初始化控件与布局结构
self.init_widgets_layout()
# 绑定信号与槽函数
self.init_signals_slots()
# 记录当前打开的文件路径,初始为None表示新文件
self.current_file_path = None
def init_window(self):
"""初始化窗口的标题、大小和位置"""
self.setWindowTitle("简易文本编辑器(仿照记事本)")
# 设置窗口初始尺寸
self.resize(800, 600)
# 计算并设置窗口居中显示
screen_geometry = QApplication.desktop().availableGeometry()
x = (screen_geometry.width() - self.width()) // 2
y = (screen_geometry.height() - self.height()) // 2
self.move(x, y)
def init_widgets_layout(self):
"""创建所有界面控件并设置布局"""
# 创建主垂直布局,设置控件间距和窗口内边距
self.main_layout = QVBoxLayout()
self.main_layout.setSpacing(10)
self.main_layout.setContentsMargins(15, 15, 15, 15)
# 创建按钮区水平布局
self.button_layout = QHBoxLayout()
self.button_layout.setSpacing(10)
# 创建功能按钮并设置固定尺寸
self.new_btn = QPushButton("新建")
self.open_btn = QPushButton("打开")
self.save_btn = QPushButton("保存")
btn_size = (80, 30)
self.new_btn.setFixedSize(*btn_size)
self.open_btn.setFixedSize(*btn_size)
self.save_btn.setFixedSize(*btn_size)
# 创建字体加粗复选框,设置文本居中显示
self.bold_check = QCheckBox("字体加粗")
self.bold_check.setStyleSheet("text-align: center;")
# 将按钮添加到水平布局
self.button_layout.addWidget(self.new_btn)
self.button_layout.addWidget(self.open_btn)
self.button_layout.addWidget(self.save_btn)
# 添加伸缩项,将复选框推至布局右侧
self.button_layout.addStretch()
self.button_layout.addWidget(self.bold_check)
# 创建文本编辑区域,设置默认字体和占位提示文本
self.text_edit = QTextEdit()
self.text_edit.setFont(QFont("微软雅黑", 12))
self.text_edit.setPlaceholderText("请输入文本内容...(支持新建、打开、保存文件)")
# 将按钮布局和文本编辑区添加到主布局
self.main_layout.addLayout(self.button_layout)
self.main_layout.addWidget(self.text_edit)
# 设置窗口的主布局
self.setLayout(self.main_layout)
def init_signals_slots(self):
"""绑定控件的信号与对应的槽函数"""
self.new_btn.clicked.connect(self.on_new_click)
self.open_btn.clicked.connect(self.on_open_click)
self.save_btn.clicked.connect(self.on_save_click)
self.bold_check.stateChanged.connect(self.on_bold_check_change)
def on_new_click(self):
"""新建文件:清空编辑区,重置文件路径"""
# 若有未保存内容,先提示保存
if self.text_edit.toPlainText() and not self.current_file_path:
reply = QFileDialog.getSaveFileName(self, "保存当前内容", "", "Text Files (*.txt)")
if reply[0]:
self.save_text_to_file(reply[0])
# 清空编辑区内容
self.text_edit.clear()
# 重置当前文件路径
self.current_file_path = None
# 更新窗口标题
self.setWindowTitle("简易文本编辑器(仿照记事本)- 未保存文件")
def on_open_click(self):
"""打开文件:选择txt文件并读取内容到编辑区"""
# 弹出文件选择对话框,筛选文本文件
file_path, _ = QFileDialog.getOpenFileName(
self,
"打开文本文件",
"",
"Text Files (*.txt);;All Files (*.*)"
)
# 验证文件路径有效性并读取内容
if file_path and os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
self.text_edit.setText(content)
self.current_file_path = file_path
# 更新窗口标题显示当前文件名
self.setWindowTitle(f"简易文本编辑器(仿照记事本)- {os.path.basename(file_path)}")
def on_save_click(self):
"""保存文件:已有路径则直接保存,无路径则弹出保存对话框"""
if self.current_file_path:
# 直接保存到当前路径
self.save_text_to_file(self.current_file_path)
else:
# 弹出保存对话框选择路径
file_path, _ = QFileDialog.getSaveFileName(
self,
"保存文本文件",
"",
"Text Files (*.txt)"
)
if file_path:
# 自动补充txt后缀
if not file_path.endswith(".txt"):
file_path += ".txt"
self.save_text_to_file(file_path)
self.current_file_path = file_path
self.setWindowTitle(f"简易文本编辑器(仿照记事本)- {os.path.basename(file_path)}")
def save_text_to_file(self, file_path):
"""将编辑区内容写入指定路径的文件"""
content = self.text_edit.toPlainText()
with open(file_path, "w", encoding="utf-8") as f:
f.write(content)
def on_bold_check_change(self, state):
"""根据复选框状态切换编辑区文本的加粗样式"""
current_font = self.text_edit.font()
# Qt.Checked对应值为2,Qt.Unchecked对应值为0
current_font.setBold(state == Qt.Checked)
self.text_edit.setFont(current_font)
if __name__ == "__main__":
# 创建应用程序实例
app = QApplication(sys.argv)
# 创建记事本窗口实例
notepad = SimpleNotepad()
# 显示窗口
notepad.show()
# 启动应用程序主循环
sys.exit(app.exec_())五、代码逐行解析(核心部分)
1. 类结构与初始化流程
我们将所有功能封装到SimpleNotepad类(继承QWidget),初始化流程分3步:
init_window():设置窗口标题、大小、居中显示,提升用户体验;init_widgets_layout():核心布局搭建,用“垂直布局+水平布局”组合实现记事本风格,按钮区在上、编辑区在下,保证窗口缩放时编辑区自适应;init_signals_slots():绑定所有控件的信号与槽,实现“点击按钮触发功能”“复选框变化触发字体调整”。
2. 布局核心逻辑
采用“嵌套布局”思路,解决控件排列问题:
- 主布局(QVBoxLayout):垂直方向排列“按钮布局”和“文本编辑区”,
addStretch()未使用,让编辑区占满剩余空间; - 按钮布局(QHBoxLayout):水平方向排列3个功能按钮,添加
addStretch()伸缩空间将“字体加粗”复选框推到右侧,让布局更美观。
3. 核心功能实现
(1)新建文件(on_new_click)
逻辑:先判断当前是否有未保存的内容,如果有则弹出保存对话框;清空编辑区,重置current_file_path(标记为新文件),更新窗口标题。
(2)打开文件(on_open_click)
逻辑:用QFileDialog.getOpenFileName()弹出文件选择框,筛选txt文件;读取选中文件的内容并显示到QTextEdit,更新current_file_path和窗口标题(显示文件名)。
(3)保存文件(on_save_click)
逻辑:如果已打开文件(current_file_path不为None),直接保存;否则弹出保存对话框,让用户选择路径,补充.txt后缀,保存内容后更新路径和标题。
(4)字体加粗(on_bold_check_change)
逻辑:监听复选框状态变化,用QFont.setBold()切换字体加粗状态,直接作用于QTextEdit的当前字体。
六、运行效果与测试步骤
1. 运行方式
将代码保存为simple_notepad.py,确保已安装PyQt5,终端运行命令:
python simple_notepad.py # Windows
python3 simple_notepad.py # macOS/Linux2. 测试步骤
- 测试新建:点击“新建”,编辑区清空,标题显示“未保存文件”;
- 测试打开:点击“打开”,选择本地txt文件,内容正常显示,标题显示文件名;
- 测试保存:编辑内容后点击“保存”,如果是新文件则弹出保存对话框,保存后可在对应路径找到txt文件;
- 测试字体加粗:勾选“字体加粗”,编辑区文本变为加粗;取消勾选则恢复正常。
七、常见问题排查
- 问题1:打开文件后中文乱码 → 解决:读取文件时指定
encoding="utf-8",保存时也用utf-8编码; - 问题2:窗口缩放时编辑区不自适应 → 解决:确保主布局是QVBoxLayout,且
QTextEdit直接添加到主布局(未设置固定大小); - 问题3:保存文件后没有.txt后缀 → 解决:代码中已添加判断,自动补充.txt后缀,无需手动输入;
- 问题4:按钮点击无反应 → 解决:检查信号与槽绑定是否正确(如
self.new_btn.clicked.connect(self.on_new_click)是否写错函数名)。
八、功能拓展思路(阶段一知识点范围内)
如果想进一步练习,可以基于当前代码拓展以下功能:
- 添加“撤销/重做”按钮:利用
QTextEdit.undo()和QTextEdit.redo()实现; - 添加“清空”按钮:绑定
self.text_edit.clear(); - 添加“字体大小调整”:用QComboBox下拉选择字体大小,绑定信号修改
QFont的setPointSize(); - 添加“换行/不换行”复选框:用
QTextEdit.setLineWrapMode()控制换行模式。
总结
本次项目完美整合了阶段一的核心知识点:窗口设置、线性布局、基础控件使用、信号与槽绑定。通过仿照记事本界面开发,你应该能深刻理解“布局管理器解决控件排列”“信号与槽实现交互”的核心逻辑。
下一篇我们将进入阶段二,学习网格布局、表单布局等进阶布局管理器,为更复杂的界面开发打基础。如果在项目实操中遇到问题,或者有拓展功能的想法,欢迎在评论区留言讨论~