版权声明

请尊重原创作品。转载请保持文章完整性,并以超链接形式注明原始作者“tingsking18”和主站点地址,方便其他朋友提问和指正。

QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数

QT源码解析(二)深入剖析QT元对象系统和信号槽机制

QT源码解析(三)深入剖析QT元对象系统和信号槽机制(续)

QT源码解析(四)剖析Qt的事件机制原理

QT源码解析(五)QLibrary跨平台调用动态库的实现

QT源码解析(六)Qt信号槽机制与事件机制的联系

QT源码解析(七)Qt创建窗体的过程

QT源码解析(八)Qt是如何处理windows消息的

QT源码解析(九)解析QDateTime

在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使Qt程序进入消息循环。下面我们就到exec()函数内部,来看一下他的实现原理。
Let's go!
首先来到QTDIR/src/corelib/kernel/qcoreapplication.cpp

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;
    //创建eventLoop
    int returnCode = eventLoop.exec();
    threadData->quitNow = false;
    if (self) {
        self->d_func()->in_exec = false;
        //退出程序
        emit self->aboutToQuit();
        sendPostedEvents(0, QEvent::DeferredDelete);
    }
    return returnCode;
}
再来到qeventloop.cpp中。
int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (d->threadData->quitNow)
        return -1;
    //已经调用过exec了。
    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }
    d->inExec = true;
    d->exit = false;
    ++d->threadData->loopLevel;
    //将事件类对象压入线程结构体中
    d->threadData->eventLoops.push(this);

// remove posted quit events when entering a new event loop
    // 这句不用翻译了把!
    if (qApp->thread() == thread())
        QCoreApplication::removePostedEvents(qApp, QEvent::Quit);

#if defined(QT_NO_EXCEPTIONS)
    while (!d->exit)
        //这里才是关键,我们还要继续跟踪进去。
        processEvents(flags | WaitForMoreEvents);
#else
    try {
        while (!d->exit)
            processEvents(flags | WaitForMoreEvents);
    } catch (...) {
        //如果使用了EXCEPTION,则继续对下一条时间进行处理。
        qWarning("Qt has caught an exception thrown from an event handler. Throwing/n"
                 "exceptions from an event handler is not supported in Qt. You must/n"
                 "reimplement QApplication::notify() and catch all exceptions there./n");
        throw;
    }
#endif
    //退出eventloop前,将时间对象从线程结构中取出。
    QEventLoop *eventLoop = d->threadData->eventLoops.pop();
    Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
    Q_UNUSED(eventLoop); // --release warning

d->inExec = false;
    --d->threadData->loopLevel;
    //退出事件循环。
    return d->returnCode;
}

来到了processEvents函数:
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    //判断事件分派器是否为空。
    if (!d->threadData->eventDispatcher)
        return false;
    if (flags & DeferredDeletion)
        QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
    //调用不同平台下的事件分派器来处理事件。
    return d->threadData->eventDispatcher->processEvents(flags);
}

processEvents是在QAbstractEventDispatcher类中定义的纯虚方法。在QEventDispatcherWin32类有processEvents的实现。

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);
    //内部数据创建。registerClass注册窗口类,createwindow创建窗体。
    //注册socket notifiers,启动所有的normal timers
    if (!d->internalHwnd)
        createInternalHwnd();

d->interrupt = false;
    emit awake();

bool canWait;
    bool retVal = false;
    do {
        QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);

DWORD waitRet = 0;
        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);

MSG msg;
            bool haveMessage;

