[原]Qt事件处理机制浅析
[原]Qt事件处理机制浅析
事件处理机制浅析是通过WM_ACTIVATE消息来分析的
// 调用堆栈
WinMainCRTStartup()
__tmainCRTStartup()
WinMain()
main( int argc = 1 , char ** argv)
QtGuid4.dll ! QApplication::exec()
QtCored4.dll ! QCoreApplication::exec()
QtCored4.dll ! QEventLoop::exec()
QtCored4.dll ! QEventLoop::processEvents()
QtGuid4.dll ! QGuiEventDispatcherWin32::processEvents()
QtCored4.dll ! QEventDispatcherWin32::processEvents()
user32.dll ! _PeekMessageW@ 20 () // 说明1,调用PeekMessage,非阻塞的取消息!
QtGuid4.dll ! QtWndProc(HWND__ * hwnd, unsigned int message = 6 , unsigned int wParam = 2 , long lParam = 0 ) // 即 WM_ACTIVATE 消息
QtGuid4.dll ! QApplication::winFocus(QWidget * widget, bool gotFocus = true )
QtGuid4.dll ! QApplication::setActiveWindow(QWidget * act)
QtCored4.dll ! QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event )
QtCored4.dll ! QCoreApplication::notifyInternal(QObject * receiver, QEvent * event )
QtGuid4.dll ! QApplication::notify(QObject * receiver, QEvent * e)
QtGuid4.dll ! QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e)
QtGuid4.dll ! QApplication:: event (QEvent * e)
QtCored4.dll ! QCoreApplication:: event (QEvent * e)
QtCored4.dll ! QObject:: event (QEvent * e)
说明2:
// QtWndProc() receives all messages from the main event loop
extern " C " LRESULT QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
// case 消息的处理
// 1538行->1670行
}
if ( ! widget)
widget = (QETWidget * )QWidget::find(hwnd);
if ( ! widget) // don't know this widget
goto do_default;
if (qt_is_translatable_mouse_event(message)) {
// message=512 第3个消息
// #define WM_MOUSEFIRST 0x0200
// #define WM_MOUSEMOVE 0x0200
if ( ! qt_tabletChokeMouse) {
result = widget -> translateMouseEvent(msg); // mouse event
}
}
else {
switch (message) {
// message = 136 第4个消息 #define WM_SYNCPAINT 0x0088
// message = 133,第5个消息 #define WM_NCPAINT 0x0085
// message = 28 ,第6个消息 #define WM_ACTIVATEAPP 0x001C
// message = 6 ,第7个消息 #define WM_ACTIVATE 0x0006 =>进入 qApp->winFocus(widget, true); // 说明3
// #define WM_NCHITTEST 0x0084 第1个消息,result = false, 进入do_default标记中的DefWindowProc处理
// #define WM_SETCURSOR 0x0020 第2个消息,result = false, 进入do_default标记中的DefWindowProc处理
case WM_NCHITTEST:
case WM_SETCURSOR:
}
if (result)
RETURN( false );
do_default:
RETURN(QWinInputContext::DefWindowProc(hwnd,message,wParam,lParam))
}
// 说明3
void QApplication::winFocus(QWidget * widget, bool gotFocus)
{
if (gotFocus) {
setActiveWindow(widget);
}
}
void QApplication::setActiveWindow(QWidget * act)
{
if ( ! previousActiveWindow) {
QEvent appActivate(QEvent::ApplicationActivate); // 关键是这里,appActivate 对象
sendSpontaneousEvent(qApp, & appActivate);
}
for ( int i = 0 ; i < toBeActivated.size(); ++ i) {
// 这里会继续调用
QWidget * w = toBeActivated.at(i);
sendSpontaneousEvent(w, & windowActivate);
sendSpontaneousEvent(w, & activationChange);
}
}
inline bool QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event )
{
if ( event ) event -> spont = true ; return self ? self -> notifyInternal(receiver, event ) : false ;
}
/* !
\internal
This function is here to make it possible for Qt extensions to
hook into event notification without subclassing QApplication
*/
bool QCoreApplication::notifyInternal(QObject * receiver, QEvent * event )
{
// Make it possible for Qt Jambi and QSA to hook into events even
// though QApplication is subclassed
bool result = false ;
void * cbdata[] = { receiver, event , & result };
if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
return result;
}
// Qt enforces the rule that events can only be sent to objects in
// the current thread, so receiver->d_func()->threadData is
// equivalent to QThreadData::current(), just without the function
// call overhead.
QObjectPrivate * d = receiver -> d_func();
QThreadData * threadData = d -> threadData;
++ threadData -> loopLevel;
bool returnValue;
QT_TRY {
returnValue = notify(receiver, event ); // 大名鼎鼎的notify(),处理后,调用notify_helper继续处理
} QT_CATCH () {
-- threadData -> loopLevel;
QT_RETHROW;
}
-- threadData -> loopLevel;
return returnValue;
}
bool QApplication::notify(QObject * receiver, QEvent * e)
{
//
// walk through parents and check for gestures
if (d -> gestureManager) {
switch (e -> type()) {
case QEvent::Paint:
case QEvent::DynamicPropertyChange:
case QEvent::NetworkReplyUpdated:
//
break ;
default :
if (receiver -> isWidgetType()) {
if (d -> gestureManager -> filterEvent(static_cast < QWidget *> (receiver), e))
return true ;
} else {
// a special case for events that go to QGesture objects.
// We pass the object to the gesture manager and it'll figure
// out if it's QGesture or not.
if (d -> gestureManager -> filterEvent(receiver, e))
return true ;
}
}
}
// User input and window activation makes tooltips sleep
switch (e -> type()) {
case QEvent::Wheel:
case QEvent::ActivationChange:
//
default :
res = d -> notify_helper(receiver, e);
break ;
}
return res;
}
bool QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e)
{
// send to all application event filters
if (sendThroughApplicationEventFilters(receiver, e))
return true ;
if (receiver -> isWidgetType()) {
QWidget * widget = static_cast < QWidget *> (receiver);
// .
if (QLayout * layout = widget -> d_func() -> layout) {
layout -> widgetEvent(e);
}
}
// send to all receiver event filters
// 说明4:这里也是关键的地方
if (sendThroughObjectEventFilters(receiver, e))
return true ;
// deliver the event
bool consumed = receiver -> event (e); // 说明5,receiver->event()调用
e -> spont = false ;
return consumed;
}
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject * receiver, QEvent * event )
{
Q_Q(QCoreApplication);
if (receiver != q) {
for ( int i = 0 ; i < receiver -> d_func() -> eventFilters.size(); ++ i) {
register QObject * obj = receiver -> d_func() -> eventFilters.at(i);
if ( ! obj)
continue ;
if (obj -> d_func() -> threadData != receiver -> d_func() -> threadData) {
qWarning( " QCoreApplication: Object event filter cannot be in a different thread. " );
continue ;
}
if (obj -> eventFilter(receiver, event ))
return true ;
}
}
return false ;
}
bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject * receiver, QEvent * event )
{
if (receiver -> d_func() -> threadData == this -> threadData) {
// application event filters are only called for objects in the GUI thread
for ( int i = 0 ; i < eventFilters.size(); ++ i) {
register QObject * obj = eventFilters.at(i);
if ( ! obj)
continue ;
if (obj -> d_func() -> threadData != threadData) {
qWarning( " QCoreApplication: Application event filter cannot be in a different thread. " );
continue ;
}
if (obj -> eventFilter(receiver, event ))
return true ;
}
}
return false ;
}
bool QApplication:: event (QEvent * e)
{
Q_D(QApplication);
if (e -> type() == QEvent::Close) {
QCloseEvent * ce = static_cast < QCloseEvent *> (e);
ce -> accept();
closeAllWindows();
QWidgetList list = topLevelWidgets();
for ( int i = 0 ; i < list.size(); ++ i) {
QWidget * w = list.at(i);
if (w -> isVisible() && ! (w -> windowType() == Qt::Desktop) && ! (w -> windowType() == Qt::Popup) &&
( ! (w -> windowType() == Qt::Dialog) || ! w -> parentWidget())) {
ce -> ignore();
break ;
}
}
if (ce -> isAccepted()) {
return true ;
} else {
}
} else if (e -> type() == QEvent::LanguageChange) {
QWidgetList list = topLevelWidgets();
for ( int i = 0 ; i < list.size(); ++ i) {
QWidget * w = list.at(i);
if ( ! (w -> windowType() == Qt::Desktop))
postEvent(w, new QEvent(QEvent::LanguageChange));
}
} else if (e -> type() == QEvent::Timer) {
QTimerEvent * te = static_cast < QTimerEvent *> (e);
Q_ASSERT(te != 0 );
if (te -> timerId() == d -> toolTipWakeUp.timerId()) {
d -> toolTipWakeUp.stop();
if (d -> toolTipWidget) {
QWidget * w = d -> toolTipWidget -> window();
// show tooltip if WA_AlwaysShowToolTips is set, or if
// any ancestor of d->toolTipWidget is the active
// window
bool showToolTip = w -> testAttribute(Qt::WA_AlwaysShowToolTips);
while (w && ! showToolTip) {
showToolTip = w -> isActiveWindow();
w = w -> parentWidget();
w = w ? w -> window() : 0 ;
}
if (showToolTip) {
QHelpEvent e(QEvent::ToolTip, d -> toolTipPos, d -> toolTipGlobalPos);
QApplication::sendEvent(d -> toolTipWidget, & e);
if (e.isAccepted())
d -> toolTipFallAsleep.start( 2000 , this );
}
}
} else if (te -> timerId() == d -> toolTipFallAsleep.timerId()) {
d -> toolTipFallAsleep.stop();
}
}
return QCoreApplication:: event (e); /// /说明6,最终QObject::event()调用
}
bool QCoreApplication:: event (QEvent * e)
{
if (e -> type() == QEvent::Quit) {
quit();
return true ;
}
return QObject:: event (e);
}
事件处理机制浅析是通过WM_ACTIVATE消息来分析的
// 调用堆栈
WinMainCRTStartup()
__tmainCRTStartup()
WinMain()
main( int argc = 1 , char ** argv)
QtGuid4.dll ! QApplication::exec()
QtCored4.dll ! QCoreApplication::exec()
QtCored4.dll ! QEventLoop::exec()
QtCored4.dll ! QEventLoop::processEvents()
QtGuid4.dll ! QGuiEventDispatcherWin32::processEvents()
QtCored4.dll ! QEventDispatcherWin32::processEvents()
user32.dll ! _PeekMessageW@ 20 () // 说明1,调用PeekMessage,非阻塞的取消息!
QtGuid4.dll ! QtWndProc(HWND__ * hwnd, unsigned int message = 6 , unsigned int wParam = 2 , long lParam = 0 ) // 即 WM_ACTIVATE 消息
QtGuid4.dll ! QApplication::winFocus(QWidget * widget, bool gotFocus = true )
QtGuid4.dll ! QApplication::setActiveWindow(QWidget * act)
QtCored4.dll ! QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event )
QtCored4.dll ! QCoreApplication::notifyInternal(QObject * receiver, QEvent * event )
QtGuid4.dll ! QApplication::notify(QObject * receiver, QEvent * e)
QtGuid4.dll ! QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e)
QtGuid4.dll ! QApplication:: event (QEvent * e)
QtCored4.dll ! QCoreApplication:: event (QEvent * e)
QtCored4.dll ! QObject:: event (QEvent * e)
说明2:
// QtWndProc() receives all messages from the main event loop
extern " C " LRESULT QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
// case 消息的处理
// 1538行->1670行
}
if ( ! widget)
widget = (QETWidget * )QWidget::find(hwnd);
if ( ! widget) // don't know this widget
goto do_default;
if (qt_is_translatable_mouse_event(message)) {
// message=512 第3个消息
// #define WM_MOUSEFIRST 0x0200
// #define WM_MOUSEMOVE 0x0200
if ( ! qt_tabletChokeMouse) {
result = widget -> translateMouseEvent(msg); // mouse event
}
}
else {
switch (message) {
// message = 136 第4个消息 #define WM_SYNCPAINT 0x0088
// message = 133,第5个消息 #define WM_NCPAINT 0x0085
// message = 28 ,第6个消息 #define WM_ACTIVATEAPP 0x001C
// message = 6 ,第7个消息 #define WM_ACTIVATE 0x0006 =>进入 qApp->winFocus(widget, true); // 说明3
// #define WM_NCHITTEST 0x0084 第1个消息,result = false, 进入do_default标记中的DefWindowProc处理
// #define WM_SETCURSOR 0x0020 第2个消息,result = false, 进入do_default标记中的DefWindowProc处理
case WM_NCHITTEST:
case WM_SETCURSOR:
}
if (result)
RETURN( false );
do_default:
RETURN(QWinInputContext::DefWindowProc(hwnd,message,wParam,lParam))
}
// 说明3
void QApplication::winFocus(QWidget * widget, bool gotFocus)
{
if (gotFocus) {
setActiveWindow(widget);
}
}
void QApplication::setActiveWindow(QWidget * act)
{
if ( ! previousActiveWindow) {
QEvent appActivate(QEvent::ApplicationActivate); // 关键是这里,appActivate 对象
sendSpontaneousEvent(qApp, & appActivate);
}
for ( int i = 0 ; i < toBeActivated.size(); ++ i) {
// 这里会继续调用
QWidget * w = toBeActivated.at(i);
sendSpontaneousEvent(w, & windowActivate);
sendSpontaneousEvent(w, & activationChange);
}
}
inline bool QCoreApplication::sendSpontaneousEvent(QObject * receiver, QEvent * event )
{
if ( event ) event -> spont = true ; return self ? self -> notifyInternal(receiver, event ) : false ;
}
/* !
\internal
This function is here to make it possible for Qt extensions to
hook into event notification without subclassing QApplication
*/
bool QCoreApplication::notifyInternal(QObject * receiver, QEvent * event )
{
// Make it possible for Qt Jambi and QSA to hook into events even
// though QApplication is subclassed
bool result = false ;
void * cbdata[] = { receiver, event , & result };
if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
return result;
}
// Qt enforces the rule that events can only be sent to objects in
// the current thread, so receiver->d_func()->threadData is
// equivalent to QThreadData::current(), just without the function
// call overhead.
QObjectPrivate * d = receiver -> d_func();
QThreadData * threadData = d -> threadData;
++ threadData -> loopLevel;
bool returnValue;
QT_TRY {
returnValue = notify(receiver, event ); // 大名鼎鼎的notify(),处理后,调用notify_helper继续处理
} QT_CATCH () {
-- threadData -> loopLevel;
QT_RETHROW;
}
-- threadData -> loopLevel;
return returnValue;
}
bool QApplication::notify(QObject * receiver, QEvent * e)
{
//
// walk through parents and check for gestures
if (d -> gestureManager) {
switch (e -> type()) {
case QEvent::Paint:
case QEvent::DynamicPropertyChange:
case QEvent::NetworkReplyUpdated:
//
break ;
default :
if (receiver -> isWidgetType()) {
if (d -> gestureManager -> filterEvent(static_cast < QWidget *> (receiver), e))
return true ;
} else {
// a special case for events that go to QGesture objects.
// We pass the object to the gesture manager and it'll figure
// out if it's QGesture or not.
if (d -> gestureManager -> filterEvent(receiver, e))
return true ;
}
}
}
// User input and window activation makes tooltips sleep
switch (e -> type()) {
case QEvent::Wheel:
case QEvent::ActivationChange:
//
default :
res = d -> notify_helper(receiver, e);
break ;
}
return res;
}
bool QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e)
{
// send to all application event filters
if (sendThroughApplicationEventFilters(receiver, e))
return true ;
if (receiver -> isWidgetType()) {
QWidget * widget = static_cast < QWidget *> (receiver);
// .
if (QLayout * layout = widget -> d_func() -> layout) {
layout -> widgetEvent(e);
}
}
// send to all receiver event filters
// 说明4:这里也是关键的地方
if (sendThroughObjectEventFilters(receiver, e))
return true ;
// deliver the event
bool consumed = receiver -> event (e); // 说明5,receiver->event()调用
e -> spont = false ;
return consumed;
}
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject * receiver, QEvent * event )
{
Q_Q(QCoreApplication);
if (receiver != q) {
for ( int i = 0 ; i < receiver -> d_func() -> eventFilters.size(); ++ i) {
register QObject * obj = receiver -> d_func() -> eventFilters.at(i);
if ( ! obj)
continue ;
if (obj -> d_func() -> threadData != receiver -> d_func() -> threadData) {
qWarning( " QCoreApplication: Object event filter cannot be in a different thread. " );
continue ;
}
if (obj -> eventFilter(receiver, event ))
return true ;
}
}
return false ;
}
bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject * receiver, QEvent * event )
{
if (receiver -> d_func() -> threadData == this -> threadData) {
// application event filters are only called for objects in the GUI thread
for ( int i = 0 ; i < eventFilters.size(); ++ i) {
register QObject * obj = eventFilters.at(i);
if ( ! obj)
continue ;
if (obj -> d_func() -> threadData != threadData) {
qWarning( " QCoreApplication: Application event filter cannot be in a different thread. " );
continue ;
}
if (obj -> eventFilter(receiver, event ))
return true ;
}
}
return false ;
}
bool QApplication:: event (QEvent * e)
{
Q_D(QApplication);
if (e -> type() == QEvent::Close) {
QCloseEvent * ce = static_cast < QCloseEvent *> (e);
ce -> accept();
closeAllWindows();
QWidgetList list = topLevelWidgets();
for ( int i = 0 ; i < list.size(); ++ i) {
QWidget * w = list.at(i);
if (w -> isVisible() && ! (w -> windowType() == Qt::Desktop) && ! (w -> windowType() == Qt::Popup) &&
( ! (w -> windowType() == Qt::Dialog) || ! w -> parentWidget())) {
ce -> ignore();
break ;
}
}
if (ce -> isAccepted()) {
return true ;
} else {
}
} else if (e -> type() == QEvent::LanguageChange) {
QWidgetList list = topLevelWidgets();
for ( int i = 0 ; i < list.size(); ++ i) {
QWidget * w = list.at(i);
if ( ! (w -> windowType() == Qt::Desktop))
postEvent(w, new QEvent(QEvent::LanguageChange));
}
} else if (e -> type() == QEvent::Timer) {
QTimerEvent * te = static_cast < QTimerEvent *> (e);
Q_ASSERT(te != 0 );
if (te -> timerId() == d -> toolTipWakeUp.timerId()) {
d -> toolTipWakeUp.stop();
if (d -> toolTipWidget) {
QWidget * w = d -> toolTipWidget -> window();
// show tooltip if WA_AlwaysShowToolTips is set, or if
// any ancestor of d->toolTipWidget is the active
// window
bool showToolTip = w -> testAttribute(Qt::WA_AlwaysShowToolTips);
while (w && ! showToolTip) {
showToolTip = w -> isActiveWindow();
w = w -> parentWidget();
w = w ? w -> window() : 0 ;
}
if (showToolTip) {
QHelpEvent e(QEvent::ToolTip, d -> toolTipPos, d -> toolTipGlobalPos);
QApplication::sendEvent(d -> toolTipWidget, & e);
if (e.isAccepted())
d -> toolTipFallAsleep.start( 2000 , this );
}
}
} else if (te -> timerId() == d -> toolTipFallAsleep.timerId()) {
d -> toolTipFallAsleep.stop();
}
}
return QCoreApplication:: event (e); /// /说明6,最终QObject::event()调用
}
bool QCoreApplication:: event (QEvent * e)
{
if (e -> type() == QEvent::Quit) {
quit();
return true ;
}
return QObject:: event (e);
}