简介《Qt5范例开发大全完整目录版》面向具备一定C基础、希望系统掌握Qt5跨平台界面与功能开发的程序员及在校学生。全书以实例驱动从开发环境搭建与Hello World起步依次覆盖窗体应用、控件应用、组件应用、文件操作、图形图像、多媒体、系统操作、注册表、数据库、网络开发、进程与线程、数据安全及打包部署等十四个章节既包含QPushButton、QLabel、QLineEdit、QComboBox、QTableView等常用控件的用法也涉及布局管理、多窗体调用、无边框窗体移动等界面技巧并延伸至实际项目中的高级场景。资源包为1个PDF文件大小约2.84MB目录结构完整清晰便于按章节检索与对照练习。目前已有2458人学习下载适合作为Qt5入门到进阶的案头参考帮助读者通过范例快速理解框架机制、积累可复用的实现思路逐步具备独立开发高效美观的Qt5应用程序的能力。1. Qt5范例开发大全从目录索引到可运行工程的最小闭环很多人拿到一本几百页的 Qt5 范例合集第一反应是收藏第二反应是“等有空再看”。结果三个月后书签还在工程一个没跑起来。问题不在书在于范例类资料天然是“碎片化”的——每个例子只讲一个控件、一个信号槽、一个对话框读者顺着目录翻永远停在“看懂了”的层面落不到“跑通了”的层面。Qt5 范例开发大全这类完整目录版 PDF 的价值恰恰在于它是一张可检索的地图你能按控件名、按模块名、按功能关键词定位到最小可运行单元然后把它抽出来塞进自己的工程里验证。这篇文章面向的是已经会写 C、装过 Qt Creator、但面对一本范例合集不知道从哪下手的开发者。我会把“怎么用目录反查工程结构”“怎么把范例拆成可编译的最小工程”“哪些范例在 Qt5 里已经过时”“参数怎么改才不崩”这几件事讲透让你拿到任何一本 Qt5 范例资料都能自己拆。2. 把 PDF 目录变成可检索的工程索引先建映射表再动手2.1 为什么不能按 PDF 页码顺序读范例合集的编排逻辑通常是按控件类型或模块分类比如“基础控件”“布局管理”“对话框”“文件操作”“网络”“数据库”“图形视图”。这个顺序是教学顺序不是工程顺序。你真正写项目时需求是“我要一个能拖拽排序的列表”而不是“我要学完第三章”。所以第一步不是读是建索引。我一般会先把 PDF 的目录页导出成文本然后用一个简单脚本把章节标题、页码、关键词抽出来做成一张可搜索的表。这样你遇到具体问题时能直接搜“拖拽”“排序”“QListWidget”定位到对应范例而不是从头翻。2.2 用 Python 把目录文本转成结构化索引假设你已经把目录页复制成了一个纯文本文件toc.txt每行格式类似第3章 基础控件 ................ 45。下面这个脚本把它转成 CSV方便后续用 Excel 或 grep 检索。import re import csv # 读取目录文本按行处理 with open(toc.txt, r, encodingutf-8) as f: lines f.readlines() # 匹配 章节名 点号 页码 的模式 # 点号数量不固定页码在末尾 pattern re.compile(r^(.?)[\.\s]{3,}(\d)\s*$) rows [] for line in lines: line line.strip() if not line: continue m pattern.match(line) if m: title m.group(1).strip() page int(m.group(2)) # 从标题里提取可能的模块关键词 # 这里简单按空格和常见分隔符切分实际可按需扩展 keywords re.split(r[\s、,:], title) keywords [k for k in keywords if len(k) 1] rows.append({ title: title, page: page, keywords: |.join(keywords) }) # 写出 CSV方便后续检索 with open(qt5_index.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[title, page, keywords]) writer.writeheader() writer.writerows(rows) print(f共提取 {len(rows)} 条目录记录)这段代码的关键在正则^(.?)[\.\s]{3,}(\d)\s*$。它要求章节名和页码之间至少有 3 个点号或空白字符这样能避开正文里偶然出现的“数字点”组合。keywords字段是把标题按常见分隔符切开比如“对话框模态与非模态”会切成“对话框”“模态与非模态”。实际使用时你可以把关键词表再人工清洗一遍去掉“第X章”“实例”这类无意义词。参数说明encodingutf-8-sig是为了让 Excel 直接打开不乱码如果你的目录文本是 GBK 编码读文件时也要相应改成encodinggbk。页码字段保留整数方便后续按章节范围筛选。2.3 用索引反查三个检索入口建好 CSV 后我通常用三个入口来查第一个是控件名入口。比如你要做QTableView的自定义委托直接搜“QTableView”或“委托”定位到对应范例页码。第二个是功能入口。比如“拖拽”“剪贴板”“托盘图标”这些词可能出现在章节标题里也可能出现在小节标题里所以索引要尽量细。第三个是模块入口。Qt5 的模块划分很清晰QtWidgets、QtNetwork、QtSql、QtCharts等。如果你知道要用哪个模块直接按模块名筛能快速排除无关章节。提示不要试图把整本 PDF 转成可搜索文本再全文检索那样噪音太大。目录索引 关键词映射表效率高一个数量级。3. 从范例到工程把单个例子抽成可编译的最小 Qt5 工程3.1 识别范例的依赖边界PDF 里的范例代码通常是片段式的可能只贴了槽函数没贴头文件或者只贴了main.cpp没贴.pro文件。你要做的第一件事是判断这个范例依赖哪些 Qt 模块。常见判断依据出现QApplication、QWidget、QMainWindow→ 需要QT widgets出现QTcpSocket、QUdpSocket→ 需要QT network出现QSqlDatabase、QSqlQuery→ 需要QT sql出现QChartView、QChart→ 需要QT chartsQt5 里 charts 是独立模块出现QOpenGLWidget→ 需要QT opengl如果范例里用了Q_OBJECT宏那对应的类必须放在头文件里或者用#include xxx.moc的方式处理。我一般建议直接建标准工程结构别偷懒。3.2 最小工程模板以“带自定义委托的 QTableView”为例假设你在目录里定位到一个范例讲的是QTableView加QSpinBox委托。PDF 里可能只给了QSpinBoxDelegate类的实现和setItemDelegateForColumn的调用。你要把它补成一个完整工程。先建.pro文件# qt5_tableview_demo.pro QT core gui widgets TARGET TableViewDelegateDemo TEMPLATE app SOURCES main.cpp mainwindow.cpp spinboxdelegate.cpp HEADERS mainwindow.h spinboxdelegate.h这里QT core gui widgets是 Qt5 的标准写法widgets模块在 Qt4 时代叫guiQt5 拆出来后必须显式加上。TEMPLATE app表示生成可执行程序不是库。然后是委托类的头文件// spinboxdelegate.h #ifndef SPINBOXDELEGATE_H #define SPINBOXDELEGATE_H #include QStyledItemDelegate class SpinBoxDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit SpinBoxDelegate(QObject *parent nullptr); // 创建编辑器返回一个 QSpinBox QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem option, const QModelIndex index) const override; // 把模型数据写入编辑器 void setEditorData(QWidget *editor, const QModelIndex index) const override; // 把编辑器数据写回模型 void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex index) const override; // 调整编辑器几何尺寸 void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem option, const QModelIndex index) const override; }; #endif // SPINBOXDELEGATE_H四个虚函数是QStyledItemDelegate的标准套路createEditor决定用什么控件编辑setEditorData决定怎么把现有值填进去setModelData决定怎么把编辑结果写回模型updateEditorGeometry决定编辑器在单元格里怎么摆放。PDF 范例通常只贴其中一两个你要自己补全。实现文件// spinboxdelegate.cpp #include spinboxdelegate.h #include QSpinBox SpinBoxDelegate::SpinBoxDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem /*option*/, const QModelIndex /*index*/) const { QSpinBox *editor new QSpinBox(parent); editor-setFrame(false); editor-setMinimum(0); editor-setMaximum(100); return editor; } void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex index) const { int value index.model()-data(index, Qt::EditRole).toInt(); QSpinBox *spinBox static_castQSpinBox*(editor); spinBox-setValue(value); } void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex index) const { QSpinBox *spinBox static_castQSpinBox*(editor); spinBox-interpretText(); int value spinBox-value(); model-setData(index, value, Qt::EditRole); } void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem option, const QModelIndex /*index*/) const { editor-setGeometry(option.rect); }注意setModelData里的interpretText()调用。如果用户手动输入了数字但没按回车直接取value()可能拿到旧值。interpretText()强制解析当前文本这是血泪经验少了这一步会出现“输入了 50 但模型里还是 10”的玄学问题。主窗口里只需要建一个QStandardItemModel填几行数据然后给某一列设置委托// mainwindow.cpp 片段 #include mainwindow.h #include spinboxdelegate.h #include QTableView #include QStandardItemModel MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QTableView *tableView new QTableView(this); QStandardItemModel *model new QStandardItemModel(4, 2, this); for (int row 0; row 4; row) { for (int col 0; col 2; col) { QStandardItem *item new QStandardItem( QString(行%1 列%2).arg(row).arg(col)); model-setItem(row, col, item); } } tableView-setModel(model); // 只给第 1 列设置 SpinBox 委托 tableView-setItemDelegateForColumn(1, new SpinBoxDelegate(this)); setCentralWidget(tableView); resize(600, 400); }setItemDelegateForColumn是 Qt5 的 APIQt4 时代用setItemDelegateForColumn也有但委托基类推荐用QStyledItemDelegate而不是QItemDelegate前者会跟随系统样式后者是旧版绘制逻辑。3.3 编译与运行qmake 和 cmake 两条路如果你用 qmake在工程目录下执行qmake qt5_tableview_demo.pro make -j4 ./TableViewDelegateDemo如果你用 CMakeQt5 的 CMake 配置如下cmake_minimum_required(VERSION 3.5) project(TableViewDelegateDemo) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) find_package(Qt5 COMPONENTS Widgets REQUIRED) add_executable(TableViewDelegateDemo main.cpp mainwindow.cpp spinboxdelegate.cpp ) target_link_libraries(TableViewDelegateDemo Qt5::Widgets)CMAKE_AUTOMOC ON是关键它让 CMake 自动处理Q_OBJECT宏的 moc 生成。少了这个链接时会报undefined reference to vtable这是新手最常见的翻车点。注意如果你的 Qt5 是通过在线安装器装的find_package可能需要指定CMAKE_PREFIX_PATH比如-DCMAKE_PREFIX_PATH/opt/Qt5.15.2/gcc_64。路径按你实际安装位置改。4. 范例里的参数怎么改控件属性、信号槽连接与样式表4.1 控件属性别直接抄 PDF 里的硬编码值PDF 范例为了演示效果经常把尺寸、颜色、字体写死。比如resize(800, 600)、setFixedWidth(200)、setStyleSheet(background-color: red)。你直接抄进项目换个分辨率就崩。我一般会把这类值抽成常量或配置文件。以QTableView的列宽为例PDF 里可能写tableView-setColumnWidth(0, 150)。实际项目里你应该用horizontalHeader()-setSectionResizeMode// 让所有列自动拉伸而不是写死宽度 tableView-horizontalHeader()-setSectionResizeMode(QHeaderView::Stretch); // 如果只想让最后一列拉伸 // tableView-horizontalHeader()-setStretchLastSection(true);QHeaderView::Stretch会让所有列平分可用宽度ResizeToContents会根据内容自动调整Interactive是默认值允许用户手动拖。这三个模式的区别PDF 范例通常不展开但你调一次就能看到效果差异。4.2 信号槽连接Qt5 新语法与旧语法的取舍Qt5 支持两种连接语法。旧语法用宏connect(button, SIGNAL(clicked()), this, SLOT(onButtonClicked()));新语法用函数指针connect(button, QPushButton::clicked, this, MainWindow::onButtonClicked);新语法的好处是编译期检查参数类型不匹配直接报错而不是运行时静默失败。PDF 范例如果比较老可能还在用SIGNAL/SLOT宏。我建议你全部改成新语法除非遇到重载信号需要qOverload// 处理 QComboBox 的重载信号 connect(comboBox, qOverloadint(QComboBox::currentIndexChanged), this, MainWindow::onIndexChanged);qOverload是 Qt5.7 引入的如果你用的 Qt5 版本低于 5.7需要用static_cast来消歧义。这是版本边界别踩。4.3 样式表QSS 的作用范围和优先级PDF 里经常用setStyleSheet给单个控件上色。但 QSS 是层叠的父控件的样式会继承到子控件。比如你给QMainWindow设了background-color: gray里面的QTableView也会变灰除非你显式覆盖。我一般会用一个独立的.qss文件在main.cpp里加载// main.cpp #include QApplication #include QFile #include mainwindow.h int main(int argc, char *argv[]) { QApplication a(argc, argv); // 加载全局样式表 QFile qssFile(:/style/app.qss); if (qssFile.open(QFile::ReadOnly | QFile::Text)) { a.setStyleSheet(qssFile.readAll()); qssFile.close(); } MainWindow w; w.show(); return a.exec(); }:/style/app.qss是 Qt 资源系统路径需要你在.qrc文件里注册。用资源文件的好处是编译进二进制发布时不用带一堆散文件。QSS 里写选择器时QTableView::item控制单元格QHeaderView::section控制表头QTableView QScrollBar控制滚动条。这些选择器在 PDF 范例里可能只出现一两个你需要自己查 Qt 样式表参考文档补全。5. 避坑与排查Qt5 范例落地时最容易翻车的五个点5.1 现象编译报 “undefined reference to vtable for Xxx”原因类里写了Q_OBJECT宏但没有执行 moc 预处理。qmake 工程里头文件没加到HEADERS列表CMake 工程里没开CMAKE_AUTOMOC。解决qmake 检查.pro的HEADERS 是否包含该头文件CMake 加set(CMAKE_AUTOMOC ON)。如果还不行执行make clean后重新qmake再make旧的目标文件会缓存错误的 moc 结果。5.2 现象界面显示中文乱码原因Qt5 默认用 UTF-8 解码字符串但源文件如果保存为 GBK或者main函数里没设置编码就会乱码。解决源文件统一保存为 UTF-8无 BOM。如果必须处理 GBK 数据用QTextCodec#include QTextCodec // Qt5 里 QTextCodec 在 core 模块但需要 QT core QTextCodec *codec QTextCodec::codecForName(GBK); QString str codec-toUnicode(gbkByteArray);更彻底的做法是在main里设置#if QT_VERSION QT_VERSION_CHECK(6, 0, 0) QTextCodec::setCodecForLocale(QTextCodec::codecForName(UTF-8)); #endifQt6 移除了QTextCodec所以这段要加版本判断。如果你只用 Qt5可以不加判断但养成习惯没坏处。5.3 现象信号槽连接成功但槽函数不执行原因连接方式用了Qt::QueuedConnection但没开事件循环或者对象在连接后被销毁或者信号参数类型没注册元类型。解决先确认connect的返回值是true。如果是跨线程连接检查QMetaType::registerType是否注册了自定义类型。如果是对象销毁问题用QPointer或deleteLater管理生命周期。PDF 范例通常不涉及跨线程但实际项目里这是高频坑。5.4 现象QTableView 数据不刷新原因直接改了模型底层数据没发dataChanged信号或者用了QStandardItemModel但没调setItem。解决自定义模型时修改数据后必须发信号emit dataChanged(index(row, 0), index(row, columnCount() - 1));用QStandardItemModel时item-setText()会自动触发信号但如果你直接操作QStandardItem的私有数据就不会。这是模型视图架构的黑匣子记住“改数据必发信号”就行。5.5 现象程序退出时崩溃原因栈上对象和堆上对象的销毁顺序问题。比如QApplication在MainWindow之前销毁或者delete了已经被父对象管理的指针。解决Qt 的对象树机制会自动管理父子关系。new出来的QWidget如果指定了parent不要手动delete。main函数里QApplication a(argc, argv)是栈对象MainWindow w也是栈对象退出时先析构w再析构a顺序是对的。如果你把w改成new MainWindow就要确保在a.exec()返回后delete w否则内存泄漏。6. 进阶用法把范例改造成可复用的组件库6.1 从“抄代码”到“抽接口”当你从 PDF 里跑了十几个范例后会发现很多代码是重复的对话框的确定/取消按钮、表格的增删改查、文件选择后的路径处理。这时候应该停下来把这些重复逻辑抽成独立组件。比如“带搜索框的列表选择对话框”PDF 里可能分散在三个范例里一个讲QListWidget一个讲QLineEdit的textChanged信号一个讲QDialog的exec()。你把它们合起来// searchlistdialog.h #ifndef SEARCHLISTDIALOG_H #define SEARCHLISTDIALOG_H #include QDialog #include QStringList class QLineEdit; class QListWidget; class SearchListDialog : public QDialog { Q_OBJECT public: explicit SearchListDialog(const QStringList items, QWidget *parent nullptr); // 返回当前选中的项未选中返回空字符串 QString selectedItem() const; private slots: void onSearchTextChanged(const QString text); private: QLineEdit *m_searchEdit; QListWidget *m_listWidget; QStringList m_allItems; }; #endif实现里onSearchTextChanged用QListWidget::findItems做过滤void SearchListDialog::onSearchTextChanged(const QString text) { // 清除当前列表 m_listWidget-clear(); // 按包含关系过滤忽略大小写 for (const QString item : m_allItems) { if (item.contains(text, Qt::CaseInsensitive)) { m_listWidget-addItem(item); } } }Qt::CaseInsensitive是contains的第二个参数控制大小写敏感。PDF 范例里可能只写contains(text)默认是大小写敏感的用户搜“abc”匹配不到“ABC”体验很差。6.2 用 QTest 给范例加回归测试范例代码跑通一次不代表一直能跑。你改了 Qt 版本、换了编译器、调了样式表都可能让某个范例失效。我一般会给核心组件加QTest用例。在.pro里加QT testlib测试代码// test_searchlistdialog.cpp #include QtTest #include searchlistdialog.h class TestSearchListDialog : public QObject { Q_OBJECT private slots: void testFilter() { QStringList items {Apple, Banana, Cherry}; SearchListDialog dlg(items); // 模拟输入 an应该匹配 Banana // 这里需要访问私有控件实际项目中可以加测试友元 // 或者通过 findChild 获取 QLineEdit *edit dlg.findChildQLineEdit*(); QVERIFY(edit ! nullptr); edit-setText(an); QListWidget *list dlg.findChildQListWidget*(); QCOMPARE(list-count(), 1); QCOMPARE(list-item(0)-text(), QString(Banana)); } }; QTEST_MAIN(TestSearchListDialog) #include test_searchlistdialog.mocfindChild是 Qt 对象树提供的运行时查找按类型找子对象。QTEST_MAIN宏生成main函数#include xxx.moc是因为测试类写在了.cpp里moc 输出需要手动包含。这是 QTest 的标准写法PDF 范例通常不讲测试但你要把范例变成可维护代码测试是后悔药。6.3 版本迁移检查表Qt5 范例在 Qt5 内部的小版本之间也可能有差异。下面这张表是我踩过的坑特性引入版本替代方案qOverloadQt 5.7低于 5.7 用static_castQTextCodec::setCodecForLocaleQt 5 全系Qt6 移除改用QStringConverterQChartQt 5.7低于 5.7 需自行编译 charts 模块QOpenGLWidgetQt 5.4低于 5.4 用QGLWidgetQt::AA_EnableHighDpiScalingQt 5.6低于 5.6 手动算缩放如果你拿到一本范例书先看它基于哪个 Qt5 小版本。低于 5.7 的范例qOverload和QChart相关代码大概率跑不起来需要按表替换。6.4 我自己的习惯范例工程只留三样东西跑了这么多年范例我现在每个范例工程只保留三样东西一个能编译的.pro或CMakeLists.txt一个记录“这个范例解决什么问题、关键 API 是什么”的README.md一个能跑通的最小main.cpp。PDF 原文不存截图不存因为那些东西三个月后你根本不会看。真正有用的是你亲手调过的参数和踩过的报错。希望帮到你。本文还有配套的精品资源点击获取