QT初识【创建项目+对象树】

一、项目文件解析

1.1 .pro 工程文件(qmake 配置)

QtFirst.pro 为例:

复制代码
QT       += core gui          # 1. 引入 core 和 gui 模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets   # 2. Qt5+ 需要 widgets 模块
TARGET = QtFirst              # 3. 生成的可执行文件名(.exe)
TEMPLATE = app                # 4. 应用程序模板(非库)
SOURCES += main.cpp widget.cpp   # 5. 所有源文件
HEADERS += widget.h           # 6. 所有头文件
FORMS   += widget.ui          # 7. 界面设计文件
CONFIG  += c++11              # 8. 使用C++11标准
  • QT += core gui:告诉qmake链接Qt的CoreGUI模块。Core提供非图形功能(如事件循环、容器),GUI提供窗口基础类。

  • greaterThan(...):如果Qt主版本号大于4(即5或6),则增加widgets模块。因为Qt5将QWidget移到了独立的widgets模块中。

  • TARGET:最终生成的程序名(Windows下为QtFirst.exe)。

  • TEMPLATE = app生成应用程序的makefile。其他选项如lib(库)、subdirs(多子项目)。

  • SOURCES / HEADERS / FORMS:列出所有源代码、头文件、UI文件。\是换行符。

  • CONFIG += c++11:启用C++11特性(如nullptrauto等)。

1.2 widget.h 头文件(类声明)

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>          // 父类头文件

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }   // 前置声明UI命名空间中的Widget类
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT                // ① 宏,启用信号与槽机制(必须)
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;         // ② 指向界面设计生成的类的指针
};
#endif
  • Q_OBJECT:这是一个必须放在类私有区域开头的宏。它让Qt的元对象系统(moc)工作,提供信号/槽、动态属性等功能。没有它,你无法使用信号槽

  • namespace Ui { class Widget; }widget.ui 会被编译成一个 Ui::Widget,包含所有控件和 setupUi 函数。这里只是前置声明,避免包含整个生成的ui_widget.h

  • Ui::Widget *ui:指针,用来访问界面上的所有控件。例如在widget.cpp中写 ui->pushButton->setText("OK")

1.3 main.cpp 入口文件(程序起点)

复制代码
#include "widget.h"
#include <QApplication>     // 应用程序类

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);   // ① 应用程序对象,每个Qt GUI程序只有一个
    Widget w;                      // ② 创建窗口对象(栈上)
    w.show();                      // ③ 显示窗口(默认隐藏)
    return a.exec();               // ④ 进入事件循环,程序阻塞在此,直到窗口关闭
}
  • QApplication a(argc, argv):管理全局事件循环、资源、命令行参数。必须在创建任何窗口对象之前定义。

  • Widget w:在 上创建窗口对象。为什么这里用栈?因为main函数结束前程序不会退出,窗口一直存在。但如果窗口内部有子控件,子控件必须在堆上创建并指定父对象

  • w.show():默认窗口是隐藏的,必须调用show()才能显示。

  • a.exec():启动Qt的事件循环。程序会停在这里,等待用户点击、键盘输入等。当最后一个窗口关闭时,exec()返回,程序结束。

1.4 widget.cpp 实现文件(核心逻辑)

复制代码
#include "widget.h"
#include "ui_widget.h"        // 自动生成的UI类头文件

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)      // 初始化UI指针
{
    ui->setupUi(this);        // 构建所有界面控件,建立它们与this的父子关系
}

Widget::~Widget()
{
    delete ui;                // 释放UI对象(它会自动删除所有界面控件)
}
  • ui(new Ui::Widget):在成员初始化列表中创建UI对象。

  • ui->setupUi(this):这个函数会根据widget.ui的XML描述,动态生成所有控件(按钮、标签等),并设置this为它们的父对象。如果你手动编写控件代码,可以不用UI设计器,但两者通常不同时混用

  • delete ui:析构时释放UI对象。由于UI对象内部持有的控件都是this的孩子**,孩子会在this(窗口)析构时自动被删除,但ui本身是堆分配的需要手动delete。**

