学习信号和槽(2)

一、自定义信号和槽

1.1、自定义槽函数

自定义slot和普通的成员函数没有什么区别。
自定义槽函数方式一:
Qt5自定义槽函数的方法

 void handleClikecd();

以前的Qt版本,自定义槽函数的方法()

    public/private/protected slots:
    void handleClikecd();

此处的slots是Qt自己扩展的关键字(不是c++标准中的语法)
在这里插入图片描述
自定义槽函数方式二:(快速的方式)
通过ui设计界面里,拖动一个控件到窗口,然后右键弹出对话框,点击“转到槽”,然后选择信号,就会生成自定义槽函数,并且与该槽函数建立连接。(无需用connect进行连接)
在这里插入图片描述
运行程序:
在这里插入图片描述
实现槽函数自动连接

  1. 如果我们通过图形化界面的方式创建控件,此时就会推荐快速的方式来连接信号槽。
  2. 如果我们是通过代码的方式来创建控件,此时就会推荐手动connect来连接信号槽。

2.1、自定义信号

自定义信号,在实际开发中比较少见。

信号就是用户的某个操作,在GUI中,用户能够进行哪些操作,是可以穷举的,Qt的内置信号,基本上覆盖了所有可能的用户操作。

信号是一种非常特殊的函数。

  1. 程序员只要告诉Qt,这是一个信号就可以了;这个函数的定义,是在Qt的编译过程中,自动生成的。(自动生成的过程,程序员无法干预)
  2. 作为信号函数,这个函数的返回值,必须为void。(有没有参数都可以,还支持重载)
  3. signals是Qt自己扩展出来的关键字。
    qmake的时候,调用一些代码的分析/生成工具,扫描到类中包含signals这个关键字的时候,就自动的把函数声明认为是信号,并对信号函数自动生成函数的定义。

自定义信号:

  1. 在.h文件里写入自定义信号,和槽函数。
//信号
signals:
    void mySignal();
//槽函数
public :
    void handleMySignal();
  1. 在.cpp文件里的定义。
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this,&Widget::mySignal,this,&Widget::handleMySignal);
    //发送出自定义信号
    emit mySignal();
}
Widget::~Widget()
{
    delete ui;
}
void Widget::handleMySignal()
{
    this->setWindowTitle("信号被触发");
}
  1. 发送自定义信号,可以在任意合适的代码中。
    列如:我们在ui界面创建一个按钮,然后点击按钮,转到槽,选择信号,然后再自定义槽函数中写入发送的自定义信号。(此时,点击按钮,就会发送自定义信号)
    在这里插入图片描述
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(this,&Widget::mySignal,this,&Widget::handleMySignal);

}
Widget::~Widget()
{
    delete ui;
}
void Widget::handleMySignal()
{
    this->setWindowTitle("信号被触发");
}
void Widget::on_pushButton_clicked()
{
    //发送出自定义信号
    //发送自定义信号,可以在任意合适的代码中,不一定非在构造函数中。
    emit mySignal();
}

补充:

emit 自定义信号;(emit是Qt扩展的关键字,表示发射了自定义信号,connect连接自定义信号和槽,必须发射自定义信号)

二、带参数的信号槽

信号和槽可以带参数,当信号带参数时,槽的参数必须和信号的参数一致。(发射信号的时候,可以给信号函数传递实参,与之对应的这个参数就会被传递给对应的槽函数中)

  1. 参数一致,主要要求的是类型一致,可以参数个数不一致,前提是信号的参数个数比槽的参数个数要多。
  2. 传参可以起到复用代码的作用。

例子1:
写一个带参数的信号槽
1.给信号和槽函数设置相同的参数

//信号
signals:
    void mySignal(const QString& Text);
//槽函数
public:
    void handleMySignal(const QString& Text);
  1. 发射信号后,传给信号,然后传给槽函数
    在这里插入图片描述
    例子2:
    如何通过一套信号槽,起到设置不同标题的效果
    在上面代码的基础上,在ui文件里创建一个按钮,然后转到槽,选择合适的信号。
    在这里插入图片描述
    补充:
  2. 通过这一套信号槽,搭配不同的参数,就可以起到设置不同标题的效果。(不止是标题,可以是其他的效果)
  3. 信号函数的参数个数,超过槽函数的参数个数,是可以正常使用得。( 信号函数的参数个数,少于槽函数的参数个数,代码无法编译通过)

三、信号和槽的其余说明

信号和槽的连接方式:
一对一:一个信号对一个槽函数;一个信号对另一个信号
一对多:一个信号对多个槽函数
多对一:多个信号对一个槽函数

3.1、信号和槽的存在的意义

  1. 为什么信号函数的参数个数,可以超过槽函数的参数个数?

1、如果我们严格要求参数个数一致,就意味着信号绑定到槽之间的要求变高了。
2、我们让信号和槽之间的绑定更加灵活,就是要信号函数的参数个数,可以超过槽函数的参数个数。
3、个数不一致时,槽函数按照参数顺序,拿到信号的前N个参数,要保证槽函数的参数都是有值的。

