-
事件监听
我们知道Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。下面我们来分析一下Qt是如何进行事件监听的。
首先当然是分析QApplication的源码,下面是QApplication::exec()的实现:
int QApplication::exec()
{
return eventLoop()->exec();// 直接交给QEventLoop::exec()
}
可见其直接调用了QEventLoop的exec()函数,再来看QEventLoop::exec(),QEventLoop::exec()中调用了QEventLoop::enterLoop()方法,而进入enterLoop()之后才是真正进入了事件监听循环中。
int QEventLoop::exec()
{
d->reset();
enterLoop();//进入事件监听循环
// cleanup
d->looplevel = 0;
d->quitnow = FALSE;
d->exitloop = FALSE;
d->shortcut = FALSE;
// don't reset quitcode!
return d->quitcode;
}
-
Qt如何将用户的键盘操作、鼠标点击转换为Qt中的事件?
再来分析QEventLoop::enterLoop方法,其中使用while循环,不断调用processEvents处理事件。
int QEventLoop::enterLoop()
{
// save the current exitloop state
bool old_exitloop = d->exitloop;
d->exitloop = FALSE;
d->shortcut = FALSE;
d->looplevel++;
while ( ! d->exitloop )//循环获取事件
processEvents( AllEvents | WaitForMore );
d->looplevel--;
// restore the exitloop state, but if quitnow is TRUE, we need to keep
// exitloop set so that all other event loops drop out.
d->exitloop = old_exitloop || d->quitnow;
d->shortcut = d->quitnow;
if ( d->looplevel < 1 ) {
d->quitnow = FALSE;
d->exitloop = FALSE;
d->shortcut = FALSE;
emit qApp->aboutToQuit();
// send deferred deletes
QApplication::sendPostedEvents( 0, QEvent::DeferredDelete );
}
return d->looplevel;
}
下面看QEventLoop::processEvents()是如何进行处理的,这里我们主要分析windows下是如何处理的。下面是QEventLoop::processEvents()的源码,可见其调用winPeekMessage从操作系统的消息队列中获取消息,调用DispatchMessage将获取到的消息分发给我们的Qt窗口进行处理。可见Qt从操作系统获取到的就已经是封装过的消息,用户操作到消息的转换时由操作系统完成的,Qt只需要使用winPeekMessage从操作系统的消息队列中取就可以了。winPeekMessage和DispatchMessage是Windows系统编程的内容,不在本文讨论范围内,不再深入研究。
bool QEventLoop::processEvents( ProcessEventsFlags flags )
{
MSG msg;
#if defined(QT_THREAD_SUPPORT)
QMutexLocker locker( QApplication::qt_mutex );
#endif
emit awake();
emit qApp->guiThreadAwake();
QApplication::sendPostedEvents();
bool canWait = d->exitloop || d->quitnow ? FALSE : (flags & WaitForMore);
if ( canWait ) { // can wait if necessary
if ( numZeroTimers ) { // activate full-speed timers
int ok = FALSE;
while ( numZeroTimers &&
!(ok=winPeekMessage(&msg,0,0,0,PM_REMOVE)) ) {// 从windows消息队列中读一条消息
activateZeroTimers();
}
if ( !ok ) { // no event
return FALSE;
}
} else {
if (!winPeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))// 从windows消息队列中读一条消息
emit aboutToBlock();
#ifdef QT_THREAD_SUPPORT
locker.mutex()->unlock();
#endif // QT_THREAD_SUPPORT
if ( !winGetMessage(&msg,0,0,0) ) {
#ifdef QT_THREAD_SUPPORT
locker.mutex()->lock();
#endif // QT_THREAD_SUPPORT
exit( 0 ); // WM_QUIT received
return FALSE;
}
#ifdef QT_THREAD_SUPPORT
locker.mutex()->lock();
#endif // QT_THREAD_SUPPORT
}
} else { // no-wait mode
if ( !winPeekMessage(&msg,0,0,0,PM_REMOVE) ) { // no pending events
if ( numZeroTimers > 0 ) { // there are 0-timers
activateZeroTimers();
}
return FALSE;
}
}
bool handled = FALSE;
if ( msg.message == WM_TIMER ) { // timer message received
if ( dispatchTimer( msg.wParam, &msg ) )//定时器事件处理,处理完后直接返回
return TRUE;
} else if ( msg.message && (!msg.hwnd || !QWidget::find(msg.hwnd)) ) {
long res = 0;
handled = qt_winEventFilter( &msg, res );
}
if ( !handled ) {// 其余事件处理
bool ignore = false;
if (flags & ExcludeUserInput) {
ignore = (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
|| (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
|| msg.message == WM_MOUSEWHEEL;
}
if (!ignore) {
QInputContext::TranslateMessage( &msg ); // translate to WM_CHAR
QT_WA( {
DispatchMessage( &msg ); // 将事件发给QtWndProc
} , {
DispatchMessageA( &msg ); // 将事件发给 QtWndProc
} );
} else if (msg.message == WM_LBUTTONUP
|| msg.message == WM_MBUTTONUP
|| msg.message == WM_RBUTTONUP
|| msg.message == WM_XBUTTONUP) {
qt_releaseAutoCapture();
}
}
if ( !(flags & ExcludeSocketNotifiers ) )
activateSocketNotifiers();
if ( configRequests ) // any pending configs?
qWinProcessConfigRequests();
QApplication::sendPostedEvents();
return TRUE;
}
-
Qt对操作系统消息的处理流程
上面说到,Qt调用DispatchMessage将从操作系统获取到的消息分发给我们的Qt窗口进行处理。
LRESULT CALLBACK QtWndProc( HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam )
{
......
#if defined(QT_TABLET_SUPPORT)
if ( !chokeMouse ) {
#endif
widget->translateMouseEvent( msg );
......
}
bool QETWidget::translateMouseEvent( const MSG &msg )
{
......
QApplication::sendSpontaneousEvent(popup, &e);
......
}
inline bool QApplication::sendEvent( QObject *receiver, QEvent *event )
{ if ( event ) event->spont = FALSE; return qApp ? qApp->notify( receiver, event ) : FALSE; }
inline bool QApplication::sendSpontaneousEvent( QObject *receiver, QEvent *event )
{ if ( event ) event->spont = TRUE; return qApp ? qApp->notify( receiver, event ) : FALSE; }
bool QApplication::notify( QObject *receiver, QEvent *e )
{
......
internalNotify( receiver, e );
......
}
bool QApplication::internalNotify( QObject *receiver, QEvent * e)
{
if ( eventFilters ) {
QObjectListIt it( *eventFilters );
register QObject *obj;
while ( (obj=it.current()) != 0 ) { // send to all filters
++it; // until one returns TRUE
if ( obj->eventFilter(receiver,e) )
return TRUE;
}
}
bool consumed = FALSE;
bool handled = FALSE;
if ( receiver->isWidgetType() ) {
QWidget *widget = (QWidget*)receiver;
// toggle HasMouse widget state on enter and leave
if ( e->type() == QEvent::Enter || e->type() == QEvent::DragEnter )
widget->setWState( WState_HasMouse );
else if ( e->type() == QEvent::Leave || e->type() == QEvent::DragLeave )
widget->clearWState( WState_HasMouse );
// throw away any mouse-tracking-only mouse events
if ( e->type() == QEvent::MouseMove &&
(((QMouseEvent*)e)->state()&QMouseEvent::MouseButtonMask) == 0 &&
!widget->hasMouseTracking() ) {
handled = TRUE;
consumed = TRUE;
} else if ( !widget->isEnabled() ) { // throw away mouse events to disabled widgets
switch(e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
( (QMouseEvent*) e)->ignore();
handled = TRUE;
consumed = TRUE;
break;
#ifndef QT_NO_DRAGANDDROP
case QEvent::DragEnter:
case QEvent::DragMove:
( (QDragMoveEvent*) e)->ignore();
handled = TRUE;
break;
case QEvent::DragLeave:
case QEvent::DragResponse:
handled = TRUE;
break;
case QEvent::Drop:
( (QDropEvent*) e)->ignore();
handled = TRUE;
break;
#endif
#ifndef QT_NO_WHEELEVENT
case QEvent::Wheel:
( (QWheelEvent*) e)->ignore();
handled = TRUE;
break;
#endif
case QEvent::ContextMenu:
( (QContextMenuEvent*) e)->ignore();
handled = TRUE;
break;
default:
break;
}
}
}
if (!handled)
consumed = receiver->event( e );//最终调用event()函数进而调用相应的事件处理器
e->spont = FALSE;
return consumed;
}
写完后发现网上也有一篇做类似分析的文章:http://mobile.51cto.com/symbian-272816.htm