QT:信号和槽学习笔记
Posted studying~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT:信号和槽学习笔记相关的知识,希望对你有一定的参考价值。
1.信号与槽的概念
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,去关联对象和信号,并设置当对象接收到这个信号时应该进行什么操作(调用什么函数),这个调用的函数就叫做槽函数,意思是,将想要处理的信号和对象自己 的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
2.系统自带的信号与槽
每个控件都有自带的信号与槽,无需我们定义,当然也可以自己定义
connect()函数最常用的一般形式:connect(sender, signal, receiver, slot);
参数解释:
sender:发出信号的对象
signal:发送对象发出的信号
receiver:接收信号的对象
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
代码示例:
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
button = new QPushButton("关闭窗口",this);
//设置按钮的父对象和文本,并创建按钮
connect(button,&QPushButton::clicked,this,&QWidget::close);
//关联信号与对象,当按钮按下、信号被窗口接收到时,关闭窗口
}
3.按钮常用的信号
那么系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档 了,在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中QPushButton,首先我们可以在Contents中寻找关键字 signals,信号 的意思,但是我们发现并没有找到,这时候我们应该想到也许这个信号 的被父类继承下来的,因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个:
由此可见,信号和槽一样,都是函数,寻找槽函数和寻找信号一样,只不过他的关键字是slot
4.自定义的槽函数
widget.h头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QDebug>
#include <QWidget>
#include <QPushButton>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
QPushButton *button;
public slots:
void print() //自定义的槽函数
{
qDebug()<<"chm"<<endl;
}
};
#endif // WIDGET_H
widget.cpp文件
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
button = new QPushButton("打印",this);
//设置按钮的父对象和文本,并创建按钮
connect(button,&QPushButton::clicked,this,&Widget::print);
//关联信号与对象,当按钮按下、信号被窗口接收到时,关闭窗口
}
5.自定义的信号
案例: 显示两个窗口,两个窗口都包含一个按钮组件,实现 按钮按下时,窗口切换(即一个窗口显示,另一个窗口隐藏)
思路:
widget.h头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPushButton>
#include "sonwidget.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
QPushButton *button1; //父窗口按钮
SonWidget *sonwidget; //子窗口
public slots: //定义槽函数
void changefather(); //隐藏父窗口,显示子窗口
void changechildr(int a); //显示父窗口,隐藏子窗口
};
#endif // WIDGET_H
widget.cpp文件
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->setWindowTitle("父窗口"); //设置窗口的名字为父窗口
this->setFixedSize(500,400); //设置父窗口的大小
sonwidget = new SonWidget; //创建子窗口
sonwidget->show(); //显示子窗口
//1
button1 = new QPushButton("隐藏父窗口,显示子窗口",this);
//创建父窗口的按钮
//2
connect(button1,&QPushButton::clicked,this,&Widget::changefather);
/*关联对象w和信号clicked,当按钮button1按下时,发出信号clicked,对象w收到信号后,隐藏父窗口,显示子窗口 */
//3
connect(sonwidget,&SonWidget::show_hide_signal,this,&Widget::changechildr);
/*关联对象w和自定义信号show_hide_signal,当按钮button2按下时,发出信号clicked,对象sonwidget接收信号clicked,在对象sonwidget的槽函数中发出信号show_hide_signal,当对象w收到信号后,显示父窗口,隐藏子窗口 */
//4
}
void Widget::changefather()
{
this->hide();
sonwidget->show();
}
void Widget::changechildr(int a)
{
qDebug()<<a;
this->show();
sonwidget->hide();
}
Widget::~Widget()
{
}
sonwidget.h头文件
#ifndef SONWIDGET_H
#define SONWIDGET_H
#include <QWidget>
#include <QPushButton>
class SonWidget : public QWidget
{
Q_OBJECT
public:
explicit SonWidget(QWidget *parent = nullptr);
QPushButton *button2; //子窗口的按钮
signals: //定义信号
//信号没有返回值 可以有参数 信号函数不需要定义 只需要声明
void show_hide_signal(int a);
public slots: //定义槽函数
void emit_mysignal(); //发送信号show_hide_signal的函数
};
#endif // SONWIDGET_H
sonwidget.cpp文件
#include "sonwidget.h"
SonWidget::SonWidget(QWidget *parent):QWidget(parent)
{
this->setWindowTitle("子窗口"); //设置窗口的名字为子窗口
this->setFixedSize(500,400); //设置子窗口的大小
//1
button2 = new QPushButton("显示父窗口,隐藏子窗口",this);
//创建子窗口的按钮
//2
connect(button2,&QPushButton::clicked,this,&SonWidget::emit_mysignal);
/*关联按钮2和对象sonwidget,当按钮2按下时,发出信号clicked,对象sonwidget接收到信号后,调用槽函数emit_mysignal()发出信号show_hide_signal*/
//4
}
void SonWidget::emit_mysignal()
{
emit show_hide_signal(10); //发出信号show_hide_signal
}
main.cpp文件
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
6.信号与槽需要注意的事项
自定义信号槽需要注意的事项
l 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
l 信号和槽函数返回值是 void
l 信号只需要声明,不需要实现
l 槽函数需要声明也需要实现
l 槽函数是普通的成员函数,作为成员函数,会受到 public、private、 protected 的影响;
l 使用 emit 在恰当的位置发送信号;
l 使用connect()函数连接信号和槽。
l 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数 l 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
l 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少, 即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)
7.信号槽的拓展
(1)一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是 不确定的。
(2)多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
(3)一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式 和信号-槽的形式没有什么区别。
(4)槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到 这个对象上面的槽。
(5)信号槽可以断开
利用disconnect关键字是可以断开信号槽的
(6)使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。 在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我 们会详细介绍什么是Lambda表达式
8.信号与槽在QT4中的写法
SIGNAL和SLOT不会检查里面的字符串
这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到 connect()函数的 signal 和 slot 都是接受字符串,一旦出现连接不成功的情 况,Qt4是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否 匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。
9 lambda表达式
① 函数对象参数; [],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传 递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到 定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的 this)。函数对象参数有以下形式:
空。没有使用任何函数对象参数。
=。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括 Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传 递了所有局部变量)。
&。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括 Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引 用传递了所有局部变量)。
this。函数体内可以使用Lambda所在类中的成员变量。
a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷 贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加 mutable修饰符。
&a。将a按引用进行传递。
a, &b。将a按值进行传递,b按引用进行传递。
=,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
&, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数; 标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值 (如:(a,b))和按引用(如:(&a,&b))两种方式进行传递
③ 可修改标示符; mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符 后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
QPushButton * myBtn = new QPushButton (this);
QPushButton * myBtn2 = new QPushButton (this);
myBtn2>move(100,100);
int m = 10;
connect(myBtn,&QPushButton::clicked,this,[m] ()mutable {
m = 100 + 10;
qDebug() << m;
});
connect(myBtn2,&QPushButton::clicked,this,[=] () {
qDebug() << m;
});
qDebug() << m;
④ 函数返回值->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一 处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略
⑤ 是函数体; {},标识函数的实现,这部分不能省略,但函数体可以为空。
代码示例:
Widget::Widget(QWidget *parent) : QWidget(parent)
{
button = new QPushButton("点我",this);
int a=10;
int b = 20;
//槽函数可以是一个Lambda表达式 8 //Lambda表达式中[]中写的是= ,代表将上面得函数中的局部变量以值传递的方式传入到L ambda表达式
//Lambda表达式中[]中写的是& ,代表将上面得函数中的局部变量以引用传递的方式传入 到Lambda表达式
//Lambda表达式中[]中写的是a ,代表将上面得函数中的局部变量a以值传递的方式传入 到Lambda表达式
// Lambda表达式中[]中写的是a,b ,代表将上面得函数中的局部变量a和b以值传递的方 式传入到Lambda表达式
//Lambda表达式中[]中写的是&a ,代表将上面得函数中的局部变量a以引用传递的方式传 入到Lambda表达式
//mutable修饰了,作用是可以在Lambda修改传入变量的值
//‐>int 代表Lambda表达式返回值是一个int类型
connect(button,&QPushButton::clicked,this,[&]() mutable‐>int{
a=100;
qDebug()<<a;
qDebug()<<b;
qDebug()<<"点我啊";
});
}
以上是关于QT:信号和槽学习笔记的主要内容,如果未能解决你的问题,请参考以下文章