让某个类可以定义信号和槽,必须在该类最开始的地方添加Q_OBJECT宏,不加宏,如果定义信号和槽,会编译出差。
在这里插入图片描述
2. GUI开发框架响应用户操作

  1. Qt信号槽,connect这个机制。
    1、解耦合:把触发用户操作的控件和处理对应用户的操作逻辑解耦合。
    2、实现多对多这个效果:一个信号,可以connect到多个槽函数上,一个槽函数,可以被多个信号connect,其他GUI开发框架不具备得。
  2. 一般网页开发。
    一对一:一个事件,只能对应一个处理函数;一个处理函数,只能对应到一个事件上。

3.2、信号和槽的断开连接

1.使用disconnect来断开信号槽连接
disconnect和connect使用的方式非常类似。

disconnect(this,&Widget::mySignal,this,&Widget::handleMySignal);

disconnect用的比较少,大部分情况下,把信号和槽连接上之后,就不要管了,主动断开往往是把信号重新绑定1到另一个槽函数上。

3.3、 用lambda表达式定义槽函数

lambda表达式是一个语法糖,本质是匿名函数,应用在回调函数场景中,适用与代码较短的槽函数。
格式:

[capture](params) opt -> ret {body;};
    - capture: 捕获列表
    - params: 参数列表
    - opt: 函数选项(可省略)
    - ret: 返回值类型(可省略)
    - body: 函数体

lambda表达式,是一个回调函数,这个函数,无法直接获取到上层作用域中的变量,为了解决这个问题,引入了“变量捕捉”语法,通过变量捕捉,获取到外层作用域中的变量;lambda表达式是一次性使用。

capture : 捕获一定范围内的变量
[] - 不捕捉任何变量
[&] - 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)
[=] - 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获):拷贝的副本在匿名函数体内部是只读的
[=, &foo] - 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo
[bar] - 按值捕获 bar 变量, 同时不捕获其他变量
[&bar] - 按引用捕获 bar 变量, 同时不捕获其他变量
[this] - 捕获当前类中的this指针
//通常常用[=]进行捕捉
mutable 或 exception 声明
mutable: 这部分可以省略。按值传递函数对象参数时,加上 mutable 修饰符后,可以修改传递进来的拷贝(注意是能修改拷贝,而不是
值本身)。
exception: exception 声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw(int)

lambda表达式的基本写法

connect(ui->pushButton,&QPushButton::clicked,[=](参数){
   //具体代码
  this->close();
   }); 

补充:
lambda 语法是c++ 11 中引入的。
对于Qt5 以及更高的版本,默认就是按照 c++11 来编译的。
如果使用Qt4或者更加古老的版本,就需要手动在 .pro 文件中加上 c++ 11 的编译选项

CONFIG += C++11

四、信号和槽的总结

  1. 信号槽是啥,和 Linux 中的信号进行了对比,
    1)信号源
    2)信号的类型
    3)信号的处理方式
  2. 信号槽使用.
    connect
  3. 如何查阅文档.
    一个控件,内置了哪些信号, 信号都是何时触发,
    一个控件,内置了哪些槽,槽都是什么作用.
    很有可能需要的信号槽,还得到这个类的父类/爷爷类/祖宗类去进行查询
  4. 自定义槽函数.
    本质上就是自定义一个普通的成员函数
    还可以让 Qt Creator(界面的方式去创建) 自动生成(虽然没有显式 connect,但是可以通过函数名字特定规则来完成自动连接)
  5. 自定义信号,
    信号本质就是成员函数.(函数的定义是 Qt 自己生成的,咱们只需要写函数声明)
    signals: 自定义关键字
    emit 来完成信号的发射(emit 也可以省略)
  6. 信号和槽还可以带有参数.
    发射信号的时候,把参数传递给对应的槽
    信号的参数和槽的参数要一致
    1)类型匹配
    2)个数,信号的参数要多于槽的参数.
  7. 信号槽存在的意义
    解耦合
    多对多效果.(非常类似于 mysql 中的多对多的)
    演示了信号槽多对多连接的情况
  8. disconnect 使用方式.
  9. lambda 表达式, 简化槽函数的定义
  10. 高内聚(写代码的时候,某个功能点的代码都被集中放在一起);低耦合(功能与功能之间干扰较少)。
  11. 信号与槽的优缺点
    优点: 松散耦合
    信号发送者不需要知道发出的信号被哪个对象的槽的数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于 Q0bject类。
    缺点: 效率较低
    与回调函数相比,信号和槽稍微慢一些,因为它们提供了更高的灵活性,尽管在实际应用程序中差别不大。通过信号调用的槽函数比直接调用的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调用速度对性能要求不是非常高的场景是可以忽略的,是可以满足绝大部分场景。
    一个客户端程序中,最慢的环节往往是"人"。
    假设本身基于回调的方式是 10us,使用信号槽的方式是 100us.对于使用程序的人来说,是感知不到的。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值