文章目录
- 信号与槽
- 1. connect
- 2. 槽 slot
- 3. 信号 signal
- 4. 带参数的信号和槽
- 5. 信号槽机制的意义
信号与槽
在Qt中,用户和控件的每一次交互都称为一个“事件”。比如,用户点击按钮,用户关闭窗口。每一次事件的发生,都会触发一个信号。信号的作用就是用来区分用户的操作类型,并针对用户的操作进行不同的处理,这种不同的处理就是槽。
信号源:信号是由谁发出的?
信号类型:不同信号的类型
接收者:信号由谁处理?
槽:对信号作出的响应动作
1. connect
Qt中提供connect
函数,用于绑定信号与槽,即先准备好信号的处理方式,才能在与用户交互过程中处理对应的信号。
QObject::connect
(const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
sender: 信号源
signal: 信号类型
receiver: 负责信号接收处理者
method: 处理信号的方式
上面是旧版本Qt中的connect
函数,因为signal和method的参数类型都是char*,但信号函数和槽函数的类型是变化的,所以每次传参还需要调用SIGNAL()
和SLOT()
宏函数将函数指针转换为char*类型,新版本Qt提供了connect
函数的泛型版本:
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(
const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
Func1 signal,
const typename QtPrivate::FunctionPointer<Func2>::Object *receiver,
Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection)
- 这是一个模板函数,
signal
和slot
的类型由传入参数决定。 -
sender
和receiver
分别是Func1
和Func2
类型萃取后的结果,保证了sender
中必须包含signal
信号,receiver
中必须包含slot
槽函数,若不存在对应的信号或槽,程序报错,避免程序员手动检查
注意:
-
click
是一个槽函数,clicked
是一个信号 -
一个信号绑定多个槽函数时,信号发生,多个槽函数同时被调用。
-
disconnect
与connect
作用相反,断开信号与槽的连接,使用方式与connect
类似。一般使用disconnect
主动断开信号与槽的连接,是为了将信号绑定到另一个槽函数上。
2. 槽 slot
槽本质上就是一个成员函数。槽函数不仅有Qt内置的,还可由用户自定义,这是开发中的关键,因为不同的业务,对于用户的交互有不同的处理逻辑。那么,如何定义槽函数呢?
-
跟定义一个普通的成员函数没有区别
widget.h
widget.cpp
-
图形化操作,对于生成的控件,选择信号,并自动生成槽函数的声明和槽函数的定义,槽函数的函数体由用户设计(默认为什么都不做),而信号与槽函数的绑定是由Qt自动完成的(connectSlotsByName)。
最终ui_widget.h中调用connectSlotsByName()
函数,根据槽函数名称,实现槽函数与信号之间的绑定
3. 信号 signal
信号本质上也是一个函数,而且也可以由用户自定义,不过自定义信号比较少见,一般Qt内置的信号类型就足以应对大部分开发场景。
emit
通过代码发射信号
- 自定义类
Widget
的信号函数mySignal()
,记得加上signals
关键字修饰(函数的定义是Qt生成的,我们只需写函数声明)
-
写一个槽函数
handleMySignal()
,用于处理自定义信号void Widget::handleMySignal() { this->setWindowTitle("mySignal信号:修改标题"); }
-
调用
connect
连接自定义的信号和槽connect(this, &Widget::mySignal, this, &Widget::handleMySignal); // Widget类的构造函数中
-
随便整一个按钮(这里用了图形化界面生成),点击它会发射自定义信号
mySignal
,验证效果void Widget::on_pushButton_2_clicked() { emit mySignal(); //发射自定义信号, emit可以省略 }
4. 带参数的信号和槽
-
信号函数可以向槽函数传递参数,以实现代码复用;
-
信号函数的参数类型必须和槽函数的参数类型一致,才能传递,否则会报错;
-
信号函数的参数个数 >= 槽函数的参数个数,这使得信号与槽之间的连接更加灵活,即一个槽函数可以与被多个不同的信号函数连接,只要保证每个信号函数传递的参数不少于槽函数的参数个数即可,槽函数会按顺序取前N个(N是槽函数的参数个数),多余的参数自动丢弃;
-
带参数的信号与槽
//助于理解的伪代码
void Widget::mySignal1(QString t1); //signal
void handleMySignal(const QString& s); //slot
void Widget::handleMySignal(const QString& s)
{
qDebug() << s;
}
connect(this, &Widget::mySignal1, this, &Widget::handleMySignal); //连接信号槽
void Widget::on_pushButton_2_clicked() //发射信号
{
emit mySignal1("mySignal1");
}
最后执行情况:
-
参数个数不一致的信号与槽
//助于理解的伪代码 void Widget::mySignal1(QString t1); //signal void Widget::mySignal2(QString t1, QString t2);//signal void handleMySignal(const QString& s); //slot void Widget::handleMySignal(const QString& s) { qDebug() << s; } connect(this, &Widget::mySignal1, this, &Widget::handleMySignal); //连接信号槽 connect(this, &Widget::mySignal2, this, &Widget::handleMySignal); void Widget::on_pushButton_2_clicked() //发射信号 { emit mySignal1("mySignal1"); emit mySignal2("mySignal2", "mySignal2"); }
最终执行结果,可以看到发送
mySignal2
也是只打印一次,因为槽函数handleMySignal
只取mySignal2
第一个参数
5. 信号槽机制的意义
-
解耦合
信号函数的定义,槽函数的定义,以及信号槽的连接是分开处理的,耦合度低。
-
多对多
一个信号可以绑定多个槽函数,一个槽函数也可以被多个信号绑定。
QPushButton* btn3 = new QPushButton(this); btn3->setText("点击此按钮,会发生三件事!"); btn3->move(0, 200); connect(btn3, &QPushButton::clicked, this, &Widget::mySlot1); connect(btn3, &QPushButton::clicked, this, &Widget::mySlot2); connect(btn3, &QPushButton::clicked, this, &Widget::mySlot3);