Qt 子类重新实现父类的槽函数
在重写槽函数时发现没有被调用(后来发现重写错了类,应该是重写类a,结果写的类b,测试时,只测试到了a),于是写了测试代码验证。
#ifndef PARENTSLOT_H
#define PARENTSLOT_H#include <QObject>
#include <QDebug>
#include <QThread>
class parentSlot:public QObject
{Q_OBJECT
public:parentSlot(QObject*p = nullptr){
// connect(this,&parentSlot::signalTest,this,&parentSlot::slotTest,Qt::QueuedConnection);
// connect(this,&parentSlot::signalTest,this,&parentSlot::slotTest,Qt::BlockingQueuedConnection);connect(this,SIGNAL(signalTest()),this,SLOT(slotTest()));
// connect(this,SIGNAL(signalTest()),this,SLOT(slotTest()),Qt::QueuedConnection);emit signalTest();
// slotTest();}
signals:void signalTest();void signal2();
public slots:virtual void slotTest(){QThread::sleep(3);qDebug() << " this is parent slotTest ";
// emit signal2();}
};
class childSlot:public parentSlot
{Q_OBJECT
public:childSlot(QObject*p = nullptr):parentSlot(p){connect(this,&childSlot::signal2,this,&childSlot::slotChildTest);
// emit signalTest();}
public slots:void slotTest(){qDebug() << " this is child slotTest start";parentSlot::slotTest();qDebug() << " this is child slotTest end";emit signal2();}void slotChildTest(){qDebug() << " this is child slotChildTest ";}
};
#endif // PARENTSLOT_H
这里在父类中关联了信号槽,也触发了信号,结果就是只调用到了父类的槽函数实现。因为信号接受和发送者是在同一个线程中,关联信号默认是Qt::AutoConnection,立即调用槽函数。
如果改成connect(this,SIGNAL(signalTest()),this,SLOT(slotTest()),Qt::QueuedConnection);这样就可以调用子类实现,如果信号关联和触发都在基类中,则只有用Qt::QueuedConnection才能调用子类实现
如果信号signalTest是在子类构造中触发,则可以直接调用子类实现
如果信号在其他地方emit触发,也是可以直接调用子类实现
parentSlot(QObject*p = nullptr)
{ connect(this,&parentSlot::signalTest,this,&parentSlot::slotTest,Qt::BlockingQueuedConnection);
qDebug() << QThread::currentThreadId() << " parentSlot," << QThread::currentThread(); emit signalTest();
}
在main.cpp里
QThread thread;
qDebug() << "thread " << thread.thread();
qDebug() << "currentThread " << QThread::currentThread()->currentThreadId();
childSlot t;
t.moveToThread(&thread);
thread.start();
qDebug() << "end";
结果报错 死锁了
thread QThread(0xbfff90)
currentThread 0x7fc83a6f4740
0x7fc83a6f4740 parentSlot, QThread(0xbfff90)
Qt: Dead lock detected while activating a BlockingQueuedConnection: Sender is parentSlot(0x7fffdaad3160), receiver is parentSlot(0x7fffdaad3160)
这里childSlot t;在主线程里构造,信号也是在构造函数中触发的,但实际确是认定信号是在子线程里触发,槽函数也是在子线程里,用了Qt::BlockingQueuedConnection,信号发起的线程要阻塞等槽的执行结束,就死锁了
parentSlot(QObject*p = nullptr)
{
// connect(this,SIGNAL(signalTest()),this,SLOT(slotTest()));
connect(this,SIGNAL(signalTest()),this,SLOT(slotTest()),Qt::QueuedConnection);
qDebug() << QThread::currentThreadId() << " parentSlot," << QThread::currentThread();
}
main.cpp
QThread thread;
qDebug() << "thread " << thread.thread();
qDebug() << "currentThread " << QThread::currentThread()->currentThreadId();
childSlot t;
t.moveToThread(&thread);
thread.start();
QMetaObject::invokeMethod(&t, "slotTest", Qt::BlockingQueuedConnection); // 主线程调用了子线程里的槽函数,且阻塞等待槽函数执行结束才执行下面语句
qDebug() << "end";
结果是
thread QThread(0x1078f90)
currentThread 0x7f2a9f1cd740
0x7f2a9f1cd740 parentSlot, QThread(0x1078f90)
this is child slotTest start
0x7f2a9a5c2700 , QThread(0x7ffc4d70c9c0)
this is parent slotTest
this is child slotTest end
this is child slotChildTest
end
如果QMetaObject::invokeMethod(&t, "slotTest", Qt::QueuedConnection);
则结果就是主线程调用了子线程里的槽函数,且继续执行下面语句,槽函数则在子线程里待事件循环空闲得以调用
thread QThread(0x2603f90)
currentThread 0x7f43b5aac740
0x7f43b5aac740 parentSlot, QThread(0x2603f90)
end
this is child slotTest start
0x7f43b0ea1700 , QThread(0x7ffcbc114600)
this is parent slotTest
this is child slotTest end
this is child slotChildTest
Qt::AutoConnection 信号发起者和信号接受者在同一个线程就是Qt::DirectConnection立即调用,不在一个线程就是Qt::QueuedConnection,待接受者的线程事件循环空闲调用
Qt::DirectConnection 信号的发起者线程里立即调用
Qt::QueuedConnection 待接受者的线程事件循环空闲调用
Qt::BlockingQueuedConnection 信号发起的线程会阻塞等待槽函数执行完才能继续,所以得是不同的线程才行,不然就会死锁
Qt::UniqueConnection 与上面的用 | 结合使用,确保不再重复连接