【QT开发笔记-基础篇】| 第一章 QT入门 | 1.8 信号槽的扩展知识

发布于:2023-01-28 ⋅ 阅读:(633) ⋅ 点赞:(0)

本节对应的视频讲解:B_站_链_接

https://www.bilibili.com/video/BV1JU4y1Q7v4

本节讲解信号槽的一些扩展知识点


1. 如何连接重载的信号和槽


在信号和槽存在重载时,Qt4Qt5 的写法是有区别的:

  • Qt4 方式

    可以在 SIGNAL/SLOT 中指定函数参数类型,因此写法比较简单;

  • Qt5 方式

    指定信号和槽时,只能指定函数名,无法向 Qt4 那样指定函数参数类型,需要单独定义函数指针,写法上稍显麻烦。

接下来,以《6. 自定义信号槽》中 “长官和士兵” 的例子为例,来看下信号槽重载时 Qt4Qt5 写法的不同

    // 1、Qt4信号槽的连接:SIGNAL/SLOT
#if 0
    Commander commander;
    Soldier soldier;

    connect(&commander, SIGNAL(go()), &soldier, SLOT(fight()));
    connect(&commander, SIGNAL(go(QString)), &soldier, SLOT(fight(QString)));

    emit commander.go();
    emit commander.go("freedom");
#endif

    // 2、Qt5信号槽的连接:函数地址
#if 0
    Commander commander;
    Soldier soldier;

    // 没有同名的信号和槽时,可以直接这样写。因为不存在二义性
    // connect(&commander, &Commander::go, &soldier, &Soldier::fight);

    // 有同名的信号和槽时,需要向下面这样定义函数指针。因为存在二义性
    // 编译器自动推断:将无参的信号go和无参的槽,赋值给函数指针(ctrl+鼠标点击可以智能跳转过去)
    void (Commander::*pGo)() = &Commander::go;
    void (Soldier::*pFight)() = &Soldier::fight;
    connect(&commander, pGo, &soldier, pFight);

    // 编译器自动推断:将有参的信号go和有参的槽,赋值给函数指针(ctrl+鼠标点击可以智能跳转过去)
    void (Commander::*pGoForFreedom)(QString) = &Commander::go;
    void (Soldier::*pFightForFreedom)(QString) = &Soldier::fight;
    connect(&commander, pGoForFreedom, &soldier, pFightForFreedom);

    emit commander.go();
    emit commander.go("freedom");
#endif

2. 一个信号连接多个槽


一个信号可以连接多个槽函数,如下:

connect(sender, SIGNAL(signal), receiver1, SLOT(fun1()));
connect(sender, SIGNAL(signal), receiver2, SLOT(fun2()));

这样,当 signal 这个信号发出时,它连接的 2 个槽函数 fun1fun2 都会被执行,并且:

  • Qt4

    信号发射时,与之相连接的槽函数的执行顺序是随机的。

  • Qt5+

    信号发射时,这些槽函数的执行顺序与建立连接的顺序相同。

接下来,以《6 自定义信号槽》中 “长官和士兵” 的例子为例:

// 士兵1很勇敢,收到冲锋的信号后,开始战斗
connect(&commander, SIGNAL(go()), &soldier1, SLOT(fight()));

// 士兵2很怕死,收到冲锋的信号后,开始逃跑
connect(&commander, SIGNAL(go()), &soldier2, SLOT(escape()));

接下来一步步实现这个需求:

首先,在 Soldier 类中添加槽函数的声明和定义,如下:

// Soldier.h
class Soldier : public QObject
{
    ...

public slots:
    void fight();
    void fight(QString);

    // 添加一个“逃跑”的槽函数
    void escape();
};

// Soldier.cpp
void Soldier::escape()
{
    qDebug() << "i'm afraid of death, escape...";
}

然后,连接信号槽并发送信号,如下:

    // 3、一个信号连接多个槽函数
#if 1
    Commander commander;
    Soldier soldier1;
    Soldier soldier2;
    
    // 士兵1很勇敢,收到冲锋的信号后,开始战斗
    connect(&commander, SIGNAL(go()), &soldier1, SLOT(fight()));

    // 士兵2很怕死,收到冲锋的信号后,开始逃跑
    connect(&commander, SIGNAL(go()), &soldier2, SLOT(escape()));

    emit commander.go();
