文章目录
- QPushButton 按钮
- Qt中对象树的概念
- 封装自定义控件
QPushButton 按钮
学习对象树之前,我们得先学习基本控件的创建。创建一个按钮
创建一个按钮:第一种方法
// 创建一个按钮QPushButton *btn = new QPushButton;// 设置控件的父对象btn->setParent(this);// 显示文本btn->setText("确认");
注意,你需要设置button的父对象,如果你不设置,那么不会显示出来。
你需要手动btn->show();
让他显示出来。但是这样显示出来是这样的
他不会出现在窗口中,而是独立出现,因为show()以顶层方式弹出控件。
所以我们需要设置他的父对象。
创建按钮:第二种方法
QPushButton* btn2 = new QPushButton("第二个按钮", this); //直接设置父对象
一些关于按钮和窗口的函数
demo::demo(QWidget *parent): QWidget(parent)
{ui.setupUi(this);// 设置窗口标题this->setWindowTitle("我的窗口标题");// 设置窗口大小this->resize(400, 300); // 将窗口大小设置为 400x300 像素//设置固定大小,不允许缩放this->setFixedSize(400, 300);//创建按钮QPushButton* btn = new QPushButton("确认按钮", this);//调整位置 w h btn->move(100, 100);btn->resize(60, 60);
}
Qt中对象树的概念
在Qt中,对象树(Object Tree)是一种层次结构,它管理着应用程序中所有对象的关系。每个Qt对象都可以有一个父对象(Parent),并且可以有零个或多个子对象(Children)。这种父子关系构成了对象树。
- 父子关系
每个Qt对象(除了顶级窗口如QMainWindow、QWidget等)都有一个父对象。当创建一个对象时,可以通过传递父对象给构造函数来建立父子关系。 - 内存管理
当父对象析构的时候,这个子对象列表中的所有对象都会被析构,当析构子对象的时候,会自动从父对象的子对象列表中删除。 - 布局管理
父对象通常负责其子对象的布局。Qt的布局管理器(如QHBoxLayout、QVBoxLayout等)可以自动调整子控件的位置和大小。 - 事件处理
事件(如鼠标点击或键盘输入)可以在对象树中向上或向下传递。子对象可以捕获来自父对象的事件,也可以将事件传递给父对象。
它的构造顺序是从上到下,析构顺序是从下到上。
所以你创建一个对象的时候,指明其父对象,就可以不用管他的析构,父对象析构时,会帮你自动析构子对象。
封装自定义控件
可以看下,我在项目中的文件(我是在vs2019下使用qt,跟qt creator不一样)
现在我们来封装一个自定义的控件。
右键点击项目,选择添加新建项。
然后选择c++类,名字自己起一个。
然后这里,上面的你都不用改。选择一个基类。我们写QPushButton,因为我们要封装一个按钮,自然是继承于Qt本来的按钮,当然了,你也可以选择QWidget,QWidget是QPushButton的父类,你选择谁作为基类,取决于你的需求。
点击创建。完成后就会多了这俩文件
我们可以看下myButton.h中生成的代码,可以看到,这个类继承于QPushButton。
继承已经完成了。这就是一个自定义的按钮,你可以在这里面重写QPushButton的各种函数,也可以封装你自己想要的功能。
只需要在其他程序中引入,然后调用即可。
我们现在要练习一下,上面讲的对象树的概念。
现在我们在.h文件中为我们的自定义类写一个析构函数和构造函数。(注意,他继承于QPushButton,不写构造和析构编译器会给你自动生成,但是你也可以显式的指明构造和析构,也就是自己写)
myButton.h中
#include <qpushbutton.h>
#include <QDebug>
class myButton : public QPushButton
{Q_OBJECT //启用Qt的元对象系统,可以使用信号、槽等
public:myButton(QWidget* parent = nullptr);//构造函数,默认传入的父对象是空,即为顶级窗口~myButton();
};
myButton.cpp中
#include "myButton.h"myButton::myButton(QWidget* parent) : QPushButton(parent)
{qDebug() << "myButton类的构造函数";
}
myButton::~myButton()
{qDebug() << "myButton类的析构函数";
}
ps:这里补充一个点:
c++里面有个语法,可以初始化成员列表
成员初始化列表是C++中的一个特性,它允许在构造函数中对类的成员变量进行初始化。成员初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。例如:
class MyClass {
public:MyClass(int a, int b) : m_a(a), m_b(b) { }
private:int m_a;int m_b;
};
这种我们都知道。但是此处:myButton::myButton(QWidget* parent) : QPushButton(parent)
这个的意思是调用基类的构造函数,初始化基类成员。
比如说:我们这个按钮是继承于QPushButton的,构造的时候会指明我们这个自定义按钮的父对象。但是基类的父对象呢?并没有指明,所以这里语法的意思就是,构造自定义按钮的时候,传入的那个parent,我会调用基类QPushButton的构造函数,并将parent传给基类的构造函数,也就是将这个父对象设置为基类的父对象。
这种语法是 C++ 的一种特性,专门用于在派生类构造函数中初始化基类成员。
如果我们没有为派生类(在这个例子中是 myButton 类)显式编写构造函数,编译器将自动为你生成一个默认的构造函数。这个默认构造函数将调用基类(这里是 QPushButton)的默认构造函数。
在 C++ 中,如果派生类的构造函数没有明确定义,编译器生成的默认构造函数将按照以下步骤工作:
- 成员初始化: 首先,使用成员初始化列表对派生类中的成员变量进行初始化。
- 基类构造函数调用: 然后,调用基类的默认构造函数。这是通过在成员初始化列表中隐式地进行的,即使你没有显式写出基类的构造函数调用。
- 构造函数体: 由于没有定义构造函数体,编译器不会添加任何额外的代码。
这意味着,即使我们没有显式定义 myButton 的构造函数,创建 myButton 对象时,编译器生成的默认构造函数也会确保 基类QPushButton 的构造函数被调用,从而正确初始化从基类继承的所有成员。
好了,回到我们的正题。
我重写了构造函数。
此时,我在另一个新的文件中,引入我自定义的这个按钮的头文件。并且调用我这个自定义的按钮;
// 创建一个自定义按钮myButton* myBtn = new myButton;myBtn->setParent(this);myBtn->setText("自定义按钮");
我们new了,但是没有释放空间,但是我们指定了按钮的父对象。运行看下。
可以看到,自定义控件的构造和析构都被正确的调用了。这验证了我们前面提到的对象树的概念。你给他设置了父对象,他的父对象会管理他,不需要你释放。