Qt学习笔记:Qt 事件机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33559992/article/details/85262521

一、Qt中的事件处理

1. 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 事件处理的核心包括事件①产生、②分发、③接受和处理。

  • 事件的产生:
    谁来产生事件? 最容易想到的是我们的输入设备,比如键盘、鼠标产生的keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent)。
  • Qt中事件的分发:
    对于non-GUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类-Receiver.
    对于Qt GUI程序,由QApplication来负责
  • 事件的接受和处理:
    QObject类 是整个Qt对象模型的心脏,事件处理机制是QObject三大职责(内存管理、内省(intropection)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject::event()函数或事件的处理权转给父类。

2. Qt平台将系统产生的消息转变成Qt事件:

Qt 程序在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。如上所述,event()函数主要用于事件的分发。
在这里插入图片描述

3. GUI应用程序的事件处理方式:

  • Qt事件产生后会立即被分发到QWidget对象(QObject的子类,如按键QPushButton对象等)
  • QWidget对象其内部会有一个event(QEVent*)函数被调用,进行事件处理
  • event()根据事件类型调用不同的事件处理函数(默认的子函数)
  • 在事件处理函数中发送Qt中预定义的信号
  • 调用信号关联的槽函数

二、事件分发、事件过滤器:

1. 事件的传递:

如果事件在目标对象上得不到处理,事件向上一层进行传播,直到最顶层的widget为止。

如果得到事件的对象,调用了accept(),则事件停止继续传播;如果调用了ignore(),事件向上一级继续传播。

Qt对自定义事件处理函数的默认返回值是accept(),但默认的事件处理函数是ingore()。

因此,如果要继续向上传播,调用QWidget的默认处理函数即可。到此为止的话,不必显式调用accept()。

添加新类QPushButtonEx,它的父类为QPushButton,QPushButtonEx 覆盖 QPushButton 的鼠标按下事件:

   void QButtonEx::mousePressEvent(QMouseEvent *e)
   {
       qDebug()<<"button pressed";
       e->ignore();//ignore的作用是使事件继续向父控件传递
   }

在一个MainWindow上添加一个QPushButton(提升为QPushButtonEx),也重写MainWindow的鼠标按下事件:

    void MainWindow::mousePressEvent(QMouseEvent *e)
    {
        qDebug()<<"mainwindow pressed";
        e->ignore();
    }

点击按钮,运行结果如下:
在这里插入图片描述
打印输出的结果验证了传递方向,子控件先接收到事件,父控件后接收到事件。尤其注意代码中的e->ignore();这句的作用是使得事件能够继续流向父控件;与之相反的是e->accept();它将事件拦截,父控件将无法收到已经被accept过的事件;

重写的事件处理函数中,如果不写accept或ignore,那么默认为:事件被accept!

上面的代码为例,如果QButtonEx::mousePressEvent()函数中不写e->ignore(),或者写e->accept(),那么MainWindow::mousePressEvent()函数将不会被触发,运行结果中只能看到打印:button pressed

因此,自定义Button控件继承自QPushbutton,然后重写鼠标事件mousePressEvent。会发现在父类中调用该自定义的button,没有发出clicked()信号。
原因是:当重写完成以后,需要在每个事件中添加:
QPushButton::mousePressEvent(e);

//重写鼠标释放事件
void myButton::mouseReleaseEvent(QMouseEvent*e)
{
	if(e->button()==Qt::RightButton)
	{
		 m_menu.exec(QCursor::pos());
	}
	//保留原有QPushButton点击事件
	QPushButton::mouseReleaseEvent(e);//不添加该句会造成不释放clicked()信号。
}

2. 事件过滤器:

Qt创建QEvent事件对象后,会调用QObject的event()函数来分发事件。

但有时,你可能需要在调用event()函数之前做一些自己的操作,比如,对话框上某些组件可能并不需要响应回车键按下的事件,此时,你就需要重新定义组件的event()函数。

如果组件很多,就需要重写很多次event()函数,这显然没有效率。为此,你可以使用一个事件过滤器,来判断是否需要调用组件的event()函数。

QOjbect有一个eventFilter()函数,用于建立事件过滤器。这个函数的声明如下:

virtual bool QObject::eventFilter (QObject * watched, QEvent * event)

如果watched对象安装了事件过滤器,这个函数会被调用并进行事件过滤,
然后才轮到组件进行事件处理。在重写这个函数时,如果你需要过滤掉某个事件,例如停止对这个事件的响应,需要返回true。

在创建了过滤器之后,下面要做的是安装这个过滤器。安装过滤器需要调用installEventFilter()函数。这个函数的声明如下:

void QObject::installEventFilter ( QObject * filterObj)

这个函数是QObject的一个函数,因此可以安装到任何QObject的子类,并不仅仅是UI组件。

这个函数接收一个QObject对象,调用了这个函数安装事件过滤器的组件会调用filterObj定义的eventFilter()函数。

例如,textField->installEventFilter(obj),则如果有事件发送到textField组件时,会先调用obj->eventFilter()函数,然后才会调用textField->event()。

当然,你也可以把事件过滤器安装到QApplication上面,这样就可以过滤所有的事件,已获得更大的控制权。不过,这样做的后果就是会降低事件分发的效率。

我们可以把Qt的事件传递看成链状:如果子类没有处理这个事件,就会继续向其他类传递。其实,Qt的事件对象都有一个accept()函数和ignore()函数,用于“接受”和“忽略”。

具体可参考该图:
在这里插入图片描述

实例:

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;	//返回true --> 将键盘事件过滤
                 } else {
                         return false;//继续处理其他事件
                 }
         } else {
                 // pass the event on to the parent class
                 return QMainWindow::eventFilter(obj, event);////调用父类函数
         }
}

上面的例子中为MainWindow建立了一个事件过滤器。为了过滤某个组件上的事件,首先需要判断这个对象是哪个组件,然后判断这个事件的类型。例如,我不想让textEdit组件处理键盘事件,于是就首先找到这个组件,如果这个事件是键盘事件,则直接返回true,也就是过滤掉了这个事件,其他事件还是要继续处理,所以返回false。对于其他组件,我们并不保证是不是还有过滤器,于是最保险的办法是调用父类的函数。

总结起来使用事件过滤器就两个步骤:
第一:对目标对象调用installEventFilter()来注册监视对象(事件过滤器);
第二:重写监视对象的eventFilter()函数处理目标对象的事件。

即:
(1). 目标对象 -> installEventFilter( 监控对象);
目标对象将事件传递给监控对象处理

(2). bool 监控对象::eventFilter(QObject *watched,QEvent *event)
{

}

猜你喜欢

转载自blog.csdn.net/qq_33559992/article/details/85262521