信号和槽
信号和槽
信号和槽是Qt的创新之一,实现了对象间的通信,它隐藏了复杂的底层实现。完成信号与槽函数的关联后,发送者并不关注此信号是否有关联的槽函数,关联的槽函数就会运行。
一、connect()函数的不同参数形式
静态函数QObject::connect()有很多参数形式,如下:
cpp
QMetaObject::Connect QObject::connect
(
const QObject *sender
,const char *signal
,const QObject *receiver
,const char *method
,Qt::ConnectionType type = Qt::AutoConnection
)
使用这种参数形式的connect()函数进行信号和槽函数之间的连接,如下
cpp
connect(sender,SIGNAL(信号(参数类型)),receiver,SLOT(槽函数(参数类型)));
这里使用宏SIGNAL()
和SLOT()
来指定信号和槽函数,如果信号和槽函数有参数,还需要明确参数类型
cpp
QMetaObject::Connect QObject::connect
(
const QObject *sender
,const QMetaMethod &signal
,const QObject *receiver
,const QMetaMethod &method
,Qt::ConnectionType type = Qt::AutoConnection
)
使用这种参数形式的connect()函数进行信号和槽函数之间的连接,如下
cpp
connect(sender,&对象::信号,receiver,&对象::槽函数);
例如:
connect(sender,&QLineEdit::textChanged,receiver,&Widget::do_textChanged);
在信号和槽函数参数较多的情况下,采用这种参数形式的connect()函数来连接比较简单
还有一个作为QObject成员函数的connect(),其函数原型如下:
cpp
QMetaObject::Connect QObject::connect
(
const QObject *sender
,const char *signal
,const char *method
,Qt::ConnectionType type = Qt::AutoConnection
)
这个函数里没有接收者参数,接收者就说对象自身,this表示窗口对象,槽函数是窗口类里定义的
cpp
connect(sender,SIGNAL(信号(参数类型)),SLOT(槽函数(参数类型)));
当信号重载了,连接槽函数需要使用模板函数qOverload()
来明确参数类型
cpp
signals:
void clicked(bool);
void clicked();
private slots:
void do_click(bool);
void do_click();
cpp
connect(sender,&对象::clicked,receiver,qOverload<bool>(&对象::do_click));
connect(sender,&对象::clicked,receiver,qOverload<>(&对象::do_click));
我们在设计槽函数的时候一般也不会设计成重载型的
connect()函数的最后一个参数:
- Qt::AutoConnection(默认值):如果信号的发射者和接收者在同一个线程中,就使用Qt::DirectConnection方式,否则Qt::QueuedConnection方式。
- Qt::DirectConnection:信号被发射时槽函数立即运行,槽函数与信号在同一个线程中。
- Qt::QueuedConnection:在事件循环回到接收者线程后运行槽函数,槽函数与信号在不同的线程中。
- Qt::BlockingQueuedConnection:与Qt::QueuedConnection相似,区别是信号线程会阻塞,知道槽函数运行完毕。当信号和槽函数在同一个线程中时,绝对不能使用这种方式,否则会被死锁。
二、disconnect()函数的使用
函数disconnect()用于解除信号和槽的连接,详细情况查看Qt帮助文档
1)解除与一个发射者所有信号的连接:
cpp
disconnect(myObject,nullptr,nullptr,nullptr);//静态函数形式
myObject->disconnect();//成员函数形式
2)解除与一个特定信号的所有连接:
cpp
disconnect(myObject,SIGNAL(信号()),nullptr,nullptr);//静态函数形式
myObject->disconnect(SIGNAL(信号()));//成员函数形式
3)解除与一个特点接收者的所有连接:
cpp
disconnect(myObject,nullptr,myReceiver,nullptr);//静态函数形式
myObject->disconnect(myReceiver);//成员函数形式
4)解除特定的一个信号与槽的连接:
cpp
disconnect(myObject,&对象::信号,myReceiver,&对象::槽函数);//静态函数形式
三、使用函数sender()获取函数的发射者
在槽函数调用函数sender()可以获取信号发射者的QObject对象指针,如果知道信号发射者的类型,我们就可以使用qobject_cast<>
转化成确定类型对象的指针
例如:界面上有一个QPushButton按钮cilcked()信号的槽函数如下:
cpp
void Widget::do_cilched()
{
QPushButton *btn=qobject_cast<QPushButton *>(sender());
}
四、自定义信号及其使用
自定义信号
cpp
class My :public QObject
{
Q_OBJECT
signals:
//自定义信号 hello
void hello();
};
发射信号
cpp
//发射信号
emit hello();
编写槽函数
cpp
class Your :public QObject
{
Q_OBJECT
private slot:
//定义槽函数
void world();
};
使用函数connect()连接信号hello()和槽world()
cpp
My m1;
Your y1;
connect(m1,&My::hello,y1,&Your::world);
自定义信号和槽函数规则:
- 信号函数必须是无返回值函数,但可以有参数
- 信号函数无需实现'
- 槽函数参数的个数不得大于信号函数参数是的个数,严格情况下,信号和槽函数的参数个数和类型需要保持一致
- 使用信号与槽的类中,必须在类的定义中插入宏
Q_OBJECT
信号和槽的使用:
- 一个信号可以连接多个槽函数
- 多个信号可以连接一个槽函数
- 一个信号可以连接另一个信号
四、信号和槽的优缺点
优点:
- 松耦合:信号和槽机制使得对象之间不需要显示的依赖关系,从而降低耦合独
- 异步通信:发射者可以在不等待接收者的响应情况下继续执行,提高程序的响应性能
- 事件驱动:图形化编程常常和事件处理有管,信号和槽的机制使得处理事件更加灵活、方便
- 支持多线程:
- 可扩展性:通过信号与槽,可以轻松地扩展系统,添加新的功能模块,不需要修改现有代码,只需在适当的位置连接信号与槽
缺点:
- 运行时错误:信号与槽机制在编译时并不会检查连接的有效性,只有在运行时才能发现连接错误。如果信号或槽的名称拼写错误,或者参数不匹配,会导致程序在运行时出错
- 性能开销:相比于直接函数调用,信号与槽机制可能会带来一定的性能开销。但对于大多数应用来说,这种开销是可以接受的
- 复杂性:在大型应用中,如果信号与槽的连接过于复杂,可能会导致程序的逻辑变得难以理解。因此,适当的使用和组织信号与槽是重要的