if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
                // process queued user input events处理用户输入事件,放入队列中。
                haveMessage = true;
                msg = d->queuedUserInputEvents.takeFirst();
            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // process queued socket events  处理socket事件,放入队列中。
                haveMessage = true;
                msg = d->queuedSocketEvents.takeFirst();
            } else {
                //从消息队列中取消息,同PeekMessage
                haveMessage = winPeekMessage(&msg, 0, 0, 0, PM_REMOVE);
                if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
                    && ((msg.message >= WM_KEYFIRST
                         && msg.message <= WM_KEYLAST)
                        || (msg.message >= WM_MOUSEFIRST
                            && msg.message <= WM_MOUSELAST)
                        || msg.message == WM_MOUSEWHEEL)) {
                    // queue user input events for later processing
                    haveMessage = false;
                    d->queuedUserInputEvents.append(msg);
                }
                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
                    && (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) {
                    // queue socket events for later processing
                    haveMessage = false;
                    d->queuedSocketEvents.append(msg);
                }
            }
            if (!haveMessage) {
                // no message - check for signalled objects
                for (int i=0; i<(int)nCount; i++)
                    pHandles[i] = d->winEventNotifierList.at(i)->handle();
                //注册signal--slot。
                waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
                if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
                    // a new message has arrived, process it
                    continue;
                }
            }
            //事件队列中有事件需要处理。
            if (haveMessage) { 
                //处理timer事件
                if (msg.message == WM_TIMER) {
                    // avoid live-lock by keeping track of the timers we've already sent
                    bool found = false;
                    for (int i = 0; !found && i < processedTimers.count(); ++i) {
                        const MSG processed = processedTimers.constData()[i];
                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
                    }
                    if (found)
                        continue;
                    processedTimers.append(msg);
                } else if (msg.message == WM_QUIT) {
                    if (QCoreApplication::instance())
                        QCoreApplication::instance()->quit();
                    return false;
                }
                //消息分发处理。
                if (!filterEvent(&msg)) {
                    TranslateMessage(&msg);
                    QT_WA({
                        DispatchMessage(&msg);
                    } , {
                        DispatchMessageA(&msg);
                    });
                }
            } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
                //处理signal--slot
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
            } else {
                // nothing todo so break
                break;
            }
            retVal = true;
        }

// still nothing - wait for message or signalled objects
        QThreadData *data = d->threadData;
        canWait = (!retVal
                   && data->canWait
                   && !d->interrupt
                   && (flags & QEventLoop::WaitForMoreEvents));
        if (canWait) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
            for (int i=0; i<(int)nCount; i++)
                pHandles[i] = d->winEventNotifierList.at(i)->handle();

emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
            emit awake();
            if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
                retVal = true;
            }
        }
    } while (canWait);

return retVal;
}

参考:http://blog.csdn.net/tingsking18/article/details/5528666

剖析Qt的事件机制原理的更多相关文章

  1. 剖析Qt的事件机制原理(源代码级别)

    在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使Qt程序进入消息循环.下面我们就到exec()函数内部,来看一下他的实现原理.Let's go ...

  2. 详解 QT 源码之 Qt 事件机制原理

    QT 源码之 Qt 事件机制原理是本文要介绍的内容,在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使 Qt 程序进入消息循环.下面我们就到ex ...

  3. 深入理解React:事件机制原理

    目录 序言 DOM事件流 事件捕获阶段.处于目标阶段.事件冒泡阶段 addEventListener 方法 React 事件概述 事件注册 document 上注册 回调函数存储 事件分发 小结 参考 ...

  4. 【React】354- 一文吃透 React 事件机制原理

    大纲 主要分为4大块儿,主要是结合源码对 react事件机制的原理 进行分析,希望可以让你对 react事件机制有更清晰的认识和理解. 当然肯定会存在一些表述不清或者理解不够标准的地方,还请各位大神. ...

  5. nginx 事件机制原理

    事件驱动模型是Nginx服务器保障完整功能和具有良好性能的重要机制之一. 事件驱动模型概述 实际上,事件驱动并不是计算机编程领域的专业词汇,它是一种比较古老的响应事件的模型,在计算机编程.公共关系.经 ...

  6. QT之事件机制

    MyPushButton.h: #ifndef MYPUSHBUTTON_H #define MYPUSHBUTTON_H #include<QPushButton> #include&l ...

  7. QT源码之Qt信号槽机制与事件机制的联系

    QT源码之Qt信号槽机制与事件机制的联系是本文要介绍的内容,通过解决一个问题,从中分析出的理论,先来看内容. 本文就是来解决一个问题,就是当signal和slot的连接为Qt::QueuedConne ...

  8. Qt 事件系统浅析 (用 Windows API 描述,分析了QCoreApplication::exec()和QEventLoop::exec的源码)(比起新号槽,事件机制是更高级的抽象,拥有更多特性,比如 accept/ignore,filter,还是实现状态机等高级 API 的基础)

    事件系统在 Qt 中扮演了十分重要的角色,不仅 GUI 的方方面面需要使用到事件系统,Signals/Slots 技术也离不开事件系统(多线程间).我们本文中暂且不描述 GUI 中的一些特殊情况,来说 ...

  9. Qt中事件分发源代码剖析(一共8个步骤,顺序非常清楚:全局的事件过滤器,再传递给目标对象的事件过滤器,最终传递给目标对象)

    Qt中事件分发源代码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,并且进行分发,这些都是在exec中进行的.下面举例说明: 1)首先看看下面一段示例代码: in ...

