qt是一个gui框架,做图形用户界面的,本地可以跑,跟一些web框架那种做好点击网址跳转的不一样,python的web常用的flask和django,python也有gui框架比如pytqt和tkinter,这个专栏讲qt5,用c++写gui
qt三个窗口基本类
在创建项目的时候就是下面这三个
他们的关系如下:
-
常用的窗口类有 3 个
- 在创建 Qt 窗口的时候,需要让自己的窗口类继承上述三个窗口类的其中一个
-
QWidget
- 所有窗口类的基类
- Qt 中的控件 (按钮,输入框,单选框…) 也属于窗口,基类都是
QWidget
- 可以内嵌到其他窗口中:没有边框
- 可以不内嵌单独显示:独立的窗口,有边框
-
QDialog
- 对话框类,有模态和非模态两种显示
- 不能内嵌到其他窗口中
-
QMainWindow
- 有工具栏,状态栏,菜单栏
- 不能内嵌到其他窗口中
4.2 窗口的显示
- 内嵌窗口
- 依附于某一个大的窗口,作为了大窗口的一部分
- 大窗口就是这个内嵌窗口的父窗口
父窗口显示的时候, 内嵌的窗口也就被显示出来了
- 不内嵌窗口
- 这类窗口有边框,有标题栏
- 需要调用函数才可以显示
c++
// QWidget是所有窗口类的基类, 调用这个提供的 show() 方法就可以显示将任何窗口显示出来
// 非模态显示
void QWidget::show(); // 显示当前窗口和它的子窗口
// 对话框窗口的非模态显示: 还是调用show() 方法
// 对话框窗口的模态显示
[virtual slot] int QDialog::exec();
给出示例代码:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; } //ui 界面的mainwindow类
QT_END_NAMESPACE
class MainWindow : public QMainWindow//QMainWindow Qt的基本窗口类
{
Q_OBJECT// 这个宏是为了能够使用Qt中的信号槽机制
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;// 定义指针指向窗口的 UI 对象
};
#endif // MAINWINDOW_H
testwidget.h
#ifndef TESTWIDGET_H
#define TESTWIDGET_H
#include <QWidget>
namespace Ui {
class TestWidget;
}
class TestWidget : public QWidget
{
Q_OBJECT
public:
explicit TestWidget(QWidget *parent = nullptr);
~TestWidget();
private:
Ui::TestWidget *ui;
};
#endif // TESTWIDGET_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "testwidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) //窗口ui 被实例化了,ui界面也是个类其实
{
ui->setupUi(this);//把cpp的mainwindow类和ui的绑定在一起
//一般在qt的构造函数中进程初始化操作
//显示当前窗口的时候,显示另一个窗口,TestWidget
//我们就在MainWindow的构造函数里面构造TestWidget对象,main只调用MainWindow就行
//创建窗口对象,没有给w对象指定父对象
TestWidget* w=new TestWidget;
w->show(); //显示窗口
}
MainWindow::~MainWindow()
{
delete ui;
}
testwidget.cpp
#include "testwidget.h"
#include "ui_testwidget.h"
TestWidget::TestWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::TestWidget)
{
ui->setupUi(this);
}
TestWidget::~TestWidget()
{
delete ui;
}
入口函数:main
#include "mainwindow.h"
#include <QApplication>//应用程序类,后台维护了一个事件循环
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //创建了一个应用程序的对象,只有一个
MainWindow w; //创建了一个窗口对象
w.show(); //显示窗口
return a.exec(); //阻塞函数,程序进入了事件循环
}
Qdwidget类
我们去创建一个ui界面类
进去选择模板,我选择了一个基类空模板widget类,进去之后自动跳到ui界面,我们就可以拖动控件去定制自己想要的样子
这么写会发现就是两个窗口
改一下MainWindow构造函数如下:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) //窗口ui 被实例化了,ui界面也是个类其实
{
ui->setupUi(this);//把cpp的mainwindow类和ui的绑定在一起
//一般在qt的构造函数中进程初始化操作
//显示当前窗口的时候,显示另一个窗口,TestWidget
//我们就在MainWindow的构造函数里面构造TestWidget对象,main只调用MainWindow就行
//创建窗口对象,没有给w对象指定父对象,他就是独立窗口
//想要显示他,就得show一下
#if 0
TestWidget* w=new TestWidget;
w->show();
#else //指定父对象,他就不是单独的窗口了
//这样的话当前父窗口显示的时候,子窗口就一并显示出来
//这时候子窗口是没有边框的
//只有独立窗口是有边框的
TestWidget* w=new TestWidget(this);
#endif
}
会发现只有一个窗口,且窗口内嵌
这样一会有边框一会没有,因为我们这个继承的是QWidget类,上面有讲他特性
我们在加个Qdialog类,看看效果
注意:qt里面这些类都是继承关系,你看传this,传的其实是父对象,也就是基类指针指向子类,达到多态的那种感觉
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) //窗口ui 被实例化了,ui界面也是个类其实
{
ui->setupUi(this);//把cpp的mainwindow类和ui的绑定在一起
//一般在qt的构造函数中进程初始化操作
//显示当前窗口的时候,显示另一个窗口,TestWidget
//我们就在MainWindow的构造函数里面构造TestWidget对象,main只调用MainWindow就行
//创建窗口对象,没有给w对象指定父对象
#if 0
TestWidget* w=new TestWidget;
w->show();
#else
TestWidget* w=new TestWidget(this);
#endif
TestDialog* dlg=new TestDialog(this);//对于他写不写父对象无所谓的,这个this
//非模态
dlg->show();
}
注意非模态,对于多个子窗口是可以来回切换的
把TestWidget那块if取开,让他走上面那两句代码,因为那两句没有传父类widget对象,这样TestWidget窗口的就不会去嵌入主窗口,你在运行就会有三个窗口且来回鼠标可以去切换,点的动的!
而模态是什么呢?就是子窗口之间不能切换了
对于Dialog的模态模式,需要调用exec这个阻塞函数,跑下面代码,会发现没有主窗口,两个子窗口没法鼠标去切换,这就是模态。那是因为有exec这个函数存在,阻塞在那,dialog这个窗口不退出,相当于主窗口mainwindows没有构造成功,当你把dialog这个窗口关了,主窗口就出来了
这个模态也就dialog能调了,对了还有构造dialog的时候,那个this父类对象传不传都是可以的,对于mainwindowsnew构造的时候来说也是传不传this都一样,毕竟都是从widget继承来的,就widget传和不传才有什么独立和嵌套之分。我上面也有说他们区别
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) //窗口ui 被实例化了,ui界面也是个类其实
{
ui->setupUi(this);//把cpp的mainwindow类和ui的绑定在一起
//一般在qt的构造函数中进程初始化操作
//显示当前窗口的时候,显示另一个窗口,TestWidget
//我们就在MainWindow的构造函数里面构造TestWidget对象,main只调用MainWindow就行
//创建窗口对象,没有给w对象指定父对象
#if 1
TestWidget* w=new TestWidget;
w->show();
#else
TestWidget* w=new TestWidget(this);
#endif
TestDialog* dlg=new TestDialog(this);//对于他写不写父对象无所谓的,这个this
//模态 ,得调一个函数exec(),阻塞程序执行
dlg->exec();
//写exec了show自动帮你调了一次show显示
}
QMainWinow类
我们创建项目一开始的类,就是继承QMainWinow这个类,算是你的主窗口,菜单栏的这种,去对应的ui界面也能去拖动按钮,控件
Qt的坐标
坐标原点在左上角,从左上角向下向右递增
每个窗口都有一个坐标原点,但是点的位置都是基于父窗口的原点去移动构建的
比如父亲是(0,0),,有个儿子是(5,5),这个5,5是相对于父亲0,0的
儿子还有个儿子(10,10),这个(10,10)是相对于(5,5)来说的,相当于说是按照爷爷(0,0)偏移的(15,,15)这个位置
所以注意窗口嵌套的坐标位置都是基于父窗口的坐标系来算的
我们给主窗口构造函数加点控件看看:
#include "mainwindow.h"
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) //窗口ui 被实例化了,ui界面也是个类其实
{
ui->setupUi(this);//把cpp的mainwindow类和ui的绑定在一起
//创建一个按钮,作为当前窗口的子控件
QPushButton *ba=new QPushButton(this);//内嵌进去
//通过move去移动控件位置
ba->move(10,10); //x y
//设置大小
ba->setFixedSize(200,200);//宽度,高度
QPushButton *ba1=new QPushButton(ba);//注意这里
//通过move去移动控件位置
ba1->move(10,10); //相对于ba的位置
//设置大小
ba1->setFixedSize(100,100);
}
Qt自带内存回收
因为qt总是来回继承的,很多类都是qobject这个类的子类或者间接子类,qt后台继承会构建一颗继承树,,父类要析构就得先去析构子类,这下就明白吧。。不过这种内存回收前提就是这个类得继承在qobject这个对象树下
综上所述,我们可以得到一个结论: Qt中有内存回收机制, 但是不是所有被new出的对象被自动回收, 满足条件才可以回收 , 如果想要在 Qt 中实现内存的自动回收,需要满足以下两个条件:
- 创建的对象必须是 QObject 类的子类 (间接子类也可以)
QObject 类是没有父类的,Qt 中有很大一部分类都是从这个类派生出去的
Qt 中使用频率很高的窗口类和控件都是 QObject 的直接或间接的子类
其他的类可以自己查阅 Qt 帮助文档
2.创建出的类对象,必须要指定其父对象是谁,一般情况下有两种操作方式:
// 方式1: 通过构造函数
// parent: 当前窗口的父对象, 找构造函数中的 parent 参数即可 .h
QWidget::QWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
QTimer::QTimer(QObject *parent = nullptr);
// 方式2: 通过setParent()方法
// 假设这个控件没有在构造的时候指定符对象, 可以调用QWidget的api指定父窗口对象
void QWidget::setParent(QWidget *parent);
void QObject::setParent(QObject *parent);