Qt Event

QT Embedded二三事之QEventLoop

    事件是GUI應用的核心概念,GUI應用就是通過執行一個個事件來完成其功能的。圍繞事件而設計的事件循環,事件分發,事件截取(hook),這些都是GUI Framework的核心部分和運行的基礎,是把其它所有對象聯接起的紐帶。在QT中,事件循環和事件機制被封裝在QEventLoop中。本文就簡略介紹一下,QT中的事件運行機制。
    1.事件分類
    QT中的事件主要有如下幾種:
    (1)QWSEvent和QWSCommand
    這是QWSClient和QWSServer通訊所使用的事件,QWS Client向QWS Server發送QWSCommand請求,QWS Server向QWS Client返回QWSEvent響應。
    QWSEvent和QWSCommand是client與server通訊所使用的socket事件轉換來的。
    QWSEvent事件保存在QApplication::Data的QPtrList queue中。
    QWSCommand保存在QWSServer的QPtrList commandQueue中;
    (2)QEvent
    QEvent是在QWS Client內部各個QObject使用的事件。封裝了各種窗口消息:QShowEvent,QFocusEvent,QMoveEvent等。
    QEvent主要使用sendEvent和postEvent發送出去的,sendEvent是同步消息,相當同步調用,不會經過事件循環,不保存,直接調用QObject::event()。postEvent是異步調用,先把事件放在globalPostedEvents隊列中,在QEventLoop::processEvents中,會循環檢查globalPostedEvents。
    (3)QSocket事件
    QSocket事件是類型爲QEvent::SockAct的QEvent,但是其處理方法和一般的QEvent不一樣。
    QSocket事件可以用來處理進程通訊,QWSEvent/QWSCommand事件就是通過QSocket傳調的,QSocket由QEventLoop管理。
    (4)QTimer事件
    和QSocket事件一樣QTimer就是類型爲QEvent::Timer的QEvent,但是其處理方法和一般的QEvent不一樣。
    QTimer事件放在全局對象:static TimerList *timerList。
    2.QSocket和QEventLoop
    QSocket和一個socket fd綁定在一起,每個socket fd都可以三種動作:read, write,exception。每一個動作由一個QSocketNotifier來封裝。
    QSocketNotifies通過QEventLoop::registerSocketNotifier註冊到QEventLoop中,通過QEventLoop::unregisterSocketNotifier取消註冊。
    QEventLoop使用QSockNot封裝一個QSocketNotifier和相應的操作類型,裝同屬於一類型操作的QSockNot封裝到QSockNotType中,建立起同一類操作的select_fds隊列,直接用於select使用。
    QEventLoop根據socket fd的三種操作(read/write/exceptions)共保存三類QSockNotType。
    3.QEventLoop::processEvents
    QEventLoop::processEvents是一次事件循環調用,分別查看各類事件列隊是否由事件。QApplication::exec/QEventLoop::exec/QEventLoop::enterLoop就是循環調用QEventLoop::processEvents。
    下面針對QEventLoop::processEvents的實現,說明事件的分發過程。
    bool QEventLoop::processEvents( ProcessEventsFlags flags )
 {
    // process events from the QWS server
    int           nevents = 0;
 ......
    // handle gui and posted events
    if (qt_is_gui_used ) {
 //a.首先執行globalPostedEvents隊列中的事件,這些事件都是通過QApplication::postEvent或QThread::postEvent發送的
 QApplication::sendPostedEvents();
 //b.檢查QWSEvent隊列:QApplication::Data的QPtrList queue隊列
 while ( qt_fbdpy->eventPending() ) {        // also flushes output buffer
     if ( d->shortcut ) {
         return FALSE;
     }
     QWSEvent *event = qt_fbdpy->getEvent();        // get next event
     nevents++;
     bool ret = qApp->qwsProcessEvent( event ) == 1;
     delete event;
     if ( ret ) {
         return TRUE;
     }
 }
    }
    if ( d->shortcut ) {
 return FALSE;
    }
 //c.如果是QWSServer,需要檢查QWSServer的QPtrList commandQueue隊列。
    extern QPtrQueue *qt_get_server_queue();
    if ( !qt_get_server_queue()->isEmpty() ) {
 QWSServer::processEventQueue();
    }
 //d.如果上面的事件執行過程中產生postedEvent,再次執行,主是要一些moveEvent,resizeEvent,paintEvent
    QApplication::sendPostedEvents();
    // don't block if exitLoop() or exit()/quit() has been called.
    bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
    // Process timers and socket notifiers - the common UNIX stuff
 //e.檢查timer事件隊列中,確發時間最小的一個,確定爲select的等待時間。
    // return the maximum time we can wait for an event.
    static timeval zerotm;
    timeval *tm = qt_wait_timer();                // wait for timer or event
    if ( !canWait ) {
 if ( !tm )
     tm = &zerotm;
 tm->tv_sec  = 0;                        // no time to wait
 tm->tv_usec = 0;
    }
 //f.準備select調用所需要的fd隊列。
    int highest = 0;
    if ( ! ( flags & ExcludeSocketNotifiers ) ) {
 // return the highest fd we can wait for input on
 if ( d->sn_highest >= 0 ) {                     // has socket notifier(s)
     if ( d->sn_vec[0].list && ! d->sn_vec[0].list->isEmpty() )
         d->sn_vec[0].select_fds = d->sn_vec[0].enabled_fds;
     else
         FD_ZERO( &d->sn_vec[0].select_fds );
     if ( d->sn_vec[1].list && ! d->sn_vec[1].list->isEmpty() )
         d->sn_vec[1].select_fds = d->sn_vec[1].enabled_fds;
     else
         FD_ZERO( &d->sn_vec[1].select_fds );
     if ( d->sn_vec[2].list && ! d->sn_vec[2].list->isEmpty() )
         d->sn_vec[2].select_fds = d->sn_vec[2].enabled_fds;
     else
         FD_ZERO( &d->sn_vec[2].select_fds );
 }
 //g.檢查有沒有註冊的preselect_handler
    if ( qt_preselect_handler ) {
 QVFuncList::Iterator it, end = qt_preselect_handler->end();
 for ( it = qt_preselect_handler->begin(); it != end; ++it )
     (**it)();
    }
 //h,執行select調用。
 ...................
 nsel = select( highest + 1,
                &d->sn_vec[0].select_fds,
                &d->sn_vec[1].select_fds,
                &d->sn_vec[2].select_fds,
                tm );
 ...............
 //i.看看是否有thread發來的喚醒消息
    // some other thread woke us up... consume the data on the thread pipe so that
    // select doesn't immediately return next time
    if ( nsel > 0 && FD_ISSET( d->thread_pipe[0], &d->sn_vec[0].select_fds ) ) {
 char c;
 ::read( d->thread_pipe[0], &c, 1 );
    }
 //j.檢查是否有註冊的postselect_hander事件
    if ( qt_postselect_handler ) {
 QVFuncList::Iterator it, end = qt_postselect_handler->end();
 for ( it = qt_postselect_handler->begin(); it != end; ++it )
     (**it)();
    }
 //k.檢查所有能進行操作的socket fd,並執行相應的QSocketNotifier::activated()
    // activate socket notifiers
 .....................
 nevents += activateSocketNotifiers();
 //l.檢查timerList是否有timeout的timer並執行.
    // activate timers
    nevents += activateTimers();
    // return true if we handled events, false otherwise
    return (nevents > 0);
 }
 4.事件攔截
 除了事件隊列和事件循環和事件分發,事件攔截也是一個重要的功能。一些對象需要知道其它對象是否同某類事件發生,並且可以攔截某個事件,這就需要加filter,在win32中被稱做是hook。QT Embeded中支持的filter有限,只能攔截同一個進程的QWSEvent和QEvent消息。
 (1)QWSEvent的攔截
 QWSEvent的攔截比較麻煩,只通過重載QApplication::qwsEventFilter來實現,QApplication::qwsProcessEvent在處理QWSEvent時,首先會調用QApplication::qwsEventFilter來確定是否需要處理這個QWSEvent,因此QApplication::qwsEventFilter是QWSEvent攔截的最好機會。
 (2)QEvent的攔截
 這個相當簡單,是QT原生支持的,通過QObject::installEventFilter安裝一個filter。
 QEvent事件的執行是由QObject::event來分發的。QObject::event執行時會檢查是否有其實被安裝過filter,如果有就會執行相應object的eventFilter函數,因此只需要重寫這個eventFilter函數,就可以實現filter功能。
               

[b]本文來自ChinaUnix博客,如果查看原文請點:[/b][url]http://blog.chinaunix.net/u3/92787/showart_1946832.html[/url]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章