#endif

3. 多个信号连接一个槽


可以将多个信号连接到同一个槽函数,如下:

connect(sender, SIGNAL(signal1), receiver, SLOT(fun()));
connect(sender, SIGNAL(signal2), receiver, SLOT(fun()));

这样,当 signal1singnal2 这 2 个信号发出时,都会执行槽函数 fun

接下来,以《6 自定义信号槽》中 “长官和士兵” 的例子为例:

// 当 commander 发射 go 信号和 move 信号时,都会执行士兵的 fight 槽函数,开始战斗
connect(&commander, SIGNAL(go()), &soldier, SLOT(fight()));
connect(&commander, SIGNAL(move()), &soldier, SLOT(fight()));

接下来一步步实现这个需求:

首先,在 Commander 类中新添加一个 move 的信号,如下:

class Commander : public QObject
{
    ...
        
signals:
    void go();
    void go(QString);

    // 新添加一个 move 信号
    void move();
};

然后,连接信号槽并发送信号,如下:

    // 4、多个信号连接一个槽函数
#if 1
    Commander commander;
    Soldier soldier;

    // 当 commander 发射 go 信号和 move 信号时,都会执行士兵的 fight 槽函数,开始战斗
    connect(&commander, SIGNAL(go()), &soldier, SLOT(fight()));
    connect(&commander, SIGNAL(move()), &soldier, SLOT(fight()));

    emit commander.go();
    emit commander.move();
#endif

4. 信号连接信号


信号不仅可以连接槽, 还可以和连接信号,如下:

connect(obj1, SIGNAL(signal1), obj2, SIGNAL(signal2));

这样,当 obj1 发送 signal1 信号时,就会触发 obj2 发送 signal2 信号。

接下来,同样以《6 自定义信号槽》中 “长官和士兵” 的例子为例:

// 按钮的点击会发射clicked信号 => commander发射move信号 => soldier执行escapse槽函数
connect(ui->btnAction, &QPushButton::clicked, commander, &Commander::move);
connect(commander, &Commander::move, soldier, &Soldier::escape);

接下来一步步实现这个需求:

首先,在 Commander 类中新添加一个 move 的信号,如下:

class MainWindow : public QMainWindow
{
	...

    // 在 MainWindow 中,添加 commander 和 soldier 两个指针类型的成员变量
    Commander *commander;
    Soldier *soldier;
};

然后,实例化 commandersoldier 两个对象,并连接信号槽,如下:

    // 5、信号连接信号
#if 1
    // 首先,成员变量初始化
    commander = new Commander();
    soldier = new Soldier();

    // 然后,信号连接信号 + 信号连接槽
    connect(ui->btnAction, &QPushButton::clicked, commander, &Commander::move);
    connect(commander, &Commander::move, soldier, &Soldier::escape);
#endif

此时,点击按钮,按钮会发射 clicked 信号, 接着 commander 发射 move 信号,move 信号的发射,会去执行 soldierescape 槽函数

注意:

此时的 commandersoldier 要定义为类的成员变量。

因为如果把 commandersoldier 定义为局部变量,MainWindow 构造执行完毕后,这两个变量就已经释放了


5. 断开连接 - disconnect


disconnect 用于断开信号和槽之间已经建立的连接。

这种情况并不常用,因为当一个对象 delete 之后, Qt 自动取消所有连接到这个对象上面的槽。

    // 6、断开信号的连接
#if 1
    Commander commander;
    Soldier soldier;

    connect(&commander, SIGNAL(go()), &soldier, SLOT(fight()));
    connect(&commander, SIGNAL(go(QString)), &soldier, SLOT(fight(QString)));

    emit commander.go();
    
    // 断开所有连接到 commander 信号上的槽函数
    commander.disconnect();
    
    emit commander.go("freedom");  // 对应的槽函数不会执行
#endif

当然了,disconnect 有多个重载的函数,具体参考 Qt 帮助文档即可。

disconnect 函数并不常用,因为当一个对象 delete 之后, Qt 自动取消所有连接到这个对象上面的槽。


本节对应的视频讲解:B_站_链_接

https://www.bilibili.com/video/BV1JU4y1Q7v4



网站公告

今日签到

点亮在社区的每一天
去签到