QObject::connect

本文详细介绍了在Qt中使用QObject::connect的五种方法,包括SIGNAL/SLOT宏、类成员函数指针、关联函数、Lambda表达式和QMetaMethod,以及它们各自的优缺点和用法示例。
摘要由CSDN通过智能技术生成

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));

优点

  1. 编译时检查是否存在信号和插槽,类型,或者是否缺少Q_OBJECT。
    (但是如果信号参数填的是非信号函数,编译阶段检查不出来,运行时报错signal not found)
    QObject::connect(obj1, &myobject::slot1, &myobject::slot1);
    //QObject::connect: signal not found
  2. 参数可以通过typedef定义,也可以使用不同的名称空间说明符,并且可以使用。
  3. 如果存在隐式转换(例如,从QString到QVariant),则可以自动转换类型。
  4. 可以连接到QObject的任何成员函数,而不仅仅是插槽。
  5. 可以与C++11 lambda表达式一起使用

缺点

  1. 更复杂的语法(您需要指定对象的类型)
  2. 重载载情况下的语法非常复杂
  3. 不再支持插槽中的默认参数。

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

  1. 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
}

  1. Qt::DirectConnection

当发出信号时,立即调用该槽。如果槽函数对象与信号不在同一线程,槽函数也会在信号线程中执行。

  1. Qt::QueuedConnection

当控制返回到接收者线程的事件循环时调用该槽。槽在接收者的线程中执行。

  1. Qt::BlockingQueuedConnection

与Qt::QueuedConnection相同,除了信号线程阻塞直到插槽返回。
如果接收方位于信号线程中,则不能使用此连接,否则应用程序将死锁。

  1. 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);
}

信号发送

  1. 使用关键字emit,emit是空的宏定义: # define emit
  2. 信号函数调用前加emit,如 emit obj1->signal1(111);
  3. 由于emit是空的宏定义,所以也可以直接调用信号函数,obj1->signal1(111);

信号发送本质就是函数调用
一般为了区分是信号和普通函数,都会加上emit关键字,提高代码可读性

槽函数

  1. 权限声明后加slots表示后面的为槽函数,如public slots:
  2. 槽函数不支持默认参数

disconnect

  1. 取消所有信号关联的所有槽

    QObject::disconnect(obj1, nullptr, nullptr, nullptr);
    obj1->disconnect();
    这同时也会取消lambda和普通函数的关联
    
  2. 取消某个信号关联的所有槽

    QObject::disconnect(obj1,SIGNAL(signal1()), nullptr, nullptr);
    QObject::disconnect(obj1,&myobject::signal1, nullptr, nullptr);
    obj1->disconnect(SIGNAL(signal1()));
    这同时也会取消lambda和普通函数的关联
    
  3. 取消某个信号关联的某个接收者的所有槽

    QObject::disconnect(obj1,SIGNAL(signal1()), obj2, nullptr);
    QObject::disconnect(obj1,&myobject::signal1, obj2, nullptr);
    这个无法取消lambda和普通函数的关联(普通函数接收者为发送者本身)
    QObject::disconnect(obj1,&myobject::signal1, obj1, nullptr);
    这个可以取消lambda和普通函数的关联
    
  4. 取消某个信号关联的某个接收者的某个槽

    如果多个相同的关联都将被取消;
    对于这种指定具体的信号和槽的关联取消: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)));
    
    
  5. 单独取消关联的某个普通函数和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);
    
  6. 取消某个接收者对象的所有关联槽

     QObject::disconnect(obj1, nullptr,obj2, nullptr);
     obj1->disconnect(obj2);
    
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值