Qt之事件循环源码剖析(一)-----源码面前了无秘密

Qt程序提供了事件循环机制,那么它是怎么工作的,工作原理又是什么呢?让我们通过qt源码揭开它神秘的面纱。

在开始之前,我们先思考下面几个问题:
1.事件循环是何时,以及如何启动的?
2.对于Windows,Qt是如何捕获事件(如:鼠标事件,键盘按键事件)?如何对捕获到的事件进行处理的?
3.如何将Windows消息转换为QEvent?
4.用户自定义的事件又是如何被处理的?

一、Windows消息机制简介

在这里插入图片描述
在Windows操作系统,所有的WIN32程序都建立在消息循环的基础之上,windows的消息机制可以分为以下几大步:

  1. 注册窗口过程处理函数
  2. 创建窗体
  3. show窗体
  4. 获取消息
  5. 翻译消息
  6. 分发消息至窗口处理函数

二、事件循环初始化

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

首先实例化QApplication对象,在构造QApplication时初始化threadData,如下所示:
在这里插入图片描述
父对象如果存在则使用父对象的threadData,表明和父对象使用同一个工作线程处理事件,反之通过QThreadData::current()拿到当前线程threadData。对于主事件循环来说,则通过QThreadData::current()拿到当前主线程,通过主线程处理事件。

class QThreadData
{
public:
    QThreadData(int initialRefCount = 1);
    ~QThreadData();

    static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true);
	
	/**
	* 省略 .......
	*/

public:
    int loopLevel;
    int scopeLevel;

    QStack<QEventLoop *> eventLoops;
    QPostEventList postEventList;  // 待处理的事件列表 
    QAtomicPointer<QThread> thread;  // 工作线程
    QAtomicPointer<void> threadId;   // 工作线程Id
    QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;  // 事件分发者,在不同平台下创建不同的事件分发者
    QVector<void *> tls;
    FlaggedDebugSignatures flaggedSignatures;
};

以windows为例,eventDispatcher的初始化:
在这里插入图片描述
事件分发者(eventDispatcher),初始化调用堆栈如上
Step 1

// src\corelib\kernel\qcoreapplication.cpp
QCoreApplication::QCoreApplication(int &argc, char **argv
#ifndef Q_QDOC
                                   , int _internal
#endif
                                   )
#ifdef QT_NO_QOBJECT
    : d_ptr(new QCoreApplicationPrivate(argc, argv, _internal))
#else
    : QObject(*new QCoreApplicationPrivate(argc, argv, _internal))
#endif
{
    d_func()->q_ptr = this;
    d_func()->init();  // 调用QCoreApplicationPrivate的init函数
#ifndef QT_NO_QOBJECT
    QCoreApplicationPrivate::eventDispatcher->startingUp();
#endif
}

程序在启动时,通过main函数实例化QApplication对象。在QCoreApplication构造函数中调用QCoreApplicationPrivate的init函数初始参数
Step 2

// src\corelib\kernel\qcoreapplication.cpp
void QCoreApplicationPrivate::init()
{
	/**
	* 省略 .......
	*/
    auto thisThreadData = threadData.loadRelaxed();
    eventDispatcher = thisThreadData->eventDispatcher.loadRelaxed();

    // otherwise we create one
    if (!eventDispatcher)
        createEventDispatcher();  // 调用createEventDispatcher创建事件分发者
    Q_ASSERT(eventDispatcher);
    
    if (!eventDispatcher->parent()) {
        eventDispatcher->moveToThread(thisThreadData->thread.loadAcquire());
        eventDispatcher->setParent(q);
    }

    thisThreadData->eventDispatcher = eventDispatcher; // 事件分发者赋值给threadData的eventDispatcher。当threadData.postEventList列表中有新加入的事件时,需要借助事件分发者向外投递一个消息,处理此事件
    eventDispatcherReady();
    /**
	* 省略 .......
	*/
}

在QCoreApplicationPrivate的init函数中,如果事件分发者没有创建则通过createEventDispatcher虚方发创建不同平台的事件分发者。
Step 3

// src\widgets\kernel\qapplication.cpp
void QApplicationPrivate::createEventDispatcher()
{
    QGuiApplicationPrivate::createEventDispatcher();
}

// src\gui\kernel\qguiapplication.cpp
void QGuiApplicationPrivate::createEventDispatcher()
{
    Q_ASSERT(!eventDispatcher);

    if (platform_integration == nullptr)
        createPlatformIntegration(); // 创建平台

    // The platform integration should not mess with the event dispatcher
    Q_ASSERT(!eventDispatcher);

    eventDispatcher = platform_integration->createEventDispatcher(); // 通过对应平台的事件分发者
}

上段代码需要注意的是createEventDispatcher是一个虚方法,并且在每个类中都有实现。在此处则调用是QApplicationPrivate的该方法。在此处创建QWindowsIntegration以及QWindowsGuiEventDispatcher
Step 4

// src\gui\kernel\qguiapplication.cpp
void QGuiApplicationPrivate::createPlatformIntegration()
{
    QHighDpiScaling::initHighDpiScaling();

    // Load the platform integration
    QString platformPluginPath = QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH"));
    
    /**
	* 代码省略 .......
	* 这部分代码通过从环境变量或者从命令行参数中解析平台名称,插件路径,插件名称
	*/

    init_platform(QLatin1String(platformName), platformPluginPath, platformThemeName, argc, argv); // 创建平台插件

    if (!icon.isEmpty())
        forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon);
}
static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
    QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';'));
    QStringList platformArguments;
    QStringList availablePlugins = QPlatformIntegrationFactory::keys(platformPluginPath);
    for (const auto &pluginArgument : plugins) {
        // Split into platform name and arguments
        QStringList arguments = pluginArgument.split(QLatin1Char(':'));
        const QString name = arguments.takeFirst().toLower();
        QString argumentsKey = name;
        argumentsKey[0] = argumentsKey.at(0).toUpper();
        arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey));

        // Create the platform integration.
        QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); // 通过插件工具,创建平台插件
        if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) {
            if (availablePlugins.contains(name)) {
                qCInfo(lcQpaPluginLoading).nospace().noquote()
                        << "Could not load the Qt platform plugin \"" << name << "\" in \""
                        << QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found.";
            } else {
                qCWarning(lcQpaPluginLoading).nospace().noquote()
                        << "Could not find the Qt platform plugin \"" << name << "\" in \""
                        << QDir::toNativeSeparators(platformPluginPath) << "\"";
            }
        } else {
            QGuiApplicationPrivate::platform_name = new QString(name);
            platformArguments = arguments;
            break;
        }
    }
    /**
	* 代码省略 .......
	*/
}

从环境变量或者从命令行参数中解析平台名称,插件路径,插件名称。将插件路径下的所有插件放入到列表中,遍历插件列表加载对应平台的插件。如在windows上则加载的是qwindows.dll
插件位于安装目录下如:C:\Qt\5.15.2\msvc2019_64\plugins\platforms
在这里插入图片描述
windows下qt程序包插件位于执行目录下的platforms目录下:
在这里插入图片描述
在linux平台下,通过QT_QPA_PLATFORM_PLUGIN_PATH环境变量加载平台插件(因为我是在交叉编译环境上运行,所以是qeglfs):
在这里插入图片描述
Step 5

// src\gui\kernel\qplatformintegrationfactory.cpp
QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platform, const QStringList &paramList, int &argc, char **argv, const QString &platformPluginPath)
{
#if QT_CONFIG(library)
    // Try loading the plugin from platformPluginPath first:
    if (!platformPluginPath.isEmpty()) {
        QCoreApplication::addLibraryPath(platformPluginPath);
        if (QPlatformIntegration *ret = qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(directLoader(), platform, paramList, argc, argv))
            return ret;
    }
#else
    Q_UNUSED(platformPluginPath);
#endif
    return qLoadPlugin<QPlatformIntegration, QPlatformIntegrationPlugin>(loader(), platform, paramList, argc, argv);
}
// src\corelib\plugin\qfactoryloader_p.h
template <class PluginInterface, class FactoryInterface, typename ...Args>
PluginInterface *qLoadPlugin(const QFactoryLoader *loader, const QString &key, Args &&...args)
{
    const int index = loader->indexOf(key);
    if (index != -1) {
        QObject *factoryObject = loader->instance(index); // 实例化平台的插件对象。此处在windows平台下,实际上是实例化QWindowsIntegrationPlugin对象
        if (FactoryInterface *factory = qobject_cast<FactoryInterface *>(factoryObject))
            if (PluginInterface *result = factory->create(key, std::forward<Args>(args)...))
                return result;
    }
    return nullptr;
}
// src\plugins\platforms\windows\main.cpp
class QWindowsIntegrationPlugin : public QPlatformIntegrationPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "windows.json") // 很关键,提供插件的导出符号。
public:
    QPlatformIntegration *create(const QString&, const QStringList&, int &, char **);
};

QPlatformIntegration *QWindowsIntegrationPlugin::create(const QString& system, const QStringList& paramList, int &, char **)
{
    if (system.compare(system, QLatin1String("windows"), Qt::CaseInsensitive) == 0)
        return new QWindowsGdiIntegration(paramList); // 创建QWindowsGdiIntegration
    return nullptr;
}

通过qt的插件机制,加载插件。通过插件提供的qt_plugin_instance接口实例化QWindowsIntegrationPlugin,然后创建QWindowsGdiIntegration
通过depends分析qwindows.dll插件,向外提供两个接口qt_plugin_instanceqt_plugin_query_metadata
在这里插入图片描述
Step 6

// rc\plugins\platforms\windows\qwindowsintegration.cpp
QAbstractEventDispatcher * QWindowsIntegration::createEventDispatcher() const
{
    return new QWindowsGuiEventDispatcher;
}

QWindowsGdiIntegration继承QWindowsIntegration,因此调用createEventDispatcher创建事件分发者,创建的是QWindowsGuiEventDispatcher

三、启动事件循环

Step 1

// 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;
    }
    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;
}

在main函数中调用QApplication的exec函数,最终调用的是QCoreApplication的exec函数,在QCoreApplication的exec函数中,实例化QEventLoop对象,通过QEventLoop的exec进入到事件循环
Step 2

// src\corelib\kernel\qeventloop.cpp
int QEventLoop::exec(ProcessEventsFlags flags)
{
    /**
	* 省略 .......
	*/
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec); // 开始处理事件
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();
    if (!threadData->hasEventDispatcher()) // 事件分发者没有被创建则返回
        return false;
    return threadData->eventDispatcher.loadRelaxed()->processEvents(flags); // 调用事件分发者的processEvents,(本例事件分发者为QEventDispatcherWin32)
}

d->exit.loadAcquire()为事件循环的状态,在不退出事件循环时,此变量一直为false。通过while循环调用事件分发者的processEvents去处理事件
Step 3

// src\corelib\kernel\qeventdispatcher_win.cpp
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd) {
        createInternalHwnd(); // d->internalHwnd作为接收windows消息的窗体,如果窗体不存在则新创建一个
        wakeUp(); // trigger a call to sendPostedEvents()
    }
    /**
	* 省略 .......
	*/
}
void QEventDispatcherWin32::createInternalHwnd()
{
    Q_D(QEventDispatcherWin32);

    if (d->internalHwnd)
        return;
    d->internalHwnd = qt_create_internal_window(this); // 创建窗体并赋值给d->internalHwnd句柄

    // setup GetMessage hook needed to drive our posted events
    d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
    if (Q_UNLIKELY(!d->getMessageHook)) {
        int errorCode = GetLastError();
        qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls",
               errorCode, qUtf16Printable(qt_error_string(errorCode)));
    }

    // start all normal timers
    for (int i = 0; i < d->timerVec.count(); ++i)
        d->registerTimer(d->timerVec.at(i));
}

事件分发者第一次调用processEvents处理事件时,d->internalHwnd窗口句柄为空。接下来准备调用操作系统API创建窗口,注册窗口过程处理函数。
Step 4

// step 5 调用qWindowsMessageWindowClassContext注册消息过程处理函数,并且通过WIN API CreateWindow创建窗体
// src\corelib\kernel\qeventdispatcher_win.cpp
static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{
    QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();  // 注册消息过程处理函数
    if (!ctx->atom)
        return 0;
    // 创建窗体
    HWND wnd = CreateWindow(ctx->className,    // classname
                            ctx->className,    // window name
                            0,                 // style
                            0, 0, 0, 0,        // geometry
                            HWND_MESSAGE,            // parent
                            0,                 // menu handle
                            GetModuleHandle(0),     // application
                            0);                // windows creation data.

    if (!wnd) {
        qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");
        return 0;
    }

#ifdef GWLP_USERDATA
    SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
#else
    SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher));
#endif

    return wnd;
}

在qWindowsMessageWindowClassContext中注册窗口过程处理函数,调用windows API CreateWindow创建窗口
Step 5

// src\corelib\kernel\qeventdispatcher_win.cpp
Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext)  // Q_GLOBAL_STATIC宏通过QGlobalStatic将QWindowsMessageWindowClassContext包装,并创建一个名为qWindowsMessageWindowClassContext的QGlobalStatic对象

#define Q_GLOBAL_STATIC(TYPE, NAME)                                         \
    Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())

// 通过宏Q_GLOBAL_STATIC_WITH_ARGS创建一个名为NAME全局变量
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)                         \
    namespace { namespace Q_QGS_ ## NAME {                                  \
        typedef TYPE Type;                                                  \
        QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
        Q_GLOBAL_STATIC_INTERNAL(ARGS)                                      \
    } }                                                                     \
    static QGlobalStatic<TYPE,                                              \
                         Q_QGS_ ## NAME::innerFunction,                     \
                         Q_QGS_ ## NAME::guard> NAME;
               
// 通过Q_GLOBAL_STATIC_INTERNAL定义一个返回值为Type* 名为innerFunction的函数,该函数通过guard判断静态变量d(使用静态变量的好处是延长变量生命周期;在下次进入该函数时,变量仍为上次的值)是否被初始化过,如果没有被初始化则创建一个新的,反之直接return变量d                     
#define Q_GLOBAL_STATIC_INTERNAL(ARGS)                                  \
    Q_DECL_HIDDEN inline Type *innerFunction()                          \
    {                                                                   \
        static Type *d;                                                 \
        static QBasicMutex mutex;                                       \
        int x = guard.loadAcquire();                                    \
        if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) {           \
            const std::lock_guard<QBasicMutex> locker(mutex);           \
            if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {        \
                d = new Type ARGS;                                      \
                static struct Cleanup {                                 \
                    ~Cleanup() {                                        \
                        delete d;                                       \
                        guard.storeRelaxed(QtGlobalStatic::Destroyed);         \
                    }                                                   \
                } cleanup;                                              \
                guard.storeRelease(QtGlobalStatic::Initialized);        \
            }                                                           \
        }                                                               \
        return d;                                                       \
    }

// 声明类型为T,通过innerFunction函数指针创建T类型静态变量,并通过guard来判断静态变量的状态
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
struct QGlobalStatic
{
    typedef T Type;

    bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
    bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; }
    operator Type *() { if (isDestroyed()) return nullptr; return innerFunction(); }
    Type *operator()() { if (isDestroyed()) return nullptr; return innerFunction(); }// 重载(),并通过innerFunction函数指针获取静态变量
    Type *operator->()
    {
      Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
      return innerFunction();
    }
    Type &operator*()
    {
      Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed");
      return *innerFunction();
    }
};
/********************************************************************************************************/
// 综上step 4的QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext(); 则为:创建类型为QWindowsMessageWindowClassContext的静态变量,并通过qWindowsMessageWindowClassContext()返回其指针赋值给ctx变量

// src\corelib\kernel\qeventdispatcher_win.cpp
QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext()
    : atom(0), className(0)
{
    /**
	* 省略 .......
	*/
    WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = qt_internal_proc;  // 消息处理回调函数
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(0);
    wc.hIcon = 0;
    wc.hCursor = 0;
    wc.hbrBackground = 0;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = className;
    atom = RegisterClass(&wc); // 注册消息过程处理函数
    if (!atom) {
        qErrnoWarning("%ls RegisterClass() failed", qUtf16Printable(qClassName));
        delete [] className;
        className = 0;
    }
}

通过实例化QWindowsMessageWindowClassContext注册消息过程处理函数
Step 6

// src\corelib\kernel\qeventdispatcher_win.cpp
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
    if (message == WM_NCCREATE)
        return true;

    MSG msg;
    msg.hwnd = hwnd;
    msg.message = message;
    msg.wParam = wp;
    msg.lParam = lp;
    QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    qintptr result;
#else
    long result;
#endif
    if (!dispatcher) {
        if (message == WM_TIMER)
            KillTimer(hwnd, wp);
        return 0;
    }
    if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) // 系统原生消息的过滤器
        return result;

#ifdef GWLP_USERDATA
    auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
#else
    auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA));
#endif
    QEventDispatcherWin32Private *d = 0;
    if (q != 0)
        d = q->d_func();

    switch (message) {
    case WM_QT_SOCKETNOTIFIER:    
     /**
	* 省略 .......
	*/
    case WM_QT_ACTIVATENOTIFIERS: {
    /**
	* 省略 .......
	*/
    }
    case WM_TIMER:
    /**
	* 省略 .......
	*/
    case WM_QT_SENDPOSTEDEVENTS: // 通过调用PostEvent投递的事件会被直接处理
        Q_ASSERT(d != 0);

        // We send posted events manually, if the window procedure was invoked
        // by the foreign event loop (e.g. from the native modal dialog).
        // Skip sending, if the message queue is not empty.
        // sendPostedEventsTimer will deliver posted events later.
        static const UINT mask = inputQueueMask();
        if (HIWORD(GetQueueStatus(mask)) == 0)
            q->sendPostedEvents();
        return 0;
    } // switch (message)

    return DefWindowProc(hwnd, message, wp, lp);
}

在窗口过程处理函数中,先通过filterNativeEvent对原生消息处理,然后再对不同的消息进行处理。
Step 7

// step 9通过PeekMessage获取系统消息,通过TranslateMessage、DispatchMessage分发消息
// src\corelib\kernel\qeventdispatcher_win.cpp
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd) {
        createInternalHwnd();
        wakeUp(); // trigger a call to sendPostedEvents()
    }

    d->interrupt.storeRelaxed(false);
    emit awake();

    // To prevent livelocks, send posted events once per iteration.
    // QCoreApplication::sendPostedEvents() takes care about recursions.
    sendPostedEvents(); // 处理postEvent队列的事件和用户输入事件

    auto threadData = d->threadData.loadRelaxed();
    bool canWait;
    bool retVal = false;
    do {
        DWORD waitRet = 0;
        DWORD nCount = 0;
        HANDLE *pHandles = nullptr;
        if (d->winEventNotifierActivatedEvent) {
            nCount = 1;
            pHandles = &d->winEventNotifierActivatedEvent;
        }
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt.loadRelaxed()) {
            MSG msg;
            bool haveMessage;
			// 队列中有消息则取一个MSG
            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
                haveMessage = true;
                msg = d->queuedSocketEvents.takeFirst();
            } else {
           		// 队列中没有消息,读取消息,并将消息放入队列
                haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
                if (haveMessage) {
                    if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
                        && isUserInputMessage(msg.message)) {
                        // queue user input events for later processing
                        d->queuedUserInputEvents.append(msg);
                        continue;
                    }
                    if ((flags & QEventLoop::ExcludeSocketNotifiers)
                        && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
                        // queue socket events for later processing
                        d->queuedSocketEvents.append(msg);
                        continue;
                    }
                }
            }
            if (!haveMessage) {
                // no message - check for signalled objects
                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) {
            	// 如果消息是内部窗口句柄并且消息号是WM_QT_SENDPOSTEDEVENTS(wakeUp()通过调用PostMessage投递给d->internalHwnd窗口WM_QT_SENDPOSTEDEVENTS消息)
            	// 存在一个疑问:如果一直在往系统中投递WM_QT_SENDPOSTEDEVENTS消息,那么这个循环就永远跳不出去,会不会存在投递的事件一直没有被处理???
                if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
                    // Set result to 'true' because the message was sent by wakeUp().
                    retVal = true;
                    continue;
                }
                if (msg.message == WM_TIMER) {
                    // Skip timer event intended for use inside foreign loop.
                    if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
                        continue;

                    // 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 (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);  // 分发消息至窗口过程处理函数
                }
            } else if (waitRet - WAIT_OBJECT_0 < nCount) {
                activateEventNotifiers();
            } else {
                // nothing todo so break
                break;
            }
            retVal = true;
        }

        // still nothing - wait for message or signalled objects
        canWait = (!retVal
                   && !d->interrupt.loadRelaxed()
                   && flags.testFlag(QEventLoop::WaitForMoreEvents)
                   && threadData->canWaitLocked());
        if (canWait) {
            emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
            emit awake();
            if (waitRet - WAIT_OBJECT_0 < nCount) {
                activateEventNotifiers();
                retVal = true;
            }
        }
    } while (canWait);

    return retVal;
}

至此对于一个Qt GUI程序的事件循环已经启动完毕

四、sendEvent

用户通过sendEvent投递一个自定义事件,sendEvent的工作原理又是怎样的
Step 1

// src\corelib\kernel\qcoreapplication.cpp
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
    Q_TRACE(QCoreApplication_sendEvent, receiver, event, event->type());

    if (event)
        event->spont = false;
    return notifyInternal2(receiver, event);
}
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
    bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
    if (!self && selfRequired)
        return false;

    // Make it possible for Qt Script to hook into events even
    // though QApplication is subclassed...
    bool result = false;
    void *cbdata[] = { receiver, event, &result };
    if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
        return result;
    }

    // Qt enforces the rule that events can only be sent to objects in
    // the current thread, so receiver->d_func()->threadData is
    // equivalent to QThreadData::current(), just without the function
    // call overhead.
    QObjectPrivate *d = receiver->d_func();
    QThreadData *threadData = d->threadData;
    QScopedScopeLevelCounter scopeLevelCounter(threadData);
    if (!selfRequired)
        return doNotify(receiver, event);
    return self->notify(receiver, event);
}

通过QApplication::sendEvent投递一个事件,实际上调用QCoreApplication::sendEvent,QApplication继承QCoreApplication。在notifyInternal2中self->notify(receiver, event),实际上调用的是QApplication的notify,notify为虚函数在QApplication中重写
Step 2

// src\widgets\kernel\qapplication.cpp
bool QApplication::notify(QObject *receiver, QEvent *e)
{
     /**
	* 代码省略 .......
	* 如果事件类型是qt定义的事件类型,则按照对应的事件处理逻辑去处理。如果是自定义的事件类型,则调用notify_helper去处理
	*/
	res = d->notify_helper(receiver, e);
	return res;
}
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
     /**
	* 代码省略 .......
	*/
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, e)) { // 先给事件过滤器处理事件
        filtered = true;
        return filtered;
    }

    // deliver the event
    consumed = receiver->event(e); // 交给event去处理

    QCoreApplicationPrivate::setEventSpontaneous(e, false);
    return consumed;
}

在QApplication::notify中如果事件类型是qt定义的事件类型,则按照对应的事件处理逻辑去处理。如果是自定义的事件类型,则调用notify_helper去处理。在notify_helper中处理事件是,先通过事件处理器处理,最后交给目标对象的event处理。
在此可以看见事件的处理顺序为:QApplication::notify > eventFilter > event

五、postEvent

Step 1

// src\corelib\kernel\qcoreapplication.cpp
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
     /**
	* 代码省略 .......
	*/

    // delete the event on exceptions to protect against memory leaks till the event is
    // properly owned in the postEventList
    QScopedPointer<QEvent> eventDeleter(event); // 在栈上创建一个智能指针,eventDeleter销毁时自动释放event对象,防止内存泄露
    Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
    data->postEventList.addEvent(QPostEvent(receiver, event, priority)); // 将投递的事件添加postEventList列表中
    eventDeleter.take();
    event->posted = true;
    ++receiver->d_func()->postedEvents;
    data->canWait = false;
    locker.unlock();

    QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
    if (dispatcher)
        dispatcher->wakeUp(); // 通知postEventList队列里面添加了一个新的事件
}
// src\corelib\kernel\qeventdispatcher_win.cpp
void QEventDispatcherWin32::wakeUp()
{
    Q_D(QEventDispatcherWin32);
    if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) {
        // post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending
        if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0)) // 调用系统接口,投递一个WM_QT_SENDPOSTEDEVENTS消息
            qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message");
    }
}

调用postEvent将投递的事件放入到postEventList队列中,并通过PostMessage向系统投递一个WM_QT_SENDPOSTEDEVENTS消息,用于通知有新的事件添加到队列中。事件循环会从系统获取到一个WM_QT_SENDPOSTEDEVENTS消息,然后去处理postEventList队列的消息。
Step 2

// src\platformsupport\eventdispatchers\qwindowsguieventdispatcher.cpp
void QWindowsGuiEventDispatcher::sendPostedEvents()
{
    QEventDispatcherWin32::sendPostedEvents();
    QWindowSystemInterface::sendWindowSystemEvents(m_flags);
}
// src\corelib\kernel\qeventdispatcher_win.cpp
void QEventDispatcherWin32::sendPostedEvents()
{
    Q_D(QEventDispatcherWin32);

    if (d->sendPostedEventsTimerId != 0)
        KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
    d->sendPostedEventsTimerId = 0;

    // Allow posting WM_QT_SENDPOSTEDEVENTS message.
    d->wakeUps.storeRelaxed(0);

    QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData.loadRelaxed());
}
//
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)
{
     /**
	* 代码省略 .......
	*/
    while (i < data->postEventList.size()) { // 遍历postEventList
        // avoid live-lock
        if (i >= data->postEventList.insertionOffset)
            break;

        const QPostEvent &pe = data->postEventList.at(i); // 从postEventList队列中取一个事件
        ++i;

        if (!pe.event)
            continue;
        if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) {
            data->canWait = false;
            continue;
        }

        if (pe.event->type() == QEvent::DeferredDelete) {
            // DeferredDelete events are sent either
            // 1) when the event loop that posted the event has returned; or
            // 2) if explicitly requested (with QEvent::DeferredDelete) for
            //    events posted by the current event loop; or
            // 3) if the event was posted before the outermost event loop.

            int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
            int loopLevel = data->loopLevel + data->scopeLevel;
            const bool allowDeferredDelete =
                (eventLevel > loopLevel
                 || (!eventLevel && loopLevel > 0)
                 || (event_type == QEvent::DeferredDelete
                     && eventLevel == loopLevel));
            if (!allowDeferredDelete) {
                // cannot send deferred delete
                if (!event_type && !receiver) {
                    // we must copy it first; we want to re-post the event
                    // with the event pointer intact, but we can't delay
                    // nulling the event ptr until after re-posting, as
                    // addEvent may invalidate pe.
                    QPostEvent pe_copy = pe;

                    // null out the event so if sendPostedEvents recurses, it
                    // will ignore this one, as it's been re-posted.
                    const_cast<QPostEvent &>(pe).event = nullptr;

                    // re-post the copied event so it isn't lost
                    data->postEventList.addEvent(pe_copy);
                }
                continue;
            }
        }

        // first, we diddle the event so that we can deliver
        // it, and that no one will try to touch it later.
        pe.event->posted = false;
        QEvent *e = pe.event;
        QObject * r = pe.receiver;

        --r->d_func()->postedEvents;
        Q_ASSERT(r->d_func()->postedEvents >= 0);

        // next, update the data structure so that we're ready
        // for the next event.
        const_cast<QPostEvent &>(pe).event = nullptr;

        locker.unlock();
        const auto relocker = qScopeGuard([&locker] { locker.lock(); });

        QScopedPointer<QEvent> event_deleter(e); // will delete the event (with the mutex unlocked)

        // after all that work, it's time to deliver the event.
        QCoreApplication::sendEvent(r, e); // 最后按照sendEvent的方式将事件处理掉

        // careful when adding anything below this point - the
        // sendEvent() call might invalidate any invariants this
        // function depends on.
    }
}

事件循环调用sendPostedEvents从而遍历整个队列的事件,将所有的事件按照sendEvent的方式处理掉。

六、总结

1.sendEvent为同步接口,从源码上看,sendEvent整个过程都为函数调用
2.postEvent为异步接口,因为postEevent先将事件添加到postevent队列中,然后再调用操作系统的异步接口PostMessage向系统投递一个消息,最后在消息循环中拿到此消息,才会去从postEevent队列中取出一个事件进行处理

至此,针对本文开头所提出的四个问题:
1.第一个问题,在事件循环初始化,启动事件循环(其实只完成了一半,因为处理用户输入事件的消息还未讲)已给出答案
2.第二个问题,至此未给出答案,在接下来的文章中会给出答案,敬请期待
3.第三个问题,至此未给出答案,在接下来的文章中会给出答案,敬请期待
4.第四个问题,sendEvent和postEvent已给出答案

Qt的事件过滤器 Qt事件模型一个真正强大的特色是一个QObject 的实例能够管理另一个QObject 实例的事件。 让我们试着设想已经有了一个CustomerInfoDialog的小部件。CustomerInfoDialog 包含一系列QLineEdit. 现在,我们想用空格键来代替Tab,使焦点在这些QLineEdit间切换。 一个解决的方法是子类化QLineEdit,重新实现keyPressEvent(),并在keyPressEvent()里调用focusNextChild()。像下面这样: void MyLineEdit::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Space) { focusNextChild(); } else { QLineEdit::keyPressEvent(event); } } 但这有一个缺点。如果CustomerInfoDialog里有很多不同的控件(比如QComboBox,QEdit,QSpinBox),我们就必须子类化这么多控件。这是一个烦琐的任务。 一个更好的解决办法是: 让CustomerInfoDialog去管理他的子部件的按键事件,实现要求的行为。我们可以使用事件过滤器。 一个事件过滤器的安装需要下面2个步骤: 1, 调用installEventFilter()注册需要管理的对象。 2,在eventFilter() 里处理需要管理的对象的事件。 一般,推荐在CustomerInfoDialog的构造函数中注册被管理的对象。像下面这样: CustomerInfoDialog::CustomerInfoDialog(QWidget *parent) : QDialog(parent){ ... firstNameEdit->installEventFilter(this); lastNameEdit->installEventFilter(this); cityEdit->installEventFilter(this); phoneNumberEdit->installEventFilter(this); } 一旦,事件管理器被注册,发送到firstNameEdit,lastNameEdit,cityEdit,phoneNumberEdit的事件将首先发送到eventFilter()。 下面是一个 eventFilter()函数的实现: bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event) { if (target == firstNameEdit || target == lastNameEdit || target == cityEdit || target == phoneNumberEdit) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_Space) { focusNextChild(); return true; } } } return QDialog::eventFilter(target, event); } 在上面的函数中,我们首先检查目标部件是否是 firstNameEdit,lastNameEdit,cityEdit,phoneNumberEdit。接着,我们判断事件是否是按键事件。如果事件是按键事件,我们把事件转换为QKeyEvent。接着,我们判断是否按下了空格键,如果是,我们调用focusNextChild(),把焦点传递给下一个控件。然后,返回,true通知Qt,我们已经处理了该事件。 如果返回false的话,Qt继续将该事件发送给目标控件,结果是一个空格被插入到QLineEdit中。 如果目标控件不是 QLineEdit,或者按键不是空格键,我们将把事件传递给基类的eventFilter()函数。 Qt提供5个级别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值