Qt信号与槽的拓展和案例


Qt信号与槽的拓展和案例

1.标准信号和槽的使用:

1648284450392

2.自定义的信号与槽的使用:

1648286422772

4. 信号槽拓展

4.1 信号槽使用拓展

  • 一个信号可以连接多个槽函数, 发送一个信号有多个处理动作

    • 需要写多个connect连接
    • 信号的接收者可以是一个对象, 也可以是多个对象
  • 一个槽函数可以连接多个信号, 多个不同的信号, 处理动作是相同的

    • 写多个connect就可以
  • 信号可以连接信号

    • 信号接收者可以不出来接收的信号, 继续发出新的信号 -> 传递了数据, 并没有进行处理

1648286822336

信号槽也是可以断开的

disconnect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::method);

1648286962578

4.2 信号槽的连接方式

  • Qt5的连接方式

    // 语法:
    QMetaObject::Connection QObject::connect(
            const QObject *sender, PointerToMemberFunction signal, 
            const QObject *receiver, PointerToMemberFunction method, 
            Qt::ConnectionType type = Qt::AutoConnection);
    
    // 信号和槽函数也就是第2,4个参数传递的是地址, 编译器在编译过程中会对数据的正确性进行检测
    connect(const QObject *sender, &QObject::signal, 
            const QObject *receiver, &QObject::method);
    
  • Qt4的连接方式

    这种旧的信号槽连接方式在Qt5中是支持的, 但是不推荐使用, 因为这种方式在进行信号槽连接的时候, 信号槽函数通过宏SIGNALSLOT转换为字符串类型。

    因为信号槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测, 如果使用者传错了数据,编译器也不会报错,但实际上信号槽的连接已经不对了,只有在程序运行起来之后才能发现问题,而且问题不容易被定位。

    // Qt4的信号槽连接方式
    [static] QMetaObject::Connection QObject::connect(
        const QObject *sender, const char *signal, 
        const QObject *receiver, const char *method, 
        Qt::ConnectionType type = Qt::AutoConnection);
    
    connect(const QObject *sender,SIGNAL(信号函数名(参数1, 参数2, ...)),
            const QObject *receiver,SLOT(槽函数名(参数1, 参数2, ...)));
    
  • 应用举例

    class Me : public QObject
    {
        Q_OBJECT
    // Qt4中的槽函数必须这样声明, qt5中的关键字 slots 可以被省略
    public slots:
           void eat();
        void eat(QString somthing);
    signals:
        void hungury();
        void hungury(QString somthing);
    };
    

基于上面写的信号与槽,我们来处理如下逻辑: 我饿了, 我要吃东西

  • 分析: 信号的发出者是我自己, 信号的接收者也是我自己

    Me m;
    // Qt4处理方式  注意不要把信号与槽的名字写错了,因为是转为字符串写错了不会报错,但是连接会失败
    connect(&m, SIGNAL(eat()), &m, SLOT(hungury()));
    connect(&m, SIGNAL(eat(QString)), &m, SLOT(hungury(QString)));
    
    // Qt5处理方式
    connect(&m, &Me::eat, &m, &Me::hungury);    // error:no matching member function for call to 'connect'
    
  • 为什么Qt4的方式没有错误,Qt5的方式却有问题了呢?

    • Qt4的方式在传信号和槽的时候用了宏进行强转,而且都带了参数,不会有二义性问题产生
    • Qt5中,信号和槽都有重载,此事connect函数根本就不知道你要使用的是重载中的哪一个,所以只能报错咯!
  • 如何解决Qt5中的信号和槽重载中的二义性问题呢?

    • 一,通过函数指针解决

      //信号
      void (Me::*funchungury)() = &Me::hungury;
      void (Me::*funchungury_QString)(QString) = &Me::hungury;
      //槽
      void (Me::*funceat)() = &Me::eat;
      void (Me::*funceat_QString)(QString) = &Me::eat;
      //有参连接
      connect(me,funchungury_QString,me,funceat_QString);
      //无参连接
      connect(me,funchungury,me,funceat);
      
    • 二,通过Qt提供的重载类(QOverload)解决

      //有参连接
      connect(this,QOverload<QString>::of(&MyButton::hungury),this,QOverload<QString>::of(&MyButton::eat));
      //无参连接
      connect(this,QOverload<>::of(&MyButton::hungury),this,QOverload<>::of(&MyButton::eat));
      
  • 总结
    • Qt4的信号槽连接方式因为使用了宏函数, 宏函数对用户传递的信号槽不会做错误检测, 容易出bug
    • Qt5的信号槽连接方式, 传递的是信号槽函数的地址, 编译器会做错误检测, 减少了bug的产生
    • 当信号槽函数被重载之后, Qt4的信号槽连接方式不受影响
    • 当信号槽函数被重载之后, Qt5中需要给被重载的信号或者槽定义函数指针

4.3 Lambda表达式

Lambda表达式是C++11最重要也是最常用的特性之一,是现代编程语言的一个特点,简洁,提高了代码的效率并且可以使程序更加灵活,Qt是完全支持c++语法的, 因此在Qt中也可以使用Lambda表达式。

Lambda表达式就是一个匿名函数, 语法格式如下:

[capture](params) opt -> ret {body;};
    - capture: 捕获列表
    - params: 参数列表
    - opt: 函数选项
    - ret: 返回值类型
    - body: 函数体
        
// 示例代码->匿名函数的调用:
int ret = [](int a) -> int
{
    return a+1;
}(100);

关于Lambda表达式的细节介绍:

  1. 捕获列表: 捕获一定范围内的变量
    • [] - 不捕捉任何变量
    • [&] - 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)
    • [=] - 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获)
      • 拷贝的副本在匿名函数体内部是只读的
    • [=, &foo] - 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo
    • [bar] - 按值捕获 bar 变量, 同时不捕获其他变量
    • [&bar] - 按值捕获 bar 变量, 同时不捕获其他变量
    • [this] - 捕获当前类中的this指针
      • 让lambda表达式拥有和当前类成员函数同样的访问权限
      • 如果已经使用了 & 或者 =, 默认添加此选项
  2. 参数列表: 和普通函数的参数列表一样
  3. opt 选项 –> 可以省略
    • mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
    • exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用throw();
  4. 返回值类型:
    • 标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略
  5. 函数体:
    • 函数的实现,这部分不能省略,但函数体可以为空。

1648287270350


文章作者: hehe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hehe !
  目录
​ ​