QT开发(十二)——QT事件处理机制
?
一、QT事件簡介
????QT程序是事件驅動的, 程序的每個動作都是由內部某個事件所觸發。QT事件的發生和處理成為程序運行的主線,存在于程序整個生命周期。
????常見的QT事件類型如下:
????鍵盤事件: 按鍵按下和松開
????鼠標事件: 鼠標移動,鼠標按鍵的按下和松開
????拖放事件: 用鼠標進行拖放
????滾輪事件: 鼠標滾輪滾動
????繪屏事件: 重繪屏幕的某些部分
????定時事件: 定時器到時
????焦點事件: 鍵盤焦點移動
????進入和離開事件: 鼠標移入widget之內,或是移出
????移動事件: widget的位置改變
????大小改變事件: widget的大小改變
????顯示和隱藏事件: widget顯示和隱藏
????窗口事件: 窗口是否為當前窗口
?QT將系統產生的消息轉化為QT事件,QT事件被封裝為對象,所有的QT事件均繼承抽象類QEvent,用于描述程序內部或外部發生的動作,任意的QObject對象都具備處理QT事件的能力。
二、QT事件的產生
1、操作系統產生
操作系統將獲取的事件,比如鼠標按鍵,鍵盤按鍵等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系統的消息隊列中,Qt事件循環的時候讀取消息隊列中的事件,轉化為QEvent,再依次處理。
2、QT應用程序程序自己產生
????程序產生事件有兩種方式, 一種是調用QApplication::postEvent(),?例如QWidget::update()函數,當需要重新繪制屏幕時,程序調用update()函數,new出來一個paintEvent,調用 QApplication::postEvent(),將其放入Qt的消息隊列中,等待依次被處理;另一種方式是調用sendEvent()函數,事件不會放入隊列, 而是直接被派發和處理, QWidget::repaint()函數用的就是這種方式。
三、QT事件的處理
1、調度方式
?事件有兩種調度方式,同步和異步。
????Qt的事件循環是異步的,當調用QApplication::exec()時,就進入了事件循環,先處理Qt事件隊列中的事件, 直至為空,再處理系統消息隊列中的消息, 直至為空, 處理系統消息的時候會產生新的Qt事件, 需要對其再次進行處理。
????調用QApplication::sendEvent的時候, 消息會立即被處理,是同步的。實際上QApplication::sendEvent()是通過調用QApplication::notify(), 直接進入了事件的派發和處理環節。
2、事件的派發和處理
????事件過濾器是Qt中一個獨特的事件處理機制, 功能強大而且使用起來靈活方便。通過事件過濾器, 可以讓一個對象偵聽攔截另外一個對象的事件。事件過濾器實現如下: 在所有Qt對象的基類QObject中有一個類型為QObjectList的成員變量,名字為eventFilters,當某個QObject(A)給另一個QObject(B)安裝了事件過濾器后, B會把A的指針保存在eventFilters中。在B處理事件前,會先去檢查eventFilters列表, 如果非空, 就先調用列表中對象的eventFilter()函數。一個對象可以給多個對象安裝過濾器,一個對象能同時被安裝多個過濾器, 在事件到達之后,?事件過濾器以安裝次序的反序被調用。事件過濾器函數( eventFilter() ) 返回值是bool型, 如果返回true, 則表示事件已經被處理完畢, Qt將直接返回, 進行下一事件的處理。如果返回false, 事件將接著被送往剩下的事件過濾器或是目標對象進行處理。
????QT中,事件的派發是從 QApplication::notify()開始的, 因為QAppliction也是繼承自QObject, 所以先檢查QAppliation對象, 如果有事件過濾器安裝在qApp上, 先調用事件過濾器,接下來QApplication::notify() 會過濾或合并一些事件(比如失效widget的鼠標事件會被過濾掉, 而同一區域重復的繪圖事件會被合并),事件被送到reciver::event()處理。
????在reciver::event()中, 先檢查有無事件過濾器安裝在reciever上。若有, 則調用之。然后根據QEvent的類型, 調用相應的特定事件處理函數。常見的事件都有特定事件處理函數, 比如:mousePressEvent(), focusOutEvent(),? resizeEvent(), paintEvent(), resizeEvent()等等。在實際應用中, 經常需要重載特定事件處理函數處理事件。對于不常見的事件, 沒有相對應的特定事件處理函數,如果要處理這些事件, 就需要使用別的辦法, 比如重載event() 函數, 或是安裝事件過濾器。
3、事件的轉發
????對于某些類別的事件,如果在整個事件的派發過程結束后還沒有被處理, 那么這個事件將會向上轉發給它的父widget, 直到最頂層窗口。Qt中和事件相關的函數通過兩種方式相互通信,一種是QApplication::notify(), QObject::eventFilter(), QObject::event()通過返回bool值來表示是否已處理;另一種是調用QEvent::ignore() 或 QEvent::accept() 對事件進行標識,只用于event()函數和特定事件處理函數之間的溝通,而且只有用在某些類別事件上是有意義的, 這些事件就是上面提到的那些會被轉發的事件, 包括: 鼠標, 滾輪, 按鍵等事件。
4、事件的處理、過濾
????QT提供了五種不同級別的事件處理和過濾:
????A、重寫特定事件處理函數.
????最常見的事件處理辦法就是重寫mousePressEvent(), keyPressEvent(), paintEvent()?等特定事件處理函數。
?B、重寫event()函數.
????重寫event()函數時, 需要調用父類的event()函數來處理不需要處理或是不清楚如何處理的事件。
????return QWidget::event(event);
????C、在Qt對象上安裝事件過濾器
????安裝事件過濾器有兩個步驟: (假設要用A來監視過濾B的事件)
????首先調用B的installEventFilter( const QOject *obj ), 以A的指針作為參數,所有發往B的事件都將先由A的eventFilter()處理。然后, A要重寫QObject::eventFilter()函數, 在eventFilter() 中對事件進行處理。
????D、給QAppliction對象安裝事件過濾器
如果給QApplication對象裝上過濾器,那么所有的事件在發往任何其他的過濾器時,都要先經過當前eventFilter()。在QApplication::notify() 中, 是先調用qApp的過濾器, 再對事件進行分析, 以決定是否合并或丟棄。
????E、繼承QApplication類,并重載notify()函數
????Qt是用QApplication::notify()函數來分發事件的,要在任何事件過濾器查看任何事件之前先得到這些事件,重寫notify()函數是唯一的辦法。通常來說事件過濾器更好用一些, 因為不需要去繼承QApplication類,而且可以給QApplication對象安裝任意個數的事件過濾器。
GUI應用程序的事件處理:
A、QT事件產生后會被立即發送到QWidget對象
B、QWwidget中的event(QEvent *)進行事件處理
C、event(QEvent *)根據事件類型調用不同的事件處理函數
D、在事件處理函數中發送QT預定義的信號
E、調用信號關聯的槽函數
四、源碼分析QT事件處理機制
QT源碼中事件梳理過程中調用函數如下:
QApplication::exec()QCoreApplication::exec()???QEventLoop::exec(ProcessEventsFlags?)????QEventLoop::processEvents(ProcessEventsFlags?)QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags)QT_WIN_CALLBACK?QtWndProc(HWND?hwnd,?UINT?message,?WPARAM?wParam,?LPARAM?lParam)?bool?QETWidget::translateMouseEvent(const?MSG?&msg)???bool?QApplicationPrivate::sendMouseEvent(...)???inline?bool?QCoreApplication::sendSpontaneousEvent(QObject?*receiver,?QEvent?*event)???bool?QCoreApplication::notifyInternal(QObject?*receiver,?QEvent?*event)???bool?QApplication::notify(QObject?*receiver,?QEvent?*e)???bool?QApplicationPrivate::notify_helper(QObject?*receiver,?QEvent?*?e)???bool?QWidget::event(QEvent?*event)1、進入Qpplication事件循環
int?main(int?argc,?char?*argv[])?????{?????QApplication?app(argc,?argv);?????Widget?window;??//?Widget?繼承自QWidget?????window.show();?????return?app.exec();?//?進入Qpplication事件循環?????}2、進入QCoreApplication事件循環
int?QApplication::exec(){3、進入QEventLoop事件隊列循環
int?QCoreApplication::exec(){????if?(!QCoreApplicationPrivate::checkInstance("exec"))????????return?-1;????//得到當前Thread數據??????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;????}???????//檢查event?loop是否已經創建?????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;???????//委任QEventLoop?處理事件隊列循環????int?returnCode?=?eventLoop.exec();????threadData->quitNow?=?false;????if?(self)?{????????self->d_func()->in_exec?=?false;????????if?(!self->d_func()->aboutToQuitEmitted)????????????emit?self->aboutToQuit();????????self->d_func()->aboutToQuitEmitted?=?true;????????sendPostedEvents(0,?QEvent::DeferredDelete);????}????return?returnCode;}4、進入QEventLoop::processEvents
int?QEventLoop::exec(ProcessEventsFlags?flags){????Q_D(QEventLoop);??//訪問QEventloop私有類實例d????//we?need?to?protect?from?race?condition?with?QThread::exit????QMutexLocker?locker(&static_cast<QThreadPrivate?*>(QObjectPrivate::get(d->threadData->thread))->mutex);????if?(d->threadData->quitNow)????????return?-1;?????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);????locker.unlock();?????//?remove?posted?quit?events?when?entering?a?new?event?loop????QCoreApplication?*app?=?QCoreApplication::instance();????if?(app?&&?app->thread()?==?thread())????????QCoreApplication::removePostedEvents(app,?QEvent::Quit);????//這里的實現代碼不少,最為重要的是以下幾行?#if?defined(QT_NO_EXCEPTIONS)????while?(!d->exit)????????processEvents(flags?|?WaitForMoreEvents?|?EventLoopExec);#else????try?{????????while?(!d->exit)??//只要沒有遇見exit,循環派發事件?????????????processEvents(flags?|?WaitForMoreEvents?|?EventLoopExec);????}?catch?(...)?{????????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");?????????//?copied?from?below????????locker.relock();????????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;?????????throw;????}#endif?????//?copied?above????locker.relock();????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;}5、事件處理QEventDispatcherWin32::processEvents
bool?QEventLoop::processEvents(ProcessEventsFlags?flags){????Q_D(QEventLoop);????if?(!d->threadData->eventDispatcher)????????return?false;????if?(flags?&?DeferredDeletion)????????QCoreApplication::sendPostedEvents(0,?QEvent::DeferredDelete);//將事件派發給與平臺相關的QAbstractEventDispatcher子類?????return?d->threadData->eventDispatcher->processEvents(flags);??}6、將獲取的事件打包為消息,傳遞給操作系統
AbstractEventDispatcher的子類QEventDispatcherWin32獲得用戶的輸入事件,并將其打包成message后,通過標準的Windows API傳遞給Windows OS
bool?QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags?flags){????Q_D(QEventDispatcherWin32);?????if?(!d->internalHwnd)????????createInternalHwnd();?????d->interrupt?=?false;????emit?awake();?????bool?canWait;????bool?retVal?=?false;????bool?seenWM_QT_SENDPOSTEDEVENTS?=?false;????bool?needWM_QT_SENDPOSTEDEVENTS?=?false;????do?{????????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????????????????haveMessage?=?true;????????????????msg?=?d->queuedSocketEvents.takeFirst();??//?從處理socket隊列中取出一條事件,處理隊列里面的socket事件????????????}?else?{????????????????haveMessage?=?PeekMessage(&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????????????????????????||?msg.message?==?WM_MOUSEHWHEEL????????????????????????||?msg.message?==?WM_TOUCH#ifndef?QT_NO_GESTURES????????????????????????||?msg.message?==?WM_GESTURE????????????????????????||?msg.message?==?WM_GESTURENOTIFY#endif????????????????????????||?msg.message?==?WM_CLOSE))?{????????????????????//?queue?user?input?events?for?later?processing????????????????????haveMessage?=?false;????????????????????d->queuedUserInputEvents.append(msg);??//?用戶輸入事件入隊列,待以后處理?????????????????}????????????????if?(haveMessage?&&?(flags?&?QEventLoop::ExcludeSocketNotifiers)????????????????????&&?(msg.message?==?WM_QT_SOCKETNOTIFIER?&&?msg.hwnd?==?d->internalHwnd))?{????????????????????//?queue?socket?events?for?later?processing????????????????????haveMessage?=?false;????????????????????d->queuedSocketEvents.append(msg);?????//?socket?事件入隊列,待以后處理???????????????????}????????????}????????????if?(!haveMessage)?{????????????????//?no?message?-?check?for?signalled?objects????????????????for?(int?i=0;?i<(int)nCount;?i++)????????????????????pHandles[i]?=?d->winEventNotifierList.at(i)->handle();????????????????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)?{#ifdef?Q_OS_WINCE????????????????//?WinCE?doesn't?support?hooks?at?all,?so?we?have?to?call?this?by?hand?:(????????????????(void)?qt_GetMessageHook(0,?PM_REMOVE,?(LPARAM)?&msg);#endif?????????????????if?(d->internalHwnd?==?msg.hwnd?&&?msg.message?==?WM_QT_SENDPOSTEDEVENTS)?{????????????????????if?(seenWM_QT_SENDPOSTEDEVENTS)?{????????????????????????//?when?calling?processEvents()?"manually",?we?only?want?to?send?posted????????????????????????//?events?once????????????????????????needWM_QT_SENDPOSTEDEVENTS?=?true;????????????????????????continue;????????????????????}????????????????????seenWM_QT_SENDPOSTEDEVENTS?=?true;????????????????}?else?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);???//將事件打包成message調用Windows?API派發出去????????????????????DispatchMessage(&msg);????//分發一個消息給窗口程序。消息被分發到回調函數,將消息傳遞給windows系統,windows處理完畢,會調用回調函數?=>?section?7?????????????????}????????????}?else?if?(waitRet?<?WAIT_OBJECT_0?+?nCount)?{????????????????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????????canWait?=?(!retVal???????????????????&&?!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?|?MWMO_INPUTAVAILABLE);????????????emit?awake();????????????if?(waitRet?<?WAIT_OBJECT_0?+?nCount)?{????????????????d->activateEventNotifier(d->winEventNotifierList.at(waitRet?-?WAIT_OBJECT_0));????????????????retVal?=?true;????????????}????????}????}?while?(canWait);?????if?(!seenWM_QT_SENDPOSTEDEVENTS?&&?(flags?&?QEventLoop::EventLoopExec)?==?0)?{????????//?when?called?"manually",?always?send?posted?events????????QCoreApplicationPrivate::sendPostedEvents(0,?0,?d->threadData);????}?????if?(needWM_QT_SENDPOSTEDEVENTS)????????PostMessage(d->internalHwnd,?WM_QT_SENDPOSTEDEVENTS,?0,?0);?????return?retVal;}7、操作系統將事件發回給QT平臺
extern?"C"?LRESULT?QT_WIN_CALLBACK?QtWndProc(HWND?hwnd,?UINT?message,?WPARAM?wParam,?LPARAM?lParam)?????{????????...????????//將消息重新封裝成QEvent的子類QMouseEvent?==>?Section?8?????????result?=?widget->translateMouseEvent(msg);?????????...?????}8、將操作系統打包的事件解包、翻譯為QApplication可識別的事件
bool?QETWidget::translateMouseEvent(const?MSG?&msg)?????{?????......?????res?=?QApplicationPrivate::sendMouseEvent(target,?&e,?alienWidget,?this,?&qt_button_down,??qt_last_mouse_receiver);}9、根據事件類型發送事件
bool?QApplicationPrivate::sendMouseEvent(QWidget?*receiver,?QMouseEvent?*event,??QWidget?*alienWidget,?QWidget?*nativeWidget,QWidget?**buttonDown,??QPointer<QWidget>?&lastMouseReceiver,bool?spontaneous){????...????if?(spontaneous)????????result?=?QApplication::sendSpontaneousEvent(receiver,?event);????else????????result?=?QApplication::sendEvent(receiver,?event);??????????...??????????return?result;}10、發送事件
inline?bool?QCoreApplication::sendSpontaneousEvent(QObject?*receiver,?QEvent?*event){???????//將event標記為自發事件?????//進一步調用?2-5?QCoreApplication::notifyInternal???????????if?(event)???????????event->spont?=?true;???????return?self???self->notifyInternal(receiver,?event)?:?false;?}11、線程內事件的處理
bool?QCoreApplication::notifyInternal(QObject?*receiver,?QEvent?*event){????????...????//?以下代碼主要意圖為Qt強制事件只能夠發送給當前線程里的對象,也就是說receiver->d_func()->threadData應該等于QThreadData::current()。????//注意,跨線程的事件需要借助Event?Loop來派發????QObjectPrivate?*d?=?receiver->d_func();????QThreadData?*threadData?=?d->threadData;????++threadData->loopLevel;????QT_TRY?{????????returnValue?=?notify(receiver,?event);????}?QT_CATCH?(...)?{????????--threadData->loopLevel;????????QT_RETHROW;????}????...????return?returnValue;}12、事件的派發
任何線程的任何對象的所有事件在發送時都會調用notify函數。
bool?QCoreApplication::notify(QObject?*receiver,?QEvent?*event){????Q_D(QCoreApplication);????//?no?events?are?delivered?after?~QCoreApplication()?has?started????if?(QCoreApplicationPrivate::is_app_closing)????????return?true;????if?(receiver?==?0)?{????????????????????????//?serious?error????????qWarning("QCoreApplication::notify:?Unexpected?null?receiver");????????return?true;????}#ifndef?QT_NO_DEBUG????d->checkReceiverThread(receiver);#endif????return?receiver->isWidgetType()???false?:?d->notify_helper(receiver,?event);}13、使用事件過濾器對發送的事件進行處理
bool?QCoreApplicationPrivate::notify_helper(QObject?*receiver,?QEvent?*?event){????//?send?to?all?application?event?filters????if?(sendThroughApplicationEventFilters(receiver,?event))????????return?true;?????//?向事件過濾器發送該事件,這里介紹一下Event?Filters.?事件過濾器是一個接受即將發送給目標對象所有事件的對象。?????//如代碼所示它開始處理事件在目標對象行動之前。過濾器的QObject::eventFilter()實現被調用,能接受或者丟棄過濾????//允許或者拒絕事件的更進一步的處理。如果所有的事件過濾器允許更進一步的事件處理,事件將被發送到目標對象本身。????//如果他們中的一個停止處理,目標和任何后來的事件過濾器不能看到任何事件。????if?(sendThroughObjectEventFilters(receiver,?event))????????return?true;????return?receiver->event(event);}14、事件派發至QObject的子類-QWidget
bool?QWidget::event(QEvent?*event){????...????switch?(event->type())?{????case?QEvent::MouseMove:????????mouseMoveEvent((QMouseEvent*)event);????????break;????case?QEvent::MouseButtonPress:????????五、自定義事件與事件處理
1、手動發送事件的流程
A、構造事件對象
QEvent?*event?=?new?QEvent(QEvent::Close);
B、發送事件給指定對象
QApplication::sendEvent(this,?event);
2、定制某個組件的事件處理
????A、確定要對組件的哪些事件進行處理,?如close、key、keyboard 事件
????B、重寫對象的?event() 函數
QEvent中的主要成員函數
void ignore();接收者忽略當前事件,事件可能傳遞給父組件
void accept();接收者期望處理當前事件
bool isAccepted();判斷當前事件是否被處理
3、事件過濾流程
?A、確定需要過濾處理哪些對象的哪些事件?
????B、構造自己的事件過濾類: 重寫類的eventFilter函數
?C、在主程序中實例化一個過濾類對象
????D、調用過濾類對象的installEventFilter(receiver, QEvent *event)函數,?
在目標對象上安裝過濾器。
事件過濾器可以對其他組件接收到的事件進行監控,任意的QObject對象都可以作為事件過濾器使用,事件過濾器對象需要重寫eventFilter函數。
組件通過installEventFilter函數安裝事件過濾器,事件過濾器在組件之前接收到事件,能夠決定是否將事件轉發到組件對象。
2、事件的發送
????sendEvent()立即同步處理要發送的event。當sendEvent()返回的時候, 表示相關的事件過濾器或目標對象處理完了event。對于多數的event類, 有一個成員函數 isAccepted() 可以用來判別event事件是已被接受處理或被拒絕處理。
????postEvent()將event提交到一個事件隊列中等待調度。在下一次?Qt 的主 event loop 運行的時候,主 event loop 就會調度所有提交到隊列中的 event, 以某種優化的方式. 例如, 如果有幾個 resize event, 他們就會被壓縮成一個事件. 同樣適用于 paint events: QWidget::update() 調用postEvent(), 以避免多次重畫來避免閃爍以及提高速度.
????postEvent()也被用于對象的初始化過程, 因為提交過的 event 通常在相應對象初始化完畢后極短的 時間內就會被調度. 在實現一個控件的時候, 在自定義控件的 constructor 中盡早支持事件機制是非常重要的, 在可能接受到任何事件之前,確保盡早初始化成員變量。
事件和信號不同,事件由具體對象進行處理,信號由具體對象產生,改寫事件處理函數可能導致程序行為發生改變。
六、自定義QDropEvent拖放事件實例
1、QDropEvent拖放事件
拖放一個文件進入窗口時將觸發拖放事件,每一個QWidget對象都可以處理拖放事件。
拖放事件的處理函數
void dragEnterEvent(QDragEnterEvent *e)
void dragEvent(QDropEvent *)
拖放事件中的QMimeData,QMimeData是QT中的多媒體數據類,拖放事件通過QMimeData對象傳遞數據,QMimeData支持多種不同類型的多媒體數據。
常用QMimeData數據處理函數
2、自定義拖放事件
自定義拖放事件的流程:
A、對接收拖放事件的對象調用setAcceptDrops成員函數
B、重寫dragEnterEvent函數并判斷MIME類型
期望類型數據:e->acceptProposedAction();
其他數據類型:e->ignore();
C、重寫dragEvent函數并判斷MIME類型
期望數據類型:從事件對象中獲取MIME數據并處理
其他數據類型:e->ignore();
//在窗口類構造函數中設置拖放事件生效setAcceptDrops(true);//重寫窗口類的dropEvent函數void?MainWindow::dropEvent(QDropEvent*?e){????if(?e->mimeData()->hasUrls()?)????{????????QList<QUrl>?list?=?e->mimeData()->urls();????????QString?path?=?list[0].toLocalFile();????????QFileInfo?fi(path);????????if(?fi.isFile()?)????????{????????????saveOldFile();????????????if(?!m_isTextChanged?)????????????{????????????????showFile(path);????????????}????????}????????else????????{????????????showErrorMessage("Cannot?open?a?folder!");????????}????}????else????{????????e->ignore();????}}//重寫窗口類的dragEnterEvent函數void?MainWindow::dragEnterEvent(QDragEnterEvent*?e){????if(?e->mimeData()->hasUrls()?)?????{?????????e->acceptProposedAction();?????}?????else?????{?????????e->ignore();?????}}?
轉載于:https://www.cnblogs.com/h2zZhou/p/9615212.html
總結
以上是生活随笔為你收集整理的QT开发(十二)——QT事件处理机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 基本的操作
- 下一篇: JSON.parse(text[, re