`信号和槽通过 QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)*函数关联
信号和槽的关联关系可以有几种模式: ● 一个信号和一个槽关联; ● 一个信号和多个槽关联; ● 多个信号和一个槽关联 一个信号与多个槽关联时,在Qt5中,: A signal can be connected to many slots and signals. Many signals can be connected to one slot. If a signal is connected to several slots, the slots are activated in the same order in which the connections were made, when the signal is emitted. .(一个信号连接多个槽,信号发射后,会按照链接顺序执行)。 测试如下:
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(OnPushButtonClicked1())); connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(OnPushButtonClicked2())); connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(OnPushButtonClicked3()));结果如下:
OnPushButtonClicked1 OnPushButtonClicked2 OnPushButtonClicked3 connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(OnPushButtonClicked2())); connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(OnPushButtonClicked1())); connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(OnPushButtonClicked3()));结果如下:
OnPushButtonClicked2 OnPushButtonClicked1 OnPushButtonClicked3参数type定义了信号和槽的关联方式。
Qt支持6种连接方式,其中3中最主要: 1.Qt::AutoConnection(自动方式) Qt的默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与排队方式相同。 2.Qt::DirectConnection(直接连接)(同步) 当信号发送后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。 3.Qt::QueuedConnection(排队连接)(异步) 当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。多线程环境下可使用。 4.Qt::BlockingQueuedConnection(阻塞连接,信号和槽必须在不同的线程中,否则就产生死锁) 发送信号后发送者所在的线程会处于阻塞状态 ,直到槽函数运行完。多线程同步环境下可使用。 5.Qt::UniqueConnection 与默认工作方式相同,只是不能重复连接相同的信号和槽,因为如果重复连接就会导致一个信号发出,对应槽函数就会执行多次。这个flag可以通过按位或(|)与以上四个结合在一起使用 6.Qt::AutoCompatConnection 是为了连接Qt4与Qt3的信号槽机制兼容方式,工作方式与Qt::AutoConnection一样。
如果这个参数不设置的话,默认表示的是那种方式呢? 没加的话与直连方式相同:当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。在这个线程内是顺序执行、同步的,但是与其它线程之间肯定是异步的了。如果使用多线程,仍然需要手动同步。
Qt的信号和槽可以传递int、double等c++常用类型变量,也可以传递QVector、QMap等Qt的容器类(当然也可以传递Qt定义的类型)。 传递自定义的结构体: 首先在定义结构体的同时需要使用Q_DECLARE_METATYPE。通过这个宏定义可以将自定义的类型注册到Qt的元类型中,从而被Qt识别。
struct PersonInfo { QString Name; int age; }; Q_DECLARE_METATYPE(PersonInfo)其次在信号端发射的信号类型应该是QVariant,QVariant是多种类型的联合,QVariant类中有个SetValue(T& value)方法,将自定义 T 类型的数据保存到QVariant对象中,可以理解为是自定义类型被封装成了QVariant的形式,这样,我们自定义的T类型的对象就能够通过所有参数和返回值为QVarian类型传递。
signals: void PersonInfoSIG(QVariant info);在发射信号之前,将自定义的结构体用QVariant包裹一下,就可以发射了。
QVariant DataInfo; DataInfo.setValue(info); emit PersonInfoSIG(DataInfo);在槽函数这边,信号类型也定义为QVariant,接收后用该结构体将数据取出来就完成了自定义结构体信号的一次传递
PersonInfo ReInfo=info.value<PersonInfo>();信号和槽机制增强了对象间通信的灵活性,然而这也损失了一些性能。与回调函数相比较,信号和槽机制有些慢。通常,通过传递一个信号来调用槽函数将会比直接调用非虚函数慢10倍。 原因主要有:● 需要定位接收信号的对象; ● 安全地遍历所有的关联(比如,一个信号关联到多个槽的情况); ● 编组(marshal)/解组(unmarshal)传递的参数; ● 多线程的时候,信号可能需要排队等待。 然而,与创建堆对象的new操作以及删除堆对象的delete操作相比较,信号和槽的代价只是它们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。与信号和槽提供的灵活性和简便性相比,这点性能的损失也是值得的。