本节对应的视频讲解:B_站_链_接
https://www.bilibili.com/video/BV1JU4y1Q7v4
本节讲解信号槽的一些扩展知识点
1. 如何连接重载的信号和槽
在信号和槽存在重载时,Qt4
和 Qt5
的写法是有区别的:
Qt4
方式可以在
SIGNAL
/SLOT
中指定函数参数类型,因此写法比较简单;Qt5
方式指定信号和槽时,只能指定函数名,无法向
Qt4
那样指定函数参数类型,需要单独定义函数指针,写法上稍显麻烦。
接下来,以《6. 自定义信号槽》中 “长官和士兵” 的例子为例,来看下信号槽重载时 Qt4
和 Qt5
写法的不同
// 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 个槽函数 fun1
,fun2
都会被执行,并且:
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()));
这样,当 signal1
和 singnal2
这 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;
};
然后,实例化 commander
和 soldier
两个对象,并连接信号槽,如下:
// 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
信号的发射,会去执行 soldier
的 escape
槽函数
注意:
此时的 commander
和 soldier
要定义为类的成员变量。
因为如果把 commander
和 soldier
定义为局部变量,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