
简介QT记事本项目是一份基于QT6的跨平台记事本应用源码包资源来源于B站嵌入式教学视频适合正在学习Qt桌面开发或嵌入式GUI的开发者。项目围绕主窗口组件、文本编辑控件和文件读写模块展开演示了界面搭建、用户交互、多编码识别与文本存取等完整实现。资源共516个文件压缩包仅4.29MB主体为cpp/h源代码、ui界面定义、qrc资源配置与pro工程文件同时包含png图标、编译生成的exe/dll/静态库以及大量Qt Creator索引文件各文件类型对应设计、构建、部署不同环节便于对照工程整体结构。项目通过uchardet库增强了对GBK、UTF-8等多种编码的兼容体现嵌入式资源受限环境下的轻量优化思路。目前已有776人学习下载。研读整个工程可系统学习Qt6的信号槽、QMainWindow布局、自定义文本编辑控件、文件编码转换以及模块化构建与资源打包流程是一份适合课程设计、毕业设计或入门实战的完整参考。 很多半路转 Qt 开发的朋友尤其是从嵌入式转上位机的第一个正经项目大概率都是记事本。原因很直白它麻雀虽小却几乎涵盖了桌面应用开发的所有核心环节——界面布局、菜单系统、文件读写、文本查找、快捷键、状态栏联动最后还要过一遍打包发布。网上关于这个项目的视频教程不少我自己也带着刚入职的新人完整跑通过一遍过程中踩过的坑比期望值多得多。这次把整个开发思路、关键代码和排错链路一次性写清楚给准备动手或者已经卡在某一步的人做个参考。1. 需求拆解记事本怎么定义够用1.1 为什么记事本是最合适的 Qt 入门项目很多人一开始想做个复杂点的项目比如音乐播放器、画图工具结果界面还没画完就被信号槽和自定义控件劝退了。记事本这类项目难度阶梯刚好不需要自定义绘制、不涉及多线程、不需要数据库核心就是QMainWindow加一个文本编辑组件再用QAction把功能串起来。但恰恰是这种简单能把 Qt 最基础的框架吃透。另一个原因是它的扩展性极好。把文件读写做完你就可以往里面加最近文件列表、编码检测、字数统计把查找替换做完就可以进一步加正则匹配、高亮全部结果。做完基础版本再迭代比一上来就啃大型项目要踏实得多。1.2 功能边界与实际清单在动手写代码之前我习惯先把功能清单列出来明确哪些做、哪些不做。记事本不再是能打字就行我给它的定位是日常写代码片段、临时记事的轻量文本工具。基础功能建议做成这样功能模块具体功能备注文件新建、打开、保存、另存为、退出打开和保存是核心编辑撤销、重做、剪切、复制、粘贴、全选用 QAction 实现查找查找、替换非模态对话框格式自动换行切换、字体设置体现界面交互帮助关于对话框测试 QMessageBox不用做的功能多标签页、代码高亮、Markdown 预览、自动保存。这些属于进阶玩法上来就堆功能难度会成倍增加而且新手很难分清到底是哪一步出了问题。2. 开发环境版本选择和容易翻车的安装细节2.1 Qt 5.15.2 还是 Qt 6.x工程上用哪个版本是个现实问题。实测下来很多嵌入式上位机场景、老项目的维护环境都还停留在 Qt 5.15.2这也是目前资料最全、报错最容易搜到结果的版本。Qt 6 在模块划分和 API 上有调整比如QRegExp被移除、QTextStream的编码处理方式变化新手如果看的是 5.x 的教程编译时经常会遇到找不到头文件这类莫名其妙的问题。我的建议是如果只是入门学习安装Qt 5.15.2。它的稳定性和社区支持度目前仍然是最好的等把项目跑通了再去看 6.x 的差异事半功倍。2.2 国内镜像下载与安装配置Qt 官方安装包在线安装经常卡在下载环节这在国内是很多人都遇到过的问题。解决方法是直接用国内镜像站清华源、中科大源都可以安装器里添加一行镜像地址即可。注意选择组件时不要图省事全选。Qt 5.15.2 的安装器里只需要选择你实际使用的编译器版本对应的模块即可。我通常选MinGW 8.1.0 64-bit和Qt Charts之类按需勾选全装不仅占磁盘C 盘满天飞的那些库文件后面根本用不上。装完之后记得检查系统PATH里是否能看到 Qt 的bin目录。这个不检查后面用windeployqt打包时会提示找不到命令或者运行程序时提示缺Qt5Core.dll。2.3 Qt Creator 与 VS Code 的取舍热词里同时出现了qt creator和vscode 配置 qt designer说明很多人在这两个工具之间反复横跳。我的真实体验是初学者老老实实用Qt Creator不要折腾 VS Code。Qt Creator 对新手最友好的一点是新建项目时能直接生成完整可运行的pro工程文件调试、打断点、看变量都是开箱即用。VS Code 的 Qt 开发需要手动配置C_Cpp插件、cmake工具链、tasks.json每一环都是坑。等你在 Creator 里把 Qt 的项目结构搞明白了再考虑迁移到 VS Code 也不迟。3. 界面结构设计以 QMainWindow 为骨架3.1 中心组件为什么选 QPlainTextEdit这是项目里第一个关键选型。记事本的编辑区可以直接用QTextEdit但我更推荐QPlainTextEdit。两者功能上都能显示富文本但QPlainTextEdit针对纯文本场景做了大量性能优化处理大文件时明显更流畅。而且在滚动、刷新方面QPlainTextEdit的机制更简单不会像QTextEdit一样因为内部维护复杂文档结构而卡顿。主窗口结构很清晰QMainWindow作为顶层容器setCentralWidget()把编辑区塞进去菜单栏、工具栏、状态栏各就各位。MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , textEdit(new QPlainTextEdit(this)) { setCentralWidget(textEdit); createActions(); createMenus(); createToolBars(); createStatusBar(); setWindowTitle(记事本); setWindowIcon(QIcon(:/icon.png)); }3.2 菜单与动作从 QAction 开始Qt 的菜单体系不推荐直接往QMenu里塞字符串而是通过QAction把动作定义好再放到菜单或工具栏里。这样同一个动作可以被菜单、工具栏、快捷键共享状态变化也只需要维护一个对象。文件菜单的代码大概长这样void MainWindow::createActions() { newAct new QAction(QIcon(:/images/new.png), tr(新建), this); newAct-setShortcut(QKeySequence::New); newAct-setStatusTip(tr(创建新文档)); connect(newAct, QAction::triggered, this, MainWindow::newFile); openAct new QAction(QIcon(:/images/open.png), tr(打开), this); openAct-setShortcut(QKeySequence::Open); connect(openAct, QAction::triggered, this, MainWindow::openFile); saveAct new QAction(QIcon(:/images/save.png), tr(保存), this); saveAct-setShortcut(QKeySequence::Save); connect(saveAct, QAction::triggered, this, MainWindow::saveFile); }这里有个细节容易被忽略图标资源如果暂时没有可以用QStyle::standardIcon()拿系统自带图标不必一开始就用.qrc资源文件。很多新手卡在图标不显示上其实不是代码问题而是资源路径没配对或者压根没建.qrc文件。3.3 状态栏的实时光标位置状态栏是记事本项目里很好展示信号槽机制的地方。当光标在文本里移动时状态栏右下角要实时显示当前行号和列号。这个需求只需要两行关键代码connect(textEdit, QPlainTextEdit::cursorPositionChanged, this, MainWindow::updateStatusBar);在updateStatusBar槽函数里通过QTextCursor拿到当前光标的位置再用blockNumber()和columnNumber()计算行列坐标。这个功能虽然简单但能把 Qt 的信号触发—槽函数响应这一整套事件循环机制串起来。4. 文件读写与编码最容易翻车的地方4.1 打开文件的正确姿势很多教程里打开文件的代码是用QTextStream直接读这是因为编辑器会偷懒。实际开发中先读QByteArray再手动处理编码才是可控的做法。void MainWindow::openFile() { QString fileName QFileDialog::getOpenFileName(this, tr(打开), QString(), tr(文本文件 (*.txt);;所有文件 (*))); if (fileName.isEmpty()) return; QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, tr(错误), tr(无法打开文件)); return; } QByteArray data file.readAll(); QTextCodec *codec QTextCodec::codecForName(UTF-8); QString text codec-toUnicode(data); textEdit-setPlainText(text); currentFile fileName; }至于为什么不直接用QTextStream后面编码部分细说。4.2 乱码的本质BOM、GBK 与 UTF-8 的纠缠网上关于记事本的热搜词里记事本打开是乱码 ascii、记事本改成 unicode 频繁出现说明这项确实是用户最容易踩的坑。乱码问题的根源在于同一个文件保存时用一种编码打开时用另一种编码去解释字自然就面目全非。Windows 上老的记事本默认存 ANSIGBK 在中文系统下就是 GBK 编码而 Qt 项目里的QString内部是 UTF-16如果我们用QTextStream默认方式读取 GBK 文件中文就会变成一堆乱码符号。所以在读取时我建议先用QFile读原始字节然后尝试按 UTF-8 解码如果结果里有明显的替换符或者 QTextCodec 检测到非 UTF-8 特征就回退到 GBK 解码。QTextCodec *codec QTextCodec::codecForName(UTF-8); QString text codec-toUnicode(data); if (text.contains(QChar(0xFFFD))) { QTextCodec *gbk QTextCodec::codecForName(GBK); text gbk-toUnicode(data); }这段逻辑不完美但对记事本项目足够了也解决了大部分打开别人的文件全是乱码的尴尬。4.3 保存时的编码策略保存时反过来要决定写进文件的字节是什么编码。最简单的方式是保存为 UTF-8这在跨平台场景下兼容性最好。但对 Windows 老用户来说UTF-8 不带 BOM 时用系统自带记事本打开可能识别不出来所以我通常会加一个另存为编码选择的选项。够用的实现是这样默认按 UTF-8 保存另存为对话框里加一个编码下拉框用户选 ASCII、UTF-16 或 GBK。核心代码就是QTextCodec *codec QTextCodec::codecForName(UTF-8); if (encodingCombo-currentText() GBK) { codec QTextCodec::codecForName(GBK); } QString text textEdit-toPlainText(); QByteArray data codec-fromUnicode(text); QFile file(fileName); file.open(QIODevice::WriteOnly); file.write(data);4.4 换行符的隐藏问题Windows 记事本默认使用\r\nCRLF作为换行符Linux 下是\nLF这就导致了一个常见现象Qt 项目里QPlainTextEdit的字符串内部是统一换行符的你按\n保存拿到 Windows 记事本里打开时整个文件可能挤成一行。解决方法是保存前把换行符统一替换为\r\n读取时再把\r\n还原为\n交给 Qt 处理。QString text textEdit-toPlainText(); text.replace(\n, \r\n); // Windows 习惯的保存格式这一步不做你的记事本写出来的文件到别人电脑上极有可能缩进全乱。5. 查找替换功能不卡界面才是关键5.1 对话框形态的选择查找替换如果做成模态对话框你会发现每点一次查找按钮对话框都会闪一下体验很差。正确做法是用QDialog创建非模态窗口配合Qt::WA_DeleteOnClose属性让它关闭时自动释放内存避免内存泄漏。FindDialog::FindDialog(QWidget *parent) : QDialog(parent) { setAttribute(Qt::WA_DeleteOnClose); setWindowFlags(Qt::Window); }构造一次之后主窗口保存一个findDialog指针再次打开时判断是否为空避免重复创建多个窗口。5.2 核心查找逻辑QTextDocument::find 的用法查找的核心函数是QTextDocument::find它接收查找字符串、起点光标和匹配模式返回一个新的QTextCursor。查到后通过textEdit-setTextCursor()定位并选中。代码示例QTextCursor MainWindow::findText(const QString text, bool forward) { QTextDocument *doc textEdit-document(); QTextCursor cursor textEdit-textCursor(); QTextDocument::FindFlags flags QTextDocument::FindCaseSensitively; if (!forward) flags | QTextDocument::FindBackward; QTextCursor found doc-find(text, cursor, flags); if (!found.isNull()) { textEdit-setTextCursor(found); } return found; }这里有个很多人忽略的细节如果查找字符串是空字符串find会返回无效光标需要提前拦截。还有匹配模式默认是大小写不敏感还是敏感要在界面上加个勾选框让用户自己选。5.3 替换的坑替换后死循环替换功能的坑比查找多。常见的写法是查到一个就立即替换但如果替换后的文本里仍然包含被查找的字符串就可能在替换完当前结果后继续查找到同一个位置造成死循环。正确做法是替换完当前匹配后从一个新位置继续查找。可以利用替换后选中的QTextCursor作为下一次查找的起点并向前移动一个字符避免原地打转。实测时我遇到过用户一直点全部替换没反应最后发现是因为每次替换后光标位置没有更新。6. 快捷键联动与动作状态控制6.1 标准快捷键的注册QAction 里直接设置标准快捷键是最省事的做法。QKeySequence::New、QKeySequence::Open、QKeySequence::Save、QKeySequence::Find这些枚举值会自动映射到 Windows 上 CtrlN、CtrlO 等常用组合。对用户来说符合直觉的快捷键能显著提升这是个正经软件的第一印象。findAct-setShortcut(QKeySequence::Find); replaceAct-setShortcut(QKeySequence(Qt::CTRL | Qt::Key_H));注意QKeySequence::Find在 Windows 上默认是 CtrlF替换如果不用默认枚举自己绑定 CtrlH 即可这个习惯也是沿袭传统编辑器。6.2 剪切复制什么时候置灰一个最容易被忽略的细节当文本区没有选中文字时剪切和复制应该是不可点击的。QPlainTextEdit提供了copyAvailable信号在文本选中状态变化时触发。在这个信号里更新动作的setEnabled状态才能做出一个专业文本框该有的交互。connect(textEdit, QPlainTextEdit::copyAvailable, cutAct, QAction::setEnabled); connect(textEdit, QPlainTextEdit::copyAvailable, copyAct, QAction::setEnabled);这个联动逻辑非常直观地演示了 Qt 信号槽的核心机制也顺便解决了新手经常忽略的未选中文本时剪切按钮点了没反应问题。6.3 文档修改状态的标题栏提示Windows 记事本在内容修改但未保存时标题栏会显示一个星号。这个细节实现并不复杂但在很多仿记事本项目里都看不到。实现方式是监听textChanged信号用一个布尔标志记录当前文档是否已修改然后在标题栏字符串里加上*。connect(textEdit, QPlainTextEdit::textChanged, this, MainWindow::documentWasModified);关闭窗口前判断maybeSave()弹一个是否保存的QMessageBox这算是一个完整项目的收官逻辑能大幅提高软件的完成度。7. 打包发布与崩溃排查从能跑到能用7.1 windeployqt 的正确打开方式开发环境中程序能跑不代表你双击发布的 exe 也能跑。Qt 程序发布需要把用到的所有动态库和插件一起带上windeployqt就是官方提供的自动部署工具。操作流程不复杂先把项目切到 Release 模式编译然后在 Qt 命令行工具里进入 exe 所在目录执行windeployqt 记事本.exe它会自动把 Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll、platforms 目录等复制到 exe 旁边。跑完之后把整个文件夹压缩发给别人就是最常见的绿色版。7.2 windows no qt platform plugin could be initialized 的排查链路这条报错信息在热词里出现了完全原样的一条我也是遇到过的。完整报错一般是windows no qt platform plugin could be initialized reinstalling the application很多人第一反应是重装 Qt其实跟重装没有任何关系。排查链路应该这样走先看 exe 同级目录下有没有 platforms 文件夹里面必须有 qwindows.dll。这个文件是 Qt 程序在 Windows 上能够创建窗口的关键平台插件丢失了就会报这条错。确认是不是用 Release 模式编译的。Debug 模式编译的程序需要对应 Debug 版本的 Qt 库在无开发环境的电脑上几乎必崩。检查路径里有没有中文或特殊字符。某些旧版 Qt 在路径带中文时加载插件会失败把整个文件夹复制到纯英文路径再运行问题往往迎刃而解。如果windeployqt执行过但依然报错多半是执行命令时选错了 Qt 版本比如目标程序是 MinGW 编译的却用 MSVC 版本的windeployqt去部署。命令行里先cd /d C:\Qt\5.15.2\mingw81_64\bin再执行工具就不会错配。7.3 离线环境与跨平台部署热词里还出现了qt离线安装 麒麟、linux找qt路径这说明不少人要在国产 Linux 环境跑 Qt 应用。其实方法和 Windows 上类似用linuxdeployqt或者手动复制依赖库到指定目录再设置LD_LIBRARY_PATH即可把应用带起来。跨平台发布的核心思路就是程序运行时需要找到对应的动态库和插件目录顺着这个思路排查大多数环境问题都能迎刃而解。8. 实际操作中的个人体会最后再分享一个我做这个项目的经验。很多人写记事本抄完代码就完事了但真正的收获在于打断点、看变量值、手动改代码去验证猜想的过程。我带着新人做这个项目时要求是必须自己画一张信号槽连接图把每个按钮从点击到最终触发哪个槽函数中间经历了哪几个信号一条条理清楚。实际踩过几次坑之后我对 Qt 项目最大的感受是运行起来不崩只是起点交互细节和边界情况才是区分学习和实战的分水岭。比如空文档保存、路径没有写权限、文件被其他程序占用、查找时字符为空这些问题都在你做项目过程中暴露出来。建议拿到代码后不要急着跑通而是故意去掉某个connect观察报错或现象再用 Qt 的调试工具找回来——这样折腾一遍掌握程度绝对比光看教程高好几倍。本文还有配套的精品资源点击获取