随机推荐

  1. 关于.NET前后台提示框的那点事

    前言 关于提示框,或多或少都用到过,提示框常见方式两种:js原生alert() 和 div模拟弹层:下面以一个常见的需求业务场景来展现提示框的那点事: 正文内容 客户:需求方: 小白:实现方(全权负责 ...

  2. Unity3D用vistual studio打卡C#脚本卡死解决

    小黑已经跟我3年了,不仅很喜欢他方正酷黑的外表,而且稳定性绝对没的说.我已经3年没有重装过系统了,而且现在装了3个系统!虽然小黑很适合程序员,但是他最大的缺点就是做设计比较吃力,显卡512M.像uni ...

  3. C#多线程(一) 入门

    本文你会了解如下内容: 1.计算机程序.进程.线程的概念 2.多线程的概念.为什么需要多线程.多线程的好处与坏处 3.C# 线程的一些概念与操作(创建线程.像线程中传递参数.给线程取名.前后台线程.线 ...

  4. 03_MySQL中文乱码处理_01_MySQl数据库字符集知识

    [MySql数据库常见字符集介绍] 在互联网环境中,使用MySql时常用的字符集有: [如何选择合适的字符集] 1.如果处理各种各样的文字,发布到不同语言的国家地区,应选Unicode字符集,对MyS ...

  5. linux - Mysql 创建用户和授权

    CREATE USER 'cui'@'%' IDENTIFIED BY 'xxxxxxxxxxxxxxxxxx'; GRANT ALL ON test_db.* TO 'cui'@'%'; REVOK ...

  6. jquery 插件的编写

    /** * 插件的学习 * 原文地址:http://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html#home */ ;(function($, ...

  7. 数据库(MSSQLServer,Oracle,DB2,MySql)常见语句以及问题(续1之拼接字符串)

    上一篇文章http://www.cnblogs.com/valiant1882331/p/4056403.html写的太长了,所以就换了一篇,链接上一节继续 字符串的拼接 MySql中可以使用&quo ...

  8. css3动画使用技巧之——transform-delay为负值时的应用。

    <html>    <head>        <title>css3动画delay为负值时的效果</title>        <meta ch ...

  9. HTML5学习笔记----html5与传统html区别

    一. HTML5语法的改变 该知识点所说变化指的是基于HTML4基础上所定义的改变,主要有如下: HTML5的文件扩展符(.html或.htm)与内容类型(text/html)保持不变. HTML5中 ...

  10. Yii 权限分级式访问控制实现(非RBAC法)

    以下由我们在信易网络公司开发项目的时候终结出的一些经验 主要参考资料:yii官网http://www.yiiframework.com/wiki/60/yii framework 提供了2套权限访问系 ...