
简介一份基于QT6的记事本桌面应用工程源自B站‘社长嵌入式手’系列适合正在学习QT跨平台开发、嵌入式GUI及文本编码处理的开发者。项目实现了文本编辑、文件读写与多编码识别等核心功能主窗口、自定义文本控件和编码模块划分清晰便于阅读和二次实践程序入口、UI布局、资源管理和工程配置均有清晰对应便于逐层拆解。资源包共516个文件、约4.29MB以zip形式分发内部包含C源码、Qt界面文件、资源文件与工程文件大量idx索引文件与字符编码识别相关png图片资源用于界面显示目录结构直观。已有776人学习下载工程对QT6模块化构建、文件编码兼容与嵌入式资源占用的处理覆盖了从工程搭建到细节优化的完整链路适合有基础或正在进阶的Qt开发者学习研究。 先聊个实际的B站“社长嵌入式手”这套QT记事本项目虽然看起来只是个增删改查的桌面小工具但我刷完整个项目流程后发现它其实把Qt开发里最该踩的坑、最常用的套路都过了一遍。从一个回车就能保存的文本框到能打包发给别人的exe里面藏的东西远比“记事本”三个字多。这篇文章我打算换个角度——不逐行抄代码而是把项目里那些设计决策、开发环境、界面搭建、数据存储、打包发布、常见报错串成一条完整路线帮你在自己机器上复现顺带把热词里那些高频问题比如no qt platform plugin、windeployqt打包、QT下载慢一并解决。1. 项目整体思路与功能拆解1.1 记事本项目的核心功能清单这个项目的功能设计走的是“极简可用”路线。原始需求其实很简单能新建、打开、编辑、保存文本支持富文本格式界面干净利落。但如果你只看这些就开写大概率会做成一个“能跑的半成品”而离“能发布的软件”还有一段距离。社长那个版本好的地方在于他把记事本拆成了几个模块编辑器核心、文件读写、界面布局、快捷键响应、状态栏信息展示。我建议你自己动手做的时候把功能清单扩充成这样新建/打开/保存/另存为支持txt和rtf格式文本编辑基础能力撤销重做、剪切复制粘贴、全选查找替换支持向下/向上循环查找字体设置与颜色设置QFontDialog、QColorDialog状态栏实时显示行列号、字符数、编码格式最近打开文件列表存到QSettings里退出时检查内容是否修改弹窗提示保存这些功能单独看都不难但它们一起构成了一个“完整应用”该有的体验。很多新手做完前三条就觉得项目结束了实际上后面那几条才是区分“练习”和“作品”的分水岭。1.2 技术选型为什么是QWidget而非QML这个项目用的是QWidget框架而不是QML。这跟嵌入式系统的显示资源有关也有开发效率上的考虑。QWidget走的是CPU绘制渲染控件层级清晰跟C桌面开发的思路一致适合刚接触Qt的人快速上手QML则更偏向声明式UI动画效果好但在Linux嵌入式环境下调试门槛略高。另一个选择是用QMainWindow还是QDialog作为主窗口。记事本这类多文档应用明显应该选QMainWindow因为它自带菜单栏、工具栏、状态栏、停靠窗口这些结构不用自己拼。工具栏上放QAction动作集合统一管理图标、快捷键、触发逻辑——这也是Qt里比较标准的主窗口写法。社长项目里大量使用了QAction和信号槽机制这是QWidget架构的经典组合。如果你后端是C、希望界面逻辑分离还可以考虑MV或MVP模式但记事本这么小的项目为了模式而模式反而增加负担。我的建议是先按直观的“界面类持有文档对象”的方式做通再考虑重构别一上来就套设计模式。2. 环境搭建Qt下载、安装与IDE选型2.1 Qt安装包的获取与镜像加速热词里出现“qt下载官网”“qt国内镜像”“qt离线安装 麒麟x86”说明卡在环境这一关的人相当多。Qt官方安装器在线版在国内经常下载到一半就断这不是网络问题是默认源的连接不稳定。解决办法是用国内镜像源。清华、中科大、阿里云都有Qt镜像以清华源为例直接在浏览器打开https://mirrors.tuna.tsinghua.edu.cn/qt/选择对应版本目录。推荐下载qt-opensource-windows-x86-5.15.2.exe这类安装包注意区分在线和离线版。离线包一般在archive/qt/5.15/5.15.2/目录下文件名里带-offline或直接就是完整安装包。选择组件时不要贪多如果做桌面开发MSVC 2019 64-bit 或 MinGW 8.1.0 64-bit 二选一即可再加上Qt Creator、Qt Debugger Tools、Sources就足够了。嵌入式Linux场景的话再勾选对应交叉编译套件。麒麟系统x86上安装Qt同理官方有Linux平台的run安装包给执行权限后图形化安装。需要注意的是Linux版Qt Creator默认依赖的xcb插件在部分国产系统上可能缺少libxcb-cursor0导致无法启动界面这个后面问题排查再细说。2.2 MSVC与MinGW套件怎么选这是新手最容易晕的地方。MSVC是微软的Visual Studio编译器MinGW是GCC的Windows移植版。选哪个取决于你的项目要不要调用Windows原生API、要不要被其他MSVC编译的库调用。嵌入式博主经常接触到串口、网络、HALCON机器视觉、OpenCV等第三方库这些预编译库很多都是MSVC版这时候你如果用了MinGW链接阶段会报一堆无法解析的外部符号。我的建议Windows桌面项目优先MSVC除非项目要求在Linux和Windows之间共享一套代码且都从源码编译第三方库。MSVC对应版本要装好比如Qt 5.15.2 msvc2019 就必须配合Visual Studio 2019的运行时环境或者至少安装对应的VC Redistributable。用MSVC套件时编译器和调试器路径需要保持一致性——Operator用Qt Creator自带的CDB调试器有时会抽风装个Windows SDK能省很多事。2.3 IDE选择Qt Creator还是VS Code热词里专门有一条“qt creator vs vs code:2024年qt开发ide终极选择指南”说明这个对比是真实痛点。我的看法是这个项目老老实实用Qt Creator。原因是Qt Creator对.pro/CMakeLists.txt的解析、UI文件的直接预览、设计模式的拖拽编辑都做得很好而且集成Qt帮助文档非常方便——鼠标悬停就能查某个API的详细说明这对新手价值巨大。VS Code作为编辑器当然更快更轻但配置编译器、CMake、Qt路径需要自己动手容易劝退。即便你更喜欢VS Code的界面也建议先熟悉Qt Creator的调试窗口和.ui设计模式学会之后再折腾VS Code插件。另外Qt 6.5以上版本官方主推CMake构建Qt Creator也全面支持CMake但像这类基于Qt 5.15.2的项目qmake的.pro文件依然很多教程在用两者都掌握是最好的。2.4 编译器、调试器与构建套件配置细节Qt Creator里“构建套件”Kit是一个容易困惑的概念。套件可以理解为一个组合配置包含了编译器、调试器、Qt版本、CMake/qmake路径。新手经常遇到的问题是明明装了Qt新建项目时却没有可用的套件。这大概率是安装Qt时没有勾选对应编译器组件MSVC或MinGW或者系统里没有安装VS Build Tools。MinGW套件调试时需要安装Windows的Debugger ToolsMSVC套件调试需要CDB。如果你要交叉编译到嵌入式板子还得在套件里设置交叉编译器路径和sysroot。这块配置一次后面能长期复用别嫌麻烦。3. 界面搭建与核心交互实现3.1 主窗口布局菜单栏、工具栏、状态栏记事本主窗口用QMainWindow作为基类这种结构是标准做法。菜单栏放“文件”“编辑”“格式”“查看”“帮助”工具栏放常用动作。在Qt Designer里拖先添加QAction设置文本、快捷键、图标再绑定triggered信号到对应槽函数。这样做的好处是同一个QAction既能给菜单用又能给工具栏用触发逻辑只写一遍。状态栏建议添加两个永久控件左边显示QTextCursor的行列位置右边显示字符计数。利用QTextEdit::cursorPositionChanged信号实时刷新。这里有个小技巧对中文内容计算字符数时如果用QString::length()结果是按UTF-16编码的字符数量一般跟“字数”一致如果要用text().count()或正则统计中文字符需要额外处理。3.2 核心编辑器控件QTextEdit与QPlainTextEdit的取舍这个选择值得认真说。QPlainTextEdit是为纯文本优化的处理超大文件时性能好、内存占用低QTextEdit则支持富文本可以做加粗、颜色、字体设置。做“记事本”到底选哪个取决于需求定位。社长项目里为了演示富文本功能用了QTextEdit打开和保存的时候可以处理RTF格式。如果只是背诵代码、写日志我建议用QPlainTextEdit——不光是性能它的setTabStopDistance()控制Tab缩进宽度更直观纯文本处理逻辑也简单。另一个容易被忽视的点是自动换行。默认QPlainTextEdit是横向滚动的需要在代码里设置setLineWrapMode(QPlainTextEdit::WidgetWidth)否则用户体验很糟。记事本项目通常要开启按窗口宽度换行。QTextEdit则默认就是按控件宽度换行的这一点需要注意。3.3 查找替换功能的实现细节查找替换是记事本项目的重头戏也是QTextEdit文档结构学习的好切入点。大多数人第一反应是直接QTextEdit::find()传入字符串返回bool值。这么写没问题但有个细节查找默认是区分大小写的如果你要在菜单“查找”和“替换”里共用一组查找状态光靠find函数不够。建议用QTextDocument::find()加QTextCursor管理搜索位置。核心思路是用光标遍历文档内容每次从当前位置向后搜索找到后在文档中创建一个选区QTextCursor::setPositionsetPosition然后mergeCharFormat来高亮选中区域。替换则是在找到选中后用QTextCursor::insertText替换当前选择内容并继续向后查找。整个过程要处理好“wrap around”从底部循环回顶部和“空搜索词”这两种边缘情况。用一个对话框类集中管理查找输入、替换输入和按钮交互会更顺手。这种写法适合看懂的人扩展到代码编辑器、日志分析器的高亮定位功能。以后再遇到“批量高亮所有匹配项”“实时正则搜索”就都是大同小异。3.4 字体与颜色设置的无障碍实现字体设置对话框QFontDialog::getFont()和颜色对话框QColorDialog::getColor()用起来很简单但设置后有效性验证要注意。比如用户点击“确定”后要检查返回的字体是否是默认字体在某些平台上默认字体会显示为Microsoft YaHei UI之类的名字但在Qt 5.15里可能返回QFont::defaultFamily()然后做合并字符格式操作。富文本编辑器里字体的设置一般分两种模式一种是设置整篇文档的默认字体一种是只修改选区字体。前者用QTextDocument::setDefaultFont()后者要对选中范围使用QTextCharFormat并mergeCharFormat。两种都实现后用户换成不同字体就不会困惑。另外记得将当前字体信息回显到状态栏不然用户改了字体往往搞不清自己当前用的什么字号。4. 数据存储与配置文件Qt的持久化机制4.1 QSettings保存界面设置和最近文件列表QSettings是Qt内置的配置存储机制。Windows上默认写入注册表Linux上写到~/.config/应用名.conf。做法很简单QSettings settings(MyCompany, MyNoteApp); settings.setValue(window/geometry, saveGeometry()); settings.setValue(recentFiles, recentFileList);读取的时候再反向取出来。关于“最近打开的文件列表”我建议用QStringList存但限制数量比如8个并且每次打开文件时更新列表、去重、把最新的放最前。菜单里动态生成打开最近文件的QAction点击后调用打开文件函数。这里要特别注意每次生成菜单时先清理旧的action避免重复累积。4.2 文件读写文本、编码与换行符的处理记事本项目的文件读写逻辑直接决定“记事本”好不好用。QTextStream是读写文本文件的推荐方式它默认处理本地编码但在Windows上有时读UTF-8文件会乱码因为Qt 5默认用的是Locale编码。建议明确设置编码QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; QTextStream in(file); in.setCodec(UTF-8); QString content in.readAll();同时要考虑BOM问题。UTF-8 BOM会带来三个不可见字符导致文件开头出现“锟斤拷”等乱码。处理方式是写入时用QIODevice::WriteOnly并手动写BOM读取时检测BOM并跳过。Linux和macOS的换行符通常是\nWindows是\r\n。Qt的QTextStream在文本模式下会做转换但如果你用QFile::write直接写字符串就可能出现换行符不一致。另一个隐蔽问题大文件的读取。如果一次性readAll()几十MB以上的文本会明显卡顿。记事本项目一般不用处理超大文件但如果想做得健壮可以考虑分段读取并用QTextCursor::insertText分批插入或者干脆用QPlainTextEdit加增量加载。4.3 会话恢复“未保存更改”的提醒机制标题栏上常见的“*”号提示符实现逻辑很简单在文档内容变化信号textChanged里设置一个bool isModified在保存后清空。当关闭窗口时在closeEvent里检查这个标志。如果尚未保存弹QMessageBox::question询问“保存/不保存/取消”。注意一个问题textChanged信号在代码里setPlainText()、clear()、替换高亮时都可能触发所以要在保存过程中用blockSignals(true)防止误标记。更进阶的做法是用QFileSystemWatcher监听文件是否在外部被修改这样能提示用户“文件已被其他程序修改是否重新加载”。虽然不是必需功能但做出来后项目质感会明显提升。5. 快捷键、命令行与Web交互等高频技术点解析5.1 自定义快捷键与全局快捷键菜单栏里为QAction设置快捷键很简单在Qt Designer动作编辑器里选中CtrlN或者代码里new QAction(tr(新建), this); newAction-setShortcut(QKeySequence::New);。但要支持用户自定义快捷键就得走QShortcut和QKeySequenceEdit设置界面。真正常见的问题是要实现“无焦点时的全局快捷键”比如后台运行时按CtrlAltN呼出这需要系统级钩子QShortcut做不到需要借助Win32的RegisterHotKey。在Windows上可以写一个事件过滤器处理QAbstractNativeEventFilter里的WM_HOTKEY消息。尽量别引第三方库单纯用WinAPI代码量也不大。5.2 Qt命令行与程序参数解析项目如果要支持“用记事本打开某个文件”就要处理命令行参数。Qt里用QCoreApplication::arguments()获取参数或者更规范地使用QCommandLineParser。基础用法QCommandLineParser parser; parser.addPositionalArgument(file, The file to open.); parser.process(app); const QStringList args parser.positionalArguments(); if (!args.isEmpty()) { mainWindow-openFile(args.first()); }这种方式比手动argv[1]可靠还能自动生成--help。把这个做进去以后这个“记事本”就能从资源管理器发送到菜单直接打开了。5.3 HTTP请求与POST不支持问题热词里“qt request method post not supported”其实很少见但HTTP请求是Qt工程中绕不开的。桌面应用里做HTTP请求推荐用QNetworkAccessManager它异步、不会阻塞主线程。GET请求QNetworkAccessManager *manager new QNetworkAccessManager(this); connect(manager, QNetworkAccessManager::finished, this, [](QNetworkReply *reply) { if (reply-error() QNetworkReply::NoError) { QByteArray data reply-readAll(); } reply-deleteLater(); }); QNetworkRequest request(QUrl(https://api.example.com/data)); manager-get(request);POST需要设置请求头Content-Type并传入QByteArray数据注意数据编码格式x-www-form-urlencoded或JSON。那些遇到“POST not supported”的情况多半是代码里用了QNetworkRequest::setHeader但没有正确设置ContentType或请求重定向时被降级成了GET。把manager-get换成manager-post再把QByteArray载荷填对问题自然就没了。5.4 Qt MQTT与TCP简析嵌入式项目里MQTT和TCP经常出现在同一个程序里。Qt官方有Qt MQTT模块历史原因不在默认安装包里要自己编译还有QTcpSocket和QTcpServer。记事本本身用不到MQTT但你可以做一个联动实验记事本保存文件时通过MQTT发布一条消息或者在一个网络的TCP端口上监听打开文件指令。这正好能把图形界面和网络通信结合起来是嵌入式入行的常见练习。6. 绘图与扩展把记事本升级成“富笔记工具”6.1 用QPainter实现手写标注与画线功能有个热词叫“qt绘图”“qt桌面画线”跟记事本衍生功能很匹配。很多笔记工具有画线标注需求——在文本上划重点、画方框。QTextEdit本身不直接支持随意绘制图形但可以用一个透明QWidget覆盖在文本区域上方重写paintEvent让它只画图不处理输入事件用setAttribute(Qt::WA_TransparentForMouseEvents)保证鼠标事件穿透。用QPainter画线需要记录起点和终点在窗口的mousePressEvent记录起点mouseMoveEvent更新终点并调用update()触发重绘。关键的几个设置painter.setRenderHint(QPainter::Antialiasing)pen.setWidth(2)使用Qt::DashLine之类笔型用橡皮擦实现就要基于图层擦除模式这个功能做好后记事本就从一个纯文本工具变成了可以做涂鸦标注的笔记伴侣。6.2 自定义控件与QSS样式美化QSS是Qt的样式表语言类似CSS。界面美感往往决定用户对软件的第一印象。给记事本做个清爽现代风大概改这几个地方QMainWindow背景色、菜单栏和工具栏按钮的hover效果、QTextEdit内边距、QMessageBox按钮和字体。最小化、最大化、关闭按钮图标可以在QProxyStyle里重画。但QSS有个性能坑动态切换QSS会导致整棵控件树重新计算样式频繁切换比如颜色选择器拖动预览会卡。解决方法是把样式级别的更新延迟到鼠标释放后或者仅对目标控件单独设置动态属性并刷新而不是setStyleSheet整个窗口。编辑器排行榜里“qt崩溃”这个热词不少就是样式和布局设置不合理引起的。6.3 数据可视化QChart与QCustomPlot再往深一点记事本可以扩展成数据记录工具。比如记录每日事项时同时记录完成时间然后用QChart画一条曲线或者用QCustomPlot第三方库展示统计数据。这跟热词里的“qchart实现图片缩放qt”“qt绘制三维曲线”是同一条路线。QChart自带缩放、平移交互但坐标轴标签的精度需要设置好QCustomPlot功能更强支持任意坐标轴组合与实时数据流。这部分的难点不在绘图本身而在数据源的组织什么时候触发生成新点、如何缓存、如何让图表从数据中独立出来遵循MV的思想。做出来后你手里这个“记事本”就变成了一个可视化工作台。7. 打包发布从源码到可分发程序的完整流程7.1 windeployqt的正确用法与常见误区热词里“qt打包应用程序 windeployqt”是最高频的问题之一。windeployqt是Qt自带的部署工具它会自动拷贝Qt运行库到程序目录。核心命令cd /d 你的构建目录 D:\Qt\5.15.2\msvc2019_64\bin\windeployqt.exe Release\NotePad.exe注意两点一是这个exe路径必须对应你编译时用的同一套Qt版本和编译器混用MSVC和MinGW会导致库加载失败二是必须在Release目录下执行如果执行错了目录工具找不到exe生成的库拷贝会不完整。执行完毕后检查有没有生成platforms文件夹里面的qwindows.dll是Qt窗口必需插件缺失就会出现“no qt platform plugin could be initialized”的报错。如果程序还用了Qt Charts、Qt MQTT等模块windeployqt默认可能不会copy对应的plugins需要追加参数比如--charts或者手动拷贝dll。翻译文件.qm也需要放到translations目录否则Qt自带的对话框控件比如文件选择对话框会显示英文。7.2 发布后的运行环境与兼容性验证打完包强烈建议在一台没有安装Qt的干净机器上测试一遍。常见问题缺少VC运行库MSVC版安装对应版本vc_redist.x64.exe即可缺少libgcc_s_seh-1.dll等MinGW版确保库路径里所有dll都复制到位。Windows下还可以用“Dependencies”或Process Explorer查看dll加载情况定位具体缺少哪个动态库。Linux下部署一般是提供AppImage或直接依赖系统库。如果目标系统低于构建系统可能遇到glibc版本过老的问题这种问题最好在目标系统机器上编译或使用Docker容器构建。麒麟等国产系统上还要注意缺库问题常用ldd ./NotePad查看动态库链接情况。7.3 用Qt Installer Framework做安装包严谨的交付场景不能只给绿色免安装包需要做安装程序。Qt官方有 Qt Installer FrameworkQIF用它做一个安装器需要准备一个config目录里面config.xml、一个packages目录每个组件一个文件夹包含meta和data。data目录放程序文件meta目录放installscript.qs和package.xml。配置完成后运行binarycreator.exe -c config/config.xml -p packages NotePadInstaller.exe就能生成安装包。QIF可以定义快捷方式、开始菜单条目、卸载程序甚至多个功能组件。缺点是文档不多踩坑成本高。我建议从最小可用的config.xml和package.xml开始一步步添加功能。再聊一个细节安装包程序的图标默认很丑可以换一个.ico或.pngmacOS用.icns直接影响用户对软件的第一印象。8. 常见问题排查Qt崩溃、链接错误与跨平台坑我这里把热词里出现的一些高频报错集中整理一下方便直接查到对策。问题可能原因解决方案no qt platform plugin could be initialized缺少platforms/qwindows.dll或插件与exe位数不匹配用windeployqt重新部署检查x86/x64一致性:-1: error: dependent ....\allinstall\qt\5.15.2\msvc2019\include\qtw.pro或CMake的INCLUDEPATH引用了一个坏的Qt路径清理构建缓存shadow build检查QT路径配置Qt Creator打不开提示xcbLinux缺libxcb相关库安装libxcb-cursor0或xcb库程序一启动就崩溃调试器没有报错静态对象初始化顺序或QSS刷爆检查全局实例构造缩小样式作用域使用MSVC编译时找不到Qt头文件Qt安装组件漏了Sources/头文件或套件路径错重新安装Qt勾选Sources组件检查Kits路径发布后exe双击启动失败错误码0xc000007b架构不匹配x86/x64混淆或VC运行库缺失用Dependencies工具检查dll匹配情况安装对应VC运行库8.1 崩溃排查的基本思路Qt程序崩溃时Windows下先看崩溃位置是Qt库内部还是自己的代码。如果是Qt库内部优先怀疑信号槽参数不匹配导致的QMetaObject::invokeMethod访问越界或者QObject::deleteLater被重复调用。记事本项目里最容易出问题的是界面线程直接更新网络请求结果——比如在QNetworkAccessManager的回调里直接操作UI如果这个回调不在主线程就会产生未定义行为。我个人的排查套路是先在所有自定义槽函数入口打qDebug缩小崩溃范围其次用Qt的QT_FATAL_WARNINGS1把警告变致命错误便于直接看到问题的严重警告最后再用ASanAddressSanitizer跑一遍核心模块查找内存问题。这套组合拳能解决绝大多数新手项目的诡异崩溃。8.2 Windows与Linux跨平台的隐藏坑同一份代码在Windows和Linux上行为可能不同最常见的坑是路径分隔符。Windows写死了\\换到Linux会找不到文件。Qt提供了QDir::separator()和/的通用写法Qt在Windows下也接受/建议从一开始就统一用/加QDir处理路径。另一个坑是回车换行。Windows保存的文件到Linux记事本里显示可能有乱码或多余的\r需要读取时做换行统一。Qt的QTextStream有setGenerateByteOrderMark但不会自动处理换行符转换。在小项目里可以简单地在写入时替换\n为\r\n在读取时反向替换或者干脆存\n并用QTextEdit的document()-setModified()处理显示。8.3 如何正确搜索和查阅Qt文档最后热词里明显有个“qt教程”“qt 面试题”。文档查询这块建议直接用Qt自带的离线帮助Qt Creator里按F1或者官方在线文档。不要一遇到问题就复制整个报错去搜索引擎先看报错里提到的类名、函数名、文件行号再结合assistant.exe查相关类文档。Qt学习路径倒是很多Qt Examples目录里有大量现成例子比如Text Edit、Rich Text、Address Book都是记事本项目很好的参考。9. 写在最后的经验总结这个QT记事本项目的价值不在“记事本”本身而是它把Qt桌面开发的主干链路完整走了一遍从环境搭建、界面搭建、文件读写、配置持久化到扩展绘图、网络通信、打包发布再到排查各种环境问题。你把这个项目从头到尾亲手做完、能发给别人正常安装运行你对Qt的理解会比刷十遍教程都扎实。我个人操作中发现最值得多花时间的地方有两个一是QTextEdit的文档光标模型这块搞明白以后做富文本编辑器、代码高亮都不愁二是从立项第一天就保持“构建-部署-测试”的习惯每加一个功能就实时包一次别等到最后才一次性打包否则会撞见一堆让你怀疑人生的环境问题。如果你已经完成了基础版下一步可以尝试把它改成一个Markdown笔记工具或者接入云同步接口再或者增加一个语音输入模块。技术点都是现成的关键在于你愿不愿意把一个“能跑的”东西推进到“能给别人用”的程度。这条路值得慢慢走。本文还有配套的精品资源点击获取