Qt源码解析之事件系统

  • 一、向事件循环里面添加事件
  • 二、QThread里面的事件循环

一、向事件循环里面添加事件

[static] void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)

1.postEvent是将event事件添加到接收者receiver对象所属线程的事件队列,并在下一个事件循环执行,为异步执行,完成后postEvent()立即返回,添加完成后,事件的所有权归线程事件队列所有,因此event事件要在堆上分配。priority为事件的优先级,优先级高的事件会优先执行。

[static] bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)

2.sendEvent()为同步事件派发,不入事件队列,直接进入派发环节,为线程内执行,换言之,调用sendEvent()接口所在线程和receiver->thread()所表示的线程,必须为同一个线程,事件发出后,sendEvent不负责event指针的删除,因此使用时,事件须在栈上创建。下面举一个我做项目时用到的一个例子:

//QTreeWidget向上翻页
void setTreeWidgetUpPage(QTreeView *tree)
{if(tree == nullptr)return;QKeyEvent pageUpEvent(QEvent::KeyPress,Qt::Key_PageUp,Qt::NoModifier);Q_ASSERT(QThread::currentThread() == tree->thread());//必然满足,否则就要使用postEventQApplication::sendEvent(tree,&pageUpEvent);
}

再举一个例子,来体会一下这两个接口的使用环境

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qcoreapplication.cpp:2024
void QCoreApplicationPrivate::quit()
{Q_Q(QCoreApplication);if (QThread::currentThread() == mainThread()) {QEvent quitEvent(QEvent::Quit);//在主线程调用quit时,使用sendEventQCoreApplication::sendEvent(q, &quitEvent);} else {//在其它工作线程调用quit时,就需要异步执行,使用postEvent,注意到此时使用new在堆上分配内存QCoreApplication::postEvent(q, new QEvent(QEvent::Quit));}
}

二、QThread里面的事件循环

1.借用另一篇文章里的例子Qt源码解析之QThread,文中提到,在真正的线程入口QThreadPrivate::start()里面调用了虚函数[virtual protected] void QThread::run(),我们就从这里开始。

void QThread::run()
{(void) exec();
}

2.除了之前文章中讲述的使用void QObject::moveToThread(QThread *targetThread)来实现多线程外,继承QThread类,然后重写QThread::run()同样也可以实现,在run()中执行的代码就是跑在新的线程中的,实现上么有问题,只是不推荐这种用法而已,run()函数的默认实现很简单,调用了exec()函数,以此开启了线程的事件循环。

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread.cpp:543
int QThread::exec()
{Q_D(QThread);QMutexLocker locker(&d->mutex);d->data->quitNow = false;if (d->exited) {d->exited = false;return d->returnCode;}locker.unlock();QEventLoop eventLoop;int returnCode = eventLoop.exec();//开启线程事件循环locker.relock();d->exited = false;d->returnCode = -1;return returnCode;
}

3.事件循环会一直运行下去,直到exit()接口被调用,可以看到,开启线程的事件循环,同样使用了QEventLoop类,QEventLoop是QObject的派生类,同样具有线程亲和性,通过在哪个线程创建就属于哪个线程的原则,eventLoop实例同样活在新线程内。

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qeventloop.cpp:181
int QEventLoop::exec(ProcessEventsFlags flags)
{Q_D(QEventLoop);auto threadData = d->threadData.loadRelaxed();//此处的QThreadData线程数据,就是在QThreadPrivate的构造函数中构造的,//所有隶属于该线程的QObject及其派生类都持有该指针,体现了QObject的线程亲和性。//we need to protect from race condition with QThread::exitQMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex);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<QMutex> &locker;bool exceptionCaught;LoopReference(QEventLoopPrivate *d, QMutexLocker<QMutex> &locker) : d(d), locker(locker), exceptionCaught(true){d->inExec = true;d->exit.storeRelease(false);auto threadData = d->threadData.loadRelaxed();++threadData->loopLevel;//开启事件循环,嵌套层数加1threadData->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.");}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 warningd->inExec = false;--threadData->loopLevel;//嵌套层数减一}};LoopReference ref(d, locker);//在构造函数和析构函数中处理事件循环的入队、出队// remove posted quit events when entering a new event loopQCoreApplication *app = QCoreApplication::instance();if (app && app->thread() == thread())QCoreApplication::removePostedEvents(app, QEvent::Quit);#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-70185if (threadData->loopLevel > 1)emscripten_sleep(1);
#endifwhile (!d->exit.loadAcquire())//只要没有退出,就一直等待并处理事件队列中的事件processEvents(flags | WaitForMoreEvents | EventLoopExec);ref.exceptionCaught = false;return d->returnCode.loadRelaxed();
}
  1. enum ProcessEventsFlags
  • QEventLoop::AllEvents:处理所有事件
  • QEventLoop::ExcludeUserInputEvents: 不处理用户输入事件,事件不会被丢弃,将会在没有此标志时被处理
  • QEventLoop::ExcludeSocketNotifiers: 不处理网络事件
  • QEventLoop::WaitForMoreEvents:在事件队列为空时,继续等待,线程不休眠
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{Q_D(QEventLoop);auto threadData = d->threadData.loadRelaxed();if (!threadData->hasEventDispatcher())return false;return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}

回忆一下,QThreadData里面有几个重要的成员,保存着线程的事件循环队列、事件队列、线程指针、线程ID以及事件分发句柄,在贴一下代码

class QThreadData
{public:QThreadData(int initialRefCount = 1);~QThreadData();...
public:int loopLevel;//事件循环嵌套层数int scopeLevel;QStack<QEventLoop *> eventLoops;//嵌套事件循环堆栈QPostEventList postEventList;//线程事件队列QAtomicPointer<QThread> thread;//线程指针QAtomicPointer<void> threadId;//线程IDQAtomicPointer<QAbstractEventDispatcher> eventDispatcher;//事件分发列表QList<void *> tls;FlaggedDebugSignatures flaggedSignatures;...
};

QEventLoop::processEvents()进一步调用了QAbstractEventDispatcher类的接口,该类是一个虚基类,定义了事件分发的具体接口,在不通的平台上有不通的派生类实现,以此来达到跨平台的目的,Linux下为QEventDispatcherUNIX,window下为QEventDispatcherWin32,下面看一下win下具体实现,先看一下构造函数

QEventDispatcherWin32::QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent): QAbstractEventDispatcher(dd, parent)
{Q_D(QEventDispatcherWin32);d->internalHwnd = qt_create_internal_window(this);
}

这里有个非常重要的地方,win32下的窗口编程,注册消息回调函数是绕不过去的,Qt也不例外,在Qt中同样是为每一个线程创建了一个看不见的窗口,然后通过为该窗口注册消息回调函数,以此来接管windows的消息循环,将windows格式的消息,翻译成Qt自己的消息格式,进入到Qt事件系统。构造函数中的 d->internalHwnd = qt_create_internal_window(this);就是具体的实现了,
看一下代码:

static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
{QWindowsMessageWindowClassContext *ctx = qWindowsMessageWindowClassContext();if (!ctx->atom)return 0;HWND wnd = CreateWindow(ctx->className,    // classnamectx->className,    // window name0,                 // style0, 0, 0, 0,        // geometry  尺寸为0,这个窗口隐身啦HWND_MESSAGE,            // parent0,                 // menu handleGetModuleHandle(0),     // application0);                // windows creation data.if (!wnd) {qErrnoWarning("CreateWindow() for QEventDispatcherWin32 internal window failed");return 0;}#ifdef GWLP_USERDATASetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
#elseSetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher));
#endifreturn wnd;
}

注意到QWindowsMessageWindowClassContext ctx = qWindowsMessageWindowClassContext();此处获取的是一个全局静态变量,使用了Qt内定义的一个宏Q_GLOBAL_STATIC,用来定义一个线程安全的全局静态变量,Q_GLOBAL_STATIC(QWindowsMessageWindowClassContext, qWindowsMessageWindowClassContext),qWindowsMessageWindowClassContext实际上是一个对象,只不过被重载了()、->、 等操作符,在行为上表现为该对象的指针我们来看一下这个类的构造函数。

QWindowsMessageWindowClassContext::QWindowsMessageWindowClassContext(): atom(0), className(0)
{// make sure that multiple Qt's can coexist in the same processconst QString qClassName = QStringLiteral("QEventDispatcherWin32_Internal_Widget")+ QString::number(quintptr(qt_internal_proc));className = new wchar_t[qClassName.size() + 1];qClassName.toWCharArray(className);className[qClassName.size()] = 0;WNDCLASS wc;wc.style = 0;wc.lpfnWndProc = qt_internal_proc;//重点来了,这就是上述隐形窗口的消息回调函数,算是windows消息系统与Qt的一个衔接点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;}
}

win32窗口编程中,在处理消息循环时,一般有类似如下处理:

MSG msg;
while(!isExit()){if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);}
}

在一个while循环中不停的通过PeekMessage从windows消息循环中获取消息然后翻译、分发消息,最终我们之前注册的回调函数qt_internal_proc(),对消息做进一步的处理,Qt中获取并分发消息的处理正是在processEvents()函数里,下面我们回归这里看看

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{Q_D(QEventDispatcherWin32);// We don't know _when_ the interrupt occurred so we have to honor it.const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);emit awake();// To prevent livelocks, send posted events once per iteration.// QCoreApplication::sendPostedEvents() takes care about recursions.sendPostedEvents();if (wasInterrupted)return false;auto threadData = d->threadData.loadRelaxed();bool canWait;bool retVal = false;do {QVarLengthArray<MSG> processedTimers;while (!d->interrupt.loadRelaxed()) {MSG msg;if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {// process queued user input eventsmsg = d->queuedUserInputEvents.takeFirst();//取出用户输入消息事件} else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {// process queued socket eventsmsg = d->queuedSocketEvents.takeFirst();//取出网络socket消息事件} else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {//PeekMessage是win32 api,用于从windows系统消息队列中if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)//获取消息&& isUserInputMessage(msg.message)) {// queue user input events for later processingd->queuedUserInputEvents.append(msg);continue;}if ((flags & QEventLoop::ExcludeSocketNotifiers)&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {// queue socket events for later processingd->queuedSocketEvents.append(msg);continue;}} else if (MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE)== WAIT_OBJECT_0) {// a new message has arrived, process itcontinue;} else {// nothing to do, so breakbreak;}if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {d->startPostedEventsTimer();// 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 sentbool 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;}//各平台原生支持的事件的过滤,如果被处理了,就会返回trueif (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);//win32 api 进入消息分发环节,此时事件进入到windows系统的消息循环了}retVal = true;}// wait for messagecanWait = (!retVal&& !d->interrupt.loadRelaxed()&& flags.testFlag(QEventLoop::WaitForMoreEvents)&& threadData->canWaitLocked());if (canWait) {emit aboutToBlock();MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);emit awake();}} while (canWait);return retVal;
}

消息分发后,经windows消息系统,就会调用之前注册的消息回调函数qt_internal_proc(),在这里将windows系统消息翻译成Qt消息,编程我们熟悉的事件的样子。

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:241
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();qintptr result;if (!dispatcher) {if (message == WM_TIMER)KillTimer(hwnd, wp);return 0;}if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result))return result;#ifdef GWLP_USERDATAauto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
#elseauto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA));
#endifQEventDispatcherWin32Private *d = nullptr;if (q != nullptr)d = q->d_func();switch (message) {case WM_QT_SOCKETNOTIFIER: {//网络socket事件// socket notifier messageint type = -1;switch (WSAGETSELECTEVENT(lp)) {case FD_READ:case FD_ACCEPT:type = 0;break;case FD_WRITE:case FD_CONNECT:type = 1;break;case FD_OOB:type = 2;break;case FD_CLOSE:type = 3;break;}if (type >= 0) {Q_ASSERT(d != nullptr);QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };QSNDict *dict = sn_vec[type];QSockNot *sn = dict ? dict->value(wp) : 0;if (sn == nullptr) {d->postActivateSocketNotifiers();} else {Q_ASSERT(d->active_fd.contains(sn->fd));QSockFd &sd = d->active_fd[sn->fd];if (sd.selected) {Q_ASSERT(sd.mask == 0);d->doWsaAsyncSelect(sn->fd, 0);sd.selected = false;}d->postActivateSocketNotifiers();// Ignore the message if a notification with the same type was// received previously. Suppressed message is definitely spurious.const long eventCode = WSAGETSELECTEVENT(lp);if ((sd.mask & eventCode) != eventCode) {sd.mask |= eventCode;QEvent event(type < 3 ? QEvent::SockAct : QEvent::SockClose);//socket 相关Qt事件QCoreApplication::sendEvent(sn->obj, &event);}}}return 0;}case WM_QT_ACTIVATENOTIFIERS: {Q_ASSERT(d != nullptr);// Postpone activation if we have unhandled socket notifier messages// in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of// event processing.MSG msg;if (!PeekMessage(&msg, d->internalHwnd,WM_QT_SOCKETNOTIFIER, WM_QT_SOCKETNOTIFIER, PM_NOREMOVE)&& d->queuedSocketEvents.isEmpty()) {// register all socket notifiersfor (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end();it != end; ++it) {QSockFd &sd = it.value();if (!sd.selected) {d->doWsaAsyncSelect(it.key(), sd.event);// allow any event to be acceptedsd.mask = 0;sd.selected = true;}}}d->activateNotifiersPosted = false;return 0;}case WM_TIMER://定时器事件Q_ASSERT(d != nullptr);if (wp == d->sendPostedEventsTimerId)q->sendPostedEvents();elsed->sendTimerEvent(wp);return 0;case WM_QT_SENDPOSTEDEVENTS://post的事件在此处处理Q_ASSERT(d != nullptr);// 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 = QS_ALLEVENTS;if (HIWORD(GetQueueStatus(mask)) == 0)q->sendPostedEvents();//将事件队列中的事件统一发送出去elsed->startPostedEventsTimer();return 0;} // switch (message)return DefWindowProc(hwnd, message, wp, lp);
}

void QEventDispatcherWin32::sendPostedEvents(),gui相关的事件和其余事件在此处处理不一样,QEvent::MetaCall这样跟槽函数回调有关的事件,通过此接口处理,而窗口相关的事件,会进入另一个重载函数中void QWindowsGuiEventDispatcher::sendPostedEvents(),这个会在主线程事件循环中讲到,下面看一下代码:

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp:892
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());
}

可以看到,此处将本线程的线程数据指针作为参数传出,第一个参数为receiver=0,event_type=0

//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qcoreapplication.cpp:1838
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,QThreadData *data)
{if (event_type == -1) {// we were called by an obsolete event dispatcher.event_type = 0;}if (receiver && receiver->d_func()->threadData != data) {qWarning("QCoreApplication::sendPostedEvents: Cannot send ""posted events for objects in another thread");return;}++data->postEventList.recursion;auto locker = qt_unique_lock(data->postEventList.mutex);// by default, we assume that the event dispatcher can go to sleep after// processing all events. if any new events are posted while we send// events, canWait will be set to false.data->canWait = (data->postEventList.size() == 0);if (data->postEventList.size() == 0 || (receiver && !receiver->d_func()->postedEvents)) {--data->postEventList.recursion;return;}data->canWait = true;// okay. here is the tricky loop. be careful about optimizing// this, it looks the way it does for good reasons.int startOffset = data->postEventList.startOffset;int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;data->postEventList.insertionOffset = data->postEventList.size();// Exception-safe cleaning up without the need for a try/catch blockstruct CleanUp {QObject *receiver;int event_type;QThreadData *data;bool exceptionCaught;inline CleanUp(QObject *receiver, int event_type, QThreadData *data) :receiver(receiver), event_type(event_type), data(data), exceptionCaught(true){}inline ~CleanUp(){if (exceptionCaught) {// since we were interrupted, we need another pass to make sure we clean everything updata->canWait = false;}--data->postEventList.recursion;if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher())data->eventDispatcher.loadRelaxed()->wakeUp();// clear the global list, i.e. remove everything that was// delivered.if (!event_type && !receiver && data->postEventList.startOffset >= 0) {const QPostEventList::iterator it = data->postEventList.begin();data->postEventList.erase(it, it + data->postEventList.startOffset);data->postEventList.insertionOffset -= data->postEventList.startOffset;Q_ASSERT(data->postEventList.insertionOffset >= 0);data->postEventList.startOffset = 0;}}};CleanUp cleanup(receiver, event_type, data);while (i < data->postEventList.size()) {// avoid live-lockif (i >= data->postEventList.insertionOffset)break;const QPostEvent &pe = data->postEventList.at(i);++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 deleteif (!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 lostdata->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->m_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);// careful when adding anything below this point - the// sendEvent() call might invalidate any invariants this// function depends on.}cleanup.exceptionCaught = false;
}

CleanUp这个内部类,用来在将队列中的事件通过sendEvent派发出去后,将事件队列清空,之前我们说过,事件队列持有在堆上分配的事件的所有权,负责它的内存释放,QScopedPointer event_deleter(e),通过将事件指针的所有权转移到智能指针event_deleter上,在超出智能指针的作用后,自动释放掉。接下来就看到我们熟悉的sendEvent了,这里就不展开啦,后续再开单章。

Qt源码解析之事件系统(一)相关推荐

  1. Qt源码解析之QThread

    Qt源码解析之QThread 一.用法简介 二.源码分享 一.用法简介 1.工作类 class Worker : public QObject {Q_OBJECT public:explicit Wo ...

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

    版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者"tingsking18"和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消 ...

  3. Qt源码解析-源码解析-QVideoWidget播放手机视频旋转问题

    Qt源码解析 索引 Qt源码解析-源码解析-QVideoWidget播放手机视频旋转问题 问题描述与复现 使用手机拍摄的视频是竖屏的,上传后,使用QVideoWidget播放,变成横屏. 总结,可以让 ...

  4. Unity中的UGUI源码解析之事件系统(2)-EventSystem组件

    Unity中的UGUI源码解析之事件系统(2)-EventSystem组件 今天介绍我们的第一个主角: EventSystem. EventSystem在整个事件系统中处于中心, 相当于事件系统的管理 ...

  5. Unity中的UGUI源码解析之事件系统(8)-输入模块(中)

    Unity中的UGUI源码解析之事件系统(8)-输入模块(中) 接上一篇文章, 继续介绍输入模块. Unity中主要处理的是指针事件, 也就是在2d平面上跟踪指针设备输入坐标的的事件, 这一类事件有鼠 ...

  6. Unity中的UGUI源码解析之事件系统(9)-输入模块(下)

    Unity中的UGUI源码解析之事件系统(9)-输入模块(下) 接上一篇文章, 继续介绍输入模块. StandaloneInputModule类是上一篇文章介绍的抽象类PointerInputModu ...

  7. Unity中的UGUI源码解析之事件系统(6)-RayCaster(下)

    Unity中的UGUI源码解析之事件系统(6)-RayCaster(下) 接上一篇文章, 继续介绍投射器. GraphicRaycaster GraphicRaycaster继承于BaseRaycas ...

  8. Unity中的UGUI源码解析之事件系统(3)-EventData

    Unity中的UGUI源码解析之事件系统(3)-EventData 为了在事件系统中传递数据, Unity提供了EventData相关的类来封装这一类数据. 了解这些结构有助于我们对后面模块的学习. ...

  9. Laravel源码解析之事件系统

    Laravel 的事件提供了一个简单的观察者实现,能够订阅和监听应用中发生的各种事件.事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器. laravel 中事件系统由两部分构 ...

最新文章

  1. qtextbrowser 大量数据卡顿_800万行的数据,Excel 10秒钟就能完成统计?这个工具太良心了!...
  2. 如何处理SAP云平台错误消息 there is no compute unit quota for subaccount
  3. java基础:数据类型
  4. windows server 2012 R2 standard 评估版过期重启
  5. Django--模板语言
  6. python 切片_零基础学python_11_列表(切片+遍历切片+复制)
  7. 3月3 pytorch模型保存的.pt, .pth, .pkl的pytorch模型文件,只是后缀不同而已(仅此而已)
  8. Audition 如何录制电脑内部声音
  9. Windows 10系统安装AutoCAD 2007
  10. 用命令行方式写Android应用
  11. HP LaserJet Pro MFP M227d安装指南
  12. Spring Boot在使用Gradle build命令卡住不动了
  13. ocm认证年薪多少_年薪 100 万在 IT 行业中,属于什么水平 ?
  14. 怎样远程开启计算机并查看桌面,远程开启远程计算机的远程桌面
  15. python进行基本的图像处理
  16. 翻译英语的软件-免费翻译软件-各种语言互相翻译
  17. 【 第八节 函数作用域】
  18. 跟你聊聊员工的离职成本,细算下来超级恐怖!
  19. 自定义交叉熵损失函数的几个问题
  20. 2021年选择老域名的2点好处和购买老域名的4点技巧

热门文章

  1. DRR影像重建的原理
  2. 八皇后问题各种解法分析
  3. matlab中怎么写拉格朗日函数,MATLAB编辑n次拉格朗日函数插值法的程序
  4. 卷积神经网络中卷积层、池化层、全连接层的作用
  5. oracle打补丁失败会怎么样,一次数据库补丁失败修复案例
  6. Java数据压缩算法——哈夫曼树
  7. Ceph论文译文--Ceph:一个可扩展,高性能分布式文件系统 (转载)
  8. Web服务器配置与管理(包含四种案例)
  9. Dubbo高性能之道
  10. 看完这篇 教你玩转渗透测试靶机vulnhub——DC5