Qt消息机制和事件
Qt中所有事件类都继承于QEvent
类。
比如QKeyEvent
是按键事件类,QMouseEvent
是鼠标事件类,QPaintEvent
是绘制事件类,QTimerEvent
是定时器事件类。
函数QApplication::exec()
的主要功能是不断地检查系统队列和Qt事件队列里是否有未处理的事件,如果有,就派发给对象去处理。
重写事件处理函数
在事件对象创建完毕后,Qt 将这个事件对象传递给QObject
的event()
函数。
注意,event()
函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数。
新建widget窗口,在父类QWidget
中,定义了很多事件处理的回调函数,如
keyPressEvent()
keyReleaseEvent()
mouseMoveEvent()
mousePressEvent()
等
这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。
比如:
在Widget.h
中重写closeEvent()
class Widget : public QWidget
{
protected:
void closeEvent(QCloseEvent *event);
};
在Widget.cpp
中实现closeEvent()
void Widget::closeEvent(QCloseEvent *event)
{
//QMessageBox的返回值是int
int ret = QMessageBox::question(this,"提示","您确定关闭窗口吗?");
if(ret == QMessageBox::Yes){
event->accept();
}
else {
event->ignore();
}
}
重写鼠标按下、释放、移动事件:
- 新建
MyLabel
类继承QLabe
l (先新建MyLabel
类继承自QWidget
,再修改为继承QLabel
- ui绘制label,label提升为
MyLabel
- 在mylabel.h中重声明事件函数
protected:
void mousePressEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent * ev);
- 在mylabel.cpp中实现事件函数
void MyLabel::mousePressEvent(QMouseEvent *ev)
{
if(ev->button()==Qt::LeftButton){
qDebug()<<"鼠标左键按下了";
}
else if(ev->button()==Qt::RightButton){
qDebug()<<"鼠标右键按下了";
}
else {
qDebug()<<"其他按下了";
}
}
重写事件分发监听按键
事件对象创建完毕后,Qt 将这个事件对象传递给QObject
的event()
函数。event()
函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器。
如上所述,event()
函数主要用于事件的分发。所以,如果希望在事件分发之前做一些操作,就可以重写这个event()
函数。
例如,欲实现在一个QWidget
组件中监听 tab
键按下的目的,可通过继承QWidget
,并重写它的event()
函数,来达到这个目的。
- .h中重写event()
protected:
bool event(QEvent* event);
- .cpp中实现event()
bool Widget::event(QEvent *event)
{
//判断事件类型
if(event->type()==QEvent::KeyPress){
//将QEvent类型的event转换类型为QKeyEvent
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if(keyEvent->key()==Qt::Key_Tab){
qDebug()<<"Tab is pressed";
return true;
}
}
return QWidget::event(event);//返回默认的event()
}
请注意:
- 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。
- 如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
- 如果返回flase,则不会继续处理之后的事,只能做return前的处理;所以,必要时q请返回父类的
event()
。 - 在
event()
函数中,调用事件对象的accept()
和ignore()
函数是没有作用的,不会影响到事件的传播。
event()
函数有两个问题:
event()
函数是一个 protected 的函数,这意味着要想重写event(),必须继承一个已有的类。protected 函数带来的另外一个问题是,如果基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的,就无法继承这种库中的组件。event()
函数的确有一定的控制,然而当要求更严格时:希望组件根本看不到这种事件。event()
函数虽然可以拦截,但仍然接收到了QMouseEvent
对象。
事件过滤器可完美解决以上两个问题。
事件过滤器
有时,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。
QObject
有一个eventFilter()
函数,用于建立事件过滤器。函数原型如下:
virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。若这个事件是目标事件,就进行处理;若不是,就继续转发。
该函数返回一个 bool 类型,如果不想让event
继续转发,就返回 true,否则返回 false。
事件过滤器的调用时间是目标对象watched
接收到事件对象之前。即,若在事件过滤器中停止了某个事件,那么watched
对象以及以后所有的事件过滤器根本不会知道此事件。
- .h中声明
protected:
bool eventFilter(QObject *obj, QEvent *event);
- .cpp中实现
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == textEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
qDebug() << "Ate key press" << keyEvent->key();
return true;
} else {
return false;
}
} else {
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
}
MainWindow
是定义的一个类。上述代码重写了它的eventFilter()
函数。
在上面的代码中,实现了组织textEdit
组件处理键盘按下的事件。
如果这个事件是键盘事件,则直接返回 true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回 false。对于其它的组件,并不能保证是否还有过滤器,所以最保险的办法是调用父类的函数。
3. 安装
安装过滤器需要调用QObject::installEventFilter()
函数。函数的原型如下:
void QObject::installEventFilter ( QObject * filterObj )
eventFilter()
函数是QObject
的一个成员函数,因此,任意QObject
都可以作为事件过滤器(问题在于,若没有重写eventFilter()
函数,这个事件过滤器是没有任何作用的,因为默认什么都不会过滤)。
4. 移除
已经存在的过滤器则可以通过QObject::removeEventFilter()
函数移除。
事件过滤器的强大之处在于,可以为整个应用程序添加一个事件过滤器。
installEventFilter()
函数是QObject
的函数,QApplication
或者QCoreApplication
对象都是QObject
的子类,因此,可以向QApplication
或者QCoreApplication
添加事件过滤器。这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。
注意:
事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。
事件派发
重写QCoreApplication::notify()
函数。