【QT】Qt 从零上手:Hello World、项目文件与实战避坑指南

🎬 个人主页艾莉丝努力练剑
专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • [1 ~> 创建Qt的Hello World程序](#1 ~> 创建Qt的Hello World程序)
    • [1.1 使用"按钮"实现](#1.1 使用“按钮”实现)
      • [1.1.1 纯代码方式实现](#1.1.1 纯代码方式实现)
      • [1.1.2 可视化操作实现](#1.1.2 可视化操作实现)
    • [1.2 使用"标签"实现](#1.2 使用“标签”实现)
      • [1.2.1 纯代码方式实现](#1.2.1 纯代码方式实现)
      • [1.2.2 可视化操作实现](#1.2.2 可视化操作实现)
    • [1.3 最佳实践](#1.3 最佳实践)
      • [1.3.1 使用输入框的方式实现](#1.3.1 使用输入框的方式实现)
      • [1.3.2 纯代码方式实现hello world](#1.3.2 纯代码方式实现hello world)
      • [1.3.3 按钮方式实现](#1.3.3 按钮方式实现)
        • [1.3.3.1 通过图形化的方式实现按钮版本的hello world](#1.3.3.1 通过图形化的方式实现按钮版本的hello world)
        • [1.3.3.2 通过纯代码的方式实现按钮版本的hello world](#1.3.3.2 通过纯代码的方式实现按钮版本的hello world)
    • [1.4 内存泄漏问题](#1.4 内存泄漏问题)
    • [1.5 浅解对象树问题](#1.5 浅解对象树问题)
    • [1.6 中文乱码问题](#1.6 中文乱码问题)
    • [1.7 小结一下](#1.7 小结一下)
  • [2 ~> 项目文件解析](#2 ~> 项目文件解析)
    • [2.1 .pro文件解析](#2.1 .pro文件解析)
    • [2.2 widget.h文件解析](#2.2 widget.h文件解析)
    • [2.3 main.cpp文件解析](#2.3 main.cpp文件解析)
    • [2.4 widget.cpp 文件解析](#2.4 widget.cpp 文件解析)
    • [2.5 widget.ui文件解析](#2.5 widget.ui文件解析)
    • [2.6 最佳实践](#2.6 最佳实践)
      • [2.6.1 纯代码的方式实现](#2.6.1 纯代码的方式实现)
      • [2.6.2 按钮的方式实现](#2.6.2 按钮的方式实现)
      • [2.6.3 对比两种实现按钮版本hello world的方式](#2.6.3 对比两种实现按钮版本hello world的方式)
      • [2.6.4 实际开发中,是通过代码的方式构造界面为主,还是通过图形化界面的方式构造界面为主](#2.6.4 实际开发中,是通过代码的方式构造界面为主,还是通过图形化界面的方式构造界面为主)
  • [3 ~> 初识Qt收尾:Qt编程注意事项](#3 ~> 初识Qt收尾:Qt编程注意事项)
    • [3.1 变量命名风格](#3.1 变量命名风格)
      • [3.1.1 理论](#3.1.1 理论)
      • [3.1.2 最佳实践](#3.1.2 最佳实践)
    • [3.2 快捷键](#3.2 快捷键)
    • [3.3 查询文档的方式](#3.3 查询文档的方式)
      • [3.3.1 使用帮助文档](#3.3.1 使用帮助文档)
      • [3.3.2 注意事项](#3.3.2 注意事项)
    • [3.4 认识Qt坐标系](#3.4 认识Qt坐标系)
      • [3.4.1 Qt坐标系](#3.4.1 Qt坐标系)
      • [3.4.2 术语扫盲:像素](#3.4.2 术语扫盲:像素)
    • [3.5 详解对象树问题](#3.5 详解对象树问题)
      • [3.5.1 Qt 对象模型(对象树)核心知识点总结](#3.5.1 Qt 对象模型(对象树)核心知识点总结)
      • [3.5.2 Qt 对象树析构机制对比与注意事项](#3.5.2 Qt 对象树析构机制对比与注意事项)
      • [3.5.3 核心总结与最佳实践](#3.5.3 核心总结与最佳实践)
      • [3.5.4 对象树演示](#3.5.4 对象树演示)
  • 结尾


1 ~> 创建Qt的Hello World程序

1.1 使用"按钮"实现

1.1.1 纯代码方式实现

效果如下所示:

1.1.2 可视化操作实现

(1)双击:"widget.ui"文件;

(2)拖拽控件至ui界面窗口并修改内容;

(3)构建并运行,效果如下所示:

1.2 使用"标签"实现

1.2.1 纯代码方式实现

实际演示效果:

1.2.2 可视化操作实现

(1)双击:"widget.ui"文件;

(2)拖拽"标签"至UI设计界面中,并双击修改标签内容;

(3)实现效果如下图所示:

1.3 最佳实践

label"标签":界面上一个用来显示内容的字符串控件。

Qt有自己实现的一套标准库("自己造轮子"):


1.3.1 使用输入框的方式实现

1.3.2 纯代码方式实现hello world

1.3.3 按钮方式实现

1.3.3.1 通过图形化的方式实现按钮版本的hello world
1.3.3.2 通过纯代码的方式实现按钮版本的hello world

1.4 内存泄漏问题

1.5 浅解对象树问题

前端开发(网页开发)也涉及到类似的对象树(DOM),本质上也是一个树形结构(N叉树),通过树形结构把界面上的各种元素组织起来。

Qt中也是类似,也是搞了一个对象树------也是N又树,把界面上的各种元素组织起来。



1.6 中文乱码问题

这里有一个查找字符编码的网站,大家可以尝试一下:utf8

演示结果如下所示:

  • Qt中有一个东西,QString,是可以帮助我们自动的处理编码方式的。

  • 不止是QString,Qt也提供了专门用来打印日志的工具,也能自动处理编码方式。

使用qDebug,还有一个好处:

1.7 小结一下

两张图小结一下:


2 ~> 项目文件解析

2.1 .pro文件解析

工程新建好之后,在工程目录列表中有一个后缀为".pro"的文件,".pro"文件就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件。如图所示:

双击进入该文件,该文件的核心内容如下:

配置项 说明 内容/值
QT += core gui 包含 Qt 的 core 和 gui 模块 core, gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 如果 Qt 主版本大于 4,则额外包含 widgets 模块 条件包含 widgets
TARGET = QtFirst 生成的应用程序名称 QtFirst.exe
TEMPLATE = app 项目模板类型为应用程序 app
SOURCES += main.cpp\ widget.cpp 项目中包含的源文件 main.cpp, widget.cpp
HEADERS += widget.h 项目中包含的头文件 widget.h

".pro"文件的写法如下:

  • 1、注释:从"#"开始,到这一行结束;

  • 2、QT += core gui // Qt包含的模块(Qt5包含的模块)如下图所示:

  • 3、greaterThan(QT_MAJOR_VERSION , 4):QT += widgets这条语句的含义是,如果QT_MAJOR_VERSION大于4(也就是当前使用的Qt5及更高版本)需要增加widgets模块。如果项目仅需支持Qt5,也可以直接添加"QT += widgets"一句。不过为了保持代码兼容,最好还是按照QtCreator生成的语句编写。

  • 4、指定生成的应用程序名:TARGET = QtDemo

  • 5、TEMPLATE = app (// 模板)。告诉qmake为这个应用程序生成哪种makefile。下面是可供选择的模板:

模板类型 说明
app 建立一个应用程序的 makefile。这是默认值,所以如果模板没有被指定,这个将被使用。
lib 建立一个库的 makefile。
vcapp 建立一个应用程序的 VisualStudio 项目文件。
vclib 建立一个库的 VisualStudio 项目文件。
subdirs 这是一个特殊的模板,它可以创建一个能够进入特定目录的 makefile 并且为它调用 make 的 makefile。
  • 6、工程中包含的源文件:SOURCES += main.cpp/widget.cpp

  • 7、工程中包含的头文件:HEADERS += widget.h

  • 8、工程中包含的资源文件:RESOURCES += painter.qrc

  • 9、工程中包含的"ui" 设计文件:FORMS += widget.ui

  • 10、配置信息:CONFIG += C++11(使用c++11的特性)CONFIG用来告诉qmake关于应用程序的配置信息。

2.2 widget.h文件解析

在Qt中,如果要使用信号与槽(signalslot)的机制就必须加入Q_OBJECT宏;

**Ui::Widget *ui;**这个指针是用前面声明的namespaceUi里的Widget类定义的,所以指针ui是指向可视化设计的界面,后面要访问界面上的组件,都需要通过这个指针ui去访问。

2.3 main.cpp文件解析

使用**Qt Creator**新建任意工程之后,main.cpp文件中都会自动生成如下代码:

要点 说明
Qt 标准类头文件 系统提供的标准类名声明头文件没有 .h 后缀
Qt 类与头文件关系 一个类对应一个头文件,类名就是头文件名
QApplication 类 应用程序类,有且仅有一个应用程序对象,管理 GUI 应用程序的控制流和主要设置,包含主事件循环
窗口对象实例化 myWidget w; // 实例化窗口对象
显示窗口 w.show(); // 调用 show 函数显示窗口
消息循环 a.exec(); // 程序进入消息循环,Qt 处理事件并传递给窗口部件,当应用程序退出时 exec() 返回值

2.4 widget.cpp 文件解析

widget.cpp文件是类Widget的实现代码,所有在窗体上要实现的功能添加在此文件中:

2.5 widget.ui文件解析


widget.ui 是窗体界面定义文件,是一个XML文件,定义了窗口上的所有组件的属性设置、布局,及其信号与槽函数的关联等。用UI设计器可视化设计的界面都由Qt自动解析,并以XML文件的形式保存下来。在设计界面时,只需在UI设计器里进行可视化设计即可,而不用管widget.ui文件是怎么生成的。

2.6 最佳实践

完成Hello World可以通过很多种控件来实现:

2.6.1 纯代码的方式实现


2.6.2 按钮的方式实现


Qt中的信号槽机制:


2.6.3 对比两种实现按钮版本hello world的方式

2.6.4 实际开发中,是通过代码的方式构造界面为主,还是通过图形化界面的方式构造界面为主


3 ~> 初识Qt收尾:Qt编程注意事项

3.1 变量命名风格

3.1.1 理论

名称类型 命名规则
类名 首字母大写,单词和单词之间首字母大写
函数名及变量名 首字母小写,单词和单词之间首字母大写

Qt偏好驼峰命名法,这一点不像咱们之前的内容中一直是偏好蛇形命名方法的。

3.1.2 最佳实践

3.2 快捷键

功能 快捷键
注释/取消注释当前行或选中行 Ctrl + /
运行项目 Ctrl + R
编译项目 Ctrl + B
缩放编辑器字体 Ctrl + 鼠标滚轮
查找文本 Ctrl + F
向上/向下移动当前行 Ctrl + Shift + ↑ / ↓
查看光标所在符号的帮助文档 F1
自动对齐代码 Ctrl + I
在同名的头文件(.h)与源文件(.cpp)之间切换 F4
快速为函数生成对应的定义(在声明处使用) Alt + Enter

3.3 查询文档的方式

3.3.1 使用帮助文档

打开帮助文档有三种方式,实际编程中使用哪种都可以。

  • 1、光标放到要查询的类名/方法名上,直接按F1
  • 2、Qt Creator左侧边栏中直接用鼠标单击 "帮助" 按钮:

点击"帮助"之后,出现如下图示界面:

  • 3、找到Qt Creator的安装路径,在"bin"目录下找到assistant.exe这个程序,双击打开:

还可以进行检索:

使用示例:

  • 1、新建项目,在新建的项目中使用Qt中的"QpushButton"控件。

  • 2、打开帮助手册,在"索引"里面输入"QpushButton"。

3.3.2 注意事项

不要使用中文文档!阅读英文文档是每个程序员必备的专业技能,必须要练,不能退缩,Qt的文档从通俗易懂的角度来说,是技术类文档中非常出类拔萃的,只要大家稍微有点耐心,基本都能读懂个八九不离十。

咱们在后续的学习和工作中,也会经常翻阅文档。

3.4 认识Qt坐标系

3.4.1 Qt坐标系

坐标体系:以左上角为原点(0,0),X向右增加,Y向下增加。

对于嵌套窗口,其坐标是 相对于父窗口来说 的。

示例:使用Qt中的坐标系设置控件的位置:

运行结果如下图示:

3.4.2 术语扫盲:像素

3.5 详解对象树问题

3.5.1 Qt 对象模型(对象树)核心知识点总结

核心概念 说明与机制
组织方式 Qt 中的 QObject 以对象树的形式组织。
父对象指针 (parent) 创建 QObject 对象时,可通过构造函数传入一个父对象指针。
自动管理 子对象会自动添加到父对象的 children() 列表中。父对象析构时,其所有子对象也会被自动析构。
GUI 应用 QWidget(所有可视化组件的基类)继承自 QObject,因此也遵循对象树规则。子组件会显示在父组件的坐标系中,并受其边界裁剪。
内存管理 对象树机制自动处理关联对象的内存释放,极大减少了内存泄漏的风险。 • 删除父对象 → 自动删除其所有子对象。 • 删除子对象 → 自动从其父对象的子对象列表中移除。
析构保证 Qt 保证没有 QObject 会被删除两次,这是由对象树的析构顺序保证的。
设计意义 使 GUI 程序设计更直观、安全(例如,删除一个窗口时,其内部的按钮、标签等子部件会自动被清理)。

简而言之,Qt 的对象树让你在创建对象时指定一个 "爹"(parent) 。当你删掉这个"爹"时,它所有的 "儿子们"(children) 都会被自动清理掉,省心又安全。

栈上局部对象的析构顺序与对象树机制

情境 析构顺序 关键机制 结果
父子对象均为栈上局部对象 创建顺序的逆序 析构 (后创建的先析构,先创建的后析构) 子对象(quit)析构时,会自动从父对象(window)的 children() 列表中移除 父对象析构时,其子对象列表已更新,不会再次删除该子对象,从而避免双重删除(double delete)

核心要点总结:

  1. 栈上对象同样受对象树管理:即使对象在栈上创建(局部对象),父子关系依然有效。
  2. C++标准析构顺序 :局部对象的析构顺序与创建顺序相反,这保证了子对象先于父对象析构
  3. 安全的自动管理
    • 子对象析构时,会自动通知父对象,将自己从父对象的子对象列表中移除。
    • 父对象随后析构时,由于子对象已不在其列表中,因此不会尝试再次删除它。
  4. 结论 :Qt 的对象树机制与 C++ 的标准析构顺序相结合,确保了无论对象在堆上还是栈上创建,都能安全地避免内存泄漏和双重删除

简单理解:先出生的(父对象)后死亡,后出生的(子对象)先死亡。在子对象"死亡"时,它会主动从父对象的"家庭成员名单"中注销,这样父对象"死亡"时就不会再处理这个已注销的子对象了。

但是,如果我们使用下面的代码:

3.5.2 Qt 对象树析构机制对比与注意事项

为了清晰理解 Qt 对象树在不同情况下的行为,避免程序崩溃,现将正常情况问题情况对比如下:

对比项 正常情况 (前文所述) 问题情况 (本文所述)
对象创建方式 父对象 (window) 与子对象 (quit) 均在栈上创建。 父对象 (window) 与子对象 (quit) 均在栈上 创建,但创建顺序相反
关键前提 子对象 (quit) 的创建晚于 父对象 (window)。 即:先创建 window,后创建 quit 并指定 window 为其父对象。 父对象 (window) 的创建晚于 子对象 (quit)。 即:先创建 quit,后创建 window 并(通过 setParent 或后续构造)成为 quit 的父对象。
C++ 析构顺序 按创建顺序的逆序:先析构 quit (子),后析构 window (父)。 按创建顺序的逆序:先析构 window (父),后析构 quit (子)。
对象树行为 1. quit 析构时,自动从 windowchildren() 列表中移除。 2. window 析构时,其子对象列表已为空。 1. window 先析构,触发其析构函数自动删除 children() 列表中的所有对象(即 quit)。 2. quit 作为局部变量,在后续超出作用域时,会第二次被析构
最终结果 安全。每个对象仅被析构一次,符合 C++ 标准。 程序崩溃 。因 quit 被析构两次,违反 C++ 规则。

3.5.3 核心总结与最佳实践

总结问题根源: Qt 对象树提供的自动内存管理 ,其安全性依赖于一个前提:父对象的生命周期应完全覆盖其子对象 。当父子对象同为栈上局部变量时,必须严格保证父对象晚于所有子对象被析构,否则就会引发"双重删除"的崩溃问题。

为避免此类难以调试的析构顺序问题,在 Qt 开发中应养成以下习惯:

  • 1、 在构造时指定 Parent:尽可能在创建对象时,通过构造函数参数直接指定其父对象。

  • 2、 大胆在堆上创建 :对于具有父子关系的对象,优先使用 new堆(heap) 上创建,并由 Qt 对象树统一管理生命周期。只需保证父对象被正确删除,其所有子对象便会自动安全释放。

一句话指南 :对于 Qt 界面对象,用 new 创建并指定好 Parent,把析构的麻烦事交给对象树。

3.5.4 对象树演示

Qt对象树如图所示:

  • 1、创建一个新工程并编译运行,生成如下窗口:
  • 2、选中工程名,鼠标右键---->"add new..."(或"添加新文件"):
  • 3、选择"choose...",弹出如下界面:
  • 4、点击"下一步",弹出如下对话框:
  • 5、点击"完成"之后,手动创建类的头文件以及源文件会自动添加到目标工程中:
  • 6、修改头文件:
  • 7、编写源文件:
  • 8、编译并运行:
  • 9、当关闭弹出的对话框时,就会自动调用按钮的析构函数:
  • 10、观察析构函数的执行顺序:
  • 11、执行结果如下所示:
  • 12、执行结果分析:

注意:调用析构函数和释放内存并非是同一件事情。

(1)内存释放顺序:对象树中,内存释放遵循"先子节点、后父节点"的顺序。

(2)析构函数调用顺序:与内存释放顺序不一定一致,有时子节点的析构函数可能在父节点之后执行。

(3)重要区分:调用析构函数与实际释放内存是两个不同的过程,不应混淆。

一句话总结:在对象树中,虽然内存释放总是从子节点到父节点,但析构函数的调用顺序可能与内存释放顺序不同,二者是分离的操作。


结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

结语:希望对学习QT相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【QT】环境搭建收尾:认识Qt Creator

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
重生之绝世牛码2 小时前
Linux软件安装 —— Flink集群安装(集成Zookeeper、Hadoop高可用)
大数据·linux·运维·hadoop·zookeeper·flink·软件安装
w***76552 小时前
PHP vs Python:如何选择?
开发语言·python·php
乾元2 小时前
黑盒之光——机器学习三要素在安全领域的投影
运维·网络·人工智能·网络协议·安全·机器学习·架构
e***98572 小时前
PHP8.4重磅更新:性能飙升新特性
开发语言
Remember_9932 小时前
Java 入门指南:从零开始掌握核心语法与编程思想
java·c语言·开发语言·ide·python·leetcode·eclipse
sheji34162 小时前
【开题答辩全过程】以 基于Python的街区医院管理系统的设计与实现为例,包含答辩的问题和答案
开发语言·python
UR的出不克2 小时前
基于Django的智能职位推荐系统设计与实现:从数据爬取到协同过滤推荐
运维·爬虫·python·数据分析·自动化
C_心欲无痕2 小时前
Next.js 的哲学思想
开发语言·前端·javascript·ecmascript·nextjs
小趴菜不能喝2 小时前
Linux 搭建SVN服务
linux·运维·svn