1.5 widget.ui 界面文件(XML结构)

这是一个XML文件,由Qt Designer自动维护。你永远不需要手动编辑。示例片段:

复制代码
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <widget class="QPushButton" name="pushButton">
   <property name="text">
    <string>Hello World</string>
   </property>
  </widget>
 </widget>
</ui>

Qt的uic工具会把它编译成ui_widget.h,其中包含一个Ui_Widget类,里面定义所有控件和setupUi方法。这就是为什么widget.cpp中要包含ui_widget.h

二、QT Hello World 程序

两种方式,实现hello world

  • 通过图形化的方式,在界面上创建除一个控件,显示hello world
  • 通过纯代码的方式,通过编写代码,在界面上创建空间,显示hello world

2.1 通过图形化方式创建


拖拽控件到 ui页面窗口并修改内容;

转到编辑区 -> widget.ui文件

为我们弹出这个窗口

2.2 通过代码方式创建

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

  • 创建对象的时候,可以在堆上创建【上面的就是】 , 也可以在栈上创建

2.2.1 QString

已经学了 C++ 标准库的 std::string,为什么 Qt 还要搞一个 QString直接传 std::string 不行吗?

当你写下第一行 Qt 代码时,IDE 的自动补全可能会加困惑:

复制代码
QLabel *label = new QLabel(this);
label->setText("Hello Qt");  // 等等,参数类型不是 std::string?

把鼠标悬停在 setText 上,你会看到它的签名是:

复制代码
void QLabel::setText(const QString &text);

QString ------ 这就是 Qt 世界里的字符串通行证。

Qt 诞生于 1991 年。那个时期:

  • C++ 还没有 ISO 标准(第一个标准 C++98 是 1998 年发布的)

  • 没有标准库(STL) 的概念

  • 当时的 C++ 编译器五花八门,对语言特性的支持参差不齐

  • 很多年以后,上述的容器等内容,已经打磨得很好了,形成了C++标准~
  • 因此,咋们开发QT代码的时候,如果需要用到上述容器,可以使用QT自己搞的这一套
  • 但是QT原生的api中,涉及到的接口,用的都是QT自己的这一套容器

QString 对应的头文件,**已经被很多QT内置的其他类给间接包含了。**因此一般不需要显示包含QString文件

2.2.2 代码

复制代码
#include "widget.h"
#include <QPushButton>      // 包含按钮头文件

Widget::Widget(QWidget *parent)
    : QWidget(parent)       // 调用父类构造函数,parent参数用于对象树
{
    // 创建一个按钮对象,在堆上分配内存
    QPushButton *btn = new QPushButton;   // ① 创建按钮
    btn->setText("Hello World");          // ② 设置按钮上显示的文字
    btn->setParent(this);                 // ③ 将按钮挂载到当前窗口(指定父对象)
    // 注:也可以一步完成:new QPushButton("Hello World", this);
}

逐行解释

  • #include <QPushButton>:Qt中每个类都需要单独包含头文件,类名就是文件名(无.h后缀)。

  • QPushButton *btn = new QPushButton;:在 上创建按钮对象。为什么不用栈对象(QPushButton btn;)?因为栈对象会在函数结束时自动销毁,导致按钮一闪而过。而堆对象配合对象树机制,会随父窗口一起销毁。

  • setText("Hello World"):设置按钮显示的文本。

  • setParent(this)这一步最关键this 指向当前 Widget 窗口。将按钮的父对象设为该窗口后,Qt会把这个按钮加入窗口的"孩子列表"中。当窗口关闭时,按钮会被自动释放,我们不需要手动 delete

最后,由于按钮已经有了父对象,它会在父窗口上显示。运行后就能看到一个带"Hello World"的按钮窗口。

三、内存泄漏问题

关注 内存泄露 是要融入到DNA中的事情。内存泄露是一个非常害怕,非常严重的事情!!(不仅仅是内存泄露,包括文件描述符泄露 等同类问题,都是非常严重的)

