Qt事件處理介紹
- Qt平台會將系統產生的消息轉換為Qt事件
- Qt事件是一個QEvent的對象
- Qt事件用來描述程序內部或外部發生的動作
- 任意的QObject對象都具備事件處理的能力
Qt常見的事件繼承圖如下:
- QInputEvent:用戶輸入事件
- QDropEvent:用戶拖放事件
- QPaintEvent:描述操作系統繪制GUI動作的事件
- QCloseEvent:用戶關閉窗口事件
- QTimerEvent:計時器事件
事件處理方式順序
1.Qt事件產生后立即被分發到QWidget對象
2.QWidget中的event(QEvent*)進行事件處理
3.event()根據事件類型調用不同的事件處理函數
4.在事件處理函數中發送Qt中預定義的信號
5.調用信號關聯的槽函數
以按鈕點擊為例,如下圖所示:
QPushButton事件處理總結
1.當點擊按鈕后,將會觸發鼠標事件
2.調用event(QEvent*)成員函數
3.調用mouseReleaseEvent(QMouseEvent*)成員函數
4.調用click()成員函數
5.觸發信號SIGNAL(clicked());
同樣,當用戶點擊窗口的關閉按鈕時,也會觸發closeEvent()事件函數,該函數需要重寫,才能實現
參考示例:
void MainWindow::closeEvent(QCloseEvent *event) { if (maybeSave()) //如果還有需要保存的數據
{ writeSettings(); event->accept(); }
else //取消關閉窗口 { event->ignore(); } }
類似的還有keyEvent()獲取鍵盤事件函數, keyReleaseEvent()鍵盤按下事件函數,enterEvent光標進入組件事件函數, leaveEvent光標離開組件事件函數等等。
其中QCloseEvent繼承與QEvent,在QEvent中常用成員函數有
void accept (); //接收者處理當前事件 void ignore (); //接收者忽略當前事件,忽略后,事件可能傳遞給父組件 bool isAccepted(); //判斷當前事件是否被處理過
當使用ignore()處理事件時,該事件可能會傳遞給其父組件對象繼續處理
步驟如下:
- 寫兩個類: QMyWidget、QMyLineEdit(QMyLineEdit是QMyWidget的類成員)
- 通過QMyLineEdit來重寫LineEdit的keyReleaseEvent()鍵盤按下事件函數
- 通過QMyWidget來重寫QWidget的keyReleaseEvent()鍵盤按下事件函數
- 然后通過ignore()處理QMyLineEdit的keyReleaseEvent()事件函數
- 判斷是否會繼續執行QMyWidget父組件的keyReleaseEvent()事件函數
QLineEdit.h如下所示:
#ifndef QMYLINEEDIT_H #define QMYLINEEDIT_H #include <QLineEdit> #include <QtGui> class QMyLineEdit : public QLineEdit { Q_OBJECT public: explicit QMyLineEdit(QWidget *parent = 0); void keyReleaseEvent( QKeyEvent * event ); };
#endif // QMYLINEEDIT_H
QLineEdit.cpp如下所示:
#include "QMyLineEdit.h" QMyLineEdit::QMyLineEdit(QWidget *parent) : QLineEdit(parent) {
}
void QMyLineEdit::keyReleaseEvent( QKeyEvent * event ) { qDebug()<<"QMyLineEdit::keyReleaseEvent"; qDebug()<<"key value:"<< event->key(); event->ignore(); //忽略當前事件 }
QMyWidget.h如下所示:
#ifndef QMYWIDGET_H #define QMYWIDGET_H #include "QMyLineEdit.h" #include <QWidget> class QMyWidget : public QWidget { Q_OBJECT QMyLineEdit line; public: explicit QMyWidget(QWidget *parent = 0); void keyReleaseEvent( QKeyEvent * event ); }; #endif // QMYWIDGET_H
QMyWidget.cpp如下所示:
#include "QMyWidget.h" QMyWidget::QMyWidget(QWidget *parent) : QWidget(parent), line(this) { } void QMyWidget::keyReleaseEvent( QKeyEvent * event ) { qDebug()<<"QMyWidget::keyReleaseEvent"; qDebug()<<"key value:"<< event->key(); QWidget::keyPressEvent(event); }
main()函數如下所示:
int main(int argc, char *argv[]) { QApplication a(argc, argv); QMyWidget w; w.show(); return a.exec(); }
效果如下:
可以看到成員調用了event->ignore()函數忽略事件后,同樣也會繼續進入QMyWidget類處理事件
Qt中的事件過濾器
- 事件過濾器可以對需要的組件接收到的事件進行過濾,以及監控
- 任意的QObject對象都可以作為事件過濾器使用
- 事件過濾器的實現,需要重寫eventFilter()函數
- 組件要想被監控,則需要通過installEventFilter()安裝事件過濾器
- 事件過濾器能夠決定是否將事件轉發給組件對象,如下圖所示:
eventFilter函數體如下所示:
bool QObject::eventFilter ( QObject * watched, QEvent * event ); // watched:代表被監控的組件 event:代表要轉發的事件 //返回true,表示該事件也被過濾掉(處理),無需再轉發了 //返回false,則正常轉發給watched
參考示例-實現文本框只允許輸入數字:
class MainWindow : public QMainWindow { public: MainWindow(); protected: bool eventFilter(QObject *obj, QEvent *ev); private: QTextEdit *textEdit; };
MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->setAttribute(Qt::WA_InputMethodEnabled, false); //禁止中文輸入法 textEdit->installEventFilter(this); } 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(); switch(keyEvent->key()) //只接受0~9數字 { case Qt::Key_0: case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: return false; default: return true; } } else { return false; } } else { return QMainWindow::eventFilter(obj, event); } }
用戶拖放事件
每個QWidget對象都能處理拖放事件
常用的拖放事件相關函數有:
void dragEnterEvent ( QDragEnterEvent * event ); //拖事件處理函數 void dropEvent ( QDropEvent * event ) ; //放事件處理函數
拖放事件所處理的數據是QMimeData類
- QMimeData類可以通過QDragEnterEvent 或者 QDropEvent 的成員函數QDropEvent()獲取
- QMimeData支持多種不同類型的文件數據
MIME類型常用處理函數如下所示:
拖放事件的步驟如下:
1.在構造函數里通過setAcceptDrops(true)函數,讓該組件能接受拖放事件
2.重寫dragEnterEvent(QDragEnterEvent* event)函數並判斷MIME類型
如果是期待的類型,則調用event ->acceptProposedAction();
否則調用 : event ->ignore();
3.重寫dropEvent()函數並判斷MIME類型
如果是期待的類型,則獲取MIME數據並處理.
否則調用 : event ->ignore();
示例:
class MainWindow : public QMainWindow { private: QTextEdit *textEdit; void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); public: MainWindow(); }; MainWindow::MainWindow() { textEdit = new QTextEdit; setCentralWidget(textEdit); textEdit->setAttribute(Qt::WA_InputMethodEnabled, false) ; textEdit->installEventFilter(this); this->setAcceptDrops(true); } void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if(event->mimeData()->hasUrls()) //判斷拖的類型 { event->acceptProposedAction(); } else { event->ignore(); } } void MainWindow::dropEvent(QDropEvent *event) { if(event->mimeData()->hasUrls()) //判斷放的類型 { textEdit->clear(); QList<QUrl> List = event->mimeData()->urls(); for(int i=0;i<List.length();i++) { textEdit->insertPlainText(List[i].toLocalFile()+"\n"); } } else { event->ignore(); } }
效果: