Research Qt event mechanism

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/yuan1164345228/article/details/87864635
  • Event Listeners

We know that the program requires Qt () function creates a QApplication object in the main, and then call its exec () function. This function is the beginning of Qt event loop. After executing the exec () function, the program will enter the event loop to listen for the event of the application. Let's analyze how Qt event is listening.

First, of course is to analyze the QApplication source, the following is QApplication realization :: exec () is:

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

It shows a direct call QEventLoop the exec () function, look QEventLoop :: exec (), QEventLoop :: Exec calls () in QEventLoop :: enterLoop () method, enter enterLoop after () is really into the event monitor cycle.

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 how the user's keyboard, mouse clicks are converting to Qt events?

Let's analyze QEventLoop :: enterLoop methods, which use a while loop, constantly calling processEvents handle the event.

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;
}

Let's look at QEventLoop :: processEvents () is how to deal with, where we mainly analyze how the windows are processed. Here is QEventLoop :: processEvents () source code, showing its call winPeekMessage get messages from the operating system message queue, call DispatchMessage will get the message to be distributed to our Qt window for processing. Qt when seen from the operating system is already acquired through the encapsulated message, converts the message to a user operation is done by the operating system, only you need to use Qt winPeekMessage taken from the operating system can be in the message queue. winPeekMessage and DispatchMessage is the contents of the Windows system programming, not within the scope of this article, no in-depth study.

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 message processing flow of the operating system

Speaking of the above, Qt calls DispatchMessage from the operating system to get the message distributed to our Qt window for processing.

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;
}

After writing an article is also found online to do a similar analysis of the article: http: //mobile.51cto.com/symbian-272816.htm

Guess you like

Origin blog.csdn.net/yuan1164345228/article/details/87864635