Qt事件(消息)循环的源码分析之创建

Qt程序的特点

Qt 是事件驱动的,尤其是你用Qt做界面开发时,你必须知道Qt的事件循环是如何开展的

从Win32到Qt

  • 程序入口WinMain
  • 创建窗口
  • 进入事件(消息)循环
int main(int argc, char *argv[])//入口main
{
    //Qt的初始化
    QApplication a(argc, argv);
    //创建窗口
    TestMoc w;
    w.show();
    //启动消息循环
    return a.exec();
}

main 函数中的exec()究竟是个什么东西?

从继承的关系上看

  • QApplication::exec()
  • QGuiApplication::exec()
  • QCoreApplication::exec()
    最终还是维护了一个QEventLoop::exec(),这就是你当前程序的主消息循环。
int QCoreApplication::exec()
{
    if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;
    //获取线程数据
    QThreadData *threadData = self->d_func()->threadData;
    //判断数据是否是当前线程
    if (threadData != QThreadData::current()) {
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    //判断是否已经创建eventloop
    if (!threadData->eventLoops.isEmpty()) {
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }

    threadData->quitNow = false;//立即停止标识符
    //在栈上创建主事件循环
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;//事件循环启动标识
    self->d_func()->aboutToQuitEmitted = false;
    //开启事件循环
    int returnCode = eventLoop.exec();
    threadData->quitNow = false;
    //主事件循环结束,退出并清理
    if (self)
        self->d_func()->execCleanup();

    return returnCode;
}
void QCoreApplicationPrivate::execCleanup()
{
    threadData.loadRelaxed()->quitNow = false;
    in_exec = false;
    //发送退出
    if (!aboutToQuitEmitted)
        emit q_func()->aboutToQuit(QCoreApplication::QPrivateSignal());
    aboutToQuitEmitted = true;
    QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}

总结一下,QCoreApplication::exec()究竟做了什么?

  1. 获取数据并检查是否是当前线程;
  2. 判断主事件(消息)循环是否存在,不存在则创建;
  3. 开启事件循环,并执行
  4. 清理主事件循环

主消息循环 QEventLoop::exec()做了什么?

int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();

    QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex);
    //如果立即退出置为true,则后续的
    if (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;//事件循环置为run状态
            d->exit.storeRelease(false);//事件停止置为false

            auto threadData = d->threadData.loadRelaxed();
            ++threadData->loopLevel;//记录线程对象的层数
            ///通过d->q_func()获取当前事件循环的指针(就是QEventLoop自己),压入到当前线程的事件循环对象栈中,一个线程可以有多个事件循环对象
            threadData->eventLoops.push(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();
            auto threadData = d->threadData.loadRelaxed();
            QEventLoop *eventLoop = threadData->eventLoops.pop();
            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
            Q_UNUSED(eventLoop); // --release warning
            d->inExec = false;
            --threadData->loopLevel;
        }
    };
    //创建事件循环对象,用d指针初始化该对象,并将“事件循环对象”添加到当前的线程维护的事件循环中
    LoopReference ref(d, locker);

    //当进入新的事件循环时,移除退出事件
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);
    //·····
    //关键的一部分,一个while循环,直到收到退出指令才结束,否则一致在处理消息
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);//进行事件的分发处理,这就是核心

    ref.exceptionCaught = false;
    return d->returnCode.loadRelaxed();
}

总结

看到最后的while循环,其实你就明白了为何整个Qt窗体在main创建后,程序不会自动退出。
后续计划针对processEvents进行源码剖析
参考源码:Qt 5.15.2
水平有限,如有错误,欢迎指出

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值