每日激励:"不设限和自我肯定的心态:I can do all things。 --- Stephen Curry"
**绪论:
本章是Qt的第二篇,带你认识Qt中几个简单的控件如何实现,以及通过信号槽的方式实现一定的用户和程序的联动,还有许多细节如:对象树、信号槽逻辑图、Qt的命名规范、帮助文档的使用、Qt窗口中的坐标系。早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。**
初始 Qt 程序
1. Qt中使用控件的两种方式:
- 图形化界面的方式,通过在界面上创建出一个控件显示
- 打开widget.ui文件
- 选择Display Widget 中的 Label控件
- 将它拉到界面上,后在内部输入英文即可(如上图)
附:
- 其中右上角会显示出界面上有那些控件
- 上述拖拽的QLabel控件的方式,本质其实是在 ui 文件中的xml中就会出现一段代码
- 当 qmake 在编译项目时,基于这个xml内容生成一段C++代码,从而构建出界面的内容(qt自动完成)
- 细节:Qt就会根据xml代码生成新的widget.h代码(就和label有关,具体如下图)
- 通过纯代码(如下)的方式,通过编写的代码,在界面创建控件显示
- 一般通过代码来构件界面的时候,退出会把构造界面的代码放到 Widget(这样一开始就会创建) 的构造函数中
- 创建QLabel对象并new构造(堆上创建),并传递this参数(给当前这个label对象指定一个父对象,可以不指定但一般都会加上,后面马上细说)
- 其中注意需要包含头文件(错误如下),Qt每个类都有一个对应的同名头文件
- #include <QLabel> 其中发现会有一个qlabel.h头文件这是古老风格的头文件
- 在1998年后 C++标准成立了 C++98 规定了 统一使用 #include <cstdio> 替换以前的 stdio.h
- 其中当我们使用某个类时要记住包含头文件,若果没有报错可能是其他类中包含过了
1.1 label 标签:界面上一个用来显示内容的字符串控件
- 给label设置文本 setText 设置控件中显示的文本
void setText(const QString &)
- 其中的 QString 其实就是 qt 的 std::string
- QString 会对编码进行一定的处理,所以比std::string 舒服一点点,因为在Qt中经常可能出现编码问题
- 它是 Qt 自己开发的基础类(因为当年c/c++中的字符串都不好用)
- 其中还有一些其他的 动态数组 QVector QList QMap,
- 在现在编写Qt代码过程中 可以使用Qt的这一套、也能使用 C++标准库的!
但这个代码 new 了对象后我们不进行delete,那岂不是内存泄漏了?
2. 对象树
其实上述代码在Qt中并不会出现内存泄漏,之所以能够释放,因为把这个对象挂到了对象树上,前端开发(网页开发)也涉及到类似的对象树(DOM)。本质上也是一个树形结构(N叉树),通过把树形结构把界面上的各种元素组织起来(具体如下图)
- 其中当有了这个对象树后,其内部的对象就会统一放到树形结构上
- 那么它们的释放也会进行和树一样的统一,也就是都会在这个界面关闭/销毁的时候释放(具体情况如下图)
- 其中若某个对象提前delete释放了,那么是不是代表他就不会在显示在界面中了
- 此处类new的时候添加了this参数,就会将这个类挂在对象树上
- 但假如放到栈上 (直接创建变量,那么就可能出现提前释放的问题/不会显示),此时可以看到无法显示(label对象随着构造结束而销毁)
2.1 验证对象树:
创建新文件:C++文件
-
选择 C++ class
-
填写类名和继承的父类
-
next即可
注意事项:
-
其中生成的文件可能会有一点点问题(此时我们就自己包含头文件就OK了:
#include <QLabel>
):
-
将 MyLabel构造初始化上 QWidget * parent
小技巧: f4 快速切换头文件和对应的 .cpp文件(Vim中 :A 的方式完成切换、:AT 的方式打开新标签页)
-
切换到对应的 .cpp 文件中,设置构造函数:
- 接收外部传递进来的父类,然后进行初始化列表将parent参数给到父类
- QLabel的构造函数初始化(如下图),这里麻烦的就是需要调用父类的构造函数,才能让自己类的对象加入到Qt对象树中
-
此时自定义析构函数,在析构函数中添加一个打印,查看自动释放时情况(注意需要先在类中添加公共的析构声明)
-
打印日志:

小技巧:在创建的类的头文件中,快速的创建.cpp中的析构,将光标放到析构上 点击 alt 键选择 + enter即可
创建自己的对象并调用
运行结果:
发现打印了 乱码(出现的乱码原因有且只有一个 就是编码方式不匹配,查看那些环节涉及了编码)
一个汉字,占几个字节?
不同的中文编码(字符集 )的不同大小就不同
主要的表示汉字字符集主要是两种方式:
- GBK(使用2byte表示一个汉字)Windows默认是这个
- utf-8 变长编码,表示一个符号,使用的字节数有变化有变化 2~4,但一般是3byte(Linux默认)
一个汉字具体的utf8/gbk编码的数值是多少,可以通过一些在线工具查看
而 Qt Creator 内置的终端不是utf8所以就出现了乱码
当前表示中文,主流的方式,还是utf8(支持各种语言文字)
解决办法:
- Qt中 有 QString可以帮我们自动处理编码问题!
- Qt 还提供了专门用来打印日志的工具 qDebug(),也能自动处理编码问题
具体如下(使用QDebug来代替cout):
最终结果:
- 使用qDebug,还有一个好处:打印的调试日志,是可以统一关闭(不希望用户看到的日志),可以通过编译开关,一键式关闭
小结:
- 认识 QLabel 类,能够在界面上显示字符串
- 通过setText设置,参数 QString(Qt 中把C++中的很多容器进行了重新封装)
- 内存泄漏 / 文件资源泄漏(申请了内存、文件就一定需要关闭!)
- 对象树:Qt中通过对象树统一的释放界面的控件对象
- Qt中还是推荐使用new的方式在堆上创建对象,通过对象树,统一释放对象
- 创建对象的时候,在构造函数中,指定父对象(此时才会挂到对象树上)
- 如果你的对象没有挂到对象树上,就必须手动释放!
- 通过继承子 Qt 内置的类,就可以达到对现有的控件进行功能的扩展效果
- 在析构函数中,加上日志,直观的观察到对象释放的过程
- 面向对象的"继承",本质上就是对现有代码进行 "扩展"
- 乱码问题 和 字符集问题
- 如何在Qt中打印日志,作为调试信息
- Qt 中更推荐使用 qDebug() 完成日志的订阅
- 虽然cout也可以,但并不是好的方法
调试的本质是观察程序执行过程和中间的结果
下面还有几种实现hello world的方式:
3. 使用编辑框控件实现 helloworld

- 使用方法同上的QLabel,可以直接在ui文件中拖拽的方式放上界面
- 同理也能使用纯代码的方式实现,在widget.cpp中的构造函数中实现:
- 还能使用多行编辑框QTextEdit(这里就不过述了因为单行即可完成)
4. 使用按钮的方式来创建 Push Button(普通按钮)

- 拖拽(双击按钮修改内部的字符)
初始信号槽
如何让按钮点击后出现效果,就要使用Qt的信号槽(后面篇章会细说,这里了解即可)
- Qt 中的 connect 的作用是连接信号和槽(和tcp中的建立连接没有任何关系)
- 也就是使用connect实现该按钮控件的点击后的触发效果
- 本质也就是给这个按钮连接一个函数
第一个参数:指定界面文件中访问的按钮(写成ui->pushButton(pushButton就是该控件的ObjectName属性,在ui文件中可以选中控件在右边属性段查看))
第二个参数:函数指针 点击按钮触发的一个信号(QPushButton::clicked点击信号,当点击该按钮时就会自动触发)
第三个参数:指定谁处理这个信号(一般来说就是this,当前类对象)
第四个参数:这个信号怎么处理(一般来说写成类成员函数) - 实现 handleClick
- 首先在widget.h头文件中声明新函数 handle
- 再在widget.cpp中添加新函数的具体实现
- 判断当前按钮中的值是否为:hello world
- 若为则 切换为 hello qt、反之若不是则为hello world(代表为hello qt)
其中注意的是:能使用 ui -> pushButton 它是因为当你添加了控件,就会生成一个标识objectName的标识,他就会在Ui::Widget中生成该控件成员,这样就能通过ui访问到
其中的为什么能访问到是因为ui_widget.h的UI::Widget类就会包含该成员对象(它是Qt自动生成的文件中根据ui界面的xml生成的代码)
- 其中对于PushButton来说他会默认生成的标识为:pushButton
- 我们也能对他进行修改,从而达到使用自己的名称来进行指定
- 它的本质其实就是通过这个名称在ui_widget.h(ui类文件)文件中生成一个相同名称的成员变量,这样就能在
ui -> ...
了
信号槽的总体逻辑图:

Ui::Widget的逻辑过程:
纯代码的方式创建:
- 创建QPushButton类成员变量 myButton(设置为Widget的成员变量这样就能跨函数(构造函数和触发函数)使用)
- 具体实现:
- 和拖拽的一样,只不过此时再使用PushButton对象时直接获取成员变量即可,不用通过ui对象获取了!
源码:
widget.h:
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QPushButton>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handle();
private:
Ui::Widget *ui;
QPushButton *mypushButton;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include "mylabel.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// QLabel lable;//不能定义成局部的,因为这个是在栈上创建,退出函数就会删除
// QLabel* lable2 = new QLabel(this);
// lable.setText("777");
// lable2->setText("666");
MyLabel* mylabel = new MyLabel(this);
mylabel->setText("这是自己的Label");
//四个参数
// 参数1:收到信号的地方、参数2:信号类型、参数3:处理的地方、参数4:处理方法
// connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handle);
mypushButton = new QPushButton(this);
connect(mypushButton,&QPushButton::clicked,this,&Widget::handle);
mypushButton->setText("hello world");
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
if(mypushButton->text() == QString("hello world")){
mypushButton->setText("hello qt");
}else{
mypushButton->setText("hello world");
}
}
//void Widget::handle()
//{
// if(ui->pushButton->text() == QString("hello world")){
// ui->pushButton->setText("hello qt");
// }else{
// ui->pushButton->setText("hello world");
// }
//}
拖拽和自己实现的区别:
- 而在实际开发中,这两种都很主要,难分主次
- 当前这个界面内容比较固定,就会以图形化的方式构建
- 但如果你的程序界面,经常要动态变化,就会以代码的方式构造界面
- 那种方便就用那种,这两种方式还能配合使用
Qt中的命名规范(和C++中差不多)
给变量/函数/文件/类 起名字,需要注意
- 起的名字有描述性,不要使用如 a 、 b ... 的无规律的名字
- 如果名字较长,有多个单词构成,需要使用适当的方式来进行区分不同单词
- 蛇形命名法:C++/Linux中的
_
(unordered_map...)C/C++、Python偏好使用 - 驼峰命名法:Qt 中偏好使用大写字母来进行单词分割,如小驼峰:studentCount(一般是变量或函数)、大驼峰QApplication(一般是类)...(Java/JS/Go偏好使用,也更广泛)
- 对于这几种命名法,不存在好坏,对于它的使用就根据自己公司的风格即可
- 蛇形命名法:C++/Linux中的
使用帮助文档 F1
- 光标放到要查询的类名/⽅法名上, 直接按 F1
- Qt Creator 左侧边栏中直接⽤⿏标单击 "帮助" 按钮:
- 也能找到 Qt Creator 的安装路径,在 "bin" ⽂件夹下找到 assistant.exe,双击打开
Qt 窗口坐标体系
坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。 (平面直角坐标系(笛卡尔坐标系))
Qt 的某个控件的位置,对于某个控件来说,坐标系原点就是相对于父窗口/控件的
代码控制:
-
创建QButton对象,调用内部设置值setText
-
其中能使用move的方式进行移动位置
-
对于move移动时,内部参数就是坐标的值(单位是像素)
- 显示器本质上就是有一堆 发光的小亮点/小灯泡构成(也就是像素)
- 其中在我们电脑中的分辨率本质就是 横向和水平方向的像素个数
- 如 1920 * 1080 (水平 1920 垂直 1080 个像素(亮点))
- 其中越好的显示器亮点越多,画面越好
- 其中水平就是 200 像素、垂直就是 300像素
- 其中还能设置窗口的位置(它没有父元素,也就是整个桌面为坐标系原点)通过this指针的方式

本章完。预知后事如何,暂听下回分解。
如果有任何问题欢迎讨论哈!
如果觉得这篇文章对你有所帮助的话点点赞吧!
持续更新大量Qt细致内容,早关注不迷路。