qt-重写event(),事件过滤器,定时器,事件总结
一、認(rèn)識(shí)事件
? ? 1、事件(event) 是由系統(tǒng)或者 Qt 本身在不同的時(shí)刻發(fā)出的。當(dāng)用戶按下鼠標(biāo)、敲下鍵盤,或者是窗口需要重新繪制的時(shí)候,都會(huì)發(fā)出一個(gè)相應(yīng)的事件。一些事件在對用戶操作做出響應(yīng)時(shí)發(fā)出,如鍵盤事件等;另一些事件則是由系統(tǒng)自動(dòng)發(fā)出,如計(jì)時(shí)器事件。
? ? 2、事件也就是我們通常說的“事件驅(qū)動(dòng)(event drive) ”程序設(shè)計(jì)的基礎(chǔ)概念。事件的出現(xiàn),使得程序代碼不會(huì)按照原始的線性順序執(zhí)行。想想看,從最初的 C 語言開始,我們的程序就是以一種線性的順序執(zhí)行代碼:這一條語句執(zhí)行之后,開始執(zhí)行下一條語句;這一個(gè)函數(shù)執(zhí)行過后,開始執(zhí)行下一個(gè)函數(shù)。這種類似“批處理”的程序設(shè)計(jì)風(fēng)格顯然不適合于處理復(fù)雜的用戶交互。我們來想象一下用戶交互的情景:我們設(shè)計(jì)了一堆功能放在界面上,用戶點(diǎn)擊了“打開文件”,于是開始執(zhí)行打開文件的操作;用戶點(diǎn)擊了“保存文件”,于是開始執(zhí)行保存文件的操作。我們不知道用戶究竟想進(jìn)行什么操作,因此也就不能預(yù)測接下來將會(huì)調(diào)用哪一個(gè)函數(shù)。如果我們設(shè)計(jì)了一個(gè)“文件另存為”的操作,如果用戶不點(diǎn)擊,這個(gè)操作將永遠(yuǎn)不會(huì)被調(diào)用。這就是所謂的“事件驅(qū)動(dòng)”,我們的程序的執(zhí)行順序不再是線性的,而是由一個(gè)個(gè)事件驅(qū)動(dòng)著程序繼續(xù)執(zhí)行。沒有事件,程序?qū)⒆枞谀抢?#xff0c;不執(zhí)行任何代碼。
? ? 3、在 Qt 中,事件的概念似乎同信號(hào)槽類似。的確如此,一般來說,使用 Qt 組件時(shí),我們并不會(huì)把主要精力放在事件上。因?yàn)樵?Qt 中,我們關(guān)心的更多的是事件關(guān)聯(lián)的一個(gè)信號(hào)。比如,對于 QPushButton 的鼠標(biāo)點(diǎn)擊,我們不需要關(guān)心這個(gè)鼠標(biāo)點(diǎn)擊事件,而是關(guān)心它的clicked()信號(hào)的發(fā)出。這與其他的一些 GUI 框架不同:在 Swing 中,你所要關(guān)心的是JButton 的 ActionListener 這個(gè)點(diǎn)擊事件。由此看出,相比于其他 GUI 框架, Qt 給了我們額外的選擇:信號(hào)槽。
? ? 4、但是, Qt 中的事件和信號(hào)槽卻并不是可以相互替代的。信號(hào)由具體的對象發(fā)出,然后會(huì)馬上交給由 connect()函數(shù)連接的槽進(jìn)行處理;而對于事件, Qt 使用一個(gè)事件隊(duì)列對所有發(fā)出的事件進(jìn)行維護(hù),當(dāng)新的事件產(chǎn)生時(shí),會(huì)被追加到事件隊(duì)列的尾部。前一個(gè)事件完成后,取出后面的事件進(jìn)行處理。但是,必要的時(shí)候, Qt 的事件也可以不進(jìn)入事件隊(duì)列,而是直接處理。信號(hào)一旦發(fā)出,對應(yīng)的槽函數(shù)一定會(huì)被執(zhí)行。但是,事件則可以使用“事件過濾器”進(jìn)行過濾,對于有些事件進(jìn)行額外的處理,另外的事件則不關(guān)心。總的來說,如果我們使用組件,我們關(guān)心的是信號(hào)槽;如果我們自定義組件,我們關(guān)心的是事件。因?yàn)槲覀兛梢酝ㄟ^事件來改變組件的默認(rèn)操作。比如,如果我們要自定義一個(gè)能夠響應(yīng)鼠標(biāo)事件的 EventLabel,我們就需要重寫 QLabel 的鼠標(biāo)事件,做出我們希望的操作,有可能還得在恰當(dāng)?shù)臅r(shí)候發(fā)出一個(gè)類似按鈕的 clicked()信號(hào)(如果我們希望讓這個(gè) EventLabel 能夠被其它組件使用)或者其它的信號(hào)。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
?
#include <QMainWindow>
#include <QMouseEvent>
#include <QLabel>
class MainWindow : public QMainWindow
{
? ? Q_OBJECT
?
public:
? ? MainWindow(QWidget *parent = 0);
? ? ~MainWindow();
};
/*
?* EventLabel 繼承了 QLabel,覆蓋了 mousePressEvent()、 mouseMoveEvent()
?* 和MouseReleaseEvent() 三個(gè)函數(shù)。
*/
class EventLabel:public QLabel{
protected:
? ? void mouseMoveEvent(QMouseEvent *event);
? ? void mousePressEvent(QMouseEvent *event);
? ? void mouseReleaseEvent(QMouseEvent *event);
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QString>
MainWindow::MainWindow(QWidget *parent)
? ? : QMainWindow(parent)
{
}
?
MainWindow::~MainWindow()
{
?
}
/*
?*QLabel支持HTML代碼,<center>設(shè)置文本為水平居中,<h1>設(shè)置字體為黑體,</h1></center>是結(jié)束符。
?* QString 的 arg() 函數(shù)可以自動(dòng)替換掉 QString 中出現(xiàn)的占位符。其占位符以 % 開始,后面是占位
?* 符的位置,例如 %1, %2 這種。
*/
void EventLabel::mouseMoveEvent(QMouseEvent *event){
? ? this->setText(QString("<center><h1>Move:(%1,%2)</h1></center>").arg(QString::number(event->x()),QString::number(event->y())));
}
?
void EventLabel::mousePressEvent(QMouseEvent *event){
? ? this->setText(QString("<center><h1>Press:(%1,%2)</h1></center>").arg(QString::number(event->x()),QString::number(event->y())));
}
?
void EventLabel::mouseReleaseEvent(QMouseEvent *event){
? ? QString msg;
? ? /*
? ? ?* 使用另外一種 QString 的構(gòu)造方法。類似C風(fēng)格的格式化函數(shù)sprintf()來構(gòu)造 QString。
? ? */
? ? msg.sprintf("<center><h1>Release:(%d,%d)</h1></center>",event->x(),event->y());
? ? this->setText(msg);
}
#include "mainwindow.h"
#include <QApplication>
?
int main(int argc, char *argv[])
{
? ? QApplication a(argc, argv);
? ? EventLabel *la=new EventLabel;
? ? la->setWindowTitle("鼠標(biāo)事件");
? ? la->resize(300,200);//設(shè)置窗口大小
? ? /*
? ? ?* QWidget 中有一個(gè) mouseTracking 屬性,該屬性用于設(shè)置是否追蹤鼠標(biāo)。只有鼠標(biāo)被追蹤時(shí),
? ? ?* mouseMoveEvent() 才會(huì)發(fā)出。如果 mouseTracking 是 false(默認(rèn)即是),組件在至少一次
? ? ?* 鼠標(biāo)點(diǎn)擊之后, 才能夠被追蹤, 也就是能夠發(fā)出 mouseMoveEvent() 事件。如果 mouseTracking
? ? ?* 為 true,則 mouseMoveEvent() 直接可以被發(fā)出。
? ? */
? ? la->setMouseTracking(true);
? ? la->show();
? ? return a.exec();
}
二、重寫event()
? ? ?event() 函數(shù)主要用于事件的分發(fā)。所以,如果你希望在事件分發(fā)之前做一些操作,就可以重寫這個(gè) event() 函數(shù)了。我們可以通過使用QEvent::type()函數(shù)可以檢查事件的實(shí)際類型,其返回值是 QEvent::Type類型的枚舉。我們處理過自己感興趣的事件之后,可以直接返回 true,表示我們已經(jīng)對此事件進(jìn)行了處理;對于其它我們不關(guān)心的事件,則需要調(diào)用父類的 event() 函數(shù)繼續(xù)轉(zhuǎn)發(fā),否則這個(gè)組件就只能處理我們定義的事件了。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
?
#include <QMainWindow>
#include <QWidget>
#include <QEvent>
class MainWindow : public QMainWindow
{
? ? Q_OBJECT
?
public:
? ? MainWindow(QWidget *parent = 0);
? ? ~MainWindow();
};
?
class CustomWidget:public QWidget{
protected:
? ? bool event(QEvent *e);
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QKeyEvent>
#include <QDebug>
#include <Qt>
MainWindow::MainWindow(QWidget *parent)
? ? : QMainWindow(parent)
{
}
?
MainWindow::~MainWindow()
{
}
/*
?* CustomWidget 是一個(gè)普通的 QWidget 子類。我們重寫了它的 event()函數(shù),這個(gè)函數(shù)有
?* 一個(gè) QEvent 對象作為參數(shù),也就是需要轉(zhuǎn)發(fā)的事件對象。函數(shù)返回值是 bool 類型。如果
?* 傳入的事件已被識(shí)別并且處理,則需要返回 true,否則返回 false。如果返回值是 true,并
?* 且,該事件對象設(shè)置了 accept(),那么 Qt 會(huì)認(rèn)為這個(gè)事件已經(jīng)處理完畢,不會(huì)再將這個(gè)事
?* 件發(fā)送給其它對象,而是會(huì)繼續(xù)處理事件隊(duì)列中的下一事件。注意,在 event()函數(shù)中,調(diào)
?* 用事件對象的 accept()和 ignore()函數(shù)是沒有作用的,不會(huì)影響到事件的傳播。
*/
bool CustomWidget::event(QEvent *e){
? ? if(e->type()==QEvent::KeyPress){
? ? ? ? QKeyEvent *keyEvent=static_cast<QKeyEvent *>(e);
? ? ? ? if(keyEvent->key()==Qt::Key_Tab){
? ? ? ? ? ? qDebug()<<"tab鍵被按下";
? ? ? ? ? ? return true;
? ? ? ? }
? ? }
? ? return QWidget::event(e);
}
三、事件過濾器
? ? 有時(shí)候,對象需要查看、甚至要攔截發(fā)送到另外對象的事件。例如,對話框可能想要攔截按鍵事件,不讓別的組件接收到;或者要修改回車鍵的默認(rèn)處理。通過前面的章節(jié),我們已經(jīng)知道, Qt 創(chuàng)建了 QEvent 事件對象之后,會(huì)調(diào)用 QObject 的event()函數(shù)處理事件的分發(fā)。顯然,我們可以在event()函數(shù)中實(shí)現(xiàn)攔截的操作。由于event()函數(shù)是protected 的,因此,需要繼承已有類。如果組件很多,就需要重寫很多個(gè) event()函數(shù)。這當(dāng)然相當(dāng)麻煩,更不用說重寫 event()函數(shù)還得小心一堆問題。好在 Qt 提供了另外一種機(jī)制來達(dá)到這一目的:事件過濾器。
? ? QObject 有一個(gè) eventFilter()函數(shù),用于建立事件過濾器。這個(gè)函數(shù)的簽名如下:
? ? virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
? ? 這個(gè)函數(shù)正如其名字顯示的那樣,是一個(gè)“事件過濾器”。所謂事件過濾器,可以理解成一種過濾代碼。想想做化學(xué)實(shí)驗(yàn)時(shí)用到的過濾器,可以將雜質(zhì)留到濾紙上,讓過濾后的液體溜走。事件過濾器也是如此:它會(huì)檢查接收到的事件。如果這個(gè)事件是我們感興趣的類型,就進(jìn)行我們自己的處理;如果不是,就繼續(xù)轉(zhuǎn)發(fā)。這個(gè)函數(shù)返回一個(gè) bool 類型,如果你想將參數(shù)event 過濾出來,比如,不想讓它繼續(xù)轉(zhuǎn)發(fā),就返回 true,否則返回 false。事件過濾器的調(diào)用時(shí)間是目標(biāo)對象(也就是參數(shù)里面的 watched 對象)接收到事件對象之前。也就是說,如果你在事件過濾器中停止了某個(gè)事件,那么, watched 對象以及以后所有的事件過濾器根本不會(huì)知道這么一個(gè)事件。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
?
#include <QMainWindow>
#include <QTextEdit>
#include <QObject>
#include <QEvent>
class MainWindow : public QMainWindow
{
? ? Q_OBJECT
private:
? ? QTextEdit *textedit;
protected:
? ?bool eventFilter(QObject *obj, QEvent *event);
public:
? ? MainWindow();
? ? ~MainWindow();
};
?
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QEvent>
#include <QDebug>
#include <QKeyEvent>
MainWindow::MainWindow(){
? ? textedit=new QTextEdit;
? ? setCentralWidget(textedit);
? ? /*
? ? ?* eventFilter() 函數(shù)相當(dāng)于創(chuàng)建了過濾器,然后我們需要安裝這個(gè)過濾器。安裝過濾器需要調(diào)用
? ? ?* QObject::installEventFilter() 函數(shù)。我們可以向一個(gè)對象上面安裝多個(gè)事件處理器,只要調(diào)
? ? ?* 用多次 installEventFilter() 函數(shù)。如果一個(gè)對象存在多個(gè)事件過濾器,那么,最后一個(gè)安裝
? ? ?* 的會(huì)第一個(gè)執(zhí)行,也就是后進(jìn)先執(zhí)行的順序。
? ? */
? ? textedit->installEventFilter(this);
}
/*
?* MainWindow 是我們定義的一個(gè)類。我們重寫了它的 eventFilter() 函數(shù)。為了過濾特定組件上的事件,
?* 首先需要判斷這個(gè)對象是不是我們感興趣的組件,然后判斷這個(gè)事件的類型。在上面的代碼中,我們不想
?* 讓 textEdit 組件處理鍵盤按下的事件。所以,首先我們找到這個(gè)組件,如果這個(gè)事件是鍵盤事件,則直
?* 接返回 true,也就是過濾掉了這個(gè)事件,其他事件還是要繼續(xù)處理,所以返回 false。對于其它的組件,
?* 我們并不保證是不是還有過濾器,于是最保險(xiǎn)的辦法是調(diào)用父類的函數(shù)。
*/
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{
? ? ? ? return QMainWindow::eventFilter(obj,event);
? ? }
}
/*
?* 注意,如果你在事件過濾器中 delete 了某個(gè)接收組件,務(wù)必將函數(shù)返回值設(shè)為 true。否則,
?* Qt 還是會(huì)將事件分發(fā)給這個(gè)接收組件,從而導(dǎo)致程序崩潰。事件過濾器和被安裝過濾器的組件
?* 必須在同一線程,否則,過濾器將不起作用。另外,如果在安裝過濾器之后,這兩個(gè)組件到了
?* 不同的線程,那么,只有等到二者重新回到同一線程的時(shí)候過濾器才會(huì)有效。
*/
MainWindow::~MainWindow()
{
}
四、定時(shí)器
? ? Qt中有兩種方法來使用定時(shí)器,一種是定時(shí)器事件,另一種是使用信號(hào)和槽。一般使用了多個(gè)定時(shí)器時(shí)最好使用定時(shí)器事件來處理。
#ifndef WIDGET_H
#define WIDGET_H
?
#include <QWidget>
#include <QTimerEvent>
#include <QLabel>
#include <QLineEdit>
#include <QTimer>
#include <QDateTime>
class Widget : public QWidget
{
? ? Q_OBJECT
private slots:
? ? void timerUpdate();
private:
? ? int id1,id2,id3;
? ? QLabel *label1;
? ? QLabel *label2;
? ? QLineEdit *lineEdit;
protected:
? ? void timerEvent(QTimerEvent *event);
public:
? ? Widget(QWidget *parent = 0);
? ? ~Widget();
};
?
#endif // WIDGET_H
#include "widget.h"
#include <QString>
Widget::Widget(QWidget *parent)
? ? : QWidget(parent)
{
? ? resize(600,600);
? ? setWindowTitle(tr("定時(shí)器和隨機(jī)數(shù)"));
? ? label1=new QLabel(this);
? ? label1->move(100,100);
? ? label2=new QLabel(this);
? ? label2->move(200,100);
? ? label2->setFixedSize(40,30);
? ? /*
? ? ?* 這里開啟了三個(gè)定時(shí)器,分別返回了它們的id,這個(gè)id用來區(qū)分不同的定時(shí)器。
? ? ?* 定時(shí)器的時(shí)間單位是毫秒。每當(dāng)一個(gè)定時(shí)器溢出時(shí),都會(huì)調(diào)用定時(shí)器事件處理函數(shù),
? ? ?* 我們可以在該函數(shù)中進(jìn)行相應(yīng)的處理。
? ? */
? ? id1=startTimer(1000);
? ? id2=startTimer(2000);
? ? id3=startTimer(10000);
? ? /*
? ? ?* 如果只是想開啟少量的定時(shí)器,也可以使用信號(hào)和槽來實(shí)現(xiàn)。
? ? ?* 這里創(chuàng)建了一個(gè)定時(shí)器,并將其溢出信號(hào)和更新槽關(guān)聯(lián)起來,最后使用start()函數(shù)來開啟定時(shí)器。
? ? */
? ? QTimer *timer=new QTimer(this);
? ? connect(timer,&QTimer::timeout,this,&Widget::timerUpdate);
? ? timer->start(1000);
? ? lineEdit=new QLineEdit(this);
? ? lineEdit->move(100,300);
? ? lineEdit->setFixedSize(200,50);
? ? /*
? ? ?*關(guān)于隨機(jī)數(shù),在Qt中是使用qrand()和qsrand()兩個(gè)函數(shù)實(shí)現(xiàn)的。在前面的程序中已經(jīng)看到了qrand()
? ? ?* 函數(shù)的使用,其可以產(chǎn)生隨機(jī)數(shù),qrand()%10可以產(chǎn)生0-9之間的隨機(jī)數(shù)。要想產(chǎn)生100以內(nèi)的隨機(jī)數(shù)
? ? ?* 就是%100,以此類推。在使用qrand()函數(shù)產(chǎn)生隨機(jī)數(shù)之前,一般要使用qsrand()函數(shù)為其設(shè)置初值,
? ? ?* 如果不設(shè)置初值,那么每次運(yùn)行程序,qrand()都會(huì)產(chǎn)生相同的一組隨機(jī)數(shù)。為了每次運(yùn)行程序時(shí),
? ? ?* 都可以產(chǎn)生不同的隨機(jī)數(shù),我們要使用qsrand()設(shè)置一個(gè)不同的初值。這里使用了QTime類的secsTo()
? ? ?* 函數(shù),它表示兩個(gè)時(shí)間點(diǎn)之間所包含的秒數(shù),比如代碼中就是指從零點(diǎn)整到當(dāng)前時(shí)間所經(jīng)過的秒數(shù)。
? ? */
? ? qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
}
?
Widget::~Widget()
{
?
}
/*
?* 這里先使用timerId()函數(shù)返回了溢出的定時(shí)器的id,然后根據(jù)該id來判斷是哪個(gè)定時(shí)器溢出了,
?* 并進(jìn)行相應(yīng)的處理。每當(dāng)?shù)谝粋€(gè)定時(shí)器溢出時(shí)都產(chǎn)生一個(gè)小于10的隨機(jī)數(shù);當(dāng)?shù)诙€(gè)定時(shí)器溢出時(shí),
?* 就更改標(biāo)簽的文本。
*/
void Widget::timerEvent(QTimerEvent *event){
? ? if(event->timerId()==id1){
? ? ? ? label1->setText(tr("%1").arg(qrand()%10));
? ? }
? ? else if(event->timerId()==id2){
? ? ? ? label2->setText(tr("呵呵"));
? ? }
? ? else{
? ? ? ? return QWidget::timerEvent(event);
? ? }
}
?
void Widget::timerUpdate(){
? ? QDateTime time=QDateTime::currentDateTime();//獲取系統(tǒng)現(xiàn)在的時(shí)間
? ? QString str=time.toString("yyyy-MM-dd hh:mm:ss ddd");//設(shè)置系統(tǒng)時(shí)間顯示格式
? ? lineEdit->setText(str);//在標(biāo)簽上顯示時(shí)間
? ? int rand=qrand()%300;//坐標(biāo)隨機(jī)數(shù)
? ? int rand2=qrand()%200;
? ? lineEdit->move(rand,rand2);
}
五、事件總結(jié)
? ? Qt 中有很多種事件:鼠標(biāo)事件、鍵盤事件、大小改變的事件、位置移動(dòng)的事件等等。如何處理這些事件,實(shí)際有兩種選擇:
? ? 1. 所有事件對應(yīng)一個(gè)事件處理函數(shù),在這個(gè)事件處理函數(shù)中用一個(gè)很大的分支語句進(jìn)行選擇。
? ? 2. 每一種事件對應(yīng)一個(gè)事件處理函數(shù)。
? ? Qt 具有這么多種事件處理函數(shù),肯定有一個(gè)地方對其進(jìn)行分發(fā),否則, Qt 怎么知道哪一種事件調(diào)用哪一個(gè)事件處理函數(shù)呢?這個(gè)分發(fā)的函數(shù),就是 event()。顯然,當(dāng) QMouseEvent產(chǎn)生之后, event()函數(shù)將其分發(fā)mouseEvent()事件處理器進(jìn)行處理。
? ? event()函數(shù)會(huì)有兩個(gè)問題:
? ? 1. event()函數(shù)是一個(gè) protected 的函數(shù),這意味著我們要想重寫 event(),必須繼承一個(gè)已有的類。試想,我的程序根本不想要鼠標(biāo)事件,程序中所有組件都不允許處理鼠標(biāo)事件,是不是我得繼承所有組件,一一重寫其 event()函數(shù)? protected 函數(shù)帶來的另外一個(gè)問題是,如果我基于第三方庫進(jìn)行開發(fā),而對方?jīng)]有提供源代碼,只有一個(gè)鏈接庫,其它都是封裝好的。我怎么去繼承這種庫中的組件呢?
? ? 2. event()函數(shù)的確有一定的控制,不過有時(shí)候我的需求更嚴(yán)格一些:我希望那些組件根本看不到這種事件。event()函數(shù)雖然可以攔截,但其實(shí)也是接收到了 QMouseEvent對象。我連讓它收都收不到。這樣做的好處是,模擬一種系統(tǒng)根本沒有那個(gè)事件的效果,所以其它組件根本不會(huì)收到這個(gè)事件,也就無需修改自己的事件處理函數(shù)。這種
需求怎么辦呢?這兩個(gè)問題是 event()函數(shù)無法處理的。于是, Qt 提供了另外一種解決方案:事件過濾器。
? ? 事件過濾器給我們一種能力,讓我們能夠完全移除某種事件。事件過濾器可以安裝到任意QObject 類型上面,并且可以安裝多個(gè)。如果要實(shí)現(xiàn)全局的事件過濾器,則可以安裝到QApplication 或者 QCoreApplication 上面。這里需要注意的是,如果使用installEventFilter()函數(shù)給一個(gè)對象安裝事件過濾器,那么該事件過濾器只對該對象有效,
只有這個(gè)對象的事件需要先傳遞給事件過濾器的 eventFilter()函數(shù)進(jìn)行過濾,其它對象不受影響。如果QApplication 對象安裝事件過濾器,那么該過濾器對程序中的每一個(gè)對象都有效,任何對象的事件都是先傳給 eventFilter()函數(shù)。
事件過濾器可以解決剛剛我們提出的 event()函數(shù)的兩點(diǎn)不足:首先,事件過濾器不是protected 的,因此我們可以向任何 QObject 子類安裝事件過濾器;其次,事件過濾器在目標(biāo)對象接收到事件之前進(jìn)行處理,如果我們將事件過濾掉,目標(biāo)對象根本不會(huì)見到這個(gè)事件。
? ? Qt 的事件處理,實(shí)際上是有五個(gè)層次:
? ? 1. 重寫 paintEvent()、 mousePressEvent() 等事件處理函數(shù)。這是最普通、最簡單的形式,同時(shí)功能也最簡單。
? ? 2. 重寫 event() 函數(shù)。 event() 函數(shù)是所有對象的事件入口, QObject 和 QWidget中的實(shí)現(xiàn),默認(rèn)是把事件傳遞給特定的事件處理函數(shù)。
? ? 3. 在特定對象上面安裝事件過濾器。該過濾器僅過濾該對象接收到的事件。
? ? 4. 在 QCoreApplication::instance() 上面安裝事件過濾器。該過濾器將過濾所有對象的所有事件,因此和 notify() 函數(shù)一樣強(qiáng)大,但是它更靈活,因?yàn)榭梢园惭b多個(gè)過濾器。全局的事件過濾器可以看到 disabled 組件上面發(fā)出的鼠標(biāo)事件。全局過濾器有一個(gè)問題:只能用在主線程。
? ? 5. 重寫 QCoreApplication::notify() 函數(shù)。這是最強(qiáng)大的,和全局事件過濾器一樣提供完全控制,并且不受線程的限制。但是全局范圍內(nèi)只能有一個(gè)被使用(因?yàn)镼CoreApplication 是單例的)。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
?
#include <QWidget>
#include <QMouseEvent>
#include <QEvent>
#include <QObject>
/*
?* 事件過濾器可以解決 event() 函數(shù)的兩點(diǎn)不足:首先,事件過濾器不是
?* protected 的,因此我們可以向任何 QObject 子類安裝事件過濾器;其次,事件過濾器在目標(biāo)
?* 對象接收到事件之前進(jìn)行處理,如果我們將時(shí)間過濾掉,目標(biāo)對象根本不會(huì)見到這個(gè)事件。
*/
class Label:public QWidget{
public:
? ? Label(){
? ? ? ? installEventFilter(this);
? ? }
? ? bool eventFilter(QObject *watched, QEvent *event);
? ? ~Label(){}
protected:
? ? void mousePressEvent(QMouseEvent *);
? ? bool event(QEvent *e);
};
class EventFilter:public QObject{
private:
? ? QObject *m_watched;
public:
? ? EventFilter(QObject *watched,QObject *parent=0):QObject(parent),m_watched(watched){}
? ? bool eventFilter(QObject *watched, QEvent *event);
? ? ~EventFilter(){}
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include <QDebug>
/*
?* 第二是全局對象上面的事件過濾器
*/
bool Label::eventFilter(QObject *watched, QEvent *event){
? ? if(watched==this){
? ? ? ? if(event->type()==QEvent::MouseButtonPress){
? ? ? ? ? ? qDebug()<<"eventFilter";
? ? ? ? }
? ? }
? ? return false;
}
/*
?* 最后才是特定的事件處理函數(shù)
*/
void Label::mousePressEvent(QMouseEvent *){
? ? qDebug()<<"mousePressEvent";
}
/*
?* 第三是重寫的event函數(shù)
*/
bool Label::event(QEvent *e){
? ? if(e->type()==QEvent::MouseButtonPress){
? ? ? ? qDebug()<<"event";
? ? }
? ? return QWidget::event(e);
}
/*
?* 全局事件過濾器最先被調(diào)用
*/
bool EventFilter::eventFilter(QObject *watched, QEvent *event){
? ? if(watched==m_watched){
? ? ? ? if(event->type()==QEvent::MouseButtonPress){
? ? ? ? ? ? qDebug()<<"Application::eventFilter";
? ? ? ? }
? ? }
? ? return false;
}
#include "mainwindow.h"
#include <QApplication>
?
int main(int argc, char *argv[])
{
? ? QApplication a(argc, argv);
? ? Label label;
? ? a.installEventFilter(new EventFilter(&label,&label));//安裝過濾器
? ? label.show();
? ? return a.exec();
}
總結(jié)
以上是生活随笔為你收集整理的qt-重写event(),事件过滤器,定时器,事件总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决:Unable to access
- 下一篇: Cookie / Session 的机制