[转]浅议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, 0 , 0 , 0 , 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
深入了解事件处理系统对于每个学习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, 0 , 0 , 0 , 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