首先看我上篇博客准备好环境:
现在进入主题。
先说答案:

因为qt源码的代码是这也写的:
eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
if (res && eventAccepted)
break;
w = w->parentWidget(); //不断寻找父窗口对象继续传递事件
源码详情,见我下面的图,就清楚了。
事件处理是异步的,也就是说你产生的事件(QWidget::update()函数,new出来一个paintEvent事件对象)会被QApplication::postEvent()放入Qt的消息队列队尾中,等待依次被处理。所以只有事件循环到这个事件的时候,期望的动作才会得到对应的执行。如果调用sendEvent()函数. 这时候事件不会放入队列, 而是直接被派发实现立即处理(qt直接调用QApplication::notify()了),此时就是同步的了。QWidget::repaint()函数用的就是这种方式。
if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) break
- //下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )
- bool CodeEditor::event(QEvent * event)
- {
- if (event->type() == QEvent::KeyPress)
- {
- QKeyEvent *keyEvent = (QKeyEvent *) event;
-
- if (keyEvent->key() == Key_Tab)
- {
- insertAtCurrentPosition('\t');
- return true;
- }
- }
- return QWidget::event(event);
- }
我们来看看 QWidget 的event()函数是如何定义的:(所以可以注意一下事件的accept状态的对返回值的影响)因此,上面的代码其实应该在第2个 if 里再加一句,event->accept();,当然好像事件有默认值的,虽传入event()函数前事件状态没有指定,但是打开QEvent构造函数,可知,m_accept变量是初始化为true的,也就是默认状态是accept的,所以只要都没有调用accept()函数和ignore()函数,那么事件状态默认就一直是accept状态的。而作为所有组件的父类QWidget的默认实现则是调用了ignore(),即鼓励继续向父对象(父控件)传递,所以QPushButton等控件,都是自己重新实现了事件处理函数,且调用的是accept()函数,就是不想给按钮的父控件传递了,这个确实应该这样,因为比如我们点击了按钮,当然不希望mainwindow等父控件也去响应一下,而是仅仅按钮自己响应这个事件即可。QAbstractButton部分源码: - void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
- {
- Q_D(QAbstractButton);
-
- xxxxxxxxxx....;
-
- if (e->button() != Qt::LeftButton) {
- e->ignore();
- return;
- }
-
- //检测mouseReleaseEvent事件发生时,坐标是否在按键区域内
- if (hitButton(e->pos())) {
- d->repeatTimer.stop();
- d->click();
- e->accept();//accept该事件,停止对事件往父对象(也就是父控件)转发
- } else {
- setDown(false);
- e->ignore();
- }
- /*!
- Contructs an event object of type \a type.
- */
- QEvent::QEvent(Type type)
- : d(0), t(type), posted(false), spont(false), m_accept(true)
- {}
- bool QWidget::event(QEvent *event)
- {
- switch (e->type()) {
- case QEvent::KeyPress:
- keyPressEvent((QKeyEvent *)event);
- if (!((QKeyEvent *)event)->isAccepted())
- return false;
- break;
- case QEvent::KeyRelease:
- keyReleaseEvent((QKeyEvent *)event);
- if (!((QKeyEvent *)event)->isAccepted())
- return false;
- break;
- // more...
- }
- return true;
- }
- MainWidget::MainWidget()
- {
- CodeEditor * ce = new CodeEditor( this, “code editor”);
- ce->installEventFilter( this );
- }
-
- bool MainWidget::eventFilter( QOject * target , QEvent * event )
- {
- if( target == ce )
- {
- if( event->type() == QEvent::KeyPress )
- {
- QKeyEvent *ke = (QKeyEvent *) event;
- if( ke->key() == Key_Tab )
- {
- ce->insertAtCurrentPosition('\t');
- return true;
- }
- }
- }
- return false;
- }
值得参考博客:Qt Event-暗夜linux-ChinaUnix博客
qt事件循环和处理过程(我跟踪源码得到的)如下图所示:只需要好好看这个图,你就能明白一切了
(目前,我仍然还有个疑问,关于windows的消息编程的那几个windows的api函数,我还是不清楚消息是怎么传递的???莫非一个是界面消息(比如鼠标点击),一个是系统异步事件消息(比如socket)???不知道我画的这个箭头传递方向对不对(我调试发现过程好像是这样的,但是原理是啥我不知道),知道的记得评论区告诉我喔)

熟悉了这个过程后,我们再来看几个例子练习一下:QT-qevent 事件的accept()和ignore()_luckyone906的博客-CSDN博客_event->accept()
threadData:记录自己线程上的事件派发器(eventDispatcher)、事件队列、对象列表等信息。是独一份的,每个派发器,派发对象它们都有它的指针
QApplication::exec()
QGuiApplication::exec();
QCoreApplication::exec();
QEventLoop eventLoop;
eventLoop.exec();
eventLoop.processEvents(flags | WaitForMoreEvents | EventLoopExec);
threadData->eventDispatcher.load()->processEvents(flags);
//eventDispatcher是基类指针,子类化的有QEventDispatcherWin32、QEventDispatcherBlackberry、QEventDispatcherUNIX等
//这个是在函数QCoreApplicationPrivate::createEventDispatcher()里面根据平台宏定义来创建的
QEventDispatcherWin32::createInternalHwnd()
//创建一个windows系统的隐形窗口,用于接收windows系统所有派发事件
static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher)
//里面为它注册了一个叫做qt_internal_proc的WNDPROC函数
QEventDispatcherWin32::installMessageHook()
//注册系统钩子qt_GetMessageHook函数,截获操作系统系统所有事件(注意:这个钩子函数是在操作系统自己的线程执行的)
while(canWait)
反复查询PeekMessage(&msg, 0, 0, 0, PM_REMOVE)的消息(这个就是上面钩子函数截获的消息)
相应放入用户输入事件的队列 queuedUserInputEvents
相应放入用户输入事件的队列 queuedSocketEvents
canWait = (!retVal && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents));
//如果消息处理完了,则阻塞自己,等到操作系统有消息发送过来,才会继续往下执行
MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
//由于这个函数是在操作系统线程里执行的???所以要尽量的快
消息是PM_REMOVE,则
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
//通过PostMessage()函数将事件发送到那个隐形窗口对来处理,对象窗口专门处理
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
//处理上面说的隐形窗口的消息
QWindowsGuiEventDispatcher::sendPostedEvents()
QEventDispatcherWin32::sendPostedEvents();
QWindowSystemInterface::sendWindowSystemEvents(m_flags);
QGuiApplicationPrivate::processWindowSystemEvent(event);
QGuiApplicationPrivate::processWheelEvent(static_cast
QGuiApplicationPrivate::processMouseEvent(static_cast
QGuiApplication::sendSpontaneousEvent(window, &ev);
notifyInternal2(receiver, event)
QCoreApplication::notify(QObject *receiver, QEvent *event)
//这个是虚函数,会对应到子类的具体函数,一般是QApplication::notify
bool eventAccepted = mouse->isAccepted(); //也就是事件默认是接收状态
QPointer
while (w)
res = d->notify_helper(w, w == receiver ? mouse : &me);
// 这里让事件过滤器先执行了
if (sendThroughObjectEventFilters(receiver, e))
return true;
// 遍历安装到receiver的事件过滤器对象们,先让它们的eventFilter()函数执行
for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i)
if (obj->eventFilter(receiver, event)) //这儿就是我们熟悉的事件过滤器函数了
return true;
// deliver the event
bool consumed = receiver->event(e); //这儿就是我们熟悉的可以重写的事件处理函数了
//这里以继承Qobject的Qwidget为例:
bool QWidget::event(QEvent *event)
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break;
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
break;
。。。
QCoreApplicationPrivate::setEventSpontaneous(e, false);
return consumed;
eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
if (res && eventAccepted)
break;
w = w->parentWidget(); //不断寻找父窗口对象
参考博客:
【QT】深入了解QT消息循环及线程相关性_伐尘的博客-CSDN博客_qt 消息循环
另外一些参考博客: