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]
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值