一、如何启动事件循环
//启动主程序的事件循环,
//QApplication调用的是QGuiApplication的,QGuiApplication内部调用的是QCoreApplication的exec
int QApplication::exec()
int QGuiApplication::exec()
int QCoreApplication::exec()
//子线程的事件循环
int QThread::exec()
//启动模态对话框的事件循环
int QDialog::exec()
注:以上所有的事件循环最终调用的都是下面的函数
//让事件循环对象启动事件循环
int QEventLoop::exec(QEventLoop::ProcessEventsFlags flags = AllEvents)
QEventLoop::exec()本质会调用 processEvents()分发事件;
QEventLoop::processEvents会调用QAbstractEventDispatcher::processEvents()实现事件分发。
QAbstractEventDispatcher类提供了一个管理Qt事件队列的接口,它从窗口系统和其他地方接收事件。
然后它将这些事件发送到QCoreApplication或QApplication实例进行处理和交付
二、、事件的循环处理
事件不同于信号(信号是QObject对象产生的),事件是由窗口系统或者QTimer产生的,不属于任何一个QObject对象。
exec()函数不断从事件队列中取出事件,一个事件一般会经历下面四个步骤处理:notify()->eventFilter()->event()->QKeyEvent()
1)事件产生:窗口系统产生一个事件,例如鼠标点击、键盘按键、定时器触发等
窗口系统产生一个事件后,会被应用程序放入到事件队列中,exec()函数循环调用不断从事件队列中取出事件,之后分发处理。
2)事件分发 :应用程序将事件分发给窗口、小部件等事件接收者
主要函数:bool QApplication::notify(QObject *receiver, QEvent *e)
此时事件刚被应用程序从事件队列中取出,进入exec()函数中,在exec()函数中会调用notify()函数,notify()函数的作用就是确认时间的接收者,并将事件发送给接受者。
事件的接收者一般都是当事件产生时的焦点控件,如果鼠标点击按钮,则按钮被选中,按钮就是焦点,这个点击事件就会分发给这个按钮
QCoreApplication::notify()函数可以在所有的事件处理函数接受到事件之前接收到该APP的所有事件,功能很强大,但是一次只能处理一个事件,一般不建议对该函数进行重写,会影响运行效率。
3)事件过滤:事件到达接收者之前可以在任意位置被截断并过滤,提前做一些处理
主要函数:bool QObject::eventFilter(QObect* watched, QEvent *event) 窗口事件过滤器
watched是被监视对象, event是触发的事件。 当一个窗口被安装了事件过滤器以后,才会进入这个函数。
例如 button->installFilter(widget) 那么这个button就是被安装了事件过滤器,所有最终被送到button的事件都会先进入widget的eventFilter函数内。在widget的eventFilter函数内我们可以通过判断被监视对象(watched)的类型和事件类型对安装了事件过滤窗口的事件进行提前处理。
默认情况下qt对所有窗口都是不安装事件过滤器的。也就是这个函数是不会运行的。除非我们重写。
eventFilter()函数一般是在父窗口中实现来监视子窗口的事件并提前处理。
4)事件分类:事件到达接收者之后,接收者将事件进行分类,交由不同函数处理
主要函数:bool QWidget::event(QEvent *event) 窗口事件分发器
事件到达目标窗口后,会先进入event()函数,对事件进行分类,将事件分为鼠标事件、键盘事件、绘图事件等。在分发到这个窗口的鼠标事件处理函数、键盘事件处理函数中处理。
一般这个函数也不用重写
5)事件处理:接收者处理特定类型的事件
主要函数:键盘点击,鼠标移动等(void QWidget::mousePressEvent(QMouseEvent *event) 等)
事件在被QWidget::event分类后,就会进入该窗口特定类型的事件处理函数进行处理。
在以上所有步骤中,如果我们想要事件在函数中处理完毕后继续按照流程进入下一个事件处理函数可以什么都不做或者return false.
如果想直接终止事件的传递可以调用return true
Ⅰ、事件传播:接收者对象未处理事件或希望将事件传递给其父对象进行处理,事件将被传播到父对象
一般事件处理到上一步就结束了,但是当接收者对象啥也不干时,就会把时间返回给父控件,一层层返回,直到父控件为空或者某一个父控件处理了这个事件。
接受者的将未处理的事件传播给父对象,这种传播是通过调用父对象的事件处理函数来实现的。具体顺序是,会首先尝试调用父对象的特定事件处理函数(如mouseMoveEvent()),如果不存在则调用通用事件处理函数(event())。此外,还可以通过事件过滤器拦截和处理事件。
Ⅱ、事件终止:在事件传播过程中,可以选择终止事件的传播。这可以通过在事件处理函数中返回 true 来实现,告诉 Qt 不要将事件传递给父对象
事件接收者不处理事件,将事件传递给父控件时,终止事件传递有两种方式:
一种是在eventFilter()/event()函数中,返回true,若返回的是true则事件就不会继续向父对象传递,系统会认为这个事件已经处理完毕。
第二种是在特定时间处理函数(mouseMoveEvent等)中调用event->accept()
事件由App传递给接受着时:
在eventFilter()/event()函数中返回true,返回false其实也可以,只不过返回false之后事件就会传递给上一个父控件,但是也同样可以打断向接收者传递该事件