首页 技术 正文
技术 2022年11月18日
0 收藏 574 点赞 4,778 浏览 36651 个字

原文地址: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()函数的代码如下,按惯例关键部分用颜色或追加注释。

3、QT分析之消息事件机制

 1 int QCoreApplication::exec()
2 {
3 if (!QCoreApplicationPrivate::checkInstance("exec"))
4 return -1;
5
6 QThreadData *threadData = self->d_func()->threadData;
7 if (threadData != QThreadData::current()) {
8 qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
9 return -1;
10 }
11 if (!threadData->eventLoops.isEmpty()) {
12 qWarning("QCoreApplication::exec: The event loop is already running");
13 return -1;
14 }
15
16 // 从这里开始是事件处理循环开始前准备
17
18 threadData->quitNow = false;
19 QEventLoop eventLoop;
20 self->d_func()->in_exec = true;
21 self->d_func()->aboutToQuitEmitted = false;
22 int returnCode = eventLoop.exec(); // 事件处理主体
23
24 // 从这里开始是事件处理循环后处理(通常为App退出)
25 threadData->quitNow = false;
26 if (self) {
27 self->d_func()->in_exec = false;
28 if (!self->d_func()->aboutToQuitEmitted)
29 emit self->aboutToQuit();
30 self->d_func()->aboutToQuitEmitted = true;
31 sendPostedEvents(0, QEvent::DeferredDelete);
32 }
33
34 return returnCode;
35 }

3、QT分析之消息事件机制

我们先看QEventLoop::exec()的声明:int exec(ProcessEventsFlags flags = AllEvents);

对于上面eventLoop.exec();  这种调用形式,意思说使用AllEvents(就是0x00值)标记

接着看QEventLoop::exec()的定义:

3、QT分析之消息事件机制

 1 int QEventLoop::exec(ProcessEventsFlags flags)
2 {
3 Q_D(QEventLoop);
4 if (d->threadData->quitNow)
5 return -1;
6
7 if (d->inExec) {
8 qWarning("QEventLoop::exec: instance %p has already called exec()", this);
9 return -1;
10 }
11 d->inExec = true;
12 d->exit = false;
13 ++d->threadData->loopLevel;
14 d->threadData->eventLoops.push(this);
15
16 // remove posted quit events when entering a new event loop
17 QCoreApplication *app = QCoreApplication::instance();
18 if (app && app->thread() == thread())
19 QCoreApplication::removePostedEvents(app, QEvent::Quit);
20
21 #if defined(QT_NO_EXCEPTIONS)
22 while (!d->exit)
23 processEvents(flags | WaitForMoreEvents | EventLoopExec);
24 #else
25 try {
26 while (!d->exit) // 如果exit变量没有设定为True的话,会一直循环处理
27
28 // flags被设定为:AllEvents、WaitForMoreEvents、EventLoopExec
29 processEvents(flags | WaitForMoreEvents | EventLoopExec);
30 } catch (...) {
31 qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
32 "exceptions from an event handler is not supported in Qt. You must\n"
33 "reimplement QApplication::notify() and catch all exceptions there.\n");
34
35 // copied from below
36 QEventLoop *eventLoop = d->threadData->eventLoops.pop();
37 Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
38 Q_UNUSED(eventLoop); // --release warning
39 d->inExec = false;
40 --d->threadData->loopLevel;
41
42 throw;
43 }
44 #endif
45
46 // copied above
47 QEventLoop *eventLoop = d->threadData->eventLoops.pop();
48 Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
49 Q_UNUSED(eventLoop); // --release warning
50 d->inExec = false;
51 --d->threadData->loopLevel;
52
53 return d->returnCode;
54 }

3、QT分析之消息事件机制

继续深入看QEventLoop::processEvents()的定义

3、QT分析之消息事件机制

 1 bool QEventLoop::processEvents(ProcessEventsFlags flags)
2 {
3 Q_D(QEventLoop);
4 if (!d->threadData->eventDispatcher)
5 return false;
6 if (flags & DeferredDeletion)
7 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
8
9 // 根据d->threadData指向对象的不同,调用不同的Dispatcher
10 return d->threadData->eventDispatcher->processEvents(flags);
11 }

3、QT分析之消息事件机制

在这里,用单步跟踪我们可以知道实际调用的是QGuiEventDispatcherWin32::processEvents(),看其实现代码:

3、QT分析之消息事件机制

 1 bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
2 {
3 if (!QEventDispatcherWin32::processEvents(flags))
4 return false;
5
6 if (configRequests) // any pending configs?
7 qWinProcessConfigRequests();
8
9 return true;
10 }

3、QT分析之消息事件机制

继续深入九层地狱,看QEventDispatcherWin32::processEvents()的定义:

3、QT分析之消息事件机制

  1 bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
2 {
3 Q_D(QEventDispatcherWin32);
4
5 if (!d->internalHwnd)
6 createInternalHwnd();
7
8 d->interrupt = false;
9 emit awake(); // emit在Win32平台下实际就是空的
10
11 bool canWait;
12 bool retVal = false;
13 do {
14 QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); //这句没有深入分析
15
16 DWORD waitRet = 0;
17 HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
18 QVarLengthArray<MSG> processedTimers;
19 while (!d->interrupt) {
20 DWORD nCount = d->winEventNotifierList.count();
21 Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
22
23 MSG msg;
24 bool haveMessage;
25
26 // 使用用户输入事件并且该事件队列不为空
27
28 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
29 // process queued user input events
30 haveMessage = true;
31 msg = d->queuedUserInputEvents.takeFirst();
32 }
33
34 // 使用Socket通知事件并且该事件队列不为空
35
36 else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
37 // process queued socket events
38 haveMessage = true;
39 msg = d->queuedSocketEvents.takeFirst();
40 } else {
41
42 // 所有其他情况,Peek一下Windows的消息
43 haveMessage = winPeekMessage(&msg, 0, 0, 0, PM_REMOVE);
44
45 // 根据消息种类放置到相应消息队列,备后面处理使用
46 if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
47 && ((msg.message >= WM_KEYFIRST
48 && msg.message <= WM_KEYLAST)
49 || (msg.message >= WM_MOUSEFIRST
50 && msg.message <= WM_MOUSELAST)
51 || msg.message == WM_MOUSEWHEEL)) {
52 // queue user input events for later processing
53 haveMessage = false;
54 d->queuedUserInputEvents.append(msg);
55 }
56 if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
57 && (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) {
58 // queue socket events for later processing
59 haveMessage = false;
60 d->queuedSocketEvents.append(msg);
61 }
62 }
63 if (!haveMessage) {
64 // no message - check for signalled objects // 没有消息的情况下,等待事件通知
65 for (int i=0; i<(int)nCount; i++)
66 pHandles[i] = d->winEventNotifierList.at(i)->handle();
67 waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
68 if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
69 // a new message has arrived, process it
70 continue;
71 }
72 }
73 if (haveMessage) {
74
75 // 定时事件的处理
76 if (msg.message == WM_TIMER) {
77 // avoid live-lock by keeping track of the timers we've already sent
78 bool found = false;
79 for (int i = 0; !found && i < processedTimers.count(); ++i) {
80 const MSG processed = processedTimers.constData()[i];
81 found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
82 }
83 if (found)
84 continue;
85 processedTimers.append(msg);
86 } else if (msg.message == WM_QUIT) {
87
88 // 退出事件的处理
89 if (QCoreApplication::instance())
90 QCoreApplication::instance()->quit();
91 return false;
92 }
93
94 if (!filterEvent(&msg)) {
95
96 // 如果没有被[消息过滤器]过滤掉,那么就派发该消息
97 TranslateMessage(&msg);
98 QT_WA({
99 DispatchMessage(&msg);
100 } , {
101 DispatchMessageA(&msg);
102 });
103 }
104 } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
105 d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
106 } else {
107 // nothing todo so break
108 break;
109 }
110 retVal = true;
111 }
112
113 // still nothing - wait for message or signalled objects
114 QThreadData *data = d->threadData;
115 canWait = (!retVal
116 && data->canWait
117 && !d->interrupt
118 && (flags & QEventLoop::WaitForMoreEvents));
119 if (canWait) {
120 DWORD nCount = d->winEventNotifierList.count();
121 Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
122 for (int i=0; i<(int)nCount; i++)
123 pHandles[i] = d->winEventNotifierList.at(i)->handle();
124
125 emit aboutToBlock();
126 waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
127 emit awake();
128 if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
129 d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
130 retVal = true;
131 }
132 }
133 } while (canWait);
134
135 return retVal;
136 }

3、QT分析之消息事件机制

至此,一个完整的路径似乎分析完毕了,等等,好像不太对。前面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类的定义:

3、QT分析之消息事件机制

 1 class Q_CORE_EXPORT QEventLoop : public QObject
2 {
3 Q_OBJECT
4 Q_DECLARE_PRIVATE(QEventLoop)
5
6 public:
7 explicit QEventLoop(QObject *parent = 0);
8 ~QEventLoop();
9
10 enum ProcessEventsFlag {
11 AllEvents = 0x00,
12 ExcludeUserInputEvents = 0x01,
13 ExcludeSocketNotifiers = 0x02,
14 WaitForMoreEvents = 0x04,
15 #ifdef QT3_SUPPORT
16 ExcludeUserInput = ExcludeUserInputEvents,
17 WaitForMore = WaitForMoreEvents,
18 #endif
19 X11ExcludeTimers = 0x08
20 #ifdef QT_DEPRECATED
21 , DeferredDeletion = 0x10
22 #endif
23 , EventLoopExec = 0x20
24 , DialogExec = 0x40
25 };
26 Q_DECLARE_FLAGS(ProcessEventsFlags, ProcessEventsFlag)
27
28 bool processEvents(ProcessEventsFlags flags = AllEvents);
29 void processEvents(ProcessEventsFlags flags, int maximumTime);
30
31 int exec(ProcessEventsFlags flags = AllEvents);
32 void exit(int returnCode = 0);
33 bool isRunning() const;
34
35 void wakeUp();
36
37 public Q_SLOTS:
38 void quit();
39 };

3、QT分析之消息事件机制

果然看到了我们想看的东西,把QEventLoop::exit(int returnCode)函数代码找到:

3、QT分析之消息事件机制

 1 void QEventLoop::exit(int returnCode)
2 {
3 Q_D(QEventLoop);
4 if (!d->threadData->eventDispatcher)
5 return;
6
7 d->returnCode = returnCode;
8 d->exit = true;
9 d->threadData->eventDispatcher->interrupt();
10 }

3、QT分析之消息事件机制

添加一个断点,运行程序点击退出按钮之后,我们可以得到下面的堆栈列表:

3、QT分析之消息事件机制

 1 QtCored4.dll!QEventLoop::exit(int returnCode=0x00000000)  行284 C++
2 QtCored4.dll!QCoreApplication::exit(int returnCode=0x00000000) 行926 + 0xc 字节 C++
3 QtCored4.dll!QCoreApplication::quit() 行1468 + 0x7 字节 C++
4 QtCored4.dll!QCoreApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000002, void * * _a=0x0012bd28) 行84 C++
5 QtGuid4.dll!QApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000006, void * * _a=0x0012bd28) 行96 + 0x15 字节 C++
6 QtCored4.dll!QMetaObject::activate(QObject * sender=0x0012ff40, int from_signal_index=0x0000001d, int to_signal_index=0x0000001e, void * * argv=0x0012bd28) 行3104 + 0x2b 字节 C++
7 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++
8 QtGuid4.dll!QAbstractButton::clicked(bool _t1=false) 行198 + 0x17 字节 C++
9 QtGuid4.dll!QAbstractButtonPrivate::emitClicked() 行545 C++
10 QtGuid4.dll!QAbstractButtonPrivate::click() 行537 C++
11 QtGuid4.dll!QAbstractButton::mouseReleaseEvent(QMouseEvent * e=0x0012c450) 行1116 C++
12 QtGuid4.dll!QWidget::event(QEvent * event=0x0012c450) 行7555 C++
13 QtGuid4.dll!QAbstractButton::event(QEvent * e=0x0012c450) 行1078 C++
14 QtGuid4.dll!QPushButton::event(QEvent * e=0x0012c450) 行663 C++
15 QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450) 行4065 + 0x11 字节 C++
16 QtGuid4.dll!QApplication::notify(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450) 行3767 + 0x2f 字节 C++
17 QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450) 行610 + 0x15 字节 C++
18 QtCored4.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450) 行216 + 0x38 字节 C++
19 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++
20 QtGuid4.dll!QETWidget::translateMouseEvent(const tagMSG & msg={...}) 行3269 + 0x28 字节 C++
21 QtGuid4.dll!QtWndProc(HWND__ * hwnd=0x00010840, unsigned int message=0x00000202, unsigned int wParam=0x00000000, long lParam=0x000c0064) 行1667 + 0xc 字节 C++
22 user32.dll!77e2b6e3()
23

3、QT分析之消息事件机制

[下面的框架可能不正确和/或缺失,没有为 user32.dll 加载符号]

3、QT分析之消息事件机制

 1 user32.dll!77e2b874()
2 user32.dll!77e2b82a()
3 user32.dll!77e2ba92()
4 user32.dll!77e2bad0()
5 QtCored4.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行751 + 0x17 字节 C++
6 QtGuid4.dll!QGuiEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行1182 + 0x15 字节 C++
7 QtCored4.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行150 C++
8 QtCored4.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行201 + 0x2d 字节 C++
9 QtCored4.dll!QCoreApplication::exec() 行888 + 0x15 字节 C++
10 QtGuid4.dll!QApplication::exec() 行3526 C++
11 test.exe!main(int argc=0x00000001, char * * argv=0x00ba7040) 行14 + 0x6 字节 C++
12 test.exe!__tmainCRTStartup() 行582 + 0x19 字节 C
13 test.exe!mainCRTStartup() 行399 C
14 kernel32.dll!7c82f23b()

3、QT分析之消息事件机制

这里我们清晰的看到了消息的传递路径,而且也看到了Clicked()和quit()的调用前后次序

我们继续昨天之分析,QEventDispatcherWin32::processEvents()派发消息之后,QtWndProc()获得该消息。我们看看QtWndProc()的具体处理:(简略)

1、根据hwnd获取QWidget指针:

1 widget = (QETWidget*)QWidget::find(hwnd);

2、处理事件:

1 result = widget->translateMouseEvent(msg);        // mouse event

获取的widget其实就是QPushButton对象的指针;事件的处理实体在QETWidget::translateMouseEvent(),该函数又是一个超长代码的实现,其处理是事件压缩之类Mouse事件的周边处理,之后是调用:

1 QApplicationPrivate::sendMouseEvent(),

我们看其实现:

3、QT分析之消息事件机制

 1 bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
2 QWidget *alienWidget, QWidget *nativeWidget,
3 QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver)
4 {
5 Q_ASSERT(receiver);
6 Q_ASSERT(event);
7 Q_ASSERT(nativeWidget);
8 Q_ASSERT(buttonDown);
9
10 if (alienWidget && !isAlien(alienWidget))
11 alienWidget = 0;
12
13 QPointer<QWidget> receiverGuard = receiver;
14 QPointer<QWidget> nativeGuard = nativeWidget;
15 QPointer<QWidget> alienGuard = alienWidget;
16 QPointer<QWidget> activePopupWidget = qApp->activePopupWidget();
17
18 const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen);
19
20 if (*buttonDown) {
21
22 // 如果是按钮&是图片窗体的话,注册该窗体使得最后一个按钮释放的时候接受leave事件
23 if (!graphicsWidget) {
24 // Register the widget that shall receive a leave event
25 // after the last button is released.
26 if ((alienWidget || !receiver->internalWinId()) && !leaveAfterRelease && !QWidget::mouseGrabber())
27 leaveAfterRelease = *buttonDown;
28 if (event->type() == QEvent::MouseButtonRelease && !event->buttons())
29 *buttonDown = 0;
30 }
31 } else if (lastMouseReceiver) {
32 // Dispatch enter/leave if we move:
33 // 1) from an alien widget to another alien widget or
34 // from a native widget to an alien widget (first OR case)
35 // 2) from an alien widget to a native widget (second OR case)
36 if ((alienWidget && alienWidget != lastMouseReceiver)
37 || (isAlien(lastMouseReceiver) && !alienWidget)) {
38 if (activePopupWidget) {
39 if (!QWidget::mouseGrabber())
40 dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver);
41 } else {
42 dispatchEnterLeave(receiver, lastMouseReceiver);
43 }
44
45 }
46 }
47
48 #ifdef ALIEN_DEBUG
49 qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver
50 << "pos:" << event->pos() << "alien" << alienWidget << "button down"
51 << *buttonDown << "last" << lastMouseReceiver << "leave after release"
52 << leaveAfterRelease;
53 #endif
54
55 // We need this quard in case someone opens a modal dialog / popup. If that's the case
56 // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver.
57 const bool wasLeaveAfterRelease = leaveAfterRelease != 0;
58 bool result = QApplication::sendSpontaneousEvent(receiver, event);
59
60 if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease
61 && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) {
62 // Dispatch enter/leave if:
63 // 1) the mouse grabber is an alien widget
64 // 2) the button is released on an alien widget
65
66 QWidget *enter = 0;
67 if (nativeGuard)
68 enter = alienGuard ? alienWidget : nativeWidget;
69 else // The receiver is typically deleted on mouse release with drag'n'drop.
70 enter = QApplication::widgetAt(event->globalPos());
71
72 dispatchEnterLeave(enter, leaveAfterRelease);
73 leaveAfterRelease = 0;
74 lastMouseReceiver = enter;
75 } else if (!wasLeaveAfterRelease) {
76 if (activePopupWidget) {
77 if (!QWidget::mouseGrabber())
78 lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : 0);
79 } else {
80 lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos());
81 }
82 }
83
84 return result;
85 }

3、QT分析之消息事件机制

根据单步跟踪(也可以通过代码分析)知道QApplication::sendSpontaneousEvent()调用的是QCoreApplication::sendSpontaneousEvent(),该函数判断self指针(this指针?)是否为空,不为空则调用notifyInternal()函数;也就是调用QCoreApplication::notifyInternal(),参数为receiver(也就是根据hwnd获取的QPushButton对象指针),和event。我们看其实现代码:

3、QT分析之消息事件机制

 1 bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
2 {
3 // Make it possible for Qt Jambi and QSA to hook into events even
4 // though QApplication is subclassed...
5 bool result = false;
6 void *cbdata[] = { receiver, event, &result };
7
8 // 检查CallBack列表中是否有QInternal::EventNotifyCallback类型,有的话则调用之
9
10 // 该CallBack的返回必须为True,否则此处会继续往下运行
11 if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
12 return result;
13 }
14
15 // Qt enforces the rule that events can only be sent to objects in
16 // the current thread, so receiver->d_func()->threadData is
17 // equivalent to QThreadData::current(), just without the function
18 // call overhead.
19 QObjectPrivate *d = receiver->d_func();
20 QThreadData *threadData = d->threadData;
21 ++threadData->loopLevel;
22
23 #ifdef QT_JAMBI_BUILD
24 int deleteWatch = 0;
25 int *oldDeleteWatch = QObjectPrivate::setDeleteWatch(d, &deleteWatch);
26
27 bool inEvent = d->inEventHandler;
28 d->inEventHandler = true;
29 #endif
30
31 #if defined(QT_NO_EXCEPTIONS)
32 bool returnValue = notify(receiver, event);
33 #else
34 bool returnValue;
35 try {
36 returnValue = notify(receiver, event); // QCoreApplication中这是一个虚函数
37 } catch(...) {
38 --threadData->loopLevel;
39 throw;
40 }
41 #endif
42
43 #ifdef QT_JAMBI_BUILD
44 // Restore the previous state if the object was not deleted..
45 if (!deleteWatch) {
46 d->inEventHandler = inEvent;
47 }
48 QObjectPrivate::resetDeleteWatch(d, oldDeleteWatch, deleteWatch);
49 #endif
50 --threadData->loopLevel;
51 return returnValue;
52 }

3、QT分析之消息事件机制

从上面的分析,我们继续看QApplication::notify()的实现:(为方便查看,大部分无关代码删除了)

3、QT分析之消息事件机制

  1 bool QApplication::notify(QObject *receiver, QEvent *e)
2 {
3 Q_D(QApplication);
4
5 ……
6 bool res = false;
7 if (!receiver->isWidgetType()) {
8 res = d->notify_helper(receiver, e);
9 } else switch (e->type()) {
10 case QEvent::ShortcutOverride:
11 case QEvent::KeyPress:
12 case QEvent::KeyRelease:
13
14 ……
15 break;
16 case QEvent::MouseButtonPress:
17 case QEvent::MouseButtonRelease:
18 case QEvent::MouseButtonDblClick:
19 case QEvent::MouseMove:
20 {
21 QWidget* w = static_cast<QWidget *>(receiver);
22
23 QMouseEvent* mouse = static_cast<QMouseEvent*>(e);
24 QPoint relpos = mouse->pos();
25
26 if (e->spontaneous()) {
27
28 if (e->type() == QEvent::MouseButtonPress) {
29 QWidget *fw = w;
30 while (fw) {
31 if (fw->isEnabled()
32 && QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) {
33 fw->setFocus(Qt::MouseFocusReason);
34 break;
35 }
36 if (fw->isWindow())
37 break;
38 fw = fw->parentWidget();
39 }
40 }
41
42 // ### Qt 5 These dynamic tool tips should be an OPT-IN feature. Some platforms
43 // …… 这里将来Qt5版本的实现描述。
44 if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) {
45 d->toolTipWidget = w;
46 d->toolTipPos = relpos;
47 d->toolTipGlobalPos = mouse->globalPos();
48 d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive()?20:700, this);
49 }
50 }
51
52 bool eventAccepted = mouse->isAccepted();
53
54 QPointer<QWidget> pw = w;
55 while (w) {
56 QMouseEvent me(mouse->type(), relpos, mouse->globalPos(), mouse->button(), mouse->buttons(),
57 mouse->modifiers());
58 me.spont = mouse->spontaneous();
59 // throw away any mouse-tracking-only mouse events
60 if (!w->hasMouseTracking()
61 && mouse->type() == QEvent::MouseMove && mouse->buttons() == 0) {
62 // but still send them through all application event filters (normally done by notify_helper)
63 for (int i = 0; i < d->eventFilters.size(); ++i) {
64 register QObject *obj = d->eventFilters.at(i);
65 if (!obj)
66 continue;
67 if (obj->d_func()->threadData != w->d_func()->threadData) {
68 qWarning("QApplication: Object event filter cannot be in a different thread.");
69 continue;
70 }
71 if (obj->eventFilter(w, w == receiver ? mouse : &me))
72 break;
73 }
74 res = true;
75 } else {
76 w->setAttribute(Qt::WA_NoMouseReplay, false);
77 res = d->notify_helper(w, w == receiver ? mouse : &me);
78 e->spont = false;
79 }
80 eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
81 if (res && eventAccepted)
82 break;
83 if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
84 break;
85 relpos += w->pos();
86 w = w->parentWidget();
87 }
88
89 mouse->setAccepted(eventAccepted);
90
91 if (e->type() == QEvent::MouseMove) {
92 if (!pw)
93 break;
94
95 w = static_cast<QWidget *>(receiver);
96 relpos = mouse->pos();
97 QPoint diff = relpos - w->mapFromGlobal(d->hoverGlobalPos);
98 while (w) {
99 if (w->testAttribute(Qt::WA_Hover) &&
100 (!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) {
101 QHoverEvent he(QEvent::HoverMove, relpos, relpos - diff);
102 d->notify_helper(w, &he);
103 }
104 if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
105 break;
106 relpos += w->pos();
107 w = w->parentWidget();
108 }
109 }
110
111 d->hoverGlobalPos = mouse->globalPos();
112 }
113 break;
114
115 ……
116 default:
117 res = d->notify_helper(receiver, e);
118 break;
119 }
120
121 return res;
122 }

3、QT分析之消息事件机制

其中res = d->notify_helper(w, w == receiver ? mouse : &me);调用的是QApplicationPrivate::notify_helper(),我们看其具体实现:

