当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,将想要处理的信号和自己的一个函数(槽函数(slot))绑定并处理这个信号。
信号和槽都是函数。
但信号是类的成员函数。
而槽不仅可以是类的成员函数,还可以是静态函数、全局函数和 Lambda 表达式。
项目 | 信号 | 槽 |
---|---|---|
声明 | 有 | 有 |
实现 | 无 | 有 |
参数 | 可有可无 | 可有可无 |
返回值 | 无 | 无 |
信号函数必须使用 signals 关键字来声明,而且 signals 前不能用 public、private、protected 等限定,因为信号函数默认是 public 的。
但槽函数是类的成员函数时可以被 public、private、protected 限定,而且槽函数需用 slots 关键字来声明(虽然新版本的 Qt 已经不使用 slots,但为了兼容性仍建议使用),类似这样:
//信号的声明
signals:
void sig1();
//槽的声明
public slots:
void slot1();
他们的参数都是可有可无,但如果有参数,对于可以相连接的信号函数和槽函数,信号函数的参数个数不能少于槽函数,而且对应的参数的数据类型必须一致。
信号函数 | 槽函数 | 正误 | 描述 |
---|---|---|---|
signal(int, float, double) | slot(int, float, double) | 正确 | 参数个数一致,类型一一对应 |
signal(int, float, double) | slot(int, float) | 正确 | 参数个数,信号多于槽,类型一一对应 |
signal(int, float, double) | slot() | 正确 | 参数个数,信号多于槽,类型一一对应 |
signal(int, float, double) | slot(double, int, float) | 错误 | 参数类型不对应 |
signal(int, float) | slot(int, float, double) | 错误 | 参数个数信号少于槽 |
助记:槽要什么信号就得给什么,能多不能少。
使用 connect() 函数对信号和槽进行连接,对于这个函数,Qt4 和 Qt5 有所不同。
Qt4:
connect(sender, // 信号发送者
SIGNAL(sig1(int, QString)), // 信号函数
receiver, // 信号接收者
SLOT(slot1(int, QString))); // 槽函数
Qt5:
connect(sender, // 信号发送者
&sig1, // 信号函数地址
receiver, // 信号接收者
&slot1); // 槽函数地址
虽然 Qt4 版本有指明函数参数的数据类型,但没有编译期错误检查,比如上面1.3表格中的例子:
信号函数 | 槽函数 | 正误 | 描述 |
---|---|---|---|
signal(int, float, double) | slot(double, int, float) | 错误 | 参数类型不对应 |
Qt4版本的连接函数,编译器是不报错的,但在执行时会出现错误:
connect(sender, // 信号发送者
SIGNAL(signal(int, float, double) ), // 信号函数
receiver, // 信号接收者
SLOT(slot(double, int, float))); // 槽函数
而在 Qt5 中,会进行编译期错误检查,所以上述代码编译时会报错,更加安全。
使用 disconnect() 函数使信号和槽的连接断开。
基本用法是断开一个信号和一个槽的连接:
disconnect(sender, // 信号发送者
&sig1, // 信号函数地址
receiver, // 信号接收者
&slot1); // 槽函数地址
另外,还有以下用法:
断开一个对象(信号发送者)的所有信号的所有连接:
disconnect(sender, 0, 0, 0);
//等价于
sender->disconnect();
断开一个信号的所有连接:
disconnect(sender, &sig1, 0, 0);
//等价于
sender->disconnect(&sig1);
断开一个对象(信号发送者)与另一个对象(信号接收者)的所有连接:
disconnect(sender, 0, receiver, 0);
//等价于
sender->disconnect(receiver);
信号连接信号不仅可以和槽连接,也可以和信号连接。
connect(sender, // 信号发送者
&sig1, // 信号函数地址
receiver, // 信号接收者
&sig2); // 另一个信号函数地址
3.2.1. 一个信号连接多个槽(信号)
一个信号可以连接多个槽(信号)函数,并且被连接的槽(信号)函数的触发顺序是随机的,不能控制。
connect(sender, &sig1, receiver, &sig2);
connect(sender, &sig1, receiver, &sig3);
connect(sender, &sig1, receiver1, &slot1);
connect(sender, &sig1, receiver2, &slot2);
3.2.2. 多个信号连接一个槽(信号)
一个槽函数可以同时被多个信号连接。
connect(sender1, &sig1, receiver, &slot);
connect(sender2, &sig2, receiver, &slot);
connect(sender3, &sig3, receiver, &slot);
connect(sender4, &sig4, receiver, &slot);
总结:信号与槽的对应,一对一,一对多,多对一。
void signal()
{
emit sig();
}