参考文献:《Qt Creator 快速入门》第三版 霍亚飞编著
事件是对各种应用程序需要知道的由应用程序内部或者外部产生的事情或者动作的通称。在Qt中事件作为一个对象,继承者QEvent类,常见的有键盘事件QKeyEvent、鼠标事件QMouseEvent和定时器事件QTimerEvent等。事件与信号并不相同,比如单击界面上的按钮,那么就会产生鼠标事件QMouseEvent(不是按钮产生的),而因为按钮被按下了,按钮会发送clicked()信号(是按钮产生的)。在Qt中任何继承自QObject子类实例都可以接收和处理事件。
一、事件的处理
事件的处理方法有5种
1、重新实现事件的paintEvent()、mouvesPressEvent()等事件处理函数。这是最常用的方法,不过这种方法的缺点是只能处理一些特定事件。前面讲拖放操作时就是用的这种方法,详见我的QT Creator学习笔记(十一)——应用程序主窗口QMainWindow之拖放操作
2、重新实现notify()函数。这个函数功能强大提供了完全的控制,可以在事件过滤器得到事件之前获得它们。但是一次只能发处理一个事件。
3、向QApplication对象上安装时间过滤器。因为一个程序只能只有一个QApplication对象,所以这样处理实际与方法2相同,优点是可以同时处理多个函数。
4、重新实现event()函数。QObject类的event函数可以在事件到达默认事件处理函数之前获得该事件。
5、在对象上安装事件过滤器。使用事件过滤器可以在同一界面上处理不同子部件的不同事件。
二、事件的传递
在程序的main函数最后都会调用QApplication类的exec()函数,它会使Qt应用程序进入事件循环,这样就可以使用应用程序在运行时接收和发送的各种事件。
1、事件处理函数获得事件的顺序
新建Qt Widget应用程序,命名为myevent,基类选择Widget。向项目中添加C++ class类,类名为MyLineEdit,基类为QLineEdit,然后mylineedit.h中添加keyPressEvent函数的声明,这个头文件内容如下
#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H
#include<QLineEdit>
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit MyLineEdit(QWidget *parent=0);
protected:
void keyPressEvent(QKeyEvent* event);
};
mylineedit.cpp文件内容如下,重点在keyPressEvent函数,里面添加打印内容,便于查看event传递顺序。另外这里调用了MyLineEdit父类QLineEdit的keyPressEvent函数来实现编辑器的默认操作。重写事件处理函数时一般都会调用父类的事件处理函数实现默认操作。
#include "mylineedit.h"
#include <QKeyEvent>
#include <QDebug>
MyLineEdit::MyLineEdit(QWidget *parent):QLineEdit(parent)
{
}
void MyLineEdit::keyPressEvent(QKeyEvent *event)//键盘按下事件
{
qDebug()<<tr("MyLineEdit键盘按下事件");
QLineEdit::keyPressEvent(event);
}
在widget中添加MyLineEdit对象,并且也实现keyPressEvent函数。修改后widget.h文件如下
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class MyLineEdit;
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void keyPressEvent(QKeyEvent *event);
private:
Ui::Widget *ui;
MyLineEdit* lineEdit;
};
widget.cpp文件如下
#include "widget.h"
#include "ui_widget.h"
#include "mylineedit.h"
#include <QKeyEvent>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
lineEdit=new MyLineEdit(this);
lineEdit->move(100,100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
qDebug()<<tr("widget键盘按下事件");
}
运行程序,结果如下
从输出可以看到只执行了MyLineEdit::keyPressEvent函数,没有执行Widget::keyPressEvent函数。可以看出事件传给获得焦点的部件。如果获得焦点的部件忽略该事件,该事件就会传给这个部件的父控件。在MyLineEdit::keyPressEvent函数的最后加入下面一行代码
event->ignore();
代码输出如下,可以看到父控件widget也获得了keyPressEvent事件。
2、event函数以及事件过滤器获得 。
在mylineedit头文件和源文件中分别添加事件函数event的声明和实现,代码分别如下
bool event(QEvent *event);
bool MyLineEdit::event(QEvent *event)
{
if(event->type()==QEvent::KeyPress)
qDebug()<<tr("MyLineEdit的event函数");
return QLineEdit::event(event);//执行QLineEdit类event函数的默认操作
}
在函数实现中使用QEvent的type()函数来获取事件的类型。如果是键盘按下事件QEvent::KeyPress,则输出信息。最后返回父类的event()函数操作结果。
在widget.h和widget.cpp中分别添加事件过滤器函数eventFilter()的声明和实现,代码分别如下
bool eventFilter(QObject *watched, QEvent *event);
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
if(watched==lineEdit&&event->type()==QEvent::KeyPress)
qDebug()<<tr("Widget的事件过滤器");
return QWidget::eventFilter(watched,event);
}
在事件过滤器函数中判断事件对象是lineEdit并且事件类型是KeyPress时,输出信息。最后返回QWidget类默认的事件过滤器处理结果。
另外在widget.cpp文件的构造函数最后加下面这行代码,在widget上为lineEdit安装事件过滤器。
lineEdit->installEventFilter(this);
程序运行结果如下图
可以看到事件传递的顺序是这样的:先是事件过滤器,然后是焦点部件event函数,最后是焦点部件的事件处理函数;如果焦点部件忽略了该事件,那么会执行父部件的事件处理函数。注意event函数和事件处理函数是在焦点部件内重新定义的,而事件过滤器却是在焦点部件的父部件中定义的。