Qt事件机制研究

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/yuan1164345228/article/details/87864635
  • 事件监听

我们知道Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。下面我们来分析一下Qt是如何进行事件监听的。

首先当然是分析QApplication的源码,下面是QApplication::exec()的实现:

int QApplication::exec()
{
    return eventLoop()->exec();// 直接交给QEventLoop::exec()
}

可见其直接调用了QEventLoopexec()函数,再来看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从操作系统的消息队列中取就可以了。winPeekMessageDispatchMessageWindows系统编程的内容,不在本文讨论范围内,不再深入研究。

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

猜你喜欢

转载自blog.csdn.net/yuan1164345228/article/details/87864635