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_OBJECT
public:
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_OBJECT
public:
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
{
//标记1
QPushButton::mousePressEvent(event);
//事件忽略,事件继续往下传递
}
//标记2
// QPushButton::mousePressEvent(event); //不影响父类的接收
}
class widget : public QWidget
{
Q_OBJECT
private:
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_OBJECT
public:
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);
}
}
注意:事件过滤器在多线程中无效,即安装事件过滤器和过滤器要在同一个线程。