
简介这是一份基于Qt框架实现的轻量级记事本源码项目面向Qt初学者、C图形界面编程入门者以及需要简洁文本编辑工具的开发者。项目定位为无状态栏、无查找替换的基础记事本界面精简适合用来理解Qt窗口程序的基本结构。压缩包共15个文件约143KB包含3个cpp源文件、3个h头文件、2个ui界面文件以及pro工程文件、qrc资源文件、ico图标和jpg图片等覆盖主窗口逻辑、对话框实现、程序入口与界面布局定义。已有302人学习下载说明其作为入门范例具有一定参考价值。读者可从中掌握Qt中窗口创建、菜单与文件读写、对话框调用、资源组织等核心操作并以此为基础自行扩展查找替换、状态栏或更多文本格式支持是学习Qt编程与文本编辑器实现的实用起点。1. 从一份 Notepad.zip 说起Qt 简单记事本到底能跑出什么效果很多人第一次接触 Qt 桌面开发都是从写一个记事本开始的。但真正拿到一份能编译、能跑、结构完整的Notepad.zip和跟着教程敲一遍感受完全不同。这份资源里包含mainwindow.cpp、dialog.cpp、main.cpp、ui.h、mainwindow.h、dialog.h、Notepad.pro、resource.qrc、ICON.ico、about.JPG等文件是一个基于 Qt 框架、用 C 实现的轻量级文本编辑器。它没有状态栏也没有查找替换界面干净到只剩菜单和编辑区。适合谁适合想学 Qt 信号槽、文件读写、资源管理又不想被复杂业务逻辑干扰的开发者。你拿到手就能编译改几行就能看到界面变化这种即时反馈对建立 Qt 手感非常关键。下面我从工程结构、编译配置、核心功能实现到避坑一步步拆开讲。2. 拆开 Notepad.pro 和 main.cpp工程结构与启动链路2.1 从 .pro 文件看 Qt 的构建规则Notepad.pro是 qmake 的工程描述文件决定了编译器去哪里找头文件、链接哪些库、生成什么目标。这份资源的.pro文件虽然简单但包含了 Qt Widgets 应用的基本要素。常见写法如下QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET Notepad TEMPLATE app SOURCES main.cpp \ mainwindow.cpp \ dialog.cpp HEADERS mainwindow.h \ dialog.h FORMS mainwindow.ui \ dialog.ui RESOURCES resource.qrc RC_ICONS ICON.ico逐行说明QT core gui引入 Qt 核心和图形模块Qt5 之后 widgets 被拆成独立模块所以用greaterThan判断版本后追加widgets。TEMPLATE app表示生成可执行程序而不是库。SOURCES、HEADERS、FORMS分别列出源文件、头文件和 Designer 生成的.ui文件。RESOURCES resource.qrc把图片、图标等资源编译进二进制避免运行时找不到文件。RC_ICONS ICON.ico是 Windows 下设置 exe 图标的快捷方式省去手动写.rc文件。这里有个容易忽略的点Notepad.pro.user、Notepad.pro.user.21、Notepad.pro.user.4fbe61b.4.8-pre1是 Qt Creator 自动生成的用户配置记录了你本机的套件、构建目录、调试器路径。它们不应该提交到版本库也不应该手动编辑。换一台机器打开工程这些文件里的路径大概率失效Qt Creator 会提示重新配置套件。我一般拿到别人的工程先删掉所有.pro.user*再让 Creator 重新生成能避开一堆“找不到编译器”的玄学问题。2.2 main.cpp 里的启动顺序与 QApplication 职责main.cpp是程序入口结构通常如下#include mainwindow.h #include QApplication int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }QApplication管理整个应用的资源、事件循环和全局状态任何 Qt Widgets 程序有且只有一个。MainWindow w;在栈上创建主窗口w.show()把窗口显示出来a.exec()进入事件循环开始接收鼠标、键盘、系统消息。很多新手会问为什么w不写成new MainWindow因为栈对象在main结束时自动析构配合 Qt 的父子对象机制窗口关闭后资源能干净释放。如果你用new又忘了delete程序退出时会有内存泄漏报告虽然不影响运行但调试时看着难受。main.cpp里通常不写业务逻辑只负责“创建应用、创建窗口、进入循环”三件事。把逻辑放在MainWindow里是为了让入口保持稳定后续加 splash screen、命令行参数解析、单实例检测都从这里扩展。3. mainwindow.ui 与 mainwindow.cpp界面和逻辑怎么绑在一起3.1 ui.h 是生成物不要手改ui.h是 Qt Designer 根据mainwindow.ui自动生成的里面定义了Ui_MainWindow类包含menubar、statusbar、centralwidget、actionNew、actionOpen、actionSave、actionAbout等成员。每次你保存.ui文件Qt Creator 都会重新生成ui.h所以手动修改ui.h的代码会在下一次构建时被覆盖。正确做法是界面布局在 Designer 里拖拽业务逻辑在mainwindow.cpp里写。mainwindow.h里通常会有namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); ~MainWindow(); private slots: void on_actionNew_triggered(); void on_actionOpen_triggered(); void on_actionSave_triggered(); void on_actionAbout_triggered(); private: Ui::MainWindow *ui; QString currentFile; };Q_OBJECT宏必须放在类声明开头否则信号槽机制无法工作编译时会报undefined reference to vtable。currentFile用来记录当前打开的文件路径保存时直接写回原文件避免每次弹保存对话框。3.2 用 connect 还是自动连接信号槽的两种写法Qt 的信号槽连接有两种常见方式。第一种是自动连接槽函数命名遵循on_对象名_信号名规则比如on_actionNew_triggered()Qt 会在setupUi时自动连接不需要手写connect。第二种是显式connectconnect(ui-actionNew, QAction::triggered, this, MainWindow::newFile);自动连接写起来快但对象名一改就失效而且调试时不容易看出连接关系。显式connect更可控推荐在构造函数里统一写。注意 Qt5 之后推荐用函数指针语法编译期就能检查参数匹配比老式SIGNAL/SLOT宏安全得多。3.3 新建、打开、保存三个动作的完整实现新建动作的核心是清空编辑区并重置当前文件路径void MainWindow::on_actionNew_triggered() { ui-textEdit-clear(); currentFile.clear(); setWindowTitle(Notepad - 未命名); }打开文件用QFileDialog::getOpenFileName拿到路径再用QFile读取void MainWindow::on_actionOpen_triggered() { QString fileName QFileDialog::getOpenFileName( this, 打开文件, , 文本文件 (*.txt);;所有文件 (*.*)); if (fileName.isEmpty()) return; QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::warning(this, 错误, 无法打开文件); return; } QTextStream in(file); ui-textEdit-setPlainText(in.readAll()); currentFile fileName; setWindowTitle(Notepad - QFileInfo(fileName).fileName()); }保存动作分两种情况如果currentFile为空弹另存为对话框否则直接写回void MainWindow::on_actionSave_triggered() { if (currentFile.isEmpty()) { currentFile QFileDialog::getSaveFileName( this, 保存文件, , 文本文件 (*.txt)); if (currentFile.isEmpty()) return; } QFile file(currentFile); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::warning(this, 错误, 无法保存文件); return; } QTextStream out(file); out ui-textEdit-toPlainText(); setWindowTitle(Notepad - QFileInfo(currentFile).fileName()); }参数说明QIODevice::Text在 Windows 下会把\n转成\r\n保证记事本能正确识别换行。如果你处理的是二进制或需要保留原始换行符去掉Text标志。QTextStream默认使用本地编码Qt5 下中文 Windows 可能是 GBK跨平台交换文件时建议显式设置in.setCodec(UTF-8)否则在 Linux 打开会乱码。4. dialog.ui 与 resource.qrc关于对话框和资源嵌入4.1 关于对话框的两种实现路径dialog.cpp和dialog.ui构成一个独立的对话框窗口通常用来显示软件名称、版本、作者和一张about.JPG图片。实现方式有两种一种是在 Designer 里拖好布局dialog.cpp只负责ui-setupUi(this)另一种是纯代码创建QMessageBox::about。这份资源用的是独立对话框好处是可以放图片、排版更自由。dialog.h里同样需要Q_OBJECT宏dialog.cpp构造函数里调用ui-setupUi(this)。如果对话框里要显示图片可以在.ui里放一个QLabel然后通过资源路径:/images/about.JPG设置 pixmap。注意路径前的:/是 Qt 资源系统的前缀不是文件系统路径。4.2 resource.qrc 的写法和常见错误resource.qrc是 XML 格式的资源清单RCC qresource prefix/ fileICON.ico/file fileabout.JPG/file /qresource /RCCprefix/表示资源根路径引用时写:/ICON.ico或:/about.JPG。常见错误有三个一是文件路径写错qrc 里的路径是相对于.qrc文件所在目录不是相对于工程根目录二是文件名大小写不一致Windows 不区分大小写Linux 区分在 Windows 编译通过、到 Linux 就找不到资源三是修改 qrc 后没有重新执行 qmakeQt Creator 有时不会自动检测 qrc 变化需要手动右键工程选“执行 qmake”。提示资源文件会被编译进 exe图片太大会显著增加二进制体积。about.JPG如果超过 1MB建议先压缩再放进 qrc。5. 避坑与排查编译、运行、跨平台最容易翻车的几个点5.1 报错 “dependent ‘…qtwidgets…’ does not exist”现象编译时提示某个 Qt 头文件路径不存在类似:-1: error: dependent ..\..\..\..\..\..\qt\5.15.2\msvc2019_64\include\qtwid... does not exist。原因通常是.pro.user里记录的 Qt 套件路径和当前机器不一致或者 Qt 版本升级后旧路径失效。解决关闭 Qt Creator删除工程目录下所有Notepad.pro.user*文件重新打开工程在“配置项目”里选择本机可用的套件再执行 qmake 和构建。5.2 中文乱码文件读写编码不统一现象在 Windows 记事本里写的中文用这个程序打开显示乱码或者程序保存的文件在 Linux 下打开乱码。原因QTextStream默认使用系统本地编码Windows 中文环境是 GBKLinux 通常是 UTF-8。解决读写时显式设置编码QTextStream in(file); in.setCodec(UTF-8); QString content in.readAll();保存时同样out.setCodec(UTF-8)。如果必须兼容 GBK 文件可以在打开时用QTextCodec::codecForName(GBK)检测或让用户选择编码。5.3 找不到平台插件 “windows”现象程序编译成功运行时报qt.qpa.plugin: Could not find the Qt platform plugin windows in 。原因动态链接的 Qt 程序运行时找不到platforms/qwindows.dll。解决用windeployqt工具自动拷贝依赖windeployqt Notepad.exe如果手动部署确保 exe 同级目录下有platforms文件夹里面放qwindows.dll。Release 和 Debug 版本的插件不通用别混用。5.4 信号槽连接失败Q_OBJECT 缺失或对象名改动现象点击菜单没反应或者编译报undefined reference to MainWindow::on_actionNew_triggered()。原因类声明里漏了Q_OBJECT宏或者.ui里对象名从actionNew改成了别的但槽函数名没跟着改。解决检查mainwindow.h里Q_OBJECT是否存在如果用自动连接确认槽函数名和.ui里的对象名完全一致改用显式connect可以避免对象名改动导致的静默失效。5.5 资源图片不显示qrc 路径和大小写现象关于对话框里图片位置空白或者程序图标没生效。原因qrc 里写的文件名和实际文件名大小写不一致或者引用时漏了:/前缀。解决在 Qt Creator 里双击resource.qrc确认文件列表引用时统一用:/about.JPG这种形式。Linux 下尤其注意大小写about.jpg和about.JPG是两个不同文件。6. 在这份代码上做扩展加状态栏和查找替换的实操思路这份记事本没有状态栏和查找替换但正好是练手扩展的好起点。加状态栏最简单在MainWindow构造函数里调用statusBar()-showMessage(就绪)然后在textEdit的cursorPositionChanged信号里更新行列号connect(ui-textEdit, QTextEdit::cursorPositionChanged, this, []() { QTextCursor cursor ui-textEdit-textCursor(); int line cursor.blockNumber() 1; int col cursor.columnNumber() 1; statusBar()-showMessage(QString(行 %1, 列 %2).arg(line).arg(col)); });查找替换可以用QTextEdit::find实现void MainWindow::on_actionFind_triggered() { bool ok; QString text QInputDialog::getText(this, 查找, 查找内容, QLineEdit::Normal, , ok); if (ok !text.isEmpty()) { if (!ui-textEdit-find(text)) { QMessageBox::information(this, 查找, 未找到匹配内容); } } }find默认从当前光标位置向后搜索找不到会返回false并自动回到开头再试一次。替换需要先选中再插入逻辑稍复杂但核心就是QTextCursor::insertText。验证扩展是否成功我一般走三步先编译看有没有Q_OBJECT相关报错再手动点一遍菜单确认信号槽触发最后用中文、英文、空文件各测一次打开保存看编码和换行是否正常。从那以后我每次拿到别人的 Qt 工程都先删.pro.user、统一 UTF-8 编码、跑一遍windeployqt这三步能挡掉八成低级翻车。希望帮到你。本文还有配套的精品资源点击获取