而且这种问题不容易第一时间发现!

  • 上述代码,QT中不会产生内存泄漏
  • label 对象会合适的时候被析构释放 ~(虽然没有手动写delete,但是的的确确可以释放)
  • 之所以能够把对象释放掉,主要是因为把这个对象挂到了对象树上
  1. 使用对象树,把这些内容组织起来,最主要的目的,就是为了能够在合适的时机【窗口关闭/销毁】 , 把这些对象统一进行释放
  2. 树上的这些对象,统一销毁是最好不过的,如果某个对象提前被销毁,此时会导致对应的控件在页面上不存在了
  3. 此处通过new的方式创建对象,也是为了把这个对象的生命周期交给QT对象树来统一管理
  4. 如果在栈上创建变量 -- 存在提前释放的问题

四、对象树

4.1 概念

在Qt中创建很多对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。

  • Q0bject是以对象树的形式组织起来的。

4.2 QT对象树

4.3 代码实例

  1. 创建一个新工程并编译运行,生成如下窗口:


2、选中工程名,鼠标右键 -------> "add new..."(或 "添加新文件" )


3、选择 "choose...",弹出如下界面;

4、点击 "下一步",弹出如下对话框;

5、点击 "完成" 之后,手动创建类的头文件以及源文件会自动添加到目标工程中;


6、修改头文件;


7、编写源文件;


8、编译并运行;

9、当关闭弹出的对话框时,就会自动调用按钮的析构函数;

10、观察析构函数的执行顺序;

五、Qt 编程必备规范与技巧

5.1 命名规范(推荐驼峰法)

元素 风格 示例
类名 首字母大写,每个单词首字母大写 QPushButton, MyMainWindow
函数/变量 首字母小写,后续单词首字母大写 setText(), userAge
常量/宏 全大写,下划线分隔 QT_VERSION, SOCKET_ERROR

Qt 官方偏爱驼峰,与许多C++标准库的蛇形命名不同,适应即可。

5.2 必记快捷键

功能 快捷键
注释/取消注释 Ctrl + /
运行 Ctrl + R
编译 Ctrl + B
字体缩放 Ctrl + 鼠标滚轮
查找 Ctrl + F
整行上下移动 Ctrl + Shift + ↑/↓
自动对齐 Ctrl + I
帮助文档(光标在类/函数上) F1
.h.cpp间切换 F4
根据声明生成定义(光标在函数上) Alt + Enter

六、乱码问题

6.1 乱码原因

编码不一致

6.2 解决乱码

查看是啥编码:

QDebug:

后续在Qt中,如果想通过打印日志的方式,输出一些调试信息,**都优先使用qDebug。**虽然使用cout 也行,但是cout 对于编码的处理不太好,在windows上容易出现乱码(如果是Linux使用 Qt Creator,一般就没事了,Linux默认的编码一般都是utf8)

使用qDebug , 还有一个好处 --> 打印的调试日志,是可以统一关闭的!!!!

输出的日志,是开发阶段,调试程序的时候,使用的。如果你的程序上线发布,日志的调试信息其实是不希望用户看到的

qDebug可以通过编译开关,来实现一键式关闭~

相关推荐
长安第一美人2 小时前
算能 BM1688 低延迟推流:Qt+WebSocket 直出 H5/HDMI
开发语言·网络·嵌入式硬件·websocket·交互
lhbian2 小时前
C++、C与易语言:编程语言对比解析
c语言·开发语言·c++
꧁细听勿语情꧂2 小时前
数据结构概念和算法、时间复杂度、空间复杂度引入
c语言·开发语言·数据结构·算法
0xDevNull2 小时前
Java 深度解析:for 循环 vs Stream.forEach 及性能优化指南
java·开发语言·性能优化
研☆香2 小时前
聊一聊如何分析js中的数据结构
开发语言·javascript·数据结构
-凌凌漆-2 小时前
【Qt】 QSerialPort::flush()介绍
开发语言·qt
徐子元竟然被占了!!2 小时前
IS-IS协议
开发语言·网络·php
小猪皮蛋粥3 小时前
python画图
开发语言·python
Felven3 小时前
A. The 67th Integer Problem
开发语言