目录
前言:
使用Qt Creator新建一个QWidget项目,QWidget本身就是一个控件,只不过该控件上可以存放其他的控件,QWidget是生成图形化界面的一个基础条件。生成一个带有控件的界面有两种方式:1、图像化的方式,2、代码的方式。然后对控件进行分析,观察当控件被创建出来时,其他文件的内容是否发生改变。
控件指的是带有不同功能的模块,界面上的一切都可以称为是控件,Qt Designer(ui文件)左边一栏就是Qt内置的控件:
1、以界面方式创建label控件
待新建了QWidget后(如何新建QWidget项目请看:Qt_了解Qt Creator),创建控件的方法很简单,打开ui文件,然后在左栏下滑找到label控件,将其拖到界面上,至此就完成了控件的创建了,并且可以更改label的文本信息:
点击运行可以看到界面中出现了hello world的字样,这就是label的文本显示信息:
1.1 观察ui文件xml代码
对比创建控件之前和之后的ui文件xml代码,如下图:
运行之所以可以在界面中显示控件,是因为qmake会基于该xml代码生成一段C++代码,然后通过该C++代码生成最终的界面。
1.2 观察ui_widget.h里的C++代码
在界面上依靠xml代码显示出了对应的控件,那么在代码逻辑中也必须支持这一控件的存在,通过观察ui_widget.h来得出这一结论:
2、以代码方式创建label控件
除了上述的界面方式,还可以通过纯代码的方式实现和上述一样的界面,因为在Qt中,所有的控件都被封装成一个类,因此创建一个控件可以抽象的看成创建一个类。不过创建控件的代码不是写在main函数中的,而是写在widget.cpp中,代码实现方法如下:
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label = new QLabel(this);
label->setText("hello world");
}
Widget::~Widget()
{
delete ui;
}
测试结果:
虽然正常将控件打印到屏幕上,但是和上述的结果还是有区别的,这里的label处于窗口的左上角。
2.1 对象树
在上述代码中,将this指针作为形参传给new出来的对象,这么做的目的就是把该控件挂到对象树上,对象树是一种树型结构,可以理解为最初树上只有Widget这一个节点,后续创建的新控件可以指定挂到Widget节点下,只要传this指针即可。
那么对象树的作用是什么呢?我们发现上述代码中,使用了new进行申请空间,但是整个代码没有对new出来的空间进行delete,原因就是对象树会在窗口关闭时对树上的所有控件进行释放。即对象树将这些控件都组织起来,以便在合适的时机由系统自动释放这些控件,不仅降低了程序员的工作量,还降低了引发内存泄漏的可能性。对象树概念图如下:
那针对上述new的逻辑,如果直接创建一个局部对象,是否可以做到既不用手动释放又可以实现一样的界面呢?测试如下:
发现界面中没有打印出label控件,原因就是局部对象出了作用域就被销毁了,即当main函数中指向到show函数时,这个控件已经不存在了,导致打印出来的界面什么也没有。因此,综上所述使用代码创建控件时,大部分场景下必须使用new的形式来创建。
2.2 验证对象树的销毁操作
可以通过创建一个继承QLabel的子类(myLabel),然后在该类的析构函数出进行打印信息(方便观察析构是否被调用),若对象树有自动释放控件的能力,那么该类的析构函数一定会被调用,myLabel的代码如下:
mylabel.h文件代码:
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QWidget>
#include <QLabel>
#include <iostream>
class myLabel : public QLabel
{
Q_OBJECT
public:
myLabel(QWidget* parent);
~myLabel();
};
#endif // MYLABEL_H
mylabel.cpp文件代码:
#include "mylabel.h"
myLabel::myLabel(QWidget* parent):QLabel(parent)
{
}
myLabel::~myLabel()
{
std::cout<<"myLabel的析构函数被调用"<<std::endl;
}
然后在widget的构造函数内,使用myLabel标签,因为myLabel继承了QLabel,所以他也具有标签的功能,并且还可以附加一些自带的功能:
#include "widget.h"
#include "ui_widget.h"
#include "mylabel.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//QLabel* label = new QLabel(this);
//QLabel label;
//label->setText("hello world");
//label.setText("hello world");
//使用自己的mylabel作为标签
myLabel* mylabel = new myLabel(this);
mylabel->setText("hello mylabel");
}
Widget::~Widget()
{
delete ui;
}
测试结果:
从结果可以看到,在关闭界面时,确实触发了myLabel的析构函数,说明对象树确实可以自动释放树上的控件,只不过打印出来的数据是乱码。
3、Qt下的编码规则
上述乱码的原因是:文件的编码方式和终端的编码方式不一样。比如字符串本身是utf8编码保存的,但是终端(控制台)是按照gbk的方式来解析字符串的,这就会出现乱码。因为解析字符串的规则和字符串存储的规则不一致。
解决方式:使用Qt中专门提供了一个打印的工具qDebug(),他是一个宏。直接使用该工具就可以正常打印了,测试结果如下:
结语
以上就是关于QWidget基本使用的讲解,创建控件的两种方式各有各的优势,具体用哪一种进行开发则根据当时的场景来判断,其次,理解界面对于控件的管理机制也能够提升对Qt界面开发的认知,当然最重要的是理解QWidget的含义。
最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!