Qt-事件循环概念及循环函数详解
Qt事件循環詳解
事件循環簡介
QT事件循環過程
啟動事件循環
QEventLoop事件循環
QEventDispatcherUNIX事件處理
QCoreApplicationPrivate事件處理
事件處理過程
QApplication的notify
處理事件過濾器
QApplication的事件過濾器
QObject的事件過濾器
對象的event方法
結尾
事件循環簡介
UI程序之所叫UI程序,是因為需要與用戶有交互,用戶交互一般是通過鼠標鍵盤等的輸入設備,那UI程序就需要有能隨時響應用戶交互的能力。
一個C++程序的main函數大概是下面這樣:
int main()
{
?? ?...
?? ?return 0;
}
我們如何使程序能隨時可以響應用戶交互呢?一個比較簡單的設計就是通過while循環:
int main()
{
?? ?while (1) {
?? ??? ?獲取用戶輸入;
?? ??? ?處理用戶輸入;
?? ?}
?? ?return 0;
}
有時一個事件的處理可能稍微會多花一點時間,如果是上面這樣,在處理一個事件時就不能響應其他事件了,所以我們需要一個隊列,系統可以將新事件放到隊列里:
int main()
{
?? ?while(1) {?
?? ??? ?event = getEventFromQueue()
?? ??? ?dealEvent(event);
?? ?}
}
上面就是一個簡單的事件循環。
QT事件循環過程
QT框架的事件循環主體框架與上面類似,不過要復雜的多。
下面來簡單看下事件循環過程,這里主要以追蹤方法調用的來了解事件循環過程:
啟動事件循環
QT程序中main函數中的app.exec()實際就是啟動事件循環:
int QCoreApplication::exec()
{
?? ?...
?? ?QEventLoop eventLoop;
?? ?eventLoop.exec();
?? ?...
}
可以看到實際是創建了QEventLoop事件循環對象來開啟事件循環。
有時我們阻塞調用某個方法,但又不想干擾事件循環時,可以創建QEventLoop來處理事件,這個我們后面再說。
QEventLoop事件循環
這里就可以看到一個while循環了,如果循環退出標志位為true,循環將一直進行下去。
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實際調用的是本線程的事件分發對象來處理事件。
不同系統的事件處理方式不一樣,所以會有多個從事件分發基類QAbstractEventDispatcher派生的子類,包括QEventDispatcherUNIX、QEventDispatcherWin32等,這里我們以linux系統下的QEventDispatcherUNIX為例來分析。
QEventDispatcherUNIX事件處理
顧名思義,QEventDispatcherUNIX就是UNIX平臺下的事件分發器,我們來看代碼:
bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
?? ?...
? ? QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
? ? ...
}
這里調用了QCoreApplicationPrivate的sendPostedEvents來處理所有post來的事件。
QCoreApplicationPrivate事件處理
QCoreApplicationPrivate::sendPostedEvents用來處理第三個參數所代表的線程的消息隊列中的消息。
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)
? ? ?? ?...
? ? }
這里調用QCoreApplication::sendEvent將消息隊列中的消息全部處理掉。
通常,我們想要同步處理一個事件,也可以直接調用sendEvent,相當于不經過消息隊列而是直接調用該事件的事件處理函數。
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
?? ?...
? ? return notifyInternal2(receiver, event);
}
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
? ? ...
? ? return self->notify(receiver, event);
}
sendEvent實際調用了notify來處理事件。
事件處理過程
到這一步開始,就涉及到具體事件的處理了。
QApplication的notify
可以這么說,每一個事件執行前,都要經過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);
? ? ...
}
處理事件過濾器
到這里調用鏈到了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;
}
可以看到,流程中涉及兩個事件過濾器的調用:sendThroughApplicationEventFilters和sendThroughObjectEventFilters,事件過濾器調用完后,才是調用接收者的event函數。
QApplication的事件過濾器
上一小節提到的sendThroughApplicationEventFilters是處理app的事件過濾器。代碼里會調用給app安裝的所有事件過濾器(從代碼中的注釋看到,app的事件過濾器只能在主線程中被調用),我們給app安裝的事件過濾器就是在這個階段被執行的。
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是處理對象的事件過濾器。里面會調用給接收者安裝的全部事件過濾器,我們通過installEventFilter給某個對象安裝的事件過濾器就是在這個階段執行的。
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代碼可以看到,處理完事件接收者的事件過濾器后,就會調用接收者的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;
? ? ? ? ...
}
到這里是不是很熟悉,各個具體的事件處理函數就是在這個階段被調用的。
結尾
通過閱讀源碼,我們大概梳理了事件循環是如何開啟的,以及事件的處理過程是怎么樣的,也知道了為什么我們攔截事件或者特殊處理事件的順序是QApplication::notify->QApplication::installEventFilter->QObject::installEventFilter->QObject::event。
總結
以上是生活随笔為你收集整理的Qt-事件循环概念及循环函数详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VUE项目中 获得多个复选框 check
- 下一篇: vue 组件间传值、兄弟组件 、bus方