1. 项目概述:用Python快速构建桌面记事本应用
在Python生态中开发GUI应用一直是个有趣且实用的技能点。今天我要分享的是如何用PyQt6在半小时内打造一个功能完整的桌面记事本应用。这个项目特别适合刚学完Python基础想接触GUI开发的朋友,也适合需要快速原型开发的程序员。
我选择PyQt6作为工具包有几个原因:首先它完全免费,不像某些商业框架需要授权;其次它基于成熟的Qt框架,提供了丰富的UI组件;最重要的是它的API设计非常Pythonic,配合Python的简洁语法能让开发效率大幅提升。这个记事本将包含核心的文本编辑、文件保存/打开功能,后续还可以轻松扩展搜索、格式设置等进阶特性。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备
2.1 安装Python与PyQt6
建议使用Python 3.8+版本以获得最佳兼容性。通过以下命令安装PyQt6:
bash复制pip install PyQt6
注意:如果遇到安装速度慢的问题,可以使用国内镜像源:
bash复制pip install PyQt6 -i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 IDE选择与配置
推荐使用VS Code或PyCharm作为开发环境。VS Code需要安装Python扩展,配置要点包括:
- 设置正确的Python解释器路径
- 启用Pylance语言服务器
- 安装Qt for Python扩展辅助代码提示
PyCharm专业版自带Qt Designer集成,社区版可以通过插件实现类似功能。
3. 核心界面构建
3.1 主窗口框架搭建
我们继承QMainWindow来创建主窗口,这是Qt的标准做法:
python复制from PyQt6.QtWidgets import QMainWindow, QApplication
class Notepad(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('我的记事本')
self.setGeometry(100, 100, 800, 600)
if __name__ == '__main__':
app = QApplication([])
notepad = Notepad()
notepad.show()
app.exec()
这段代码创建了一个800x600像素的窗口,是记事本的基础框架。QMainWindow提供了菜单栏、状态栏等标准组件的支持。
3.2 添加文本编辑区域
使用QTextEdit作为核心编辑组件:
python复制from PyQt6.QtWidgets import QTextEdit
def initUI(self):
# ...前面代码不变...
self.text_edit = QTextEdit()
self.setCentralWidget(self.text_edit)
QTextEdit支持富文本编辑、撤销重做等高级功能,比QPlainTextEdit更适合作为记事本的核心组件。
4. 实现文件操作功能
4.1 添加菜单栏
标准的记事本需要"文件"和"编辑"菜单:
python复制from PyQt6.QtWidgets import QMenuBar, QMenu
def initUI(self):
menubar = self.menuBar()
# 文件菜单
file_menu = menubar.addMenu('文件')
file_menu.addAction('新建')
file_menu.addAction('打开...')
file_menu.addAction('保存')
file_menu.addSeparator()
file_menu.addAction('退出')
# 编辑菜单
edit_menu = menubar.addMenu('编辑')
edit_menu.addAction('撤销')
edit_menu.addAction('重做')
edit_menu.addSeparator()
edit_menu.addAction('复制')
edit_menu.addAction('粘贴')
edit_menu.addAction('剪切')
4.2 实现文件对话框
使用QFileDialog处理文件选择:
python复制from PyQt6.QtWidgets import QFileDialog
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(
self, "打开文件", "", "文本文件 (*.txt);;所有文件 (*)"
)
if filename:
with open(filename, 'r', encoding='utf-8') as f:
self.text_edit.setText(f.read())
def save_file(self):
filename, _ = QFileDialog.getSaveFileName(
self, "保存文件", "", "文本文件 (*.txt);;所有文件 (*)"
)
if filename:
with open(filename, 'w', encoding='utf-8') as f:
f.write(self.text_edit.toPlainText())
提示:Windows平台下QFileDialog有时会出现卡顿,这是Qt的已知问题。解决方法是在创建QApplication时添加:
python复制import ctypes ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('myappid')
5. 功能完善与优化
5.1 添加动作连接
将菜单项连接到实际功能:
python复制def initUI(self):
# ...前面代码不变...
# 获取动作对象
open_action = file_menu.actions()[1] # 打开动作
save_action = file_menu.actions()[2] # 保存动作
# 连接信号与槽
open_action.triggered.connect(self.open_file)
save_action.triggered.connect(self.save_file)
5.2 添加状态栏反馈
在状态栏显示操作信息:
python复制def initUI(self):
self.statusBar().showMessage('就绪')
def open_file(self):
# ...打开文件代码...
self.statusBar().showMessage(f'已打开: {filename}')
def save_file(self):
# ...保存文件代码...
self.statusBar().showMessage(f'已保存: {filename}')
6. 常见问题与解决方案
6.1 中文编码问题
处理不同编码格式的文件时可能出现乱码,改进版的文件打开方法:
python复制def open_file(self):
filename, _ = QFileDialog.getOpenFileName(
self, "打开文件", "", "文本文件 (*.txt);;所有文件 (*)"
)
if filename:
try:
with open(filename, 'r', encoding='utf-8') as f:
self.text_edit.setText(f.read())
except UnicodeDecodeError:
try:
with open(filename, 'r', encoding='gbk') as f:
self.text_edit.setText(f.read())
except Exception as e:
self.statusBar().showMessage(f'打开失败: {str(e)}')
6.2 未保存提示
关闭时检查是否有未保存的修改:
python复制from PyQt6.QtWidgets import QMessageBox
def closeEvent(self, event):
if self.text_edit.document().isModified():
reply = QMessageBox.question(
self, '提示',
"文档已修改,是否保存?",
QMessageBox.StandardButton.Save |
QMessageBox.StandardButton.Discard |
QMessageBox.StandardButton.Cancel
)
if reply == QMessageBox.StandardButton.Save:
self.save_file()
event.accept()
elif reply == QMessageBox.StandardButton.Discard:
event.accept()
else:
event.ignore()
7. 进阶功能扩展思路
7.1 添加查找替换功能
实现基本的文本搜索:
python复制from PyQt6.QtWidgets import QInputDialog
def find_text(self):
text, ok = QInputDialog.getText(
self, '查找', '输入查找内容:'
)
if ok and text:
if not self.text_edit.find(text):
self.statusBar().showMessage('未找到匹配内容')
7.2 支持多标签页
使用QTabWidget实现多文档界面:
python复制from PyQt6.QtWidgets import QTabWidget
class Notepad(QMainWindow):
def __init__(self):
super().__init__()
self.tabs = QTabWidget()
self.setCentralWidget(self.tabs)
self.add_new_tab()
def add_new_tab(self):
text_edit = QTextEdit()
self.tabs.addTab(text_edit, "未命名")
8. 完整代码示例
以下是整合所有功能的完整实现:
python复制import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QTextEdit,
QFileDialog, QMenuBar, QMenu, QMessageBox,
QInputDialog)
from PyQt6.QtGui import QAction
class Notepad(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('我的记事本')
self.setGeometry(100, 100, 800, 600)
# 文本编辑区
self.text_edit = QTextEdit()
self.setCentralWidget(self.text_edit)
# 状态栏
self.statusbar = self.statusBar()
self.statusbar.showMessage('就绪')
# 菜单栏
self.create_menus()
def create_menus(self):
menubar = self.menuBar()
# 文件菜单
file_menu = menubar.addMenu('文件')
new_action = QAction('新建', self)
new_action.triggered.connect(self.new_file)
open_action = QAction('打开...', self)
open_action.triggered.connect(self.open_file)
save_action = QAction('保存', self)
save_action.triggered.connect(self.save_file)
exit_action = QAction('退出', self)
exit_action.triggered.connect(self.close)
file_menu.addAction(new_action)
file_menu.addAction(open_action)
file_menu.addAction(save_action)
file_menu.addSeparator()
file_menu.addAction(exit_action)
# 编辑菜单
edit_menu = menubar.addMenu('编辑')
undo_action = QAction('撤销', self)
undo_action.triggered.connect(self.text_edit.undo)
redo_action = QAction('重做', self)
redo_action.triggered.connect(self.text_edit.redo)
copy_action = QAction('复制', self)
copy_action.triggered.connect(self.text_edit.copy)
paste_action = QAction('粘贴', self)
paste_action.triggered.connect(self.text_edit.paste)
cut_action = QAction('剪切', self)
cut_action.triggered.connect(self.text_edit.cut)
find_action = QAction('查找', self)
find_action.triggered.connect(self.find_text)
edit_menu.addAction(undo_action)
edit_menu.addAction(redo_action)
edit_menu.addSeparator()
edit_menu.addAction(copy_action)
edit_menu.addAction(paste_action)
edit_menu.addAction(cut_action)
edit_menu.addSeparator()
edit_menu.addAction(find_action)
def new_file(self):
self.text_edit.clear()
self.statusbar.showMessage('新建文档')
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(
self, "打开文件", "", "文本文件 (*.txt);;所有文件 (*)"
)
if filename:
try:
with open(filename, 'r', encoding='utf-8') as f:
self.text_edit.setText(f.read())
self.statusbar.showMessage(f'已打开: {filename}')
except UnicodeDecodeError:
try:
with open(filename, 'r', encoding='gbk') as f:
self.text_edit.setText(f.read())
self.statusbar.showMessage(f'已打开: {filename}')
except Exception as e:
self.statusbar.showMessage(f'打开失败: {str(e)}')
def save_file(self):
filename, _ = QFileDialog.getSaveFileName(
self, "保存文件", "", "文本文件 (*.txt);;所有文件 (*)"
)
if filename:
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write(self.text_edit.toPlainText())
self.statusbar.showMessage(f'已保存: {filename}')
except Exception as e:
self.statusbar.showMessage(f'保存失败: {str(e)}')
def find_text(self):
text, ok = QInputDialog.getText(
self, '查找', '输入查找内容:'
)
if ok and text:
if not self.text_edit.find(text):
self.statusbar.showMessage('未找到匹配内容')
def closeEvent(self, event):
if self.text_edit.document().isModified():
reply = QMessageBox.question(
self, '提示',
"文档已修改,是否保存?",
QMessageBox.StandardButton.Save |
QMessageBox.StandardButton.Discard |
QMessageBox.StandardButton.Cancel
)
if reply == QMessageBox.StandardButton.Save:
self.save_file()
event.accept()
elif reply == QMessageBox.StandardButton.Discard:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
notepad = Notepad()
notepad.show()
sys.exit(app.exec())
9. 打包与分发
使用PyInstaller将应用打包为可执行文件:
bash复制pip install pyinstaller
pyinstaller --onefile --windowed notepad.py
打包时常见问题处理:
- 图标不显示:添加
--icon=app.ico参数 - 控制台窗口闪现:确保使用
--windowed参数 - 文件大小过大:使用UPX压缩工具
10. 性能优化建议
- 对于大文件处理,改用QPlainTextEdit性能更好
- 实现延迟加载机制处理超大文件
- 添加最近打开文件列表功能
- 实现自动保存功能
- 添加语法高亮支持
这个记事本虽然简单,但涵盖了GUI开发的核心概念:窗口管理、组件布局、事件处理和文件操作。通过这个项目,你可以掌握PyQt6的基础用法,为开发更复杂的应用打下坚实基础。
