天下

记录修行的印记

[转]浅议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, 000, 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

posted on 2013-07-04 11:59 天下 阅读(1768) 评论(0)  编辑 收藏 引用 所属分类: QT


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2013年7月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

导航

统计

常用链接

留言簿(4)

随笔分类(377)

随笔档案(327)

链接

最新随笔

搜索

最新评论