QT分析之消息事件机制
原文地址:http://blog.163.com/net_worm/blog/static/127702419201001432028526/
上回我们分析到QPushButton的初始化,知道了Windows的窗口注册和消息处理函数QtWndProc。
跳过test.cpp中的其他语句,我们先分析最后一行代码a.exec()语句。
我们知道WinSDK中,简单Windows程序里的WinMain函数主要就这么几件事:
1、窗体注册;2、消息处理函数;3、等待和消息处理循环
QApplication::exec()只做了两件事:设定根对象和调用QCoreApplication::exec()。
QCoreApplication::exec()函数的代码如下,按惯例关键部分用颜色或追加注释。
int QCoreApplication::exec()
{
if (!QCoreApplicationPrivate::checkInstance("exec"))
return -; QThreadData *threadData = self->d_func()->threadData;
if (threadData != QThreadData::current()) {
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -;
}
if (!threadData->eventLoops.isEmpty()) {
qWarning("QCoreApplication::exec: The event loop is already running");
return -;
} // 从这里开始是事件处理循环开始前准备 threadData->quitNow = false;
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
int returnCode = eventLoop.exec(); // 事件处理主体 // 从这里开始是事件处理循环后处理(通常为App退出)
threadData->quitNow = false;
if (self) {
self->d_func()->in_exec = false;
if (!self->d_func()->aboutToQuitEmitted)
emit self->aboutToQuit();
self->d_func()->aboutToQuitEmitted = true;
sendPostedEvents(, QEvent::DeferredDelete);
} return returnCode;
}
我们先看QEventLoop::exec()的声明:int exec(ProcessEventsFlags flags = AllEvents);
对于上面eventLoop.exec(); 这种调用形式,意思说使用AllEvents(就是0x00值)标记
接着看QEventLoop::exec()的定义:
int QEventLoop::exec(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (d->threadData->quitNow)
return -; if (d->inExec) {
qWarning("QEventLoop::exec: instance %p has already called exec()", this);
return -;
}
d->inExec = true;
d->exit = false;
++d->threadData->loopLevel;
d->threadData->eventLoops.push(this); // remove posted quit events when entering a new event loop
QCoreApplication *app = QCoreApplication::instance();
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit); #if defined(QT_NO_EXCEPTIONS)
while (!d->exit)
processEvents(flags | WaitForMoreEvents | EventLoopExec);
#else
try {
while (!d->exit) // 如果exit变量没有设定为True的话,会一直循环处理 // flags被设定为:AllEvents、WaitForMoreEvents、EventLoopExec
processEvents(flags | WaitForMoreEvents | EventLoopExec);
} catch (...) {
qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
"exceptions from an event handler is not supported in Qt. You must\n"
"reimplement QApplication::notify() and catch all exceptions there.\n"); // copied from below
QEventLoop *eventLoop = d->threadData->eventLoops.pop();
Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
Q_UNUSED(eventLoop); // --release warning
d->inExec = false;
--d->threadData->loopLevel; throw;
}
#endif // copied above
QEventLoop *eventLoop = d->threadData->eventLoops.pop();
Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
Q_UNUSED(eventLoop); // --release warning
d->inExec = false;
--d->threadData->loopLevel; return d->returnCode;
}
继续深入看QEventLoop::processEvents()的定义
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->eventDispatcher)
return false;
if (flags & DeferredDeletion)
QCoreApplication::sendPostedEvents(, QEvent::DeferredDelete); // 根据d->threadData指向对象的不同,调用不同的Dispatcher
return d->threadData->eventDispatcher->processEvents(flags);
}
在这里,用单步跟踪我们可以知道实际调用的是QGuiEventDispatcherWin32::processEvents(),看其实现代码:
bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
if (!QEventDispatcherWin32::processEvents(flags))
return false; if (configRequests) // any pending configs?
qWinProcessConfigRequests(); return true;
}
继续深入九层地狱,看QEventDispatcherWin32::processEvents()的定义:
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32); if (!d->internalHwnd)
createInternalHwnd(); d->interrupt = false;
emit awake(); // emit在Win32平台下实际就是空的 bool canWait;
bool retVal = false;
do {
QCoreApplicationPrivate::sendPostedEvents(, , d->threadData); //这句没有深入分析 DWORD waitRet = ;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - ];
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - ); MSG msg;
bool haveMessage; // 使用用户输入事件并且该事件队列不为空 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
haveMessage = true;
msg = d->queuedUserInputEvents.takeFirst();
} // 使用Socket通知事件并且该事件队列不为空 else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// process queued socket events
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else { // 所有其他情况,Peek一下Windows的消息
haveMessage = winPeekMessage(&msg, , , , 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)) {
// queue user input events for later processing
haveMessage = false;
d->queuedUserInputEvents.append(msg);
}
if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) {
// queue socket events for later processing
haveMessage = false;
d->queuedSocketEvents.append(msg);
}
}
if (!haveMessage) {
// no message - check for signalled objects // 没有消息的情况下,等待事件通知
for (int i=; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, , QS_ALLINPUT, MWMO_ALERTABLE);
if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
// a new message has arrived, process it
continue;
}
}
if (haveMessage) { // 定时事件的处理
if (msg.message == WM_TIMER) {
// avoid live-lock by keeping track of the timers we've already sent
bool found = false;
for (int i = ; !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);
QT_WA({
DispatchMessage(&msg);
} , {
DispatchMessageA(&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;
} // still nothing - wait for message or signalled objects
QThreadData *data = d->threadData;
canWait = (!retVal
&& data->canWait
&& !d->interrupt
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - );
for (int i=; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle(); emit aboutToBlock();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
emit awake();
if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
retVal = true;
}
}
} while (canWait); return retVal;
}
至此,一个完整的路径似乎分析完毕了,等等,好像不太对。前面QtWndProc没有用上!
昨天分析到QApplication::exec()的具体实现,其中还留有一些疑问:一是QEventLoop::exec()里面和QEventDispatcherWin32::processEvents()的while循环退出条件的分析;另一个是QtWndProc()消息处理函数与QEventDispatcherWin32::processEvents()的关系。
到目前我们都是从上至下几乎都是直接看代码的方式(静态),今天换种方式用实际运行到代码的方式分析。我们知道test.cpp例子程序运行的时候,出来一个button,点击按钮之后退出。我们看QEventLoop::exec()里面的while循环:while (!d->exit) 。在规范设计中,功能模块都是封闭的,也就是说d->exit的值一定会在QEventLoop类的某个地方被赋值成True。
为证实我们的猜想,细看QEventLoop类的定义:
class Q_CORE_EXPORT QEventLoop : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QEventLoop) public:
explicit QEventLoop(QObject *parent = );
~QEventLoop(); enum ProcessEventsFlag {
AllEvents = 0x00,
ExcludeUserInputEvents = 0x01,
ExcludeSocketNotifiers = 0x02,
WaitForMoreEvents = 0x04,
#ifdef QT3_SUPPORT
ExcludeUserInput = ExcludeUserInputEvents,
WaitForMore = WaitForMoreEvents,
#endif
X11ExcludeTimers = 0x08
#ifdef QT_DEPRECATED
, DeferredDeletion = 0x10
#endif
, EventLoopExec = 0x20
, DialogExec = 0x40
};
Q_DECLARE_FLAGS(ProcessEventsFlags, ProcessEventsFlag) bool processEvents(ProcessEventsFlags flags = AllEvents);
void processEvents(ProcessEventsFlags flags, int maximumTime); int exec(ProcessEventsFlags flags = AllEvents);
void exit(int returnCode = 0);
bool isRunning() const; void wakeUp(); public Q_SLOTS:
void quit();
};
果然看到了我们想看的东西,把QEventLoop::exit(int returnCode)函数代码找到:
void QEventLoop::exit(int returnCode)
{
Q_D(QEventLoop);
if (!d->threadData->eventDispatcher)
return; d->returnCode = returnCode;
d->exit = true;
d->threadData->eventDispatcher->interrupt();
}
添加一个断点,运行程序点击退出按钮之后,我们可以得到下面的堆栈列表:
QtCored4.dll!QEventLoop::exit(int returnCode=0x00000000) 行284 C++
QtCored4.dll!QCoreApplication::exit(int returnCode=0x00000000) 行926 + 0xc 字节 C++
QtCored4.dll!QCoreApplication::quit() 行1468 + 0x7 字节 C++
QtCored4.dll!QCoreApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000002, void * * _a=0x0012bd28) 行84 C++
QtGuid4.dll!QApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000006, void * * _a=0x0012bd28) 行96 + 0x15 字节 C++
QtCored4.dll!QMetaObject::activate(QObject * sender=0x0012ff40, int from_signal_index=0x0000001d, int to_signal_index=0x0000001e, void * * argv=0x0012bd28) 行3104 + 0x2b 字节 C++
QtCored4.dll!QMetaObject::activate(QObject * sender=0x0012ff40, const QMetaObject * m=0x6590d328, int from_local_signal_index=0x00000002, int to_local_signal_index=0x00000003, void * * argv=0x0012bd28) 行3198 + 0x15 字节 C++
QtGuid4.dll!QAbstractButton::clicked(bool _t1=false) 行198 + 0x17 字节 C++
QtGuid4.dll!QAbstractButtonPrivate::emitClicked() 行545 C++
QtGuid4.dll!QAbstractButtonPrivate::click() 行537 C++
QtGuid4.dll!QAbstractButton::mouseReleaseEvent(QMouseEvent * e=0x0012c450) 行1116 C++
QtGuid4.dll!QWidget::event(QEvent * event=0x0012c450) 行7555 C++
QtGuid4.dll!QAbstractButton::event(QEvent * e=0x0012c450) 行1078 C++
QtGuid4.dll!QPushButton::event(QEvent * e=0x0012c450) 行663 C++
QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450) 行4065 + 0x11 字节 C++
QtGuid4.dll!QApplication::notify(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450) 行3767 + 0x2f 字节 C++
QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450) 行610 + 0x15 字节 C++
QtCored4.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450) 行216 + 0x38 字节 C++
QtGuid4.dll!QApplicationPrivate::sendMouseEvent(QWidget * receiver=0x0012ff40, QMouseEvent * event=0x0012c450, QWidget * alienWidget=0x00000000, QWidget * nativeWidget=0x0012ff40, QWidget * * buttonDown=0x65af67d4, QPointer<QWidget> & lastMouseReceiver={...}) 行2924 + 0xe 字节 C++
QtGuid4.dll!QETWidget::translateMouseEvent(const tagMSG & msg={...}) 行3269 + 0x28 字节 C++
QtGuid4.dll!QtWndProc(HWND__ * hwnd=0x00010840, unsigned int message=0x00000202, unsigned int wParam=0x00000000, long lParam=0x000c0064) 行1667 + 0xc 字节 C++
user32.dll!77e2b6e3()
[下面的框架可能不正确和/或缺失,没有为 user32.dll 加载符号]
user32.dll!77e2b874()
user32.dll!77e2b82a()
user32.dll!77e2ba92()
user32.dll!77e2bad0()
QtCored4.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行751 + 0x17 字节 C++
QtGuid4.dll!QGuiEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行1182 + 0x15 字节 C++
QtCored4.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行150 C++
QtCored4.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行201 + 0x2d 字节 C++
QtCored4.dll!QCoreApplication::exec() 行888 + 0x15 字节 C++
QtGuid4.dll!QApplication::exec() 行3526 C++
test.exe!main(int argc=0x00000001, char * * argv=0x00ba7040) 行14 + 0x6 字节 C++
test.exe!__tmainCRTStartup() 行582 + 0x19 字节 C
test.exe!mainCRTStartup() 行399 C
kernel32.dll!7c82f23b()
这里我们清晰的看到了消息的传递路径,而且也看到了Clicked()和quit()的调用前后次序
我们继续昨天之分析,QEventDispatcherWin32::processEvents()派发消息之后,QtWndProc()获得该消息。我们看看QtWndProc()的具体处理:(简略)
1、根据hwnd获取QWidget指针:
widget = (QETWidget*)QWidget::find(hwnd);
2、处理事件:
result = widget->translateMouseEvent(msg); // mouse event
获取的widget其实就是QPushButton对象的指针;事件的处理实体在QETWidget::translateMouseEvent(),该函数又是一个超长代码的实现,其处理是事件压缩之类Mouse事件的周边处理,之后是调用:
QApplicationPrivate::sendMouseEvent(),
我们看其实现:
bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
QWidget *alienWidget, QWidget *nativeWidget,
QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver)
{
Q_ASSERT(receiver);
Q_ASSERT(event);
Q_ASSERT(nativeWidget);
Q_ASSERT(buttonDown); if (alienWidget && !isAlien(alienWidget))
alienWidget = ; QPointer<QWidget> receiverGuard = receiver;
QPointer<QWidget> nativeGuard = nativeWidget;
QPointer<QWidget> alienGuard = alienWidget;
QPointer<QWidget> activePopupWidget = qApp->activePopupWidget(); const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen); if (*buttonDown) { // 如果是按钮&是图片窗体的话,注册该窗体使得最后一个按钮释放的时候接受leave事件
if (!graphicsWidget) {
// Register the widget that shall receive a leave event
// after the last button is released.
if ((alienWidget || !receiver->internalWinId()) && !leaveAfterRelease && !QWidget::mouseGrabber())
leaveAfterRelease = *buttonDown;
if (event->type() == QEvent::MouseButtonRelease && !event->buttons())
*buttonDown = ;
}
} else if (lastMouseReceiver) {
// Dispatch enter/leave if we move:
// 1) from an alien widget to another alien widget or
// from a native widget to an alien widget (first OR case)
// 2) from an alien widget to a native widget (second OR case)
if ((alienWidget && alienWidget != lastMouseReceiver)
|| (isAlien(lastMouseReceiver) && !alienWidget)) {
if (activePopupWidget) {
if (!QWidget::mouseGrabber())
dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver);
} else {
dispatchEnterLeave(receiver, lastMouseReceiver);
} }
} #ifdef ALIEN_DEBUG
qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver
<< "pos:" << event->pos() << "alien" << alienWidget << "button down"
<< *buttonDown << "last" << lastMouseReceiver << "leave after release"
<< leaveAfterRelease;
#endif // We need this quard in case someone opens a modal dialog / popup. If that's the case
// leaveAfterRelease is set to null, but we shall not update lastMouseReceiver.
const bool wasLeaveAfterRelease = leaveAfterRelease != ;
bool result = QApplication::sendSpontaneousEvent(receiver, event); if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease
&& !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) {
// Dispatch enter/leave if:
// 1) the mouse grabber is an alien widget
// 2) the button is released on an alien widget QWidget *enter = ;
if (nativeGuard)
enter = alienGuard ? alienWidget : nativeWidget;
else // The receiver is typically deleted on mouse release with drag'n'drop.
enter = QApplication::widgetAt(event->globalPos()); dispatchEnterLeave(enter, leaveAfterRelease);
leaveAfterRelease = ;
lastMouseReceiver = enter;
} else if (!wasLeaveAfterRelease) {
if (activePopupWidget) {
if (!QWidget::mouseGrabber())
lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : );
} else {
lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos());
}
} return result;
}
根据单步跟踪(也可以通过代码分析)知道QApplication::sendSpontaneousEvent()调用的是QCoreApplication::sendSpontaneousEvent(),该函数判断self指针(this指针?)是否为空,不为空则调用notifyInternal()函数;也就是调用QCoreApplication::notifyInternal(),参数为receiver(也就是根据hwnd获取的QPushButton对象指针),和event。我们看其实现代码:
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 }; // 检查CallBack列表中是否有QInternal::EventNotifyCallback类型,有的话则调用之 // 该CallBack的返回必须为True,否则此处会继续往下运行
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; #ifdef QT_JAMBI_BUILD
int deleteWatch = ;
int *oldDeleteWatch = QObjectPrivate::setDeleteWatch(d, &deleteWatch); bool inEvent = d->inEventHandler;
d->inEventHandler = true;
#endif #if defined(QT_NO_EXCEPTIONS)
bool returnValue = notify(receiver, event);
#else
bool returnValue;
try {
returnValue = notify(receiver, event); // QCoreApplication中这是一个虚函数
} catch(...) {
--threadData->loopLevel;
throw;
}
#endif #ifdef QT_JAMBI_BUILD
// Restore the previous state if the object was not deleted..
if (!deleteWatch) {
d->inEventHandler = inEvent;
}
QObjectPrivate::resetDeleteWatch(d, oldDeleteWatch, deleteWatch);
#endif
--threadData->loopLevel;
return returnValue;
}
从上面的分析,我们继续看QApplication::notify()的实现:(为方便查看,大部分无关代码删除了)
bool QApplication::notify(QObject *receiver, QEvent *e)
{
Q_D(QApplication); ……
bool res = false;
if (!receiver->isWidgetType()) {
res = d->notify_helper(receiver, e);
} else switch (e->type()) {
case QEvent::ShortcutOverride:
case QEvent::KeyPress:
case QEvent::KeyRelease: ……
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
{
QWidget* w = static_cast<QWidget *>(receiver); QMouseEvent* mouse = static_cast<QMouseEvent*>(e);
QPoint relpos = mouse->pos(); if (e->spontaneous()) { if (e->type() == QEvent::MouseButtonPress) {
QWidget *fw = w;
while (fw) {
if (fw->isEnabled()
&& QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) {
fw->setFocus(Qt::MouseFocusReason);
break;
}
if (fw->isWindow())
break;
fw = fw->parentWidget();
}
} // ### Qt 5 These dynamic tool tips should be an OPT-IN feature. Some platforms
// …… 这里将来Qt5版本的实现描述。
if (e->type() == QEvent::MouseMove && mouse->buttons() == ) {
d->toolTipWidget = w;
d->toolTipPos = relpos;
d->toolTipGlobalPos = mouse->globalPos();
d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive()?:, this);
}
} bool eventAccepted = mouse->isAccepted(); QPointer<QWidget> pw = w;
while (w) {
QMouseEvent me(mouse->type(), relpos, mouse->globalPos(), mouse->button(), mouse->buttons(),
mouse->modifiers());
me.spont = mouse->spontaneous();
// throw away any mouse-tracking-only mouse events
if (!w->hasMouseTracking()
&& mouse->type() == QEvent::MouseMove && mouse->buttons() == ) {
// but still send them through all application event filters (normally done by notify_helper)
for (int i = ; i < d->eventFilters.size(); ++i) {
register QObject *obj = d->eventFilters.at(i);
if (!obj)
continue;
if (obj->d_func()->threadData != w->d_func()->threadData) {
qWarning("QApplication: Object event filter cannot be in a different thread.");
continue;
}
if (obj->eventFilter(w, w == receiver ? mouse : &me))
break;
}
res = true;
} else {
w->setAttribute(Qt::WA_NoMouseReplay, false);
res = d->notify_helper(w, w == receiver ? mouse : &me);
e->spont = false;
}
eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
if (res && eventAccepted)
break;
if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
break;
relpos += w->pos();
w = w->parentWidget();
} mouse->setAccepted(eventAccepted); if (e->type() == QEvent::MouseMove) {
if (!pw)
break; w = static_cast<QWidget *>(receiver);
relpos = mouse->pos();
QPoint diff = relpos - w->mapFromGlobal(d->hoverGlobalPos);
while (w) {
if (w->testAttribute(Qt::WA_Hover) &&
(!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) {
QHoverEvent he(QEvent::HoverMove, relpos, relpos - diff);
d->notify_helper(w, &he);
}
if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
break;
relpos += w->pos();
w = w->parentWidget();
}
} d->hoverGlobalPos = mouse->globalPos();
}
break; ……
default:
res = d->notify_helper(receiver, e);
break;
} return res;
}
其中res = d->notify_helper(w, w == receiver ? mouse : &me);调用的是QApplicationPrivate::notify_helper(),我们看其具体实现:
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 !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR))
// toggle HasMouse widget state on enter and leave
if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
(!qApp->activePopupWidget() || qApp->activePopupWidget() == widget->window()))
widget->setAttribute(Qt::WA_UnderMouse, true);
else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
widget->setAttribute(Qt::WA_UnderMouse, false);
#endif if (QLayout *layout=widget->d_func()->layout) {
layout->widgetEvent(e);
}
} // send to all receiver event filters
if (sendThroughObjectEventFilters(receiver, e))
return true; // deliver the event
bool consumed = receiver->event(e); // 调用QPushButton::event()
e->spont = false;
return consumed;
}
继续看QPushButton::event()的实现:
bool QPushButton::event(QEvent *e)
{
Q_D(QPushButton);
if (e->type() == QEvent::ParentChange) {
if (QDialog *dialog = d->dialogParent()) {
if (d->defaultButton)
dialog->d_func()->setMainDefault(this);
}
} else if (e->type() == QEvent::StyleChange
#ifdef Q_WS_MAC
|| e->type() == QEvent::MacSizeChange
#endif
) {
d->resetLayoutItemMargins();
updateGeometry();
}
return QAbstractButton::event(e);
}
我们进一步看QAbstractButton::event的实现,忽略次要处理,主要是调用QWidget::event(e);
QWidget::event(QEvent *event)主体是根据参数event的类型switch分支处理各种事件,我们回头看QETWidget::translateMouseEvent(const MSG &msg)中,event对象生成时候的语句:
QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button),
Qt::MouseButtons(state & Qt::MouseButtonMask),
Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
根据查找,这里type内容是MouseButtonPress。同样忽略其他无关代码,我们看QWidget::event()的代码:
bool QWidget::event(QEvent *event)
{
Q_D(QWidget);
…… switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break; case QEvent::MouseButtonPress:
// Don't reset input context here. Whether reset or not is
// a responsibility of input method. reset() will be
// called by mouseHandler() of input method if necessary
// via mousePressEvent() of text widgets.
mousePressEvent((QMouseEvent*)event);
break;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
break;
…… default:
return QObject::event(event);
}
return true;
}
我们看该事件处理的具体代码:
void QAbstractButton::mousePressEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}
if (hitButton(e->pos())) { // <-- 根据鼠标点击点的位置匹配按钮
setDown(true);
repaint(); //flush paint event before invoking potentially expensive operation
QApplication::flush();
d->emitPressed();
e->accept();
} else {
e->ignore();
}
}
在MouseButtonProcess事件处理之后,就是前面注册的Release事件(前面粉色部分代码)。
目前还有两个疑问:一个是自定义的信号(SIGNAL)如何跟Windows的消息关联的;另一个是信号和槽(SLOT)是如何关联的。根据前面的分析,对第一个问题的猜测是在event()函数里增加相应处理;对第二个问题,应该有一个函数指针表使之关联。带着这些问题我们接着分析mouseReleaseEvent()。
void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
} if (!d->down) {
e->ignore();
return;
} if (hitButton(e->pos())) {
d->repeatTimer.stop();
d->click(); // 调用QAbstractButtonPrivate::click()
e->accept();
} else {
setDown(false);
e->ignore();
}
}
进一步看QAbstractButtonPrivate::click()的实现代码:
void QAbstractButtonPrivate::click()
{
Q_Q(QAbstractButton); down = false;
blockRefresh = true;
bool changeState = true;
if (checked && queryCheckedButton() == q) {
// the checked button of an exclusive or autoexclusive group cannot be unchecked
#ifndef QT_NO_BUTTONGROUP
if (group ? group->d_func()->exclusive : autoExclusive)
#else
if (autoExclusive)
#endif
changeState = false;
} QPointer<QAbstractButton> guard(q);
if (changeState) {
q->nextCheckState();
if (!guard)
return;
}
blockRefresh = false;
refresh();
q->repaint(); //flush paint event before invoking potentially expensive operation
QApplication::flush();
if (guard)
emitReleased();
if (guard)
emitClicked();
}
主要就是调用emitReleased()和emitClicked(),我们看其中QAbstractButtonPrivate::emitClicked()的实现
void QAbstractButtonPrivate::emitClicked()
{
Q_Q(QAbstractButton);
QPointer<QAbstractButton> guard(q);
emit q->clicked(checked); // 这里调用的是QAbstractButton::clicked()
#ifndef QT_NO_BUTTONGROUP
if (guard && group) {
emit group->buttonClicked(group->id(q));
if (guard && group)
emit group->buttonClicked(q);
}
#endif
}
上面调用的函数实体,在moc_QAbstractButton.cpp里可以看到。这个文件实际是由QT的MOC工具自动产生的。
void QAbstractButton::clicked(bool _t1)
{
void *_a[] = { , const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, , , _a); // 和SLOT的调用关系应该是这里实现的。
}
我们先来看看QMetaObject类的定义:
struct Q_CORE_EXPORT QMetaObject
{ …… struct { // private data
const QMetaObject *superdata;
const char *stringdata;
const uint *data;
const void *extradata;
} d;
};
再看QMetaObjectPrivate的定义:
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
};
实际上QMetaObject::d.data指向的就是QMetaObjectPrivate结构体 我们看看staticMetaObject对象的定义:(同样在moc_QAbstractButton.cpp文件中)
const QMetaObject QAbstractButton::staticMetaObject = {
{ &QWidget::staticMetaObject, qt_meta_stringdata_QAbstractButton,
qt_meta_data_QAbstractButton, }
};
这是一个常对象,除设定父类的staticMetaObject外,还设定了两个全局变量:qt_meta_stringdata_QAbstractButton和qt_meta_data_QAbstractButton。
所有的奥秘都在这两个变量里面了。根据前面分析qt_meta_data_QAbstractButton实际是QMetaObjectPrivate结构。
static const uint qt_meta_data_QAbstractButton[] = { // content:
, // revision
, // classname
, , // classinfo
, , // methods
, , // properties
, , // enums/sets
, , // constructors // signals: signature, parameters, type, tag, flags , , , , 0x05,
// 17 -- 指的是stringdata中No.17字节开始的字符串,结合下面定义实际就是pressed()
, , , , 0x05, // released()
, , , , 0x05, // clicked(bool)
, , , , 0x25, // clicked()
, , , , 0x05, // toggled(bool) // slots: signature, parameters, type, tag, flags
, , , , 0x0a, // setIconSize(QSize)
, , , , 0x0a,
, , , , 0x2a,
, , , , 0x0a,
, , , , 0x0a,
, , , , 0x0a, // properties: name, type, flags
, , 0x0a095103, // text
, , 0x45095103, // icon
, , 0x15095103,
, , 0x4c095103,
, , 0x01095103,
, , 0x01595103,
, , 0x01095103,
, , 0x01095103,
, , 0x02095103,
, , 0x02095103,
, , 0x01094103, // properties: notify_signal_id
,
,
,
,
,
,
,
,
,
,
, // eod
}; static const char qt_meta_stringdata_QAbstractButton[] = {
"QAbstractButton\0\0pressed()\0released()\0"
"checked\0clicked(bool)\0clicked()\0"
"toggled(bool)\0size\0setIconSize(QSize)\0"
"msec\0animateClick(int)\0animateClick()\0"
"click()\0toggle()\0setChecked(bool)\0"
"QString\0text\0QIcon\0icon\0QSize\0iconSize\0"
"QKeySequence\0shortcut\0bool\0checkable\0"
"autoRepeat\0autoExclusive\0int\0"
"autoRepeatDelay\0autoRepeatInterval\0"
"down\0"
};
我们接着看QMetaObject::activate()的代码:
void QMetaObject::activate(QObject *sender, const QMetaObject *m,
int from_local_signal_index, int to_local_signal_index, void **argv)
{
int offset = m->methodOffset(); // 指向qt_meta_data_QAbstractButton[27]字节,也就是clicked(bool)
int from_signal_index = offset + from_local_signal_index; // 27 + 2 = 29
int to_signal_index = offset + to_local_signal_index; // 27 + 3 = 30
if (to_signal_index <
&& !qt_signal_spy_callback_set.signal_begin_callback
&& !qt_signal_spy_callback_set.signal_end_callback) {
uint signal_mask = ( << (to_signal_index + )) - ;
signal_mask ^= ( << from_signal_index) - ; // sender指的是QPushButton,下面指向的是:QPushButtonPrivate::connectedSignals
if ((sender->d_func()->connectedSignals & signal_mask) == )
// nothing connected to these signals, and no spy
return;
}
activate(sender, from_signal_index, to_signal_index, argv);
}
可以看到,在判断本信号是否连接有槽之后,就调用了activate的重载函数:
void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)
{
if (sender->d_func()->blockSig)
return; void *empty_argv[] = { };
if (qt_signal_spy_callback_set.signal_begin_callback != ) {
qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,
argv ? argv : empty_argv);
} QMutexLocker locker(&sender->d_func()->threadData->mutex);
QThreadData *currentThreadData = QThreadData::current(); QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
if (!connectionLists) {
if (qt_signal_spy_callback_set.signal_end_callback != )
qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
return;
}
++connectionLists->inUse; // emit signals in the following order: from_signal_index <= signals <= to_signal_index, signal < 0
for (int signal = from_signal_index;
(signal >= from_signal_index && signal <= to_signal_index) || (signal == -);
(signal == to_signal_index ? signal = - : ++signal))
{
if (signal >= connectionLists->count()) {
signal = to_signal_index;
continue;
}
int count = connectionLists->at(signal).count(); // 就是在这里获取信号接收的槽函数指针的。 // connectionLists里的数据,猜测是由QObject::connect()填进去的。
for (int i = ; i < count; ++i) {
const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i];
39 if (!c->receiver)
40 continue; QObject * const receiver = c->receiver; // determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection
&& (currentThreadData != sender->d_func()->threadData
|| receiver->d_func()->threadData != sender->d_func()->threadData))
|| (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal, *c, argv);
continue;
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
blocking_activate(sender, signal, *c, argv);
continue;
} const int method = c->method;
QObjectPrivate::Sender currentSender;
currentSender.sender = sender;
currentSender.signal = signal < ? from_signal_index : signal;
currentSender.ref = ;
QObjectPrivate::Sender *previousSender = ;
if (currentThreadData == receiver->d_func()->threadData)
previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);
locker.unlock(); if (qt_signal_spy_callback_set.slot_begin_callback != ) {
qt_signal_spy_callback_set.slot_begin_callback(receiver,
method,
argv ? argv : empty_argv);
} #if defined(QT_NO_EXCEPTIONS)
receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
try { // 在我们的分析中,连接的槽是QApplication::quit(),qt_metacall在哪定义的呢?
receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
} catch (...) {
locker.relock(); QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); --connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= );
if (connectionLists->orphaned && !connectionLists->inUse)
delete connectionLists;
throw;
}
#endif locker.relock(); if (qt_signal_spy_callback_set.slot_end_callback != )
qt_signal_spy_callback_set.slot_end_callback(receiver, method); QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); if (connectionLists->orphaned)
break;
} if (connectionLists->orphaned)
break;
} --connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= );
if (connectionLists->orphaned) {
if (!connectionLists->inUse)
delete connectionLists;
} else {
sender->d_func()->cleanConnectionLists();
} locker.unlock(); if (qt_signal_spy_callback_set.signal_end_callback != )
qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
}
单步跟踪,receiver->qt_metacall();实际调用的是QApplication::qt_metacall(),根据调用参数实现不同的函数调用。在moc_QApplication.cpp中定义,是由MOC工具自动产生的代码。至此,信号与槽的关联分析完毕。
明天接着分析QObject::connect()如何把相关数据填入connectionLists。自定义信号如何与windows消息关联在明天分析完毕之后再来证实。
在继续分析之前,我们回头看看test.cpp的main()函数:
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QPushButton quit( "Quit", );
quit.resize( , );
quit.setFont( QFont( "Times", , QFont::Bold ) );
QObject::connect( &quit, SIGNAL(clicked()), &a, SLOT(quit()) );
quit.show();
return a.exec();
}
根据我们猜测,就是上面红色部分语句把消息处理函数指针填入了connectionLists。
首先我们看看SIGNAL宏和SLOT宏的定义:(另外一种定义是为DEBUG用的,可忽略)
# define METHOD(a) ""#a
# define SLOT(a) ""#a
# define SIGNAL(a) ""#a
使用的是宏转义,SIGNAL(clicked())被展开成"2clicked()"(字符串);SLOT(quit())被展开成"1quit()"。
再看QObject::connect()的声明:
static bool connect(const QObject *sender, const char *signal,
const QObject *receiver,const char *member,
Qt::ConnectionType = Qt::AutoConnection );
上面的调用语句展开之后就是:
QObject::connect(&quit, "2clicked()", &a, "1quit()");
然后看QObject::connect()的定义:
bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
{
const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
return true;
} if (type == Qt::AutoCompatConnection) {
type = Qt::AutoConnection;
} if (sender == || receiver == || signal == || method == ) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+ : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+ : "(null)");
return false;
}
QByteArray tmp_signal_name; // 检查signal是否以2开头 if (!check_signal_macro(sender, signal, "connect", "bind"))
return false;
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code // 获得signal的函数编号
int signal_index = smeta->indexOfSignal(signal);
if (signal_index < ) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - );
signal = tmp_signal_name.constData() + ; signal_index = smeta->indexOfSignal(signal);
if (signal_index < ) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
} QByteArray tmp_method_name;
int membcode = extract_code(method); // 检查receiver是否以1开头 if (!check_method_code(membcode, receiver, method, "connect"))
return false;
const char *method_arg = method;
++method; // skip code // 获得receiver的函数编号 const QMetaObject *rmeta = receiver->metaObject();
int method_index = -;
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
if (method_index < ) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
switch (membcode) {
case QSLOT_CODE:
method_index = rmeta->indexOfSlot(method);
break;
case QSIGNAL_CODE:
method_index = rmeta->indexOfSignal(method);
break;
}
} if (method_index < ) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return false;
}
if (!QMetaObject::checkConnectArgs(signal, method)) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return false;
} int *types = ;
if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
&& !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
return false; QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
const_cast<QObject*>(sender)->connectNotify(signal - 1);
return true;
}
用红色标记出来的三个主要调用,我们先看QInternal::activateCallbacks()的实现:
bool QInternal::activateCallbacks(Callback cb, void **parameters)
{
Q_ASSERT_X(cb >= , "QInternal::activateCallback()", "Callback id must be a valid id"); QInternal_CallBackTable *cbt = global_callback_table();
if (cbt && cb < cbt->callbacks.size()) {
QList<qInternalCallback> callbacks = cbt->callbacks[cb];
bool ret = false;
for (int i=; i<callbacks.size(); ++i)
ret |= (callbacks.at(i))(parameters);
return ret;
}
return false;
}
这是优先处理回调函数(钩子函数),在我们这里的应用中没有回调,所以可以忽略。
接着看QMetaObject::connect()的实现:
bool QMetaObject::connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver); QOrderedMutexLocker locker(&s->d_func()->threadData->mutex,
&r->d_func()->threadData->mutex); QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) };
s->d_func()->addConnection(signal_index, &c);
r->d_func()->refSender(s, signal_index); if (signal_index < )
sender->d_func()->connectedSignals = ~0u;
else if (signal_index < )
sender->d_func()->connectedSignals |= ( << signal_index); return true;
}
s->d_func()指向的是QPushButtonPrivate指针,QPushButtonPrivate没有addConnection()成员实际调用的是其基类成员,
s->d_func()->addConnection()调用的是QObjectPrivate::addConnection()。进一步看其实现:
void QObjectPrivate::addConnection(int signal, Connection *c)
{
if (!connectionLists)
connectionLists = new QObjectConnectionListVector();
if (signal >= connectionLists->count())
connectionLists->resize(signal + ); ConnectionList &connectionList = (*connectionLists)[signal];
connectionList.append(*c); cleanConnectionLists();
}
这里填入了发送消息的SIGNAL的函数指针!我们接着看r->d_func()->refSender(s, signal_index);其中r指向的是QApplication对象(a),所以r->d_func()是QApplicationPrivate对象指针,同样因其本身没有refSender()成员函数,调用的是其基类QObjectPrivate::refSender()。我们看其实现:
void QObjectPrivate::refSender(QObject *sender, int signal)
{
for (int i = ; i < senders.count(); ++i) {
Sender &s = senders[i];
if (s.sender == sender && s.signal == signal) {
++s.ref;
return;
}
} Sender s = { sender, signal, };
senders.append(s);
}
至此,我们的猜想得到证实。分析完毕。
QT分析之消息事件机制的更多相关文章
- 3、QT分析之消息事件机制
原文地址:http://blog.163.com/net_worm/blog/static/127702419201001432028526/ 上回我们分析到QPushButton的初始化,知道了Wi ...
- Atitit.事件机制 与 消息机制的联系与区别
Atitit.事件机制 与 消息机制的联系与区别 1. 消息/事件机制是几乎所有开发语言都有的机制,在某些语言称之为消息(Event),有些地方称之为(Message).1 2. 发布/订阅模式1 3 ...
- C++服务器设计(五):多设备类型及消息事件管理
在传统的服务器系统中,服务器仅针对接收到的客户端消息进行解析,并处理后回复响应.在该过程中服务器并不会主动判断客户端类型.但在现实中,往往存在多种类型的客户端设备,比如物联网下的智能家居系统,就存在智 ...
- Qt事件机制浅析(定义,产生,异步事件循环,转发,与信号的区别。感觉QT事件与Delphi的事件一致,而信号则与Windows消息一致)
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- Qt 事件系统浅析 (用 Windows API 描述,分析了QCoreApplication::exec()和QEventLoop::exec的源码)(比起新号槽,事件机制是更高级的抽象,拥有更多特性,比如 accept/ignore,filter,还是实现状态机等高级 API 的基础)
事件系统在 Qt 中扮演了十分重要的角色,不仅 GUI 的方方面面需要使用到事件系统,Signals/Slots 技术也离不开事件系统(多线程间).我们本文中暂且不描述 GUI 中的一些特殊情况,来说 ...
- Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- Qt 事件机制
[1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...
- QT源码之Qt信号槽机制与事件机制的联系
QT源码之Qt信号槽机制与事件机制的联系是本文要介绍的内容,通过解决一个问题,从中分析出的理论,先来看内容. 本文就是来解决一个问题,就是当signal和slot的连接为Qt::QueuedConne ...
- Qt事件机制---信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。
转:http://www.cnblogs.com/findumars/p/8001484.html Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和.先处理自己队列中的消息,然后再处 ...
随机推荐
- 关于java的wait、notify、notifyAll方法
wait.notify.notifyAll 遇到的问题 之前开发打印机项目,因为需要使用多线程技术,当时并不怎么理解,一开始随意在方法体内使用wait.notify.notifyAll 方法导致出现了 ...
- 成都Uber优步司机奖励政策(4月8日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- 宁波Uber优步司机奖励政策(1月4日~1月10日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- orm4sqlite
//-------------------------------------------------------------------------- // // Copyright (c) BUS ...
- NB-IOT使用LWM2M移动onenet对接之MTU最大传输单元设置
1. 最近遇到的一个项目NB-IOT使用LWM2M移动onenet对接,要求设置传输的MTU,因此首先需要搞懂MTU是什么? 以太网的MTU值是1500 bytes,假设发送者的协议高层向IP层发送了 ...
- Hadoop3.0新特性
1. Hadoop3.0简介 Hadoop 2.0是基于JDK 1.7开发的,而JDK 1.7在2015年4月已停止更新,这直接迫使Hadoop社区基于JDK1.8重新发布一个新的Hadoop版本,而 ...
- 【独家】K8S漏洞报告 | 近期bug fix解读&1.9.11主要bug fix汇总
*内容提要: 1. Kube-proxy长连接优雅断开机制及IPVS模式实现 2. 10/29--11/19 bug fix汇总分析 3. 1.9.11重要bug fix汇总 在本周的跟踪分析中,以1 ...
- pyhon文件操作典型代码实现(非常经典!)
1. 编写一个程序,统计当前目录下每个文件类型的文件数,程序实现如图: 实现代码: import os all_files = os.listdir(os.chdir("D:\\" ...
- Unity自带标准资源包中的特效
- 悲剧文本(Broken Keyboard (a.k.a. Beiju Text),UVA 11988)
题目描述: 题目思路: 1.使用链表来重新定位各个字符 2.用数组实现链表 3.开一个数组list[i]来存储字符数组下一个字符的位置 #include <iostream> #inclu ...