在Qt中,事件作爲一個對象,繼承自QEvent類,常見的有鍵盤事件QKeyEvent、鼠標事件QMouseEvent和定時器事件QTimerEvent等。以下內容主要講解3個常見的事件,會涉及事件過濾器、自定義事件和隨機數的知識。相關內容請各位親在Qt幫助文檔中查看The Event System關鍵字。
事件是對各種應用程序需要知道的由應用程序內部或者外部產生的事情或者動作的通稱。在Qt中使用一個對象來表示一個事件,繼承自QEvent類。同時,事件與信號並不相同,信號是繼事件產生而已產生的,比如單擊一下界面上的按鈕,那麼就會產生鼠標事件QMouseEvent(不是按鈕產生的),而因爲按鈕被按下了,所以會發出clicked()單擊信號(是按鈕產生的)。對於事件,只有重繪按鈕或者產生其他效果的時候,纔有必要去關心鼠標事件。所以,事件與信號是兩個不同層面的東西,發出者不同,作用也不同。在Qt中,任何QObject子類實例都可以接收和處理事件。
1、事件的處理
一個事件由一個特定的QEvent子類來表示,但是有時一個事件又包含多個事件類型,比如鼠標事件又可以分爲鼠標按下、雙擊和移動等多種操作。這些事件類型都由QEvent類的枚舉類型QEvent::Type來表示,其中包含了一百多種事件類型,可以在QEvent類的幫助文檔中查看。雖然QEvent的子類可以表示一個事件,但是卻不能用來處理事件,只能通過QCoreApplication類的notify()函數的幫助文檔處給出了5種處理事件的方法:
方法一:重新實現部件的paintEvent()、mousePressEvent()等事件處理函數。這是最常用的一種方法,不過它只能用來處理特定部件的特定事件。
方法二:重新實現notify()函數。這個函數功能強大,提供了完全的控件,可以再事件過濾器得到事件之前就獲得它們。但是,它一次只能處理一個事件。
方法三:向QApplication對象上安裝事件過濾器。因爲一個程序只有一個QApplication對象,所以這樣實現的功能與使用notify()函數是相同的,優點是可以同時處理多個事件。
方法四:重新實現event()函數。QObject類的event()函數可以在事件到達默認的事件處理函數之前獲得該事件。
方法五:在對象上安裝事件過濾器。使用事件過濾器可以在一個界面類中同時處理不同子部件的不同事件。
在實際的編程中,最常用的是方法一,其次是方法五。因爲方法二需要繼承自QApplication類;而方法三要使用一個全局的事件過濾器,這將減緩事件的傳遞。所以,雖然這兩種方法功能很強大,但是卻很少唄用到。
每個程序的main()函數中調用QApplication類的exec()函數,它會使Qt應用程序進入事件循環,這樣就可以使應用程序在運行時接收發生的各種事件。在QWidget類的虛函數中,mouseMoveEvent ( QMouseEvent * ),mousePressEvent ( QMouseEvent * )和mouseReleaseEvent ( QMouseEvent * )等虛接口。只要在繼承QWidget中的自定義類重載,實現自定義操作。
2、鼠標事件和滾輪事件
QMouseEvent類用來表示一個鼠標事件,通過這個類可以獲知鼠標是哪個鍵按下了、鼠標指針的當前位置等信息。QWheelEvent類用來表示鼠標滾輪事件,通過這個類主要獲取滾輪移動的方向和距離。重載QWidget虛函數實現鼠標移動,滾輪,雙擊,單擊和釋放事件。函數原型如下:
void mousePressEvent(QMouseEvent *event);//單擊(左右)
void mouseReleaseEvent(QMouseEvent *event);//釋放(左右)
void mouseDoubleClickEvent(QMouseEvent *event);//雙擊(左右)
void mouseMoveEvent(QMouseEvent *event);//移動
void wheelEvent(QWheelEvent *event);//滾輪
- #include "widget.h"
- #include "ui_widget.h"
- #include <QMouseEvent>
- #include <QDebug>
- Widget::Widget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- // 設置鼠標跟蹤,只有設置才能在鼠標進入範圍內,也可以觸發mouseMoveEvent事件(開啓與關閉試試效果)
- //setMouseTracking(true);
- // 創建光標對象
- //QCursor cursor;
- // 設置光標形狀
- //cursor.setShape(Qt::OpenHandCursor);
- // 使用光標
- //setCursor(cursor);
- }
- Widget::~Widget()
- {
- delete ui;
- }
- // 鼠標按下事件
- void Widget::mousePressEvent(QMouseEvent *event)
- {
- // 如果是鼠標左鍵按下
- if(event->button() == Qt::LeftButton){
- // 使鼠標指針暫時變爲小手抓取的樣子
- QCursor cursor;
- cursor.setShape(Qt::ClosedHandCursor);
- QApplication::setOverrideCursor(cursor);
- // 獲取指針位置和窗口位置的差值,以便移動時使用
- offset = event->globalPos() - pos();
- }
- // 如果是鼠標右鍵按下
- else if(event->button() == Qt::RightButton){
- // 使用自定義的圖片作爲鼠標指針
- QCursor cursor(QPixmap("../yafeilinux.png"));
- QApplication::setOverrideCursor(cursor);
- }
- }
- // 鼠標移動事件
- void Widget::mouseMoveEvent(QMouseEvent *event)
- {
- qDebug() << tr("Widget的mouseMoveEvent()函數");
- // 這裏必須使用buttons()
- if(event->buttons() & Qt::LeftButton){
- // 我們使用鼠標指針當前的位置減去差值,就得到了窗口應該移動的位置
- QPoint temp;
- temp = event->globalPos() - offset;
- move(temp);
- }
- }
- // 鼠標釋放事件
- void Widget::mouseReleaseEvent(QMouseEvent *event)
- {
- // 恢復鼠標指針形狀
- QApplication::restoreOverrideCursor();
- }
- // 鼠標雙擊事件
- void Widget::mouseDoubleClickEvent(QMouseEvent *event)
- {
- // 如果是鼠標左鍵按下
- if(event->button() == Qt::LeftButton){
- // 如果現在不是全屏,將窗口設置爲全屏
- if(windowState() != Qt::WindowFullScreen)
- setWindowState(Qt::WindowFullScreen);
- // 如果現在已經是全屏狀態,那麼恢復以前的大小
- else setWindowState(Qt::WindowNoState);
- }
- }
- // 滾輪事件
- void Widget::wheelEvent(QWheelEvent *event)
- {
- // 當滾輪遠離使用者時進行放大,當滾輪向使用者方向旋轉時進行縮小
- if(event->delta() > 0){
- ui->textEdit->zoomIn();
- }else{
- ui->textEdit->zoomOut();
- }
- }
QKeyEvent類用來表示一個鍵盤事件(鍵盤按下或者釋放)。通過QKeyEvent的key()函數可以獲取具體的按鍵,但獲取鍵盤上的修飾鍵(如Ctrl和Shift等)要通過QKeyEvent的modifiers()函數來獲取。
- #include "widget.h"
- #include "ui_widget.h"
- #include <QKeyEvent>
- #include <QDebug>
- Widget::Widget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- // 使主界面獲得焦點
- setFocus();
- // 初始化變量
- keyUp = false;
- keyLeft = false;
- move = false;
- }
- Widget::~Widget()
- {
- delete ui;
- }
- // 鍵盤按下事件
- void Widget::keyPressEvent(QKeyEvent *event)
- {
- if(event->key() == Qt::Key_Up){
- // 按鍵重複時不做處理
- if(event->isAutoRepeat()) return;
- // 標記向上方向鍵已經按下
- keyUp = true;
- }else if(event->key() == Qt::Key_Left){
- if(event->isAutoRepeat()) return;
- keyLeft = true;
- }
- }
- // 按鍵釋放事件
- void Widget::keyReleaseEvent(QKeyEvent *event)
- {
- if(event->key() == Qt::Key_Up){
- if(event->isAutoRepeat()) return;
- // 釋放按鍵後將標誌設置爲false
- keyUp = false;
- // 如果已經完成了移動
- if(move){
- // 設置標誌爲false
- move = false;
- // 直接返回
- return;
- }
- // 如果向左方向鍵已經按下且沒有釋放
- if(keyLeft){
- // 斜移
- ui->pushButton->move(30,80);
- // 標記已經移動
- move = true;
- }else{
- // 否則直接上移
- ui->pushButton->move(120,80);
- }
- }
- else if(event->key() == Qt::Key_Left){
- if(event->isAutoRepeat()) return;
- keyLeft = false;
- if(move){
- move = false;
- return;
- }
- if(keyUp) {
- ui->pushButton->move(30,80);
- move = true;
- }else{
- ui->pushButton->move(30,120);
- }
- }
- // 使用向下方向鍵來還原按鈕的位置
- else if(event->key() == Qt::Key_Down){
- ui->pushButton->move(120,120);
- }
- }
QTimerEvent類用來描述一個定時器事件。對於QObject的子類,只需要使用int QObject::startTimer( int interval );函數來開啓一個定時器,其返回的整形編號是用來代表這個定時器;而停止定時器可以通過void QObject::killTimer( int id );函數。當定時器溢出時就可以在timerEvent()函數中獲取該定時器的編號來進行相關操作。
其實編程中更多的是使用QTimer類來實現一個定時器,它提供了更高層次的編程接口,比如可以使用信號和槽,還可以設置只運行一次的定時器。
關於隨機數,在Qt中是使用qrand()和qsrand()兩個函數實現的。
- #include "widget.h"
- #include "ui_widget.h"
- #include <QDebug>
- #include <QTimer>
- #include <QTime>
- Widget::Widget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- // 開啓一個1秒定時器,返回其ID
- id1 = startTimer(1000);
- id2 = startTimer(2000);
- id3 = startTimer(3000);
- // 創建一個新的定時器
- QTimer *timer = new QTimer(this);
- // 關聯定時器的溢出信號到我們的槽函數上
- connect(timer,SIGNAL(timeout()),this,SLOT(timerUpdate()));
- // 設置溢出時間爲1秒,並啓動定時器
- timer->start(1000);
- // 爲隨機數設置初值,如果不是設置初值,每次運行qrand()函數都會產生相同的一組隨機數
- qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
- // 開啓只運行一次的定時器
- QTimer::singleShot(10000,this,SLOT(close()));
- }
- Widget::~Widget()
- {
- delete ui;
- }
- void Widget::timerEvent(QTimerEvent *event)
- {
- // 判斷是哪個定時器
- if(event->timerId() == id1){
- qDebug() << "timer1";
- }
- else if(event->timerId() == id2){
- qDebug() << "timer2";
- }
- else{
- qDebug() << "timer3";
- }
- }
- // 定時器溢出處理
- void Widget::timerUpdate()
- {
- // 獲取當前時間
- QTime time = QTime::currentTime();
- // 轉換爲字符串
- QString text = time.toString("hh:mm:ss");
- // 注意單引號間要輸入一個空格
- // 每隔一秒就將“:”顯示爲空格
- if((time.second()%2) == 0) text[2]=' ';
- ui->lcdNumber->display(text);
- // 產生300以內的正整數
- int rand = qrand()%300;
- ui->lcdNumber->move(rand,rand);
- }
Qt提供了事件過濾器來在一個部件中監控其他多個部件的事件。事件過濾器與其他部件不同,它不是一個類,只是由installEventFilter()和eventFilter()兩個函數組成的一種操作,用來完成一個部件對其他部件的事件的監視。
- #include "widget.h"
- #include "ui_widget.h"
- #include <QKeyEvent>
- #include <QWheelEvent>
- Widget::Widget(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- // 爲編輯器部件在本窗口上安裝事件過濾器
- ui->textEdit->installEventFilter(this);
- ui->spinBox->installEventFilter(this);
- QKeyEvent myEvent(QEvent::KeyPress,Qt::Key_Up,Qt::NoModifier);
- // 發送鍵盤事件到spinBox部件
- //qApp->sendEvent(ui->spinBox,&myEvent);
- //sendEvent中的QEvent對象參數在事件發送完成後無法自動刪除,所以需要在棧中創建QEvent對象;
- //而postEvent中的QEvent對象參數必須在堆中進行創建(new),當事件被髮送後事件隊列會自動刪除它
- QApplication::sendEvent(ui->spinBox,&myEvent);
- }
- Widget::~Widget()
- {
- delete ui;
- }
- // 事件過濾器
- bool Widget::eventFilter(QObject *obj, QEvent *event)
- {
- // 判斷部件
- if(obj == ui->textEdit){
- // 判斷事件
- if(event->type() == QEvent::Wheel){
- // 將event強制轉換爲發生的事件的類型
- QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event);
- if(wheelEvent->delta() > 0) ui->textEdit->zoomIn();
- else ui->textEdit->zoomOut();
- // 該事件已經被處理
- return true;
- }else{
- // 如果是其他事件,可以進行進一步的處理
- // 讓其他部件去處理
- return false;
- }
- }
- else if(obj == ui->spinBox){
- if(event->type() == QEvent::KeyPress){
- QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
- if(keyEvent->key() == Qt::Key_Space){
- ui->spinBox->setValue(0);
- return true;
- }else{
- return false;
- }
- }else{
- return false;
- }
- }
- return QWidget::eventFilter(obj,event);
-
}