Qt第二课----信号和槽
作者前言
🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言、C++和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂
Qt
- **作者前言**
- 打印hello world
- QT的编码格式
- 使用编辑框进行输出hello wrold
- 点击按钮
- 信号和槽
- 信号
- 使用
- 自定义信号
- 自定义槽
- 带参数的信号和槽
- 窗口坐标体系
- Q_OBJECT的介绍
- 信号和槽的连接方式
- 信号和槽 的断开
- 总结
打印hello world
第一种方法,
在ui文件里面进行编辑
输入内容,然后运行文件,可以打印出来
第二种方法可以写c++代码进行运行出来
这里需要包含QLabel头文件, 这个头文件是之前Qt用的头文件风格
就好比19989年的c++98规定统一使用#include代替#include<stdio.h>头文件
还需要注意的是: 在Qt中,有自己的一套轮子,
QString、 QVector、QList 这些是Qt代码中常见的类型,一一对应的就是C++的string 、vector、list等类型
QLabel *labe = new QLabel(this);//推荐在堆上创建,给labe指定一个父节点
labe->setText("Hello world");//
图中代码,调用了QLabel的方法,使其显示出来,setText的参数是一个Qstring
如图:
这个方法默认显示到左上角,
观察代码,可以发现没有释放空间,会造成内存泄漏,这是潜意识,
但是是不会内存泄漏的,因为是把这个对象挂到了对象树上了
使用对象树,把内容组织起来,在合适的时候统一释放(窗口关闭时),但是如果挂在树上的对象提前销毁了,就会导致对应的的控件在界面上不存在了
如图:
所以说,labe对象会随对象树释放
我们也可以写一个类来进行观察,继承这个QLabel这个内置类,
.h
#include<QLabel>
class mylabel : public QLabel
{
public:
mylabel(QWidget* parent);
~mylabel();
};
.cpp
#include<QDebug>
mylabel::mylabel(QWidget* parent)
:QLabel(parent)
{
}
mylabel::~mylabel()
{
qDebug()<< "释放了";//字符的编码格式和这个文件的编码格式一样
//终端的编码格式不一样
//
}
在构造函数中,必须指定父类,这样才能挂在对象树上,否则的话,只能自己手动的进行delete了
回归到原来问题上去,
QT的编码格式
在qt中,我们使用std::cout打印输出就会出现编码格式错误,
例如:
所以qt有自己的处理工具,或是使用Qstring类型
我们需要包含头文件<QDebug>,使用qDebug()来打印输出,
qDebug()是一个宏,里面封装了一个QDebug对象,所以使用qDebug()相当于std::cout
需要注意是windos不要使用cout, 但是Linux可以,因为Linux的编码格式是utf-8,cpp的文件的编码格式也是utf-8
使用编辑框进行输出hello wrold
上面在程序面板中简单 输出了hello wrold, 这次我们来试试编辑框 QLineEdit,在设计中也可以通过ui文件进行设计,
接下来我们使用c++的纯代码来进行实现
还是一样的道理,使用啥控件就导入对应控件的名字(头文件), 导入#include<QLineEdit>
如图:
代码的写法还是一样的, 控件对象初始化一定要指明父类(父节点),
点击按钮
除了上面的方法,我们也可以通过点击按钮在程序面板中输出
这里需要我们导入头文件#include<QPushButton>
QPushButton * qbutton = new QPushButton(this);
qbutton->setText("hell world");
如图:
这个按钮点击是没有效果的,需要建立一个点击操作,这就涉及到信号和槽了
信号和槽
在Qt中,⽤户和控件的每次交互过程称为⼀个事件。⽐如"⽤⼾点击按钮"是⼀个事件,"⽤户关闭窗⼝"也是⼀个事件。每个事件都会发出⼀个信号,例如⽤⼾点击按钮会发出"按钮被点击"的信号,⽤⼾关闭窗⼝会发出"窗口被关闭"的信号,而在Qt中,对信号做出的响应动作就称之为槽
注意:信号的本质就是事件
槽的本质
槽(Slot)就是对信号响应的函数。槽就是⼀个函数,与⼀般的C++函数是⼀样的,可以定义在类的任何位置(public、protected或private),可以具有任何参数,可以被重载,也可以被直接调⽤(但是不能有默认参数)。槽函数与⼀般的函数不同的是:槽函数可以与⼀个信号关联,当信号被发射时,关联的槽函数被⾃动执⾏
说明
(1)信号和槽机制底层是通过函数间的相互调⽤实现的。每个信号都可以⽤函数来表⽰,称为信号函数;每个槽也可以⽤函数表⽰,称为槽函数。例如:"按钮被按下"这个信号可以⽤clicked()函数表⽰,"窗⼝关闭"这个槽可以⽤close()函数表⽰,假如使⽤信号和槽机制实现:"点击按钮会关闭窗⼝"的功能,其实就是clicked()函数调⽤close()函数的效果。
(2)信号函数和槽函数通常位于某个类中,和普通的成员函数相⽐,它们的特别之处在于:
• 信号函数⽤ signals关键字修饰,槽函数⽤public slots、protected slots或者private slots修饰。signals和slots是Qt在C++的基础上扩展的关键字,专⻔⽤来指明信号函数和槽函数;
• 信号函数只需要声明,不需要定义(实现),⽽槽函数需要定义(实现)
信号
在Linux中,进程的通讯方式是信号, signal,
谁发出来的,啥类型的信号, 怎么处理,
而在Qt中也有自己的信号,
涉及到三个要素
简单的理解的就是, 信号源、 信号类型, 处理方式(槽, connect的使用)
使用
在Qt中Qobject类中提供一个静态函数connet(),该函数专⻔⽤来关联指定的信号函数和槽函数
connect() 函数原型:
bool QObject::connect(const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
sender:信号的发送者;
•
signal:发送的信号(信号函数);传入函数指针
•
receiver:信号的接收者;
•
method:接收信号的槽函数;传入函数指针
type
= Qt::AutoConnection )
•
type:⽤于指定关联⽅式,默认的关联⽅式为Qt::AutoConnection,通常不需要⼿动设定。
这写了两个,从图中可以看出,qbutton是new出来的, pushButton是ui文件通过qmake生成的,
这个变量名我们是可以控制的,如果要更改的话,
如图:
qmake会根据ui文件的objectName对应的值,生成的变量名(唯一),我们也可以更改这个值,
我们这个值修改为puBu
如图:
需要注意的是:&QPushButton::clicked 是函数指针,
代码:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
,qbutton(new QPushButton(this))
{
ui->setupUi(this);
/*QLineEdit* qlineedit = new QLineEdit(this);
qlineedit->setText("hello world");*/ // 单编辑框
qbutton->setText("hell world");
connect(qbutton,&QPushButton::clicked,this, &Widget::funtion);
connect(ui->puBu,&QPushButton::clicked,this, &Widget::funtion);
}
void Widget:: funtion()
{
qDebug() << "点击成功";
if(ui->puBu->text()==QString("点击"))
{
ui->puBu->setText("成功点击");
}
else{
ui->puBu->setText("点击");
}
}
.h
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void funtion();
private:
Ui::Widget *ui;
QPushButton * qbutton;
};
看到这里, 很多人会迷惑,为啥在qt中,会使用不同的类型的指针进行传参,在c++中不能这样做的,
这是因为在以前的Qt版本中, connect传参需要传入宏的, 分别是SIGNAL和SLOT,
写法如下:
connect(btn, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));
这两个宏的作用就是把函数指针类型转换成char*
而在QT5之后,就增加了泛型编程,也就是函数模板,
自定义信号
信号本质就是事件, 也就是一个“函数”,但是信号是一个特殊的函数, 程序员只要声明这个函数就行,至于函数的定义,是qt在编译的过程自动生成的(生成过程,程序员无法干预)
需要注意的是: 信号函数的返回值必须是void, 也没有形参都可以,重载函数也可以
在qt中有关键字signals:这个是c++没有的
至于怎么定义的函数, 是qmake在调用一些工具的时候,扫描到signal的时候,就会根据函数声明写出一个函数定义,
下面我们先声明一个信号函数,然后使用connect把信号和槽建立连接
然后我们运行起来, 就会发现。我们该怎么触发信号, 前面我们使用内置的点击可以触发信号,但是现在我们自定义信号, 没有这样的功能 这里Qt就引入了emit关键字, 使用这个关键字触发我们声明的信号函数
emit this->myclicked();
如图:
在Qt5中不写这个emit也可以
自定义槽
顾名思义,简单的理解就是一个函数的声明加定义,所以说当我们要定义一个槽函数需要按照Qt的写法进行,
槽函数写法1
在Qt中有c++中没有的关键字,为了区分普通函数和槽函数,qt增加了如下关键字
public slots:
protected slots;
private slots
槽函数定义和声明在其中,这样可以一目了样,就算不写在其中,也是可以的,这个要看程序员怎么写
槽函数写法2
除了上面的纯代码方式,还可以在ui文件进行
如图:
点击进行就会看到如下:
然后选择好信号就会生成对应的槽函数
然后我们只需要函数里面写我们的内容就行,
当我们运行的时候就会发现, qt文件中没有使用connect,而生成的ui_widget.h的头文件也没有,
这是因为,在QT中,信号和槽的链接不局限于使用connect, 还可以通过函数名的方式来链接
而生成的函数名字也是有讲究的,如图:
我们知道QPushButton有clicked这个函数,如果我们当名字更改了就会报错,
当我们更改后,
就会发现,输出的那里有一个函数名字叫做connectSlotsByName,这个对应着头文件ui_widget.h
当调用这个函数的时候,就会调用对应的函数名链接的规则,进行信号和槽的链接, 如果这个函数(connectSlotsByName)没有调用, 哪怕在Qt文件中没有更改函数名,也会报错,
总结:
如果我们时使用图形化工具创建控件的话,就使用函数名链接规则进行信号和槽的连接,
如果是纯代码的方式来创建控件的话,要使用connect来,因为我们没有调用connectSlotsByName这个函数
带参数的信号和槽
前面我们自定义或者内置的类的成员函数,都是没有参数的,如果要想写一个带有参数的信号函数,对应的槽函数也要带参数, 并且参数类型要一致,个数不一致也可以,信号函数参数个数 >= 槽函数参数个数
下面我写了一段代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
btn = new QPushButton("点击",this);
connect(btn, &QPushButton::clicked, this,&Widget::HandBtn);
connect(this, &Widget::myclicked, this, &Widget::handmyclicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handmyclicked(const QString& ch)
{
this->setWindowTitle(ch);
}
void Widget::HandBtn()
{
btn->move(200,300);
emit this->myclicked("无敌大厂");
}
大致思路:
窗口坐标体系
前面我们使用代码写出来的,都是默认在程序面板的左上角显示的,而在qt中的
坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加。
在qt中,给某个控件设置位置的时候,相对于的坐标原点,就是父窗口的左上角
前面我们简单的理解了对象树, QPushButton作为Qwidget的子节点,对应的坐标原点就是QWidget框的左上角
当我们运行起来就会默认在qwidget的左上角,
我们可以使用move函数
void move(int x, int y);
就会在(x,y)处开始显示,这个是相对于Qwidget这个父框的
如图:
我们也可以给Qwidget这个窗口进行设置,
Q_OBJECT的介绍
前面我们知道,如果一个类中要定义和使用信号和槽(或者定义和声明信号和槽),就需要类开始的地方写入宏 Q_OBJECT,否则就会报错
信号和槽的连接方式
在大部分的GUI网页开发中,主要形式不是使用connect这样的形式,(一对一形式)
funtion handle()
{
.....
}
button.onclick = handle;
只要点击就会触发函数, 这种方式有很多, 但是在Qt中, 搞出connect这样的方式,是为了解决信号和槽的耦合, 也就是实现处理 用户的操作控件和处理用户的操作的逻辑,(一个控件需要产生不同的效果),也叫解耦合
还有就是实现一个信号连接多个槽函数,或者一个槽函数连接多个信号,
总结: 在qt中信号和槽的连接有一对一. 一对多以及多对多的方式
信号和槽 的断开
关键字: disconnect
代码如下:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->pushButton1, &QPushButton::clicked, this, &Widget::Myslot1);
connect(ui->pushButton2, &QPushButton::clicked, this, &Widget::Myslot2);
}
Widget::~Widget()
{
delete ui;
}
void Widget::Myslot1()
{
qDebug()<< "Myslot1";
this->setWindowTitle("东东");
}
void Widget::Myslot2()
{
qDebug()<< "Myslot2";
//断开
disconnect(ui->pushButton1, &QPushButton::clicked, this, &Widget::Myslot1);
//重新连接
connect(ui->pushButton1, &QPushButton::clicked, this, &Widget::Myslot3);
}
void Widget::Myslot3()
{
qDebug()<< "Myslot3";
this->setWindowTitle("喜喜");
}
如图:
disconnect一般不怎么使用,主动断开连接,一般情况下是连接其他的槽函数,
除了上面这样声明和定义槽函数, 还可以使用lambda表达式
但是需要注意到是Qt中的lambda表达式支持是在c++11的基础上的,Qt5之后的版本,如果是Qt5之前的版本,就需要在.pro文件中更改一下,如图:
总结
Linux中的信号和Qt中的信号有很多相似点,
- 信号源
2)信号类型
3)信号的处理方式
信号和槽的连接和使用
connect的使用和disconnect的使用
自定义信号和槽函数
Q_OBJECT宏
lambda表达式