QObject
QObject::connect的5种用法
1. 使用SIGNAL()和SLOT()宏(Qt4)
class myobject : public QObject
{
Q_OBJECT
public:
myobject(QObject *parent = nullptr):QObject(parent){
}
signals:
void signal1(int i);
public slots:
void slot1(int i){
qDebug()<<"slot1,i="<<i;
}
//测试重载时打开
/*
void slot1(float f){
qDebug()<<"slot1,i="<<i;
}
*/
};
#普通函数
void funslot(int i)
{
qDebug()<<"fun:i="<<i;
}
myobject *obj1 = new myobject("boj1");
myobject *obj2 = new myobject("boj2");
QObject::connect(obj1, SIGNAL(signal1(int)), obj2, SLOT(slot1(int)),Qt::AutoConnection);
QObject::connect(obj1, "2signal1(int)", obj2, "1slot1(int)",Qt::AutoConnection);
emit obj1->signal1(123);
注意,信号和槽参数不能包含任何变量名,只能包含类型。名称错误编译阶段无法检测出来
#define SLOT(a) “1”#a
#define SIGNAL(a) “2”#a
2. 使用类成员函数指针(Qt5新增,并推荐)
QObject::connect(obj1, &myobject::signal1, obj2, &myobject::slot1,Qt::AutoConnection);
QObject::connect(obj1, &myobject::signal1, &myobject::slot1);
QObject::connect(obj1, &myobject::signal1, funslot);
注意,信号和槽地址错误,编译阶段可以检测出来
有重载的情况怎么办?QOverload或static_cast转换解决
最好的做法可能是建议不要重载信号或槽
QObject::connect(obj1, &myobject::signal1, obj2, QOverload<float>::of(&myobject::slot1),Qt::AutoConnection);
QObject::connect(obj1, &myobject::signal1, static_cast<void (myobject::*)(int)>(&myobject::slot1));
优点
- 编译时检查是否存在信号和插槽,类型,或者是否缺少Q_OBJECT。
(但是如果信号参数填的是非信号函数,编译阶段检查不出来,运行时报错signal not found)
QObject::connect(obj1, &myobject::slot1, &myobject::slot1);
//QObject::connect: signal not found- 参数可以通过typedef定义,也可以使用不同的名称空间说明符,并且可以使用。
- 如果存在隐式转换(例如,从QString到QVariant),则可以自动转换类型。
- 可以连接到QObject的任何成员函数,而不仅仅是插槽。
- 可以与C++11 lambda表达式一起使用
缺点
- 更复杂的语法(您需要指定对象的类型)
- 重载载情况下的语法非常复杂
- 不再支持插槽中的默认参数。
3. 关联函数
QObject::connect(obj1, &myobject::signal1, funslot);
4. Lambda
QObject::connect(obj1, &myobject::signal1, [](int i){
qDebug()<<"Lambda:i="<<i;
});
QObject::connect(obj1, &myobject::signal1,obj2, [](int i){
qDebug()<<"Lambda:i="<<i;
},Qt::AutoConnection);
5. QMetaMethod 很少用到
QMetaMethod sig = obj1->metaObject()->method(obj1->metaObject()->methodOffset()); // void signal1(int i);
QMetaMethod slt = obj2->metaObject()->method(obj2->metaObject()->methodOffset()+1);//void slot1(int i);
QObject::connect(obj1, sig, obj2, slt, Qt::AutoConnection);
可以根据QObject的metaObject()方法获取元对象QMetaObject,然后根据元对象的method方法获取信号或槽的元方法对象QMetaMethod
元对象QMetaObject的methodOffset()函数获取当前对象的第一个信号的位置(不一定为0),如果没有信号就是第一个槽的位置,按照信号槽函数的声明顺序来的,信号在前面槽在后面
可以打印出来观看:
for(int i = obj1->metaObject()->methodOffset(); i < metaObject->methodCount(); ++i)
qDebug() << QString::fromLatin1(metaObject->method(i).methodSignature());
Qt::ConnectionType
- Qt::AutoConnection
<(默认)如果接收器位于发出信号的线程中,则使用Qt::DirectConnection。否则,使用Qt::QueuedConnection。在发出信号时确定连接类型。
receiverInSameThread判断发送信号对象和接收的槽对象是否在同一个线程
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection))
{
QMetaCallEvent *ev = new QMetaCallEvent(c->slotObj, sender, signal, nargs);
QCoreApplication::postEvent(receiver, ev);
}
else if (c->connectionType == Qt::BlockingQueuedConnection)
{
//信号量会被放入事件里面,在事件析构的时候release
QSemaphore semaphore;
{
QBasicMutexLocker locker(signalSlotLock(sender));
QMetaCallEvent *ev = new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore);
QCoreApplication::postEvent(receiver, ev);
}
semaphore.acquire();
}else
{
QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);
}
bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::MetaCall:
{
QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e);
QBasicMutexLocker locker(signalSlotLock(this));
mce->placeMetaCall(this);
break;
}
}
}
void QMetaCallEvent::placeMetaCall(QObject *object)
{
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod,
d.method_offset_ + d.method_relative_, d.args_);
}
QAbstractMetaCallEvent::~QAbstractMetaCallEvent()
{
#if QT_CONFIG(thread)
if (semaphore_)
semaphore_->release();
#endif
}
- Qt::DirectConnection
当发出信号时,立即调用该槽。如果槽函数对象与信号不在同一线程,槽函数也会在信号线程中执行。
- Qt::QueuedConnection
当控制返回到接收者线程的事件循环时调用该槽。槽在接收者的线程中执行。
- Qt::BlockingQueuedConnection
与Qt::QueuedConnection相同,除了信号线程阻塞直到插槽返回。
如果接收方位于信号线程中,则不能使用此连接,否则应用程序将死锁。
- Qt::UniqueConnection
这是一个标志,可以使用位或与上述任何一种连接类型组合使用。当Qt::UniqueConnection设置时,如果连接已经存在,QObject::connect()将失败(即,如果相同的信号已经连接到同一插槽的同一对对象)。这个标志是在Qt 4.6中引入的。
信号
信号的定义
使用关键字:signals 加冒号, 后面声明的函数就是信号,其本质就是函数
其被宏定义为:# define signals public qt_signal
信号不需要也不可以由我们自己实现,因为其会被moc编译器编译成函数
信号与槽的返回值要兼容
signals: //等于 public qt_signal :
void signal1(int i);
void signal2(int i);
void signal3(int i);
moc编译器编译后的myobject::signal1函数,实现如下:
void myobject::signal1(int _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
信号发送
- 使用关键字emit,emit是空的宏定义: # define emit
- 信号函数调用前加emit,如 emit obj1->signal1(111);
- 由于emit是空的宏定义,所以也可以直接调用信号函数,obj1->signal1(111);
信号发送本质就是函数调用
一般为了区分是信号和普通函数,都会加上emit关键字,提高代码可读性
槽函数
- 权限声明后加slots表示后面的为槽函数,如public slots:
- 槽函数不支持默认参数
disconnect
-
取消所有信号关联的所有槽
QObject::disconnect(obj1, nullptr, nullptr, nullptr); obj1->disconnect(); 这同时也会取消lambda和普通函数的关联
-
取消某个信号关联的所有槽
QObject::disconnect(obj1,SIGNAL(signal1()), nullptr, nullptr); QObject::disconnect(obj1,&myobject::signal1, nullptr, nullptr); obj1->disconnect(SIGNAL(signal1())); 这同时也会取消lambda和普通函数的关联
-
取消某个信号关联的某个接收者的所有槽
QObject::disconnect(obj1,SIGNAL(signal1()), obj2, nullptr); QObject::disconnect(obj1,&myobject::signal1, obj2, nullptr); 这个无法取消lambda和普通函数的关联(普通函数接收者为发送者本身) QObject::disconnect(obj1,&myobject::signal1, obj1, nullptr); 这个可以取消lambda和普通函数的关联
-
取消某个信号关联的某个接收者的某个槽
如果多个相同的关联都将被取消;
对于这种指定具体的信号和槽的关联取消:SIGNAL/SLOT关联的,必须SIGNAL/SLOT方式取消关联,类成员函数关联的比类成员函数取消关联QObject::connect(obj1,SIGNAL(signal1(int)), obj2, SLOT(slot1(int))); QObject::disconnect(obj1,SIGNAL(signal1(int)), obj2, SLOT(slot1(int))); QObject::connect(obj1, &myobject::signal1, obj2, &myobject::slot1); QObject::disconnect(obj1, &myobject::signal1, obj2, &myobject::slot1); 下面的将无法被取消: QObject::connect(obj1,SIGNAL(signal1(int)), obj2, SLOT(slot1(int))); QObject::disconnect(obj1, &myobject::signal1, obj2, &myobject::slot1); 下面的将无法被取消: QObject::connect(obj1, &myobject::signal1, obj2, &myobject::slot1); QObject::disconnect(obj1,SIGNAL(signal1(int)), obj2, SLOT(slot1(int)));
-
单独取消关联的某个普通函数和Lambda表达式
使用连接时返回的QMetaObject::Connection变量,取消关联对应连接
auto func =QObject::connect(obj1,&myobject::signal1,funslot); QMetaObject::Connection lambdac = QObject::connect(obj1,&myobject::signal1,[](int a){ qDebug("lambda %d\n",a); }); QObject::disconnect(func); QObject::disconnect(lambdac);
-
取消某个接收者对象的所有关联槽
QObject::disconnect(obj1, nullptr,obj2, nullptr); obj1->disconnect(obj2);