事件是對各種應用程序需要知道的由應用程序內部或者外部產生的事情或者動作的通稱。在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函數和事件處理函數是在焦點部件內重新定義的,而事件過濾器卻是在焦點部件的父部件中定義的。