3、QT分析之消息事件机制

 1 bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
2 {
3 // send to all application event filters
4 if (sendThroughApplicationEventFilters(receiver, e))
5 return true;
6
7 if (receiver->isWidgetType()) {
8 QWidget *widget = static_cast<QWidget *>(receiver);
9
10 #if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR))
11 // toggle HasMouse widget state on enter and leave
12 if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
13 (!qApp->activePopupWidget() || qApp->activePopupWidget() == widget->window()))
14 widget->setAttribute(Qt::WA_UnderMouse, true);
15 else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
16 widget->setAttribute(Qt::WA_UnderMouse, false);
17 #endif
18
19 if (QLayout *layout=widget->d_func()->layout) {
20 layout->widgetEvent(e);
21 }
22 }
23
24 // send to all receiver event filters
25 if (sendThroughObjectEventFilters(receiver, e))
26 return true;
27
28 // deliver the event
29 bool consumed = receiver->event(e); // 调用QPushButton::event()
30 e->spont = false;
31 return consumed;
32 }

3、QT分析之消息事件机制

继续看QPushButton::event()的实现:

3、QT分析之消息事件机制

 1 bool QPushButton::event(QEvent *e)
2 {
3 Q_D(QPushButton);
4 if (e->type() == QEvent::ParentChange) {
5 if (QDialog *dialog = d->dialogParent()) {
6 if (d->defaultButton)
7 dialog->d_func()->setMainDefault(this);
8 }
9 } else if (e->type() == QEvent::StyleChange
10 #ifdef Q_WS_MAC
11 || e->type() == QEvent::MacSizeChange
12 #endif
13 ) {
14 d->resetLayoutItemMargins();
15 updateGeometry();
16 }
17 return QAbstractButton::event(e);
18 }

3、QT分析之消息事件机制

我们进一步看QAbstractButton::event的实现,忽略次要处理,主要是调用QWidget::event(e);

QWidget::event(QEvent *event)主体是根据参数event的类型switch分支处理各种事件,我们回头看QETWidget::translateMouseEvent(const MSG &msg)中,event对象生成时候的语句:

1 QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button),
2 Qt::MouseButtons(state & Qt::MouseButtonMask),
3 Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));

根据查找,这里type内容是MouseButtonPress。同样忽略其他无关代码,我们看QWidget::event()的代码:

3、QT分析之消息事件机制

 1 bool QWidget::event(QEvent *event)
2 {
3 Q_D(QWidget);
4 ……
5
6 switch (event->type()) {
7 case QEvent::MouseMove:
8 mouseMoveEvent((QMouseEvent*)event);
9 break;
10
11 case QEvent::MouseButtonPress:
12 // Don't reset input context here. Whether reset or not is
13 // a responsibility of input method. reset() will be
14 // called by mouseHandler() of input method if necessary
15 // via mousePressEvent() of text widgets.
16 mousePressEvent((QMouseEvent*)event);
17 break;
18 case QEvent::MouseButtonRelease:
19 mouseReleaseEvent((QMouseEvent*)event);
20 break;
21 ……
22
23 default:
24 return QObject::event(event);
25 }
26 return true;
27 }

3、QT分析之消息事件机制

我们看该事件处理的具体代码:

3、QT分析之消息事件机制

 1 void QAbstractButton::mousePressEvent(QMouseEvent *e)
2 {
3 Q_D(QAbstractButton);
4 if (e->button() != Qt::LeftButton) {
5 e->ignore();
6 return;
7 }
8 if (hitButton(e->pos())) { // <-- 根据鼠标点击点的位置匹配按钮
9 setDown(true);
10 repaint(); //flush paint event before invoking potentially expensive operation
11 QApplication::flush();
12 d->emitPressed();
13 e->accept();
14 } else {
15 e->ignore();
16 }
17 }

3、QT分析之消息事件机制

在MouseButtonProcess事件处理之后,就是前面注册的Release事件(前面粉色部分代码)。

目前还有两个疑问:一个是自定义的信号(SIGNAL)如何跟Windows的消息关联的;另一个是信号和槽(SLOT)是如何关联的。根据前面的分析,对第一个问题的猜测是在event()函数里增加相应处理;对第二个问题,应该有一个函数指针表使之关联。带着这些问题我们接着分析mouseReleaseEvent()。

3、QT分析之消息事件机制

 1 void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
2 {
3 Q_D(QAbstractButton);
4 if (e->button() != Qt::LeftButton) {
5 e->ignore();
6 return;
7 }
8
9 if (!d->down) {
10 e->ignore();
11 return;
12 }
13
14 if (hitButton(e->pos())) {
15 d->repeatTimer.stop();
16 d->click(); // 调用QAbstractButtonPrivate::click()
17 e->accept();
18 } else {
19 setDown(false);
20 e->ignore();
21 }
22 }

3、QT分析之消息事件机制

进一步看QAbstractButtonPrivate::click()的实现代码:

3、QT分析之消息事件机制

 1 void QAbstractButtonPrivate::click()
2 {
3 Q_Q(QAbstractButton);
4
5 down = false;
6 blockRefresh = true;
7 bool changeState = true;
8 if (checked && queryCheckedButton() == q) {
9 // the checked button of an exclusive or autoexclusive group cannot be unchecked
10 #ifndef QT_NO_BUTTONGROUP
11 if (group ? group->d_func()->exclusive : autoExclusive)
12 #else
13 if (autoExclusive)
14 #endif
15 changeState = false;
16 }
17
18 QPointer<QAbstractButton> guard(q);
19 if (changeState) {
20 q->nextCheckState();
21 if (!guard)
22 return;
23 }
24 blockRefresh = false;
25 refresh();
26 q->repaint(); //flush paint event before invoking potentially expensive operation
27 QApplication::flush();
28 if (guard)
29 emitReleased();
30 if (guard)
31 emitClicked();
32 }

3、QT分析之消息事件机制

主要就是调用emitReleased()和emitClicked(),我们看其中QAbstractButtonPrivate::emitClicked()的实现

3、QT分析之消息事件机制

 1 void QAbstractButtonPrivate::emitClicked()
2 {
3 Q_Q(QAbstractButton);
4 QPointer<QAbstractButton> guard(q);
5 emit q->clicked(checked); // 这里调用的是QAbstractButton::clicked()
6 #ifndef QT_NO_BUTTONGROUP
7 if (guard && group) {
8 emit group->buttonClicked(group->id(q));
9 if (guard && group)
10 emit group->buttonClicked(q);
11 }
12 #endif
13 }

3、QT分析之消息事件机制

上面调用的函数实体,在moc_QAbstractButton.cpp里可以看到。这个文件实际是由QT的MOC工具自动产生的。

1 void QAbstractButton::clicked(bool _t1)
2 {
3 void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
4 QMetaObject::activate(this, &staticMetaObject, 2, 3, _a); // 和SLOT的调用关系应该是这里实现的。
5 }

我们先来看看QMetaObject类的定义:

3、QT分析之消息事件机制

 1 struct Q_CORE_EXPORT QMetaObject
2 {
3
4 ……
5
6 struct { // private data
7 const QMetaObject *superdata;
8 const char *stringdata;
9 const uint *data;
10 const void *extradata;
11 } d;
12 };

3、QT分析之消息事件机制

再看QMetaObjectPrivate的定义:

3、QT分析之消息事件机制

 1 struct QMetaObjectPrivate
2 {
3 int revision;
4 int className;
5 int classInfoCount, classInfoData;
6 int methodCount, methodData;
7 int propertyCount, propertyData;
8 int enumeratorCount, enumeratorData;
9 int constructorCount, constructorData;
10 };

3、QT分析之消息事件机制

实际上QMetaObject::d.data指向的就是QMetaObjectPrivate结构体 我们看看staticMetaObject对象的定义:(同样在moc_QAbstractButton.cpp文件中)

1 const QMetaObject QAbstractButton::staticMetaObject = {
2 { &QWidget::staticMetaObject, qt_meta_stringdata_QAbstractButton,
3 qt_meta_data_QAbstractButton, 0 }
4 };

这是一个常对象,除设定父类的staticMetaObject外,还设定了两个全局变量:qt_meta_stringdata_QAbstractButton和qt_meta_data_QAbstractButton。

所有的奥秘都在这两个变量里面了。根据前面分析qt_meta_data_QAbstractButton实际是QMetaObjectPrivate结构。

3、QT分析之消息事件机制

 1 static const uint qt_meta_data_QAbstractButton[] = {
2
3 // content:
4 2, // revision
5 0, // classname
6 0, 0, // classinfo
7 11, 12, // methods
8 11, 67, // properties
9 0, 0, // enums/sets
10 0, 0, // constructors
11
12 // signals: signature, parameters, type, tag, flags
13
14 17, 16, 16, 16, 0x05,
15 // 17 -- 指的是stringdata中No.17字节开始的字符串,结合下面定义实际就是pressed()
16 27, 16, 16, 16, 0x05, // released()
17 46, 38, 16, 16, 0x05, // clicked(bool)
18 60, 16, 16, 16, 0x25, // clicked()
19 70, 38, 16, 16, 0x05, // toggled(bool)
20
21 // slots: signature, parameters, type, tag, flags
22 89, 84, 16, 16, 0x0a, // setIconSize(QSize)
23 113, 108, 16, 16, 0x0a,
24 131, 16, 16, 16, 0x2a,
25 146, 16, 16, 16, 0x0a,
26 154, 16, 16, 16, 0x0a,
27 163, 16, 16, 16, 0x0a,
28
29 // properties: name, type, flags
30 188, 180, 0x0a095103, // text
31 199, 193, 0x45095103, // icon
32 210, 204, 0x15095103,
33 232, 219, 0x4c095103,
34 246, 241, 0x01095103,
35 38, 241, 0x01595103,
36 256, 241, 0x01095103,
37 267, 241, 0x01095103,
38 285, 281, 0x02095103,
39 301, 281, 0x02095103,
40 320, 241, 0x01094103,
41
42 // properties: notify_signal_id
43 0,
44 0,
45 0,
46 0,
47 0,
48 4,
49 0,
50 0,
51 0,
52 0,
53 0,
54
55 0 // eod
56 };
57
58 static const char qt_meta_stringdata_QAbstractButton[] = {
59 "QAbstractButton\0\0pressed()\0released()\0"
60 "checked\0clicked(bool)\0clicked()\0"
61 "toggled(bool)\0size\0setIconSize(QSize)\0"
62 "msec\0animateClick(int)\0animateClick()\0"
63 "click()\0toggle()\0setChecked(bool)\0"
64 "QString\0text\0QIcon\0icon\0QSize\0iconSize\0"
65 "QKeySequence\0shortcut\0bool\0checkable\0"
66 "autoRepeat\0autoExclusive\0int\0"
67 "autoRepeatDelay\0autoRepeatInterval\0"
68 "down\0"
69 };

3、QT分析之消息事件机制

我们接着看QMetaObject::activate()的代码:

3、QT分析之消息事件机制

 1 void QMetaObject::activate(QObject *sender, const QMetaObject *m,
2 int from_local_signal_index, int to_local_signal_index, void **argv)
3 {
4 int offset = m->methodOffset(); // 指向qt_meta_data_QAbstractButton[27]字节,也就是clicked(bool)
5 int from_signal_index = offset + from_local_signal_index; // 27 + 2 = 29
6 int to_signal_index = offset + to_local_signal_index; // 27 + 3 = 30
7 if (to_signal_index < 32
8 && !qt_signal_spy_callback_set.signal_begin_callback
9 && !qt_signal_spy_callback_set.signal_end_callback) {
10 uint signal_mask = (1 << (to_signal_index + 1)) - 1;
11 signal_mask ^= (1 << from_signal_index) - 1;
12
13 // sender指的是QPushButton,下面指向的是:QPushButtonPrivate::connectedSignals
14 if ((sender->d_func()->connectedSignals & signal_mask) == 0)
15 // nothing connected to these signals, and no spy
16 return;
17 }
18 activate(sender, from_signal_index, to_signal_index, argv);
19 }

3、QT分析之消息事件机制

可以看到,在判断本信号是否连接有槽之后,就调用了activate的重载函数:

3、QT分析之消息事件机制

  1 void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)
2 {
3 if (sender->d_func()->blockSig)
4 return;
5
6 void *empty_argv[] = { 0 };
7 if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
8 qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,
9 argv ? argv : empty_argv);
10 }
11
12 QMutexLocker locker(&sender->d_func()->threadData->mutex);
13 QThreadData *currentThreadData = QThreadData::current();
14
15 QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
16 if (!connectionLists) {
17 if (qt_signal_spy_callback_set.signal_end_callback != 0)
18 qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
19 return;
20 }
21 ++connectionLists->inUse;
22
23 // emit signals in the following order: from_signal_index <= signals <= to_signal_index, signal < 0
24 for (int signal = from_signal_index;
25 (signal >= from_signal_index && signal <= to_signal_index) || (signal == -2);
26 (signal == to_signal_index ? signal = -2 : ++signal))
27 {
28 if (signal >= connectionLists->count()) {
29 signal = to_signal_index;
30 continue;
31 }
32 int count = connectionLists->at(signal).count();
33
34 // 就是在这里获取信号接收的槽函数指针的。
35
36 // connectionLists里的数据,猜测是由QObject::connect()填进去的。
37 for (int i = 0; i < count; ++i) {
38 const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i];
39 if (!c->receiver)
40 continue;
41
42 QObject * const receiver = c->receiver;
43
44 // determine if this connection should be sent immediately or
45 // put into the event queue
46 if ((c->connectionType == Qt::AutoConnection
47 && (currentThreadData != sender->d_func()->threadData
48 || receiver->d_func()->threadData != sender->d_func()->threadData))
49 || (c->connectionType == Qt::QueuedConnection)) {
50 queued_activate(sender, signal, *c, argv);
51 continue;
52 } else if (c->connectionType == Qt::BlockingQueuedConnection) {
53 blocking_activate(sender, signal, *c, argv);
54 continue;
55 }
56
57 const int method = c->method;
58 QObjectPrivate::Sender currentSender;
59 currentSender.sender = sender;
60 currentSender.signal = signal < 0 ? from_signal_index : signal;
61 currentSender.ref = 1;
62 QObjectPrivate::Sender *previousSender = 0;
63 if (currentThreadData == receiver->d_func()->threadData)
64 previousSender = QObjectPrivate::setCurrentSender(receiver, &currentSender);
65 locker.unlock();
66
67 if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
68 qt_signal_spy_callback_set.slot_begin_callback(receiver,
69 method,
70 argv ? argv : empty_argv);
71 }
72
73 #if defined(QT_NO_EXCEPTIONS)
74 receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
75 #else
76 try {
77
78 // 在我们的分析中,连接的槽是QApplication::quit(),qt_metacall在哪定义的呢?
79 receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
80 } catch (...) {
81 locker.relock();
82
83 QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);
84
85 --connectionLists->inUse;
86 Q_ASSERT(connectionLists->inUse >= 0);
87 if (connectionLists->orphaned && !connectionLists->inUse)
88 delete connectionLists;
89 throw;
90 }
91 #endif
92
93 locker.relock();
94
95 if (qt_signal_spy_callback_set.slot_end_callback != 0)
96 qt_signal_spy_callback_set.slot_end_callback(receiver, method);
97
98 QObjectPrivate::resetCurrentSender(receiver, &currentSender, previousSender);
99
100 if (connectionLists->orphaned)
101 break;
102 }
103
104 if (connectionLists->orphaned)
105 break;
106 }
107
108 --connectionLists->inUse;
109 Q_ASSERT(connectionLists->inUse >= 0);
110 if (connectionLists->orphaned) {
111 if (!connectionLists->inUse)
112 delete connectionLists;
113 } else {
114 sender->d_func()->cleanConnectionLists();
115 }
116
117 locker.unlock();
118
119 if (qt_signal_spy_callback_set.signal_end_callback != 0)
120 qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
121 }

3、QT分析之消息事件机制

单步跟踪,receiver->qt_metacall();实际调用的是QApplication::qt_metacall(),根据调用参数实现不同的函数调用。在moc_QApplication.cpp中定义,是由MOC工具自动产生的代码。至此,信号与槽的关联分析完毕。

明天接着分析QObject::connect()如何把相关数据填入connectionLists。自定义信号如何与windows消息关联在明天分析完毕之后再来证实。

在继续分析之前,我们回头看看test.cpp的main()函数:

3、QT分析之消息事件机制

 1 int main( int argc, char **argv )
2 {
3 QApplication a( argc, argv );
4 QPushButton quit( "Quit", 0 );
5 quit.resize( 75, 30 );
6 quit.setFont( QFont( "Times", 18, QFont::Bold ) );
7 QObject::connect( &quit, SIGNAL(clicked()), &a, SLOT(quit()) );
8 quit.show();
9 return a.exec();
10 }

3、QT分析之消息事件机制

根据我们猜测,就是上面红色部分语句把消息处理函数指针填入了connectionLists。

首先我们看看SIGNAL宏和SLOT宏的定义:(另外一种定义是为DEBUG用的,可忽略

1 # define METHOD(a)   "0"#a
2 # define SLOT(a) "1"#a
3 # define SIGNAL(a) "2"#a

使用的是宏转义,SIGNAL(clicked())被展开成”2clicked()”(字符串);SLOT(quit())被展开成”1quit()”。

再看QObject::connect()的声明:

1 static bool connect(const QObject *sender, const char *signal,
2 const QObject *receiver,const char *member,
3 Qt::ConnectionType = Qt::AutoConnection );

上面的调用语句展开之后就是:

1 QObject::connect(&quit, "2clicked()", &a, "1quit()");

然后看QObject::connect()的定义:

3、QT分析之消息事件机制

  1 bool QObject::connect(const QObject *sender, const char *signal,
2 const QObject *receiver, const char *method,
3 Qt::ConnectionType type)
4 {
5 {
6 const void *cbdata[] = { sender, signal, receiver, method, &type };
7 if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
8 return true;
9 }
10
11 if (type == Qt::AutoCompatConnection) {
12 type = Qt::AutoConnection;
13 }
14
15 if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
16 qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
17 sender ? sender->metaObject()->className() : "(null)",
18 (signal && *signal) ? signal+1 : "(null)",
19 receiver ? receiver->metaObject()->className() : "(null)",
20 (method && *method) ? method+1 : "(null)");
21 return false;
22 }
23 QByteArray tmp_signal_name;
24
25 // 检查signal是否以2开头
26
27 if (!check_signal_macro(sender, signal, "connect", "bind"))
28 return false;
29 const QMetaObject *smeta = sender->metaObject();
30 const char *signal_arg = signal;
31 ++signal; //skip code
32
33 // 获得signal的函数编号
34 int signal_index = smeta->indexOfSignal(signal);
35 if (signal_index < 0) {
36 // check for normalized signatures
37 tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
38 signal = tmp_signal_name.constData() + 1;
39
40 signal_index = smeta->indexOfSignal(signal);
41 if (signal_index < 0) {
42 err_method_notfound(sender, signal_arg, "connect");
43 err_info_about_objects("connect", sender, receiver);
44 return false;
45 }
46 }
47
48 QByteArray tmp_method_name;
49 int membcode = extract_code(method);
50
51 // 检查receiver是否以1开头
52
53 if (!check_method_code(membcode, receiver, method, "connect"))
54 return false;
55 const char *method_arg = method;
56 ++method; // skip code
57
58 // 获得receiver的函数编号
59
60 const QMetaObject *rmeta = receiver->metaObject();
61 int method_index = -1;
62 switch (membcode) {
63 case QSLOT_CODE:
64 method_index = rmeta->indexOfSlot(method);
65 break;
66 case QSIGNAL_CODE:
67 method_index = rmeta->indexOfSignal(method);
68 break;
69 }
70 if (method_index < 0) {
71 // check for normalized methods
72 tmp_method_name = QMetaObject::normalizedSignature(method);
73 method = tmp_method_name.constData();
74 switch (membcode) {
75 case QSLOT_CODE:
76 method_index = rmeta->indexOfSlot(method);
77 break;
78 case QSIGNAL_CODE:
79 method_index = rmeta->indexOfSignal(method);
80 break;
81 }
82 }
83
84 if (method_index < 0) {
85 err_method_notfound(receiver, method_arg, "connect");
86 err_info_about_objects("connect", sender, receiver);
87 return false;
88 }
89 if (!QMetaObject::checkConnectArgs(signal, method)) {
90 qWarning("QObject::connect: Incompatible sender/receiver arguments"
91 "\n %s::%s --> %s::%s",
92 sender->metaObject()->className(), signal,
93 receiver->metaObject()->className(), method);
94 return false;
95 }
96
97 int *types = 0;
98 if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
99 && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
100 return false;
101
102 QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
103 const_cast<QObject*>(sender)->connectNotify(signal - 1);
104 return true;
105 }

3、QT分析之消息事件机制

用红色标记出来的三个主要调用,我们先看QInternal::activateCallbacks()的实现:

3、QT分析之消息事件机制

 1 bool QInternal::activateCallbacks(Callback cb, void **parameters)
2 {
3 Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");
4
5 QInternal_CallBackTable *cbt = global_callback_table();
6 if (cbt && cb < cbt->callbacks.size()) {
7 QList<qInternalCallback> callbacks = cbt->callbacks[cb];
8 bool ret = false;
9 for (int i=0; i<callbacks.size(); ++i)
10 ret |= (callbacks.at(i))(parameters);
11 return ret;
12 }
13 return false;
14 }

3、QT分析之消息事件机制

这是优先处理回调函数(钩子函数),在我们这里的应用中没有回调,所以可以忽略。

接着看QMetaObject::connect()的实现:

3、QT分析之消息事件机制

 1 bool QMetaObject::connect(const QObject *sender, int signal_index,
2 const QObject *receiver, int method_index, int type, int *types)
3 {
4 QObject *s = const_cast<QObject *>(sender);
5 QObject *r = const_cast<QObject *>(receiver);
6
7 QOrderedMutexLocker locker(&s->d_func()->threadData->mutex,
8 &r->d_func()->threadData->mutex);
9
10 QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) };
11 s->d_func()->addConnection(signal_index, &c);
12 r->d_func()->refSender(s, signal_index);
13
14 if (signal_index < 0)
15 sender->d_func()->connectedSignals = ~0u;
16 else if (signal_index < 32)
17 sender->d_func()->connectedSignals |= (1 << signal_index);
18
19 return true;
20 }

3、QT分析之消息事件机制

s->d_func()指向的是QPushButtonPrivate指针,QPushButtonPrivate没有addConnection()成员实际调用的是其基类成员,

s->d_func()->addConnection()调用的是QObjectPrivate::addConnection()。进一步看其实现:

3、QT分析之消息事件机制

 1 void QObjectPrivate::addConnection(int signal, Connection *c)
2 {
3 if (!connectionLists)
4 connectionLists = new QObjectConnectionListVector();
5 if (signal >= connectionLists->count())
6 connectionLists->resize(signal + 1);
7
8 ConnectionList &connectionList = (*connectionLists)[signal];
9 connectionList.append(*c);
10
11 cleanConnectionLists();
12 }

3、QT分析之消息事件机制

这里填入了发送消息的SIGNAL的函数指针!我们接着看r->d_func()->refSender(s, signal_index);其中r指向的是QApplication对象(a),所以r->d_func()是QApplicationPrivate对象指针,同样因其本身没有refSender()成员函数,调用的是其基类QObjectPrivate::refSender()。我们看其实现:

3、QT分析之消息事件机制

 1 void QObjectPrivate::refSender(QObject *sender, int signal)
2 {
3 for (int i = 0; i < senders.count(); ++i) {
4 Sender &s = senders[i];
5 if (s.sender == sender && s.signal == signal) {
6 ++s.ref;
7 return;
8 }
9 }
10
11 Sender s = { sender, signal, 1 };
12 senders.append(s);
13 }

3、QT分析之消息事件机制

至此,我们的猜想得到证实。分析完毕。

转自:http://www.cnblogs.com/lfsblack/p/5279143.html

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,918
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,444
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,255
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,069
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,702
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,741