通过上两篇博客https://blog.csdn.net/Master_Cui/article/details/109093845和https://blog.csdn.net/Master_Cui/article/details/109109972
知道了QT如果处理事件以及事件发送、事件队列和事件循环相关的类,但是这些东西是如何串联在一起的呢?下面通过源码分析下事件机制的时序
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();//进入事件循环
}
int QApplication::exec()
{
return QGuiApplication::exec();
}
int QGuiApplication::exec()
{
#ifndef QT_NO_ACCESSIBILITY
QAccessible::setRootObject(qApp);//设置根对象,一般不会调用,因为在创建Widget是,会调用父类的构造函数,会设置对象树的根对象
#endif
return QCoreApplication::exec();
}
int QCoreApplication::exec()
{
if (!QCoreApplicationPrivate::checkInstance("exec"))//判断QCoreApplication的实例是否存在,不存在,返回错误码-1,退出
return -1;//一般情况下不会返回-1,因为在QCoreApplicationPrivate中的init函数会初始化QCoreApplication::self
QThreadData *threadData = self->d_func()->threadData;//获取QCoreApplication的线程数据
if (threadData != QThreadData::current()) {//如果QCoreApplication所在线程和当前主线程的数据不一致,退出
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -1;
}
if (!threadData->eventLoops.isEmpty()) {//如果线程数据中的eventLoops不为空,说明主线程中已经有事件循环对象在运行,退出
qWarning("QCoreApplication::exec: The event loop is already running");
return -1;//eventLoops是一个容器,存储的是QEventLoop的指针 QStack<QEventLoop *> eventLoops;
}
threadData->quitNow = false;
QEventLoop eventLoop;//创建了一个QEventLoop对象
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
int returnCode = eventLoop.exec();//关键代码,调用事件循环对象的exec
threadData->quitNow = false;
if (self)
self->d_func()->execCleanup();//当主事件循环退出时,会调用该函数,该函数内部发射了信号void QCoreApplication::aboutToQuit(),做了一些清理动作
return returnCode;
}
可见,上述代码最终调用了QCoreApplication::exec()进入应用程序的主事件循环,QCoreApplication::exec()的实现如下,而在QCoreApplication::exec()中最终又创建了一个QEventLoop对象并调用了其中的exec进入主事件循环。当QEventLoop对象的exec退出时(主事件循环退出),会调用execCleanup进行一些清理工作,关于QEventLoop和QCoreApplication见博客https://blog.csdn.net/Master_Cui/article/details/109093845和https://blog.csdn.net/Master_Cui/article/details/109109972
int QEventLoop::exec(ProcessEventsFlags flags)
{
Q_D(QEventLoop);//创建一个指向QEventLoopPrivate的指针d
//we need to protect from race condition with QThread::exit
QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread.loadAcquire()))->mutex);//防止多线程竞争
if (d->threadData->quitNow)
return -1;
if (d->inExec) {
qWarning("QEventLoop::exec: instance %p has already called exec()", this);
return -1;
}
struct LoopReference {//定义一个循环事件数据相关的结构体
QEventLoopPrivate *d;
QMutexLocker &locker;
bool exceptionCaught;
LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
{
d->inExec = true;
d->exit.storeRelease(false);
++d->threadData->loopLevel;
d->threadData->eventLoops.push(d->q_func());//通过d->q_func()获取当前事件循环的指针并添加到当前线程的数据中
locker.unlock();
}
~LoopReference()
{
if (exceptionCaught) {
qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
"exceptions from an event handler is not supported in Qt.\n"
"You must not let any exception whatsoever propagate through Qt code.\n"
"If that is not possible, in Qt 5 you must at least reimplement\n"
"QCoreApplication::notify() and catch all exceptions there.\n");
}
locker.relock();
QEventLoop *eventLoop = d->threadData->eventLoops.pop();
Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
Q_UNUSED(eventLoop); // --release warning
d->inExec = false;
--d->threadData->loopLevel;
}
};
LoopReference ref(d, locker);//创建一个LoopReference的对象,用Q_D(QEventLoop)创建的d指针初始化该对象,并将事件循环对象添加到当前的线程数据
QCoreApplication *app = QCoreApplication::instance();
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit);// remove posted quit events when entering a new event loop
#ifdef Q_OS_WASM
// Partial support for nested event loops: Make the runtime throw a JavaSrcript
// exception, which returns control to the browser while preserving the C++ stack.
// Event processing then continues as normal. The sleep call below never returns.
// QTBUG-70185
if (d->threadData->loopLevel > 1)
emscripten_sleep(1);
#endif
while (!d->exit.loadAcquire())//判断事件循环是否已经退出
processEvents(flags | WaitForMoreEvents | EventLoopExec);//核心代码,调用QEventLoop::processEvents()处理事件,
ref.exceptionCaught = false;
return d->returnCode.loadRelaxed();
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return false;
return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags);//eventDispatcher是个指针,调用QAbstractEventDispatcher的processEvents
}
上面代码中Q_D是一个宏,定义如下
#define Q_D(Class) Class##Private * const d = d_func()
d_func()的作用就是将QEventLoop的指针转化为QEventLoopPrivate的指针,见博客https://blog.csdn.net/Master_Cui/article/details/109112367
d->threadData->eventLoops.push(d->q_func());
d是指向QEventLoopPrivate的指针,QEventLoopPrivate的定义如下(在qeventloop_p.h中定义)
class QEventLoopPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QEventLoop)
public:
inline QEventLoopPrivate()
: inExec(false)
{
returnCode.storeRelaxed(-1);
exit.storeRelaxed(true);
}
//...
};
Q_DECLARE_PUBLIC(QEventLoop)展开就是
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
friend class Class;
q_func的作用就是将q_ptr转化为对应类的指针,而q_ptr定义在纯虚基类QObjectData中,所以d->q_func()就是获取QEventLoop的指针
上面的代码表明,当调用QEventLoop::exec时,会先定义一个结构体,将QEventLoop的添加操作封装在结构体的构造函数中,然后创建该结构体的对象,之后调用QEventLoop::processEvents进行事件处理,而在QEventLoop::processEvents中,会调用QAbstractEventDispatcher的processEvents
而QAbstractEventDispatcher是一个纯虚基类,具体见https://blog.csdn.net/Master_Cui/article/details/109109972,QAbstractEventDispatcher的子类有很多,在创建事件分发器时,会根据平台信息,创建出不同平台的事件分发器,根据平台特性,进行具体的实现。在Ubuntu18.04下,通过qtcreater调试获得函数的调用顺序如下
所以,最终指向的是子类QEventDispatcherGlib,所以接着会调用QEventDispatcherGlib的processEvents函数
QEventDispatcherGlib::processEvents的实现如下
bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherGlib);//创建QEventDispatcherGlibPrivate的指针
const bool canWait = (flags & QEventLoop::WaitForMoreEvents);
if (canWait)
emit aboutToBlock();
else
emit awake();
// tell postEventSourcePrepare() and timerSource about any new flags
QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags;
d->timerSource->processEventsFlags = flags;
if (!(flags & QEventLoop::EventLoopExec)) {
// force timers to be sent at normal priority
d->timerSource->runWithIdlePriority = false;
}
bool result = g_main_context_iteration(d->mainContext, canWait);
while (!result && canWait)
result = g_main_context_iteration(d->mainContext, canWait);//核心代码,迭代main_context
d->timerSource->processEventsFlags = savedFlags;
if (canWait)
emit awake();
return result;
}
g_main_context_iteration是glic中主事件循环机制的函数,所以,在Ubuntu18.04,QT的事件机制实际上是采用glic中的事件循环机制来完成的
参考
Qt5.14源码
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出