Qt信号和槽 .png

2.1 信号和槽

image.png
信号:singal
click(单击)
press(按压)
release(释放)
toggle(切换)
槽函数:slot

连接函数connect
参数1: 信号的发送者
参数2:发送的信号
参数3:信号的接受者
参数4:处理的槽函数
松散耦合
通过帮助文档寻找相应的信号和槽

eg:点击按钮,关闭窗口

  1. //参数1 信号的发送者
  2. //参数2 具体信号 发送的信号(函数的地址)
  3. //参数3 信号的接受者
  4. //参数4 处理的槽函数
  5. 下面两种实现方法相同
  6. // connect(mybutton, &Mypushbutton::clicked, this, &myWidget::close);
  7. connect(mybutton, &QPushButton::clicked, this, &QWidget::close);
  8. connect(mybutton, SIGNAL(clicked(bool)), this, SLOT(close())); //这种方式也行 QT4 不做动态类型检查
  9. connect 函数有多种实现形式 主要是运用的运算符重载

2.2 自定义的信号和槽

出现一个问题
undefined reference to `vtable for’…….”错误
重新构件项目

// ProManager 项目经理类
// Programmer 程序员
// 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求

注意:
1.第一点:当信号与槽函数的参数数量相同时,它们参数类型要完全一致。
2.第二点:当信号的参数与槽函数的参数数量不同时,只能是信号的参数数量多于槽函数的参数数量,且前面相同数量的参数类型应一致,信号中多余的参数会被忽略。
此外,在不进行参数传递时,信号槽绑定时也是要求信号的参数数量大于等于槽函数的参数数量。

2.2.1自定义信号

1.返回值void 只需要声明,不需要实现
2.可以有参数,可以重载

  1. #ifndef PROMANAGER_H
  2. #define PROMANAGER_H
  3. #include <QObject>
  4. //项目经理
  5. class ProManager : public QObject
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit ProManager(QObject *parent = nullptr);
  10. signals:
  11. //自定义信号
  12. //返回值void 只需要声明,不需要实现
  13. //可以有参数,可以重载
  14. void newDemand();
  15. public slots:
  16. };
  17. #endif // PROMANAGER_H

2.2.2自定义槽

  1. #ifndef PROGRAMMER_H
  2. #define PROGRAMMER_H
  3. #include <QObject>
  4. class Programmer : public QObject
  5. {
  6. Q_OBJECT
  7. public:
  8. explicit Programmer(QObject *parent = nullptr);
  9. signals:
  10. public slots:
  11. //早期QT版本必须写到这里,高版本可以写到public中,或者全局
  12. //返回值是void,需要声明,也需要实现
  13. //可以有参数,也可以重载
  14. void completeDemand();
  15. };
  16. #endif // PROGRAMMER_H
  17. // 槽函数
  18. void Programmer::completeDemand()
  19. {
  20. qDebug() << "程序员完成需求";
  21. }

2.2.3触发

先建立连接
然后通过函数的方式来触发
触发某个信号使用emit关键字,如下

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. // ProManager 项目经理类
  4. // Programmer 程序员
  5. // 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
  6. Widget::Widget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11. //创建一个项目经理
  12. this->manage = new ProManager(this);
  13. //创建一个程序员
  14. this->pro = new Programmer(this);
  15. //创建链接 项目经理有需求
  16. connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  17. //调用客户需求函数
  18. clientNewDemand();
  19. }
  20. //信号触发
  21. void Widget::clientNewDemand()
  22. {
  23. //客户需求 调用后 项目经理出发新需求的信号
  24. emit manage->newDemand();
  25. }
  26. Widget::~Widget()
  27. {
  28. delete ui;
  29. }

2.2.4 自定义信号和槽出现重载(QString 转为 cahr*)

QStirng-> char* 不会有引号
完成的需求 “增加功能”
这个是新增的槽函数
qDebug() << “完成的需求” << content.toUtf8().data();

需要利用函数指针,明确指向函数的地址
void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;

QString 转为 cahr
ToUtf8() -> QByteArrary
data() 转为 cahr

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. // ProManager 项目经理类
  4. // Programmer 程序员
  5. // 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
  6. Widget::Widget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11. //创建一个项目经理
  12. this->manage = new ProManager(this);
  13. //创建一个程序员
  14. this->pro = new Programmer(this);
  15. //创建链接 项目经理有需求
  16. //connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  17. //调用客户需求函数
  18. //clientNewDemand();
  19. //链接带重载 如下写会发生二义性
  20. //connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  21. //函数指针->函数地址
  22. void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
  23. void (Programmer::* programmerSlot)(QString) = &Programmer::completeDemand;
  24. connect(manage, manageSingal, pro, programmerSlot);
  25. //写成一体的
  26. //connect(manage, static_cast<void (ProManager::*)(QString)>(&ProManager::newDemand),
  27. pro, static_cast<void (Programmer::*)(QString)>(&Programmer::completeDemand));
  28. connect(manage, SIGNAL(newDemand(QString)), pro, SLOT(completeDemand(QString))); //另一种方法,更直观,但是不检测
  29. clientNewDemand();
  30. }
  31. //信号触发
  32. void Widget::clientNewDemand()
  33. {
  34. //客户需求 调用后 项目经理出发新需求的信号
  35. //emit manage->newDemand();
  36. emit manage->newDemand("增加功能");
  37. }
  38. Widget::~Widget()
  39. {
  40. delete ui;
  41. }

2.2.5 按钮触发

1.使用按钮连接函数

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. // ProManager 项目经理类
  4. // Programmer 程序员
  5. // 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
  6. Widget::Widget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11. //创建一个项目经理
  12. this->manage = new ProManager(this);
  13. //创建一个程序员
  14. this->pro = new Programmer(this);
  15. //创建链接 项目经理有需求
  16. //connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  17. //调用客户需求函数
  18. //clientNewDemand();
  19. //链接带重载 如下写会发生二义性
  20. //connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  21. //函数指针->函数地址
  22. void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
  23. void (Programmer::* programmerSlot)(QString) = &Programmer::completeDemand;
  24. connect(manage, manageSingal, pro, programmerSlot);
  25. //connect(manage, SIGNAL(newDemand(QString)), pro, SLOT(completeDemand(QString)));
  26. QPushButton *btn = new QPushButton("客户需求", this);
  27. this->resize(400, 500);
  28. connect(btn, &QPushButton::clicked, this, &Widget::clientNewDemand);
  29. }
  30. //信号触发
  31. void Widget::clientNewDemand()
  32. {
  33. //客户需求 调用后 项目经理出发新需求的信号
  34. //emit manage->newDemand();
  35. emit manage->newDemand("增加功能");
  36. }
  37. Widget::~Widget()
  38. {
  39. delete ui;
  40. }

2.信号连接信号

使用按钮信号,来触发需求信号

注意这里:为什么只能触发无参数的?
虽然前后都是信号,但是参数要匹配,如果前面的参数和后面的参数不匹配,无法进行传参操作。

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. // ProManager 项目经理类
  4. // Programmer 程序员
  5. // 客户有需求, 项目经理出发信号:有需求, 程序员:完成需求
  6. Widget::Widget(QWidget *parent) :
  7. QWidget(parent),
  8. ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11. //创建一个项目经理
  12. this->manage = new ProManager(this);
  13. //创建一个程序员
  14. this->pro = new Programmer(this);
  15. //创建链接 项目经理有需求
  16. //connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  17. //调用客户需求函数
  18. //clientNewDemand();
  19. //链接带重载 如下写会发生二义性
  20. //connect(manage, &ProManager::newDemand, pro, &Programmer::completeDemand);
  21. //函数指针->函数地址
  22. // void (ProManager::* manageSingal)(QString) = &ProManager::newDemand;
  23. // void (Programmer::* programmerSlot)(QString) = &Programmer::completeDemand;
  24. // connect(manage, manageSingal, pro, programmerSlot);
  25. //connect(manage, SIGNAL(newDemand(QString)), pro, SLOT(completeDemand(QString)));
  26. QPushButton *btn = new QPushButton("客户需求", this);
  27. this->resize(400, 500);
  28. void (ProManager::* manageSingal)(void) = &ProManager::newDemand;
  29. void (Programmer::* programmerSlot)(void) = &Programmer::completeDemand;
  30. connect(manage, manageSingal, pro, programmerSlot);
  31. connect(btn, SIGNAL(clicked()), manage, SIGNAL(newDemand()));
  32. disconnect(manage, manageSingal, pro, programmerSlot);
  33. }
  34. //信号触发
  35. void Widget::clientNewDemand()
  36. {
  37. //客户需求 调用后 项目经理出发新需求的信号
  38. //emit manage->newDemand();
  39. emit manage->newDemand("增加功能");
  40. }
  41. Widget::~Widget()
  42. {
  43. delete ui;
  44. }

image.png

3.断开信号

将两人的信号断开
然后再点击按钮也没有用了

  1. disconnect(manage, manageSingal, pro, programmerSlot);

4.拓展+QT4版本
拓展
1、信号是可以链接信号
2、一个信号可以链接多个槽函数
3、多个信号 可以链接 同一个槽函数
4、信号和槽函数的参数,必须一一对应
QT4版本的连接信号和槽

  1. connect(btn, SIGNAL(clicked()), manage, SIGNAL(newDemand()));
  2. //不做检测
  3. connect(btn, SIGNAL(clicked()), manage, SIGNAL(newDemand(QString)));

优点:参数直观,
缺点: 类型不做检测
QT5 以上支持QT4,反之不支持
为什么不做检测:
将内容转换为字符串进行比较

2.3 Lambda表达式

C++ 11新特性
早期配置:CONFIG += C++11

转载自:https://www.cnblogs.com/DswCnblog/p/5629165.html

1.Lambda表达式声明

  1. [capture list] (params list) mutable exception-> return type { function body }

各项具体含义如下

  1. capture list:捕获外部变量列表
  2. params list:形参列表
  3. mutable指示符:用来说用是否可以修改捕获的变量
  4. exception:异常设定
  5. return type:返回类型
  6. function body:函数体

此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:
image.png

  • 格式1声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值。
  • 格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型:

(1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定;
(2):如果function body中没有return语句,则返回值为void类型。

  • 格式3中省略了参数列表,类似普通函数中的无参函数。

    2.捕获外界变量

    Lambda表达式可以使用其可见范围内的外部变量,但必须明确声明(明确声明哪些外部变量可以被该Lambda表达式使用)。那么,在哪里指定这些外部变量呢?Lambda表达式通过在最前面的方括号[]来明确指明其内部可以访问的外部变量,这一过程也称过Lambda表达式“捕获”了外部变量。 ```cpp

    include

    using namespace std;

int main() { int a = 123; auto f = [a] { cout << a << endl; }; f(); // 输出:123

  1. //或通过“函数体”后面的‘()’传入参数
  2. auto x = [](int a){cout << a << endl;}(123);

}

  1. 上面这个例子先声明了一个整型变量a,然后再创建Lambda表达式,该表达式“捕获”了a变量,这样在Lambda表达式函数体中就可以获得该变量的值。<br />类似参数传递方式(值传递、引入传递、指针传递),在Lambda表达式中,外部变量的捕获方式也有值捕获、引用捕获、隐式捕获。
  2. 值捕获:<br />值捕获和参数传递中的值传递类似,被捕获的变量的值在Lambda表达式创建时通过值拷贝的方式传入,因此随后对该变量的修改不会影响影响Lambda表达式中的值。
  3. ```cpp
  4. int main()
  5. {
  6. int a = 123;
  7. auto f = [a] { cout << a << endl; };
  8. a = 321;
  9. f(); // 输出:123
  10. }

这里需要注意的是,如果以传值方式捕获外部变量,则在Lambda表达式函数体中不能修改该外部变量的值。

引用捕获
使用引用捕获一个外部变量,只需要在捕获列表变量前面加上一个引用说明符&。如下:

  1. int main()
  2. {
  3. int a = 123;
  4. auto f = [&a] { cout << a << endl; };
  5. a = 321;
  6. f(); // 输出:321
  7. }

从示例中可以看出,引用捕获的变量使用的实际上就是该引用所绑定的对象。

隐式捕获
上面的值捕获和引用捕获都需要我们在捕获列表中显示列出Lambda表达式中使用的外部变量。除此之外,我们还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。

  1. int main()
  2. {
  3. int a = 123;
  4. auto f = [=] { cout << a << endl; }; // 值捕获
  5. f(); // 输出:123
  6. }
  1. int main()
  2. {
  3. int a = 123;
  4. auto f = [&] { cout << a << endl; }; // 引用捕获
  5. a = 321;
  6. f(); // 输出:321
  7. }

混合方式
上面的例子,要么是值捕获,要么是引用捕获,Lambda表达式还支持混合的方式捕获外部变量,这种方式主要是以上几种捕获方式的组合使用。
到这里,我们来总结一下:C++11中的Lambda表达式捕获外部变量主要有以下形式:
image.png
**

3.修改捕获变量

前面我们提到过,在Lambda表达式中,如果以传值方式捕获外部变量,则函数体中不能修改该外部变量,否则会引发编译错误。那么有没有办法可以修改值捕获的外部变量呢?这是就需要使用mutable关键字,该关键字用以说明表达式体内的代码可以修改值捕获的变量,示例:

  1. int main()
  2. {
  3. int a = 123;
  4. auto f = [a]()mutable { cout << ++a; }; // 不会报错
  5. cout << a << endl; // 输出:123
  6. f(); // 输出:124
  7. }

4.表达式的参数

  1. 参数列表中不能有默认参数
  2. 不支持可变参数
  3. 所有参数必须有参数名

5.QT应用

  1. connect(btn2, &QPushButton::clicked, this, [=](){
  2. emit manage->newDemand("增加功能");
  3. //在这里可以运行多个函数
  4. });

如上,通过该表达式,可以实现参数传递(其实就是调用了一个函数)的功能,
而且在函数体里面可以调用多个函数

= 与 &区别

  1. = 要可修改需要加mutable,而且是深拷贝一份结果,对原数据不产生影响。

    1. int k = 5;
    2. int m = [=]()mutable{
    3. k = k + 10;
    4. return k;
    5. }();
    6. qDebug() << "m=" << m;
    7. qDebug() << "k=" << k;

    运行结果:
    image.png

  2. &相当于引用,改变变量不需要加mutable,直接会改变,而且会对原数据产生影响。

    1. int k = 5;
    2. int m = [&](){
    3. k = k + 10;
    4. return k;
    5. }();
    6. qDebug() << "m=" << m;
    7. qDebug() << "k=" << k;

    image.png