[转]浅议Qt的事件处理机制一

[转]浅议Qt的事件处理机制一

浅议Qt的事件处理机制 一 .

    深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集。 大家熟知Signals
/ Slots在多线程的实现也依赖于Qt的事件处理机制。

    在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.  接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件:

     
1 .  谁来产生事件: 最容易想到的是我们的输入设备,比如键盘、鼠标产生的

keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent),这些事件来自于底层的操作系统,它们以异步的形式通知Qt事件处理系统,后文会仔细道来。当然Qt自己也会产生很多事件,比如QObject::startTimer()会触发QTimerEvent. 用户的程序可还以自己定制事件。

     
2 .  谁来接受和处理事件:答案是QObject。在Qt的内省机制剖析一文已经介绍QObject 类是整个Qt对象模型的心脏,事件处理机制是QObject三大职责(内存管理、内省(intropection)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject:: event ()函数或事件的处理权转给父类。

     
3 .   谁来负责分发事件:对于non - GUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类 - Receiver. 对于Qt GUI程序,由QApplication来负责。

      接下来,将通过对代码的解析来看看QT是利用event loop从事件队列中获取用户输入事件,又是如何将事件转义成QEvents,并分发给相应的QObject处理。

#include 
< QApplication >    
#include 
" widget.h "    
// Section 1   
int  main( int  argc,  char   * argv[])  
{  
    QApplication app(argc, argv);  
    Widget window;  
//  Widget 继承自QWidget   
    window.show();  
    
return  app.exec();  //  进入Qpplication事件循环,见section 2   
}  
//  Section 2:    
int  QApplication::exec()  
{  
   
// skip codes   
   
// 简单的交给QCoreApplication来处理事件循环=〉section 3   
    return  QCoreApplication::exec();  
}  
//  Section 3   
int  QCoreApplication::exec()  
{  
    
// 得到当前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 ;  
    }  
      
    QEventLoop eventLoop;  
    self
-> d_func() -> in_exec  =   true ;  
    self
-> d_func() -> aboutToQuitEmitted  =   false ;  
    
// 委任QEventLoop 处理事件队列循环 ==> Section 4   
     int  returnCode  =  eventLoop.exec();  
    .  
    }  
    
return  returnCode;  
}  
//  Section 4   
int  QEventLoop::exec(ProcessEventsFlags flags)  
{  
   
// 这里的实现代码不少,最为重要的是以下几行   
   Q_D(QEventLoop);  //  访问QEventloop私有类实例d   
         try  {  
        
// 只要没有遇见exit,循环派发事件   
         while  ( ! d -> exit)  
            processEvents(flags 
|  WaitForMoreEvents  |  EventLoopExec);  
    } 
catch  () {}  
}  
//  Section 5   
bool  QEventLoop::processEvents(ProcessEventsFlags flags)  
{  
    Q_D(QEventLoop);  
    
if  ( ! d -> threadData -> eventDispatcher)  
        
return   false ;  
    
if  (flags  &  DeferredDeletion)  
        QCoreApplication::sendPostedEvents(
0 , QEvent::DeferredDelete);  
    
// 将事件派发给与平台相关的QAbstractEventDispatcher子类 =>Section 6   
     return  d -> threadData -> eventDispatcher -> processEvents(flags);  
}  
#include 
< QApplication >
#include 
" widget.h "
// Section 1
int  main( int  argc,  char   * argv[])
{
    QApplication app(argc, argv);
    Widget window;  
//  Widget 继承自QWidget
    window.show();
    
return  app.exec();  //  进入Qpplication事件循环,见section 2
}
//  Section 2: 
int  QApplication::exec()
{
   
// skip codes
   
// 简单的交给QCoreApplication来处理事件循环=〉section 3
    return  QCoreApplication::exec();
}
//  Section 3
int  QCoreApplication::exec()
{
    
// 得到当前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 ;
    }
    
    QEventLoop eventLoop;
    self
-> d_func() -> in_exec  =   true ;
    self
-> d_func() -> aboutToQuitEmitted  =   false ;
    
// 委任QEventLoop 处理事件队列循环 ==> Section 4
     int  returnCode  =  eventLoop.exec();
    .
    }
    
return  returnCode;
}
//  Section 4
int  QEventLoop::exec(ProcessEventsFlags flags)
{
   
// 这里的实现代码不少,最为重要的是以下几行
   Q_D(QEventLoop);  //  访问QEventloop私有类实例d
         try  {
        
// 只要没有遇见exit,循环派发事件
         while  ( ! d -> exit)
            processEvents(flags 
|  WaitForMoreEvents  |  EventLoopExec);
    } 
catch  () {}
}
//  Section 5
bool  QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    
if  ( ! d -> threadData -> eventDispatcher)
        
return   false ;
    
if  (flags  &  DeferredDeletion)
        QCoreApplication::sendPostedEvents(
0 , QEvent::DeferredDelete);
    
// 将事件派发给与平台相关的QAbstractEventDispatcher子类 =>Section 6
     return  d -> threadData -> eventDispatcher -> processEvents(flags);
}
 
//  Section 6,QTDIR/src/corelib/kernel/qeventdispatcher_win.cpp   
//  这段代码是完成与windows平台相关的windows c++。 以跨平台著称的Qt同时也提供了对Symiban,Unix等平台的消息派发支持   
//  其事现分别封装在QEventDispatcherSymbian和QEventDispatcherUNIX   
//  QEventDispatcherWin32派生自QAbstractEventDispatcher.   
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()) {  
                
//  从处理socket队列中取出一条事件   
                haveMessage  =   true ;  
                msg 
=  d -> queuedSocketEvents.takeFirst();  
            } 
else  {
                
// 用非阻塞的PeekMessage()从消息队列中取消息,然后删除消息
                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  
                        
//
                         ||  msg.message  ==  WM_CLOSE)) {  
                    
//  用户输入事件入队列,待以后处理   
                    haveMessage  =   false ;  
                    d
-> queuedUserInputEvents.append(msg);  
                }  
                
if  (haveMessage  &&  (flags  &  QEventLoop::ExcludeSocketNotifiers)  
                    
&&  (msg.message  ==  WM_QT_SOCKETNOTIFIER  &&  msg.hwnd  ==  d -> internalHwnd)) {  
                    
//  socket 事件入队列,待以后处理   
                    haveMessage  =   false ;  
                    d
-> queuedSocketEvents.append(msg);  
                }  
            }
            
            
if  ( ! haveMessage) {
                
//  no message - check for signalled objects
                 for  ( int  i = 0 ; i < ( int )nCount; i ++ )
                    pHandles[i] 
=  d -> winEventNotifierList.at(i) -> handle();
                
                
// MsgWaitForMultipleObjectsEx 阻塞时仍可以响应消息
                
// 但它会在“对象被激发”或“消息到达队列”时被唤醒而返回。MsgWaitForMultipleObjects()多接收一个参数,允许指定哪些消息是观察对象。
                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) {
                
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派发出去   
                       
// 分发一个消息给窗口程序。消息被分发到回调函数,将消息传递给windows系统,windows处理完毕,会调用回调函数 => section 7                       
                  DispatchMessage( & msg);  
                }  
            } 
else   if  (waitRet  >=  WAIT_OBJECT_0  &&  waitRet  <  WAIT_OBJECT_0  +  nCount) {
                d
-> activateEventNotifier(d -> winEventNotifierList.at(waitRet  -  WAIT_OBJECT_0));
            } 
else  {
                
//  nothing todo so break
                 break ;
            }
            retVal 
=   true ;
        }
            }               
        }  
    } 
while  (canWait);  
        
    
return  retVal;  


//  Section 7 windows窗口回调函数 定义在QTDIR/src/gui/kernel/qapplication_win.cpp   
extern   " C "  LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
{  
     
   
// 将消息重新封装成QEvent的子类QMouseEvent ==> Section 8   
    result  =  widget -> translateMouseEvent(msg);      
     
}  
   
//  Section 7 windows窗口回调函数 定义在QTDIR/src/gui/kernel/qapplication_win.cpp
extern   " C "  LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   
   
// 将消息重新封装成QEvent的子类QMouseEvent ==> Section 8
    result  =  widget -> translateMouseEvent(msg);    
   
}
 

从Section 
1 ~ Section7, Qt进入QApplication的event loop,经过层层委任,最终QEventloop的processEvent将通过与平台相关的QAbstractEventDispatcher的子类QEventDispatcherWin32获得用户的用户输入事件,并将其打包成message后,通过标准Windows API ,把消息传递给了Windows OS,Windows OS得到通知后回调QtWndProc,  至此事件的分发与处理完成了一半的路程。

在下文中,我们将进一步讨论当我们收到来在Windows的回调后,事件又是怎么一步步打包成QEvent并通过QApplication分发给最终事件的接受和处理者QObject::
event .

下文的链接:
http:
// blog.csdn.net/changsheng230/archive/2010/12/22/6092978.aspx

你可能感兴趣的:([转]浅议Qt的事件处理机制一)