
**前引:**本文介绍了QT框架的基本概念、开发环境搭建及核心功能实现。首先阐述了QT作为跨平台GUI应用程序框架的特点,包括其面向对象的架构和高效的开发流程。然后详细说明了开发环境配置步骤,包括编译器安装、QTSDK部署和IDE选择。重点解析了QT项目文件结构、对象树机制和信号槽系统,通过HelloWorld示例展示了控件创建和事件处理的方法。文章还介绍了坐标体系、编码问题解决、快捷键使用等实用技巧,最后讲解了如何查阅官方文档。全文系统性地呈现了QT开发的核心知识体系,为初学者提供了清晰的入门路径。
目录
[(1)QT SDK下载](#(1)QT SDK下载)
[(2)QT 环境变量配置](#(2)QT 环境变量配置)
[四、第一个项目:Hello World](#四、第一个项目:Hello World)
[六、QT Creater中的快捷键](#六、QT Creater中的快捷键)
[八、QT 坐标体系](#八、QT 坐标体系)
一、QT介绍
(1)定义
简单直接,从大的范围来说:QT属于桌面客户端开发
客户端:直接和用户打交道的界面,背后可能存在服务端,也可能属于单客户端
而桌面客户端又可以分为:指令式客户端(TUI) 、图形化客户端(GUI)
而QT就属于:桌面客户端的图形化界面方向,更严格来说属于图形化 用户界⾯ 应⽤程序框架

(2)框架理解
在了解框架之前,我们先看一下库:
库提供了具体的方法实现接口 ,这个工具流程如何设计,完全交给你,你调用库的代码
那么框架恰恰相反:
框架规定了大致的主导流程 ,你需要实现细节,那么需要按照框架的结构,框架调用你的细节代码
(3)优点
至于解释为什么,不重要,它有什么优点,我们拿到知道怎么用就行:
(1)跨平台支持,几所所有平台
(2)接口简单,上手快
(3)开发效率高,快速的构建程序,因为它已经给你固定了大致的主导流程
(4)可以进行嵌入式开发
二、QT开发环境
简单点,它的开发环境需要完成三步:
(1)C++(为例)的编译器,比如gcc,而VS Code这种属于IDE,这需要做一个区分!
(而IDE内部可以自动调用 编译器)
(2)QT SDK,封装好的软件开发工具包
(3)QT的集成开发环境(IDE)
{
(1)官方提供的QT creator------>上手快,适合新手
(2)IDE插件 Visual Studio------>功能强、但配置多
(3)Eclipse
}
那么综上,我们搞一个 IDE 就行了,以QT creator 为例!
(1)QT SDK下载
http://download.qt.io/archive/qt/
选择:5.14.2版本
再选择Windows 桌面应用环境:
下载完成之后双击安装,在SDK组件中选择下面三个:


完成安装:

(2)QT 环境变量配置
为什么设置环境变量:
在Windows上设置Qt的环境变量是为了能够在命令⾏或其他应⽤程序中
直接访问Qt相关的命令和⼯具
找到QT的安装路径,复制,打开"系统环境变量"

在"系统环境变量中"添加(用户或者系统都可以)


(3)第一个项目
(1)文件------>新建项目------>选择"Application",

(2)选择QT构建工具------>老牌稳!(qmake最终生成makefile文件)


QT构建工具选择解释:
(3)选择父类(QWidget)
前面我们提到了框架------>那不就是父类和基类的关系吗?我们是新手,就选择 Qwidget

(4)勾选 generate from
原因:可以以图形化的方式生成界面

(5)选择翻译语言(默认下一步即可)

(6)选择编译器(默认下一步即可)

(7)版本控制(git 或者 none)

(8)完成创建

(4)注意点
在项目的目录中不能出现中文

三、配置文件讲解
(1)main.cpp
cpp
#include "new_project.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
new_project w;
w.show();
return a.exec();
}
main函数的命令行参数就不多说了:参数个数、对应参数内容
QApplication a(argc, argv):创建QT框架对象,就是实例化一份QT资源
new_project w:要实现的具体功能对象
w.show():触发绘制逻辑指令
return a.exec():一直循环显示窗口
作用:项目的整个调用逻辑顺序
(2)new_project.cpp
cpp
#include "new_project.h"
#include "ui_new_project.h"
//第一部分
new_project::new_project(QWidget *parent)
: QWidget(parent)
, ui(new Ui::new_project)
{
ui->setupUi(this);
}
//第二部分
new_project::~new_project()
{
delete ui;
}
第一部分:完成自定义功能窗口的初始化工作
第二部分:对你的自定义窗口资源进行释放
那么为什么不直接利用 main.cpp 实例化 new_project 对象自动调用构造和析构呢?
作用:通过自定义构造和析构提高工程扩展性和可维护性,将类的声明和定义分离
(3)new_project.h
cpp
#ifndef NEW_PROJECT_H
#define NEW_PROJECT_H
#include <QWidget>
//第一部分
QT_BEGIN_NAMESPACE
namespace Ui { class new_project; }
QT_END_NAMESPACE
//第二部分
class new_project : public QWidget
{
Q_OBJECT
public:
new_project(QWidget *parent = nullptr);
~new_project();
private:
Ui::new_project *ui;
};
#endif // NEW_PROJECT_H
首先这是一个头文件,开头的#ifndef....就是保护相同的头文件只出现一次
第一部分:定义了一个命令空间 Ui,里面包含了 new_project 类
第二部分:new_project 的具体实现
那么为什么要这么做呢?需要额外定义一个命令空间?
前置声明:只告诉编译器 "存在一个Ui::new_project类",提高编译效率
作用:
它像项目整个蓝图描述,只标注 "项目继承了整个父类、有哪些函数、有哪些成员变量",
不包含具体的实现细节(那是
.cpp的事)
(4)new_project.ui

作用:
- 提效:可视化设计界面,省去所有手写布局 / 控件属性的繁琐代码;
- 解耦:界面布局和功能逻辑分离,改界面不影响功能,改功能不影响界面;
(因为.cpp是管具体的功能实现,那么 .ui 就是管这个图形化界面排版)
(5)project_test.pro

核心配置文件,由qmake工具解析,其核心作用是定义项目的构建规则
告诉 qmake 如何编译代码、链接库、处理资源,最终生成可执行文件
(6)项目整体逻辑
编译阶段:
qmake 把.pro的配置、.ui的设计(图形排版)、.h/.cpp的代码(功能实现),转为可执行文件
运行阶段:
从main函数启动,先初始化 QT 框架,再加载自定义界面,最后启动事件循环响应操作,执行析构
四、第一个项目:Hello World
(1)图形化
直接在 .ui 文件中,选择 Label 控件(标签意思),调整大小后,输入要显示的内容

(2)直接运行即可

(2)代码化
在构造函数中创建一个 Qlabel 对象(栈、堆都可以),然后使用里面的接口即可

为什么要不在 new_project.h 中实现?
new_project.h 中是声明,可能需要向外暴露,但在实际中,具体的实现和声明应该是分离的
为什么 Qlabel 的参数是 this ?
很好理解,谁调用的这个类那么this就指向的谁,在外面是 w(自定义功能具体实现) 调用的,所以它的具体含义是:将 Qlabel 这个控件交给 this 也就是 w 进行管理
五、对象树
例如:

(1)针对内存泄漏
那么这样 new 出来的对象需要担心内存泄漏吗?

首先明白:堆上内存什么时候结束?程序结束自动释放或者手动,那么这里将QLabel挂在了 w 上面,形成一棵N叉树,可以按照(派生类->基类)的内存释放顺序理解!树状结构的对象!
cpp
QApplication a(顶层根节点)
↳ new_project w(窗口,父对象)
↳ QLabel label(子对象,父是w)
同时也表明了:QApplication 是框架,其余只是添加"框架"的枝干!
(2)在栈上创建
完全不用担心,我们先看另外一种形式的创建:在栈上创建,可以看见没有输出

具体原因:我们是在 new_project 类对象 w 构造中创建的,所以 w 在当前栈一销毁,就没了

(3)如何使用自定义类
(1)首先在文件里面创建一个C++的类,自定义类名称

(2)将这个类加入到对象树

(3)实现具体的方法

(4)几大问题
为什么 new_project 类实例的 w 对象,它的构造类型是 Qwidget 类型?
首先原本 this 指针的类型是 new_project * 类型,而子类指针可以安全的转化为父类指针也就是 Qwidget * 类型,从而 this 指针类型可以进行一个转化!
一个类(自定义或内置生类)是如何加入到对象树的?
**原则:**只有继承了QObject的类,才能被加入 QT 对象树(QWidget继承⾃QObject)
内置类:内置类天上继承了 QObject,可以直接用,不用管
自定义类:这个自定义类现在是派生类,只有让派生类继承某个内置类才能进入对象树 
字符串编码显示问题?
首先一个汉字占多少字节取决于编码方式:
(1)GBK------>2个字节一个汉字
(2)UTF-8------>3个字节一个汉字
在QT中,可能因为编码格式不统一(编码和显示器)会形成乱码,所以将 cout 切换为 qDebug
例如:
同时:
qDebug 在调试阶段可看见打印内容,但是在发布版本中是不显示的,很方便用来打印日志
六、QT Creater中的快捷键
注释:Ctrl + /
运行:Ctrl + R
编译:Ctrl + B
字体缩放:Ctrl + 鼠标滑轮
查找:Ctrl + F
整行移动:Ctrl + shift + 向上箭头/向下箭头
自动对齐:Ctrl + i
同类的源文件和头文件切换:F4
立刻给函数声明实现定义:选中这个函数 + Alt + Enter
七、QT文档查询
(1)直接打开assistant.exe

(2)帮助

八、QT 坐标体系
对于嵌套窗⼝,其坐标是相对于⽗窗⼝来说的
通过 move(x,y)内置接口来完成相较于父窗口的移动

例如:

九、信号与槽
(1)理解
触发了什么信号------>执行什么样的方法
(2)三要素
信号源:由哪个控件触发
信号的类型:比如点击信号、输入信号........本质也是一个类
槽:信号处理的方法------>函数
(3)connect函数
那么如何把信号与槽关联起来?
用 **connect()**接口将二者进行关联:它属于QObject的接口,由于具有继承关系,可以直接用!
接口原型:
cpp
connect(
发送者对象指针, // 参数1:谁发信号
&发送者类::信号函数名, // 参数2:发什么信号
接收者对象指针, // 参数3:谁接收信号
&接收者类::槽函数名 // 参数4:接收后执行什么(或Lambda表达式)
);
简单易上手:
第一个参数:如果是内置信号,就是控件;自定义信号就是 this(因为是当前 Widget 对象成员)
第三个参数:槽函数属于哪个对象,就写哪个对象的指针
参数说明:
(1)**参数:**发送对象的指针
举例:如点击按钮,指针指向的是QPushbutton按钮(堆上存储),含义就是由QPushbutton发出
注意:必须是QObject*类型(或其子类指针,如QPushButton*、QWidget*)
(2)**参数:**要发出的信号
举例:信号本质也是一个类函数,那么比如点击,不就是点击里面的一个信号函数吗:
| 控件 | 信号函数名 | 含义 |
|---|---|---|
| QPushButton | clicked() | 按钮被点击 |
| QLineEdit | textChanged(QString) | 输入框文本改变 |
| QCheckBox | toggled(bool) | 复选框选中状态改变 |
connect ( btn , &QPushButton::clicked , ...); // 参数2是按钮的"点击信号"
注意:类型要求: 必须是「发送者类的成员函数指针」,格式是&类名::信号函数名
(3)**参数 :**信号接收者
举例:接收到了某个信号,谁来处理?那不当前窗口类型的指针吗?比如 this
注意:和参数 1 一样,必须是QObject*类型(或其子类指针)。
(4)**参数:**槽函数或者Lambda表达式
例如:(1)直接使用接受者里面的成员函数方法
cpp
// new_project.h里声明了void onBtnClicked();
connect(btn, &QPushButton::clicked, this, &new_project::onBtnClicked);
(2)Lambda表达式
cpp
connect(btn, &QPushButton::clicked, this, [=]() {
// 这里直接写槽逻辑,比如修改标签文本
label->setText("按钮被点击了!");
});
注意:类型要求 :「接收者类的成员函数指针」,格式是&接收者类::槽函数名
(1)自定义槽函数(图形)
我们可以直接在 .ui 文件中选择操作,比如点击操作,然后右键"槽函数"

然后使用 ui 来操作这个点击按钮,就比如 " ui -> pushbutton"就拿到了这个点击按钮控件,而通过图形化自定义槽函数我们只需要实现 槽函数即可,QT自动完成了connect连接!

为什么是通过 ui 操作,而不是 this?
可以看到我们自定义的细节功能 Widget 只是 ui 里面的一个类,而我们的图形化操作是直接属于 ui

ui管 "可视化拖的控件",this管 "自己写的 Widget 类本身"
(2)自定义槽函数(代码)
通过代码实现的话,就是通过 connect 接口进行关联信号和槽:
第一个参数:哪里来的信号,这个我们已经具备,就是 ui 里面的(控件名称)
第二个参数:触发的是什么信号,很明显,是这个控件的点击信号(为例)
第三个参数:信号的接受者,就是当前窗口 this
第四个参数:任务执行函数,所以我们需要自定义一个任务函数

效果例如:

(3)自定义信号
自定义信号只需要在类里面增加一个:signals修饰,信号的本质就是一个成员函数


这里的 emit 不用加域的原因是,它是在Widget函数里面使用的,自带域
(4)槽函数带参
为什么槽函数支持带参?让信号在触发时,将参数传给槽函数------>信号给槽函数传参
**规则1:**信号的参数个数 >= 槽函数的参数个数
**规则2:**槽函数会从左到右与信号进行参数匹配,即槽函数可以不全部获取完信号的参数
例如:

那么在connect后,槽函数会从左到右自动匹配参数类型:

效果:

(5)解除绑定
绑定信号和槽函数是:connect
解除信号和槽保证参数相同,将 connect 改成 disconnect
(6)多关联性
即一个信号可以对应多个槽函数,那么一个槽函数也可以被多个信号同时触发




