事件的处理
一个事件由一个特定的QEvent 子 类来表示,但是有时一个事件又包含多个事件类型,比如鼠标事件又可以分为鼠标按下,双击和移动等多种操作。
这些事件类型都由QEvent类 的枚举型QEvent : : Type 来表示,其中包含了一百多种事件类型,可以在QEvent类的帮助文档中进行查看。
虽然QEvent的子类可以表示一个事件,但是却不能用来处理事件,那么应该怎样来处理一个事件呢?QCoreApplication类的notify()函数 的帮助文档给出了5种处理事件的方法
方法一:重新实现部件的paintEvent() , mousePressEvent() 等事件处理函数。这是最常用的一种方法,不过只能用来处理特定部件的特定事件。例如,第5章实现拖放操作就是用的这种方法。
方法二:重新实现notify() 函数。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。但是,它一次只能处理一个事件。
方法三:向QApplication 对象上安装事件过滤器 。因为一个程序只有一个QAp-plication对象,所以这样实现的功能与使用notify()函数是相同的,优点是可以同时处理多个事件。 方法四:重新实现event()函数 。QObject类的event()函数可以在事件到达默认的事件处理函数之前获得该事件。
方法五:在对象上安装事件过滤器 。使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。
在实际编程中,最常用的是方法一 ,其次是方法五 。因为方法二需要继承自QAp-plication类;而方法三要使用一个全局的事件过滤器,这将减缓事件的传递,所以,虽然这两种方法功能很强大,但是却很少被用到。
事件的传递
讲解 helloworld程序代码时就曾提到过,每个程序 main()函数的最后都会调用QApplication类的exec() 函数﹐它会使Qt应用程序进入事件循环,这样就可以使应用程序在运行时接收发生的各种事件。一旦有事件发生,Qt便会构建一个相应的QEvent子类的对象来表示它,然后将它传递给相应的QObject对象或其子对象。下面通过例子来看一下Qt中的事件传递过程。
新建Qwidget项目 向项目中添加新文件,模板选择c++ class,类名为MyLineEdit,基类手动填写为QLineEdit。
将mylineedit.h进行修改,如下:
#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);
};
#endif
添加了keyPressEvent函数的声明,下面转到mylineedit.cpp文件中,添加头文件:
#include <QKeyEvent>
#include <QDebug>
修改构造函数如下:
MyLineEdit::MyLineEdit(QWidget *parent) :
QLineEdit(parent)
{
}
然后添加事件函数
void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
qDebug() << tr("MyLineEdit键盘按下事件");
}
下面进入widget.h文件中,添加类前置声明:
class MyLineEdit;
然后添加函数声明:
protected:
void keyPressEvent(QKeyEvent *event);
在用private对象指针添加一个对象
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键盘按下事件");
}
新建了一个lineEdit 移动到100,100 实现了按键按下事件
注意:这里既实现了MyLineEdit类的键盘按下事件处理函数,也实现了 Widget类的键盘按下事件处理函数
现在运行程序,按下键,就会出现事件,qDebug() 这里实现了MyLineEdit类的键盘事件
下面到mylineedit.cpp文件的keyPressEvent函数最后添加
void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
qDebug() << tr("MyLineEdit键盘按下事件");
QLineEdit::keyPressEvent(event);
event->ignore();
}
这里调用了MyLineEdit父类 QLineEdit的keyPressEvent()函数来实现行编辑器的默认操作。这里一定要注意代码的顺序,ignore()函数要在最后调用。
从这个例子中可以看到,事件是先传递给指定窗口部件的,确切地说应该是先传递给获得焦点的窗口部件。但是如果该部件忽略掉该事件,那么这个事件就会传递给这个部件的父部件。重新实现事件处理函数时,一般要调用父类的相应事件处理函数来实现默认操作。下面将这个例子再进行改进,看一下事件过滤器等其他方法获取事件的顺序。
先焦点部件在父部件
下面再来个例子看顺序: 先在mylineedit.h中添加public函数声明:
bool event(QEvent *event);
在mylineedit.cpp文件中对改函数进行定义:
bool MyLineEdit::event(QEvent *event)
{
if(event->type() == QEvent::KeyPress)
qDebug() << tr("MyLineEdit的event()函数");
return QLineEdit::event(event);
}
MyLineEdit的event()函数中使用了QEvent的type() 函数来获取事件的类型 如果是键盘按下事件QEvent: :KeyPress ,则输出信息。
因为event()函数具有bool型的返回值,所以该函数的最后要使用return语句,这里一般是返回父类的event()函数的操作结果。
下面进人 widget.h文件中进行public函数的声明:
bool eventFilter(QObject *obj, QEvent *event);
然后到widget.cpp中构造函数添加事件过滤器
lineEdit->installEventFilter(this);
然后添加事件过滤器的定义
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
if(obj == lineEdit){
if(event->type() == QEvent::KeyPress)
qDebug() << tr("Widget的事件过滤器");
}
return QWidget::eventFilter(obj, event);
}
这是先判断事件的对象是不是lineEdit,如果是,在判断事件类型,最后返回QWidget类默认的事件过滤器的执行结果。
先进行事件过滤器
然后是焦点部件的event,这里就是Linediet的 然后是焦点的事件处理函数 因为有个忽略 event->ignore(); // 忽略该事件 所以执行父部件的事件处理函数 执行程序过程是这样的
一言,一思,一走,一生
|