Qt所有的事件类都继承于QEvent。主要实现方式是通过通过事件对象传递给QObject的event()函数,event()函数再按照事件对象类型来分给特定事件处理函数来处理。
常用的事件处理回调函数有
[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event)[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event)[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event)[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event)[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event)[virtual protected] void QWidget::enterEvent(QEnterEvent *event)[virtual protected] void QWidget::leaveEvent(QEvent *event)[virtual protected] void QWidget::dragEnterEvent(QDragEnterEvent *event)[virtual protected] void QWidget::dragLeaveEvent(QDragLeaveEvent *event)[virtual protected] void QWidget::wheelEvent(QWheelEvent *event)
这些函数均为virtual protected,且都定义于QWidget类中,因此,可以在QWidget的子类中重新实现,不过要注意定义方式。
protected:void mouseMoveEvent(QMouseEvent *event);void mousePressEvent(QMouseEvent *event);void mouseReleaseEvent(QMouseEvent *event);
然后根据不同的事件类QMouseEvent去进行不同的操作。详情根据帮助文档查找有哪些操作。
如果event不使用,则在函数中添加Q_UNUSED(event); 来规避编译警告。
void widget::mousePressEvent(QMouseEvent *event){Q_UNUSED(event); //规避编译警告}
定时器事件
[virtual protected] void QObject::timerEvent(QTimerEvent *event)
很明显继承于QObject类,因此可以在相应的子类中重写。
主要的函数有
int QObject::startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer)void QObject::killTimer(int id)int QTimerEvent::timerId() const
其中,
startTimer()为启动定时器,参数int interval单位为毫秒
killTimer()停止定时器,参数int id则为startTimer()的返回id,通过timerId()得到
例子:
class MyObject : public QObject{Q_OBJECTpublic:MyObject(QObject *parent = nullptr);protected:void timerEvent(QTimerEvent *event) override;private:int timer1;int timer2;int timer3;};MyObject::MyObject(QObject *parent): QObject(parent){timer1=startTimer(50); // 50ms定时器timer2=startTimer(1000); // 1s定时器timer3=startTimer(60000); // 1minute定时器// using namespace std::chrono;// startTimer(milliseconds(50));// startTimer(seconds(1));// startTimer(minutes(1));// since C++14 we can use std::chrono::duration literals, e.g.:// startTimer(100ms);// startTimer(5s);// startTimer(2min);// startTimer(1h);}void MyObject::timerEvent(QTimerEvent *event){if(event->timerId() == this->timer1){static int sec = 0;sec++;if(5 == sec) //定时5*50ms时间{this->killTimer(this->timer1); //停止50ms定时器}}qDebug() << "Timer ID:" << event->timerId();}
事件的接收和忽略
在一个子类中重写事件时,要注意不会影响到父类原先信号的使用。
即事件接收后,要确保事件可以传递给父类。
例子
class MyButton : public QPushButton{Q_OBJECTpublic:MyButton(QWidget *parent = nullptr);protected:void mousePressEvent(QMouseEvent *event);};MyButton::MyButton(QWidget *parent): QPushButton(parent){}void MyButton::mousePressEvent(QMouseEvent *event){if(event->button() == Qt::LeftButton){qDebug() << "左键按下";//事件接收后,不往下传递//标记3// event->ignore(); //事件忽略,事件继续往下传递//传递给了父组件Widget,非父类(基类)QPushButton}else{//标记1QPushButton::mousePressEvent(event);//事件忽略,事件继续往下传递}//标记2// QPushButton::mousePressEvent(event); //不影响父类的接收}class widget : public QWidget{Q_OBJECTprivate:MyButton *button;protected:void mousePressEvent(QMouseEvent *event);};Widget::widget(QWidget *parent){button = new MyButton(this);connect(button,&MyButton::clicked,this[=](){qDebug() << "按键按下";});}void Widget::mousePressEvent(QMouseEvent *event){qDebug() << "+++++";}
在例子中,重写一个MyButton类继承于QPushButton,
如果在标记1中,则只打印输出 左键按下 ,说明事件传递到此就不继续传递
如果在标记2中,则打印输出 左键按下 按键按下,通过调用父类事件函数继续传递消除影响
如果在标记3中,则打印输出 左键按下 +++++,说明事件传递给父组件,非父类
即,重写事件类时要注意不会影响基类的事件传递。
accept() 和 ignore() 函数
主要使用在closeEvent()中。
void Widget::closeEvent(QCloseEvent *event){int ret = QMessageBox::question(this,"question","是否需要关闭窗口");if(ret == QMessageBox::Yes){//关闭窗口//处理关闭窗口事件,接收事件,事件就不会再往下传递event->accept();}else{//不关闭窗口//忽略事件,事件继续传递给父组件event->ignore();}}
事件分发函数 event()
[override virtual protected] bool QWidget::event(QEvent *event)
可以看到事件是bool类型,如果传入的事件已被识别并且处理,则返回true,否则返回false。
如果返回值是true,则Qt认为该次事件已经处理完毕,不会再将该次事件发送给其他对象,而是会继续处理事件队列的下一个事件。
可以通过重写 event(),来重新进行事件分发,标记点1。
也可以通过event(),来中止某个事件,标记点2。
class MyWidget : public QWidget{Q_OBJECTpublic:MyWidget(QWidget *parent = nullptr);protected:void event(QEvent *event) override;};MyWidget::MyWidget(QWidget *parent): QWidget(parent){}bool MyWidget::event(QEvent *event){//标记点1 事件分发switch(event->type()){case QEvent::Close :{QCloseEvent *e = static_cast<QCloseEvent *>(event);closeEvent(event);return true;}break;case QEvent::MouseButtonPress :{QMouseEvent *e = static_cast<QMouseEvent *>(event);mousePressEvent(e);return true;}break;/*...*/}//标记点2// if(event->type() == QEvent::Timer)// {// //干掉定时器// //如果返回true,事件停止传播// //QTimerEvent *event //需要进行event类型的强制转化// //QTimerEvent *e = static_cast<QTimerEvent *>(event);// //timerEvent(e);// return true;// }// else if()// {// //类型转换// QKeyEvent *e = static_cast<QKeyEvent *>(event);// if(e->key() == Qt::Key_B)// {// return QWidget::event(event);// }// return true;// }// else// { //// return QWidget::event(event);// }}
事件过滤器:eventFilter()
事件过滤器会检查接收到的事件,如果这个事件是我们要的,就自己处理,否则就继续转发。
这个函数返回一个bool类型,如果你想将event过滤出来,比如,不想让它继续转发,就返回true,否则返回false。
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
首先重写eventFilter(),然后安装事件过滤器,之后过滤事件。
例子
注意:事件过滤器在多线程中无效,即安装事件过滤器和过滤器要在同一个线程。
class MainWindow : public QMainWindow{public:MainWindow();protected:bool eventFilter(QObject *obj, QEvent *ev) override;private:QTextEdit *textEdit;};MainWindow::MainWindow(){textEdit = new QTextEdit;setCentralWidget(textEdit);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();return true; //不让事件继续传播} else {// 按照原来的方式传播事件return QMainWindow::eventFilter(obj, event);}} else {// 按照原来的方式传播事件return QMainWindow::eventFilter(obj, event);}}
