资讯动态

《Qt从零入门系列(四):Qt项目结构与基础编程详解——从Hello World到对象树》

发布时间:2026/8/13 17:43:51 来源:尧图企业网站定制
学习Qt真正让人开始“感觉到门槛”的往往不是控件有多少也不是API有多复杂而是第一次打开Qt Creator后面对.pro、h、cpp、ui这些文件时突然发现一个看似简单的窗口背后竟然藏着这么多东西。所以这一篇我们不急着堆砌控件也不急着深入复杂的信号与槽而是从最经典的 Hello World 入手。先用一个按钮、一个标签把Qt程序真正跑起来再分别通过纯代码和可视化两种方式构造界面在动手的过程中理解Qt是怎样创建控件、组织对象以及响应事件的。当Hello World不再只是“会运行”而是能够说清楚它为什么这样写、每一个文件分别负责什么、对象为什么能够自动释放才算真正迈过了Qt入门的第一道门槛。接下来我们还会进一步拆解一个完整Qt项目的文件结构认识.pro、main.cpp、widget.cpp、widget.h和widget.ui各自承担的职责并在最后补上变量命名、快捷键、文档查询、坐标系以及对象树等开发中极其常见却又容易被初学者忽略的细节。这一篇不追求“学很多”而是希望把Qt最基础的那几块砖一块一块摆正。基础打牢之后后面的窗口、控件、信号与槽、布局管理才能真正做到越学越顺。目录一、创建Qt的Hello World程序1.1 使用“按钮”实现Hello World1.1.1 纯代码方式实现1.1.2 可视化方式实现1.2 使用“标签”实现Hello World1.2.1 纯代码方式实现1.2.2 可视化方式实现1.3 Hello World的多种实现方式与最佳实践1.3.1 使用输入框实现1.3.2 纯代码方式实现Hello World1.3.3 使用按钮实现Hello World1.3.3.1 图形化方式实现按钮版Hello World1.3.3.2 纯代码方式实现按钮版Hello World1.4 Qt中的内存泄漏问题1.5 初识Qt对象树1.6 Qt中的中文乱码问题1.7 小结二、Qt项目文件解析2.1 .pro工程文件解析2.2 widget.h头文件解析2.3 main.cpp主函数文件解析2.4 widget.cpp源文件解析2.5 widget.ui界面文件解析2.6 Qt界面构造方式的最佳实践2.6.1 纯代码方式实现2.6.2 按钮方式实现2.6.3 两种按钮版Hello World实现方式对比2.6.4 实际开发中代码构界面与可视化构界面如何选择三、初识Qt收尾——Qt编程注意事项3.1 Qt变量命名风格3.1.1 命名规范与设计思路3.1.2 命名最佳实践3.2 Qt常用快捷键3.3 Qt官方文档查询方法3.3.1 使用Qt帮助文档3.3.2 查询文档时的注意事项3.4 认识Qt坐标系3.4.1 Qt坐标系3.4.2 术语扫盲——什么是像素3.5 深入理解Qt对象树3.5.1 Qt对象模型与对象树核心知识3.5.2 Qt对象树的析构机制及注意事项3.5.3 核心总结与最佳实践3.5.4 对象树析构过程演示一、创建Qt的Hello World程序1.1 使用“按钮”实现Hello World1.1.1 纯代码方式实现效果如下所示1.1.2 可视化方式实现1双击widget.ui文件2拖拽控件至ui界面窗口并修改内容3构建并运行效果如下所示1.2 使用“标签”实现Hello World1.2.1 纯代码方式实现实际演示效果1.2.2 可视化方式实现1双击“widget.ui”文件2拖拽“标签”至UI设计界面中并双击修改标签内容3实现效果如下图所示1.3 Hello World的多种实现方式与最佳实践label“标签”界面上一个用来显示内容的字符串控件。Qt有自己实现的一套标准库“自己造轮子”1.3.1 使用输入框实现1.3.2 纯代码方式实现Hello World1.3.3 使用按钮实现Hello World1.3.3.1 图形化方式实现按钮版Hello World1.3.3.2 纯代码方式实现按钮版Hello World1.4 Qt中的内存泄漏问题1.5 初识Qt对象树前端开发网页开发中也存在类似的思想例如DOM文档对象模型。浏览器会将网页中的各种元素抽象成一棵树本质上也是一种N叉树结构通过这种层级关系将页面中的标签、节点以及它们之间的关系组织起来。Qt中同样采用了类似的设计思想引入了对象树机制。Qt会将界面中的各种对象按照父子关系组织成一棵树通过这种结构统一管理对象的生命周期和层级关系。1.6 Qt中的中文乱码问题这里有一个查找字符编码的网站大家可以尝试一下utf8Qt中有一个东西QString是可以帮助我们自动的处理编码方式的。不止是QStringQt也提供了专门用来打印日志的工具也能自动处理编码方式。使用qDebug还有一个好处1.7 小结两张图小结一下二、Qt项目文件解析在使用Qt Creator创建一个新的Qt工程后工程目录中会自动生成一系列项目文件。其中.pro文件是Qt工程中最核心的配置文件之一它负责描述整个项目的基本信息、编译配置以及工程结构。2.1 .pro工程文件解析工程创建完成后在项目目录列表中可以看到一个后缀为.pro的文件例如MyProject.pro。其中pro是Project项目的缩写.pro文件就是Qt工程文件。.pro文件本质上是qmake的工程配置文件它用于告诉qmake当前项目包含哪些源文件、头文件、资源文件以及项目需要使用哪些编译选项。在Qt的传统构建流程中.pro文件并不会直接参与程序编译而是作为输入文件交给qmakeqmake会读取.pro文件中的配置信息根据不同平台生成对应的Makefile之后编译工具如make、nmake等再根据Makefile完成源代码的编译和链接。可以简单理解.pro文件负责描述“这个项目是什么样的”qmake负责根据描述生成编译规则Makefile负责真正执行编译过程。双击打开这个文件核心内容如下配置项说明QT core gui告诉Qt我们需要core和gui这两个模块。没有它们窗口都画不出来。greaterThan(QT_MAJOR_VERSION, 4): QT widgets一条带条件的语句。如果Qt主版本号大于4就把widgets模块也捎上。这是Qt4到Qt5的历史遗留问题widgets在Qt5里从 gui 模块独立出来了老项目得靠这行兜底。TARGET QtFirst最终生成的可执行文件叫什么名。编译完你会看到QtFirst.exe。TEMPLATE app告诉qmake这是个应用程序不是库不是插件是个能跑起来的程序。SOURCES main.cpp\widget.cpp项目里需要编译的源文件清单。新增.cpp文件时别忘了往这加不然编译器压根不知道它的存在。HEADERS widget.h项目里的头文件清单。.pro文件的写法.pro文件本质上就是一张工程清单告诉qmake这个项目叫什么、用了哪些Qt模块、要编译哪些源文件。写法不复杂逐条过一遍就清楚了。1. 注释从#开始到这一行结束。跟shell脚本一个习惯。2. 模块声明QT core gui告诉Qt链接core和gui这两个基础模块。没有它们窗口都画不出来。3. 版本兼容greaterThan(QT_MAJOR_VERSION, 4): QT widgets这行是个条件判断。意思是如果Qt主版本号大于4也就是Qt5及以上就额外加上widgets模块。因为widgets在Qt5里从gui模块里拆了出来老项目需要这行兜底。如果你的项目只打算跑在Qt5上直接写QT widgets也行但为了兼容性建议保留Qt Creator自动生成的这个写法。4. 目标文件名TARGET QtDemo。编译完输出的可执行程序叫什么就在这里定。不写的话qmake会用默认名。5. 模板类型TEMPLATE app。告诉qmake要给哪种项目生成Makefile。可选的模板有模板说明app应用程序。这是默认值不写TEMPLATE就自动按这个来。lib库文件。vcapp给Visual Studio用的应用程序工程文件。vclib给Visual Studio用的库工程文件。subdirs多目录工程。能进入子目录递归调用make适合大型项目的分模块管理。6. 源文件清单SOURCES main.cpp widget.cpp。项目里所有.cpp文件都列在这。新增源文件时别忘了补一行不然编译器根本不知道它的存在。7. 头文件清单HEADERS widget.h。头文件也在这里登记。虽然头文件不参与编译但列进去后Qt Creator能在项目视图里把它们展示出来跳转和补全也会更顺畅。8. 资源文件RESOURCES painter.qrc。图片、图标、翻译文件等资源都通过.qrc文件管理在这里声明。9. UI 设计文件FORMS widget.ui。用Qt Designer拖出来的界面文件在这里登记。10. 配置信息CONFIG c11。CONFIG用来告诉qmake 一些全局配置选项。比如c11就是启用C11特性支持。还可以用CONFIG控制很多其他编译选项用到了再查就行。2.2 widget.h头文件解析在Qt中信号与槽这套机制不是默认就开着的。想用上它类里面必须加上Q_OBJECT宏它就像给这个类插上了信号槽的“天线”。没有它后面的connect根本找不到你的信号和槽程序跑起来也会莫名其妙地报错。而那个绕不开的指针Ui::Widget *ui;它的来头也要说清楚。这个指针是用前面声明的namespace Ui里的Widget类定义的它指向的正是我们用Qt Designer可视化拖拽出来的那个界面。以后想访问界面上的按钮、标签、输入框全都要通过这个ui指针去够到。简单说ui就是操作界面的总把手界面上的每个组件都攥在它手里。2.3 main.cpp主函数文件解析随手新建一个Qt工程打开main.cpp你都会看到下面这段自动生成的代码。别急着跳过它可是整个程序启动逻辑的起点。新建一个Qt工程后main.cpp里那几行自动生成的代码其实是一套固定不变的启动模板。别急着跳过我们拆开看每一行到底在做什么。Qt 标准类头文件注意Qt的头文件名没有.h后缀。这是Qt的命名习惯类名就是头文件名。比如QApplication这个类就对应QApplication头文件一一对应清晰利落。QApplication类这是整个GUI程序的“大管家”。一个程序里有且仅有一个QApplication对象它负责管理应用程序的控制流和主要设置其中最核心的就是主事件循环。没有它程序就不知道该怎么响应用户操作。窗口对象实例化Widget w; 这行代码创建了一个具体的窗口对象。窗口是什么样、上面有哪些控件全由这个w说了算。显示窗口w.show();调用 show 函数把窗口真正显示到屏幕上。这一步相当于拉开帷幕让窗口从内存里走到用户眼前。消息循环a.exec();是整段代码的灵魂。程序在这里进入消息循环Qt开始监听各种事件鼠标点击、键盘输入、窗口重绘等等并把它们一个个分发给对应的窗口部件去处理。直到用户关闭窗口、应用程序退出时exec()才会返回一个值整个程序也随之结束。2.4 widget.cpp源文件解析widget.cpp是Widget类的实现文件。窗体上要实现的所有功能按钮的响应逻辑、数据的处理流程、界面的动态刷新几乎都集中在这一个文件里。如果说widget.h是窗体的骨架声明那么widget.cpp就是血肉所在所有真正的行为都在这一具躯体里落地。2.5 widget.ui界面文件解析widget.ui是窗体界面的定义文件本质是一个XML文件。窗口上所有组件的属性、布局关系以及信号与槽的关联全部以XML的形式记录在这个文件里。你在Qt Designer里拖一拖控件、改一改属性Qt会自动帮你把这些操作解析并保存成widget.ui。设计界面的时候你完全不用关心这个文件内部是怎么写的只要在UI设计器里可视化地拖拽、配置就行。生成的XML交给Qt去处理你专心把界面搭好。2.6 Qt界面构造方式的最佳实践完成一个Hello World路子不止一条。Qt里的控件琳琅满目能扛起“显示第一行文字”这个任务的远比你想象的多标签、按钮、文本框甚至一个弹窗都能成为你亮相的第一个舞台。接下来我们挑几种最典型的控件看看它们各自怎么把“Hello World”搬到屏幕上。2.6.1 纯代码方式实现2.6.2 按钮方式实现Qt中的信号槽机制2.6.3 两种按钮版Hello World实现方式对比2.6.4 实际开发中代码构界面与可视化构界面如何选择三、初识Qt收尾——Qt编程注意事项3.1 Qt变量命名风格3.1.1 命名规范与设计思路Qt的命名规则跟我们之前习惯的那套不太一样。直接看表名称类型命名规则类名首字母大写单词与单词之间首字母也大写函数名、变量名首字母小写单词与单词之间首字母大写没错Qt 偏爱的是驼峰命名法。这跟我们之前一直用的蛇形命名法my_function这种路子完全反着来。习惯了蛇形刚上手Qt 时总会有那么一点别扭但没事写几天手指头就记住了。入乡随俗写Qt代码就按Qt的规矩来。3.1.2 命名最佳实践3.2 Qt常用快捷键这些快捷键是你和Qt Creator熟络起来的捷径。刚开始可能记不住几个但有几个每天都会被按到用着用着就长在手指上了。功能快捷键注释/取消注释当前行或选中行Ctrl /运行项目Ctrl R编译项目Ctrl B缩放编辑器字体Ctrl 鼠标滚轮查找文本Ctrl F向上 / 向下移动当前行Ctrl Shift ↑ / ↓查看光标所在符号的帮助文档F1自动对齐代码Ctrl I在同名的头文件.h与源文件.cpp之间切换F4快速为函数生成对应的定义在声明处使用Alt Enter3.3 Qt官方文档查询方法3.3.1 使用Qt帮助文档打开Qt帮助文档的入口有三个平时用哪个顺手就选哪个效果完全一样。光标定位一键直达把光标停在你想查询的类名或方法名上直接按F1对应的帮助文档马上弹出来。这是最快、最不打断思路的方式。侧边栏入口在Qt Creator左侧边栏里用鼠标点一下“帮助”按钮进入帮助模式就会看到下图这样的帮助界面。适合慢慢翻文档、系统学习某个模块时用。点击“帮助”之后出现如下图示界面独立帮助程序找到Qt Creator的安装目录进入bin文件夹双击里面的assistant.exe。它会启动一个独立的Qt助手程序不依赖Qt Creator也能查文档。适合在不开 IDE 的时候单独查资料。还可以进行检索使用示例新建项目在新建的项目中使用Qt中的“QpushButton”控件。打开帮助手册在“索引”里面输入“QpushButton”。3.3.2 查询文档时的注意事项尽量别依赖中文文档。阅读英文文档是每个程序员绕不开的基本功这一关迟早要过没得躲。Qt的官方文档在技术文档里算得上出类拔萃用词清晰示例到位只要耐着性子读下去看懂个八九成并不难。我们往后的学习和工作中翻文档会是家常便饭。与其等到那时候再硬着头皮上不如从现在就开始练。3.4 认识Qt坐标系3.4.1 Qt坐标系Qt的坐标系跟我们数学课上习惯的那套笛卡尔坐标系不太一样。它的原点固定在左上角也就是(0, 0)。从这里出发X轴向右增长Y轴向下增长。换句话说越往右X越大越往下Y越大。屏幕上任何一个控件的位置都由这样一对(x, y)坐标唯一确定。把这点刻在脑子里后面设置控件位置、处理鼠标点击事件时就不会因为方向感错乱而犯迷糊了。这里有个容易忽略的细节如果窗口是嵌套在另一个窗口里面的那么它的坐标就不是相对整个屏幕左上角来算的了而是相对它的父窗口左上角来算的。换句话说子窗口的(0,0) 点落在父窗口的坐标系里而不是整个屏幕的坐标系里。示例用 Qt 坐标系设置控件位置掌握了坐标系我们来看看代码里怎么用。下面这段代码演示了如何在一个窗口里用move()函数把一个按钮摆到指定的坐标位置上运行结果如下图示3.4.2 术语扫盲——什么是像素3.5 深入理解Qt对象树3.5.1 Qt对象模型与对象树核心知识Qt的对象管理靠的是一棵看不见的“对象树”。这套机制是整个Qt内存管理的地基也是Qt跟传统C裸指针管理最不一样的地方。我们把它的核心逻辑拆成几块核心概念说明与机制组织方式Qt中的QObject以对象树的形式组织。一个父对象下面挂着一堆子对象形成清晰的父子层级。父对象指针parent创建QObject对象时构造函数里可以传入一个父对象指针从此确立父子关系。自动管理子对象一旦出生就会自动登记到父对象的children()列表里。父对象析构时会顺手把自己名下所有子对象一并析构掉。GUI 应用QWidget是所有可视化组件的基类它也继承自QObject所以同样吃这套对象树规则。子组件会显示在父组件的坐标系里还会被父组件的边界裁剪。内存管理对象树自动处理关联对象的内存释放内存泄漏的风险因此大幅降低。删除父对象它下面的所有子对象自动跟着走单独删除某个子对象它也会自觉从父对象的子对象列表里除名。析构保证Qt保证没有任何一个QObject会被删除两次。这个安全底线正是靠对象树的析构顺序来兜住的。设计意义这套机制让GUI程序设计更直观、更安全。删除一个窗口它里面的按钮、标签、输入框等所有子部件全都会跟着一起被清理干净不用你手动一个个delete。简而言之Qt的对象树就干了一件事让你在创建对象时给它认个“爹”parent。日后你把这个“爹”删掉它名下的所有“儿子”children都会自动跟着一起走不用你挨个手动清理。省心还安全。栈上局部对象的析构顺序与对象树机制情境析构顺序关键机制结果父子对象都是栈上的局部对象按创建顺序的逆序来后创建的先死先创建的后死子对象比如quit析构时会主动把自己从父对象比如window的children()名单里摘出去父对象随后析构时名单里已经查无此子自然不会重复删除double delete也就无从谈起核心要点拆开揉碎栈上对象一样归对象树管。别以为只有new出来的堆对象才认这个父子关系。栈上直接创建的局部对象只要构造函数里传了parent一样被对象树牢牢盯着。C的标准析构顺序是天然护栏。局部对象析构时顺序跟创建顺序相反这看似是语言的死规矩却恰好保证了一个关键前提子对象永远先于父对象离开。子对象临死前会自首。它析构时不是悄无声息地消失而是会主动找到父对象把自己从children()列表里注销掉。等父对象轮到析构时翻遍名单也找不到这个已经“销户”的孩子自然就不会再动它第二次。结论对象树机制跟C的析构顺序天衣无缝地咬合在一起。不管对象是堆上new的还是栈上现造的都能稳稳躲开内存泄漏和双重删除这两个大坑。一句话记住它先出生的后离世后出生的先离世。子对象咽气前会自觉从父对象的“户口本”上把自己的名字划掉。等父对象走的时候翻开本子一看那孩子早就不在了于是挥挥手不带走一片云彩。整套流程干净得没有一丝残留。但是如果我们使用下面的代码3.5.2 Qt对象树的析构机制及注意事项对象树这套机制用对了是省心神器用错了就是崩溃陷阱。下面把正常写法和一种极易踩雷的错误写法摆在一起对比看完你就知道坑在哪了。对比项正常情况前面讲过的安全写法问题情况顺序反了直接崩溃创建顺序先创建父对象window再创建子对象quit并在quit的构造函数里把window指定为它的父对象。先创建子对象quit之后才创建父对象window再通过setParent或后续操作让window变成quit的父对象。C 析构顺序按创建顺序的逆序走quit后创建先析构window先创建后析构。同样逆序但这次是window后创建先析构quit先创建后析构。对象树行为1. quit析构时自己主动从window的children()列表里注销不给老爹添麻烦。2. window析构时翻开名单一看儿子已经没了安心闭眼。1. window先析构按照对象树规则它会把children()列表里的所有对象包括quit挨个删除一遍。2. 等quit作为局部变量超出作用域时它又会被第二次析构。最终结果安全。每个对象只死一次符合C标准程序平稳落地。崩溃。quit被杀了两次double delete程序当场暴毙。这个坑的根源就一句话别让父对象比子对象后创建。正常逻辑是“先有爹后有儿子”析构时儿子先走爹后走刚好安全反过来搞爹先走的时候已经把儿子带走了儿子自己再走一次不死才怪。写Qt代码时记住这条顺序先建父再建子让父在子前面出生。这样对象树和C析构顺序就能默契配合保你平安。3.5.3 核心总结与最佳实践问题根源一句话就能说透Qt对象树能帮你管内存但有个前提父对象的命必须比子对象长。一旦父子俩都在栈上你就得死守一条铁律父对象必须晚于子对象析构。顺序一乱子对象就会被删两次程序当场崩给你看。这类析构顺序问题调试起来极其头疼。所以Qt开发里最好养成两个习惯能在出生时认爹就别拖到后面。创建对象时直接通过构造函数把父对象传进去关系一早定死别等后面再用setParent补救。堆上创建大胆用new。有父子关系的对象优先扔到堆里让Qt对象树统一接管它们的生命周期。你只管把父对象删对它名下的子对象会一个不落地自动释放。一句话指南Qt界面对象用new创建顺手指定好Parent剩下的交给对象树。 省心稳当不背锅。3.5.4 对象树析构过程演示Qt对象树如图所示创建一个新工程并编译运行生成如下窗口选中工程名鼠标右键----“add new…”或“添加新文件”选择“choose…”弹出如下界面点击“下一步”弹出如下对话框点击“完成”之后手动创建类的头文件以及源文件会自动添加到目标工程中修改头文件编写源文件编译并运行当关闭弹出的对话框时就会自动调用按钮的析构函数观察析构函数的执行顺序执行结果如下所示执行结果分析注意这里有个很容易绕晕的点调用析构函数和释放内存其实是两码事。1内存释放顺序在对象树里内存释放永远遵循“先子节点、后父节点”的顺序。先清理孩子再清理父亲这条规矩是写死的。2析构函数调用顺序这个顺序跟内存释放并不一定同步。有时候子节点的析构函数反而会在父节点之后才执行。别想当然地以为“先释放内存就一定先调析构”两者之间没有必然的先后绑定。3关键区分调用析构函数跟实际把内存归还给系统是两个独立的过程。一个是对象临终前交代后事一个是把遗体火化下葬动作不同时机也可能不同不能混为一谈。一句话总结对象树中内存释放总是从子节点走向父节点但析构函数的调用顺序可能跟它不一致。二者是两套独立操作谁先谁后别搞混了。如果这篇文章对你有帮助欢迎点赞、收藏、关注三连支持。你的每一次反馈都是我继续肝下去的最大动力。下一篇见。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价