日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Qt-事件循环概念及循环函数详解

發(fā)布時間:2023/12/18 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt-事件循环概念及循环函数详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Qt事件循環(huán)詳解
事件循環(huán)簡介
QT事件循環(huán)過程
啟動事件循環(huán)
QEventLoop事件循環(huán)
QEventDispatcherUNIX事件處理
QCoreApplicationPrivate事件處理
事件處理過程
QApplication的notify
處理事件過濾器
QApplication的事件過濾器
QObject的事件過濾器
對象的event方法
結(jié)尾
事件循環(huán)簡介
UI程序之所叫UI程序,是因為需要與用戶有交互,用戶交互一般是通過鼠標(biāo)鍵盤等的輸入設(shè)備,那UI程序就需要有能隨時響應(yīng)用戶交互的能力。
一個C++程序的main函數(shù)大概是下面這樣:

int main()
{
?? ?...
?? ?return 0;
}

我們?nèi)绾问钩绦蚰茈S時可以響應(yīng)用戶交互呢?一個比較簡單的設(shè)計就是通過while循環(huán):

int main()
{
?? ?while (1) {
?? ??? ?獲取用戶輸入;
?? ??? ?處理用戶輸入;
?? ?}
?? ?return 0;
}

有時一個事件的處理可能稍微會多花一點時間,如果是上面這樣,在處理一個事件時就不能響應(yīng)其他事件了,所以我們需要一個隊列,系統(tǒng)可以將新事件放到隊列里:

int main()
{
?? ?while(1) {?
?? ??? ?event = getEventFromQueue()
?? ??? ?dealEvent(event);
?? ?}
}

上面就是一個簡單的事件循環(huán)。

QT事件循環(huán)過程
QT框架的事件循環(huán)主體框架與上面類似,不過要復(fù)雜的多。
下面來簡單看下事件循環(huán)過程,這里主要以追蹤方法調(diào)用的來了解事件循環(huán)過程:

啟動事件循環(huán)
QT程序中main函數(shù)中的app.exec()實際就是啟動事件循環(huán):

int QCoreApplication::exec()
{
?? ?...
?? ?QEventLoop eventLoop;
?? ?eventLoop.exec();
?? ?...
}

可以看到實際是創(chuàng)建了QEventLoop事件循環(huán)對象來開啟事件循環(huán)。
有時我們阻塞調(diào)用某個方法,但又不想干擾事件循環(huán)時,可以創(chuàng)建QEventLoop來處理事件,這個我們后面再說。

QEventLoop事件循環(huán)
這里就可以看到一個while循環(huán)了,如果循環(huán)退出標(biāo)志位為true,循環(huán)將一直進行下去。

int QEventLoop::exec(ProcessEventsFlags flags)
{
?? ?...
? ? while (!d->exit.loadAcquire())
? ? ? ? processEvents(flags | WaitForMoreEvents | EventLoopExec);
?? ?...
}

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
? ? Q_D(QEventLoop);
? ? if (!d->threadData->hasEventDispatcher())
? ? ? ? return false;
? ? return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}

QEventLoop::processEvents實際調(diào)用的是本線程的事件分發(fā)對象來處理事件。
不同系統(tǒng)的事件處理方式不一樣,所以會有多個從事件分發(fā)基類QAbstractEventDispatcher派生的子類,包括QEventDispatcherUNIX、QEventDispatcherWin32等,這里我們以linux系統(tǒng)下的QEventDispatcherUNIX為例來分析。

QEventDispatcherUNIX事件處理
顧名思義,QEventDispatcherUNIX就是UNIX平臺下的事件分發(fā)器,我們來看代碼:

bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
?? ?...
? ? QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
? ? ...
}

這里調(diào)用了QCoreApplicationPrivate的sendPostedEvents來處理所有post來的事件。

QCoreApplicationPrivate事件處理
QCoreApplicationPrivate::sendPostedEvents用來處理第三個參數(shù)所代表的線程的消息隊列中的消息。

void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?QThreadData *data)
? ? ...
? ? while (i < data->postEventList.size()) {
? ? ?? ?...
? ? ?? ?const QPostEvent &pe = data->postEventList.at(i);
? ? ?? ?...
? ? ?? ?QEvent *e = pe.event;
? ? ? ? QObject * r = pe.receiver;
? ? ? ? ...
? ? ? ? QCoreApplication::sendEvent(r, e)
? ? ?? ?...
? ? }

這里調(diào)用QCoreApplication::sendEvent將消息隊列中的消息全部處理掉。
通常,我們想要同步處理一個事件,也可以直接調(diào)用sendEvent,相當(dāng)于不經(jīng)過消息隊列而是直接調(diào)用該事件的事件處理函數(shù)。

bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
?? ?...
? ? return notifyInternal2(receiver, event);
}

bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
? ? ...
? ? return self->notify(receiver, event);
}

sendEvent實際調(diào)用了notify來處理事件。

事件處理過程
到這一步開始,就涉及到具體事件的處理了。

QApplication的notify
可以這么說,每一個事件執(zhí)行前,都要經(jīng)過QApplication::notify,所以,我們可以重寫notify來對事件做特殊處理。

bool QApplication::notify(QObject *receiver, QEvent *event)
{
? ?...
? ?switch (e->type()) {
? ? ...
? ? case QEvent::Wheel: // User input and window activation makes tooltips sleep
? ? case QEvent::ActivationChange:
? ? case QEvent::KeyPress:
? ? case QEvent::KeyRelease:
? ? case QEvent::FocusOut:
? ? case QEvent::FocusIn:
? ? case QEvent::MouseButtonPress:
? ? case QEvent::MouseButtonRelease:
? ? case QEvent::MouseButtonDblClick:
? ? ...
? ? res = d->notify_helper(receiver, e);
? ? ...
}

處理事件過濾器
到這里調(diào)用鏈到了QCoreApplicationPrivate::notify_helper,這個方法比較重要,我們來看主體代碼:

bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
?? ?...
? ? // send to all application event filters (only does anything in the main thread)
? ? if (QCoreApplication::self
? ? ? ? ? ? && receiver->d_func()->threadData->thread.loadAcquire() == mainThread()
? ? ? ? ? ? && QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
? ? ? ? filtered = true;
? ? ? ? return filtered;
? ? }
? ? // send to all receiver event filters
? ? if (sendThroughObjectEventFilters(receiver, event)) {
? ? ? ? filtered = true;
? ? ? ? return filtered;
? ? }

? ? // deliver the event
? ? consumed = receiver->event(event);
? ? return consumed;
}

可以看到,流程中涉及兩個事件過濾器的調(diào)用:sendThroughApplicationEventFilters和sendThroughObjectEventFilters,事件過濾器調(diào)用完后,才是調(diào)用接收者的event函數(shù)。

QApplication的事件過濾器
上一小節(jié)提到的sendThroughApplicationEventFilters是處理app的事件過濾器。代碼里會調(diào)用給app安裝的所有事件過濾器(從代碼中的注釋看到,app的事件過濾器只能在主線程中被調(diào)用),我們給app安裝的事件過濾器就是在這個階段被執(zhí)行的。

bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event)
{
? ? // We can't access the application event filters outside of the main thread (race conditions)
? ? Q_ASSERT(receiver->d_func()->threadData->thread.loadAcquire() == mainThread());

? ? if (extraData) {
? ? ? ? // application event filters are only called for objects in the GUI thread
? ? ? ? for (int i = 0; i < extraData->eventFilters.size(); ++i) {
? ? ? ? ? ? QObject *obj = extraData->eventFilters.at(i);
? ? ? ? ? ? ...
? ? ? ? ? ? if (obj->eventFilter(receiver, event))
? ? ? ? ? ? ? ? return true;
? ? ? ? }
? ? }
? ? return false;
}

QObject的事件過濾器
sendThroughObjectEventFilters是處理對象的事件過濾器。里面會調(diào)用給接收者安裝的全部事件過濾器,我們通過installEventFilter給某個對象安裝的事件過濾器就是在這個階段執(zhí)行的。

bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
? ? ? ? ...
? ? ? ? for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
? ? ? ? ? ? QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);
? ? ? ? ? ? ...
? ? ? ? ? ? if (obj->eventFilter(receiver, event))
? ? ? ? ? ? ? ? return true;
? ? ? ? }
? ? }
? ? return false;
}

對象的event方法
從QCoreApplicationPrivate::notify_helper代碼可以看到,處理完事件接收者的事件過濾器后,就會調(diào)用接收者的event方法來處理事件。
每一個從QObject繼承出來的子類都有event方法,都可以處理自己的事件。
我們簡單看下QWidget的event方法:

bool QWidget::event(QEvent *event)
{
? ? ...
? ? switch (event->type()) {
? ? ...
? ? case QEvent::MouseMove:
? ? ? ? mouseMoveEvent((QMouseEvent*)event);
? ? ? ? break;

? ? case QEvent::MouseButtonPress:
? ? ? ? mousePressEvent((QMouseEvent*)event);
? ? ? ? break;

? ? case QEvent::MouseButtonRelease:
? ? ? ? mouseReleaseEvent((QMouseEvent*)event);
? ? ? ? break;

? ? case QEvent::MouseButtonDblClick:
? ? ? ? mouseDoubleClickEvent((QMouseEvent*)event);
? ? ? ? break;
? ? ? ? ...
}

到這里是不是很熟悉,各個具體的事件處理函數(shù)就是在這個階段被調(diào)用的。

結(jié)尾
通過閱讀源碼,我們大概梳理了事件循環(huán)是如何開啟的,以及事件的處理過程是怎么樣的,也知道了為什么我們攔截事件或者特殊處理事件的順序是QApplication::notify->QApplication::installEventFilter->QObject::installEventFilter->QObject::event。

總結(jié)

以上是生活随笔為你收集整理的Qt-事件循环概念及循环函数详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。