事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件是****用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。
如图所示:
app.exe()--------->事件过滤器----->事件分发器-----------------》事件处理函数
下面依次从下往上介绍。
提升一个按钮。重写鼠标按下与鼠标释放事件。
mybutton.h
#include
#include
#include
#include
class MyButton : public QPushButton
{
Q_OBJECT
public:
explicit MyButton(QWidget *parent = nullptr);
//鼠标点击事件
void mousePressEvent(QMouseEvent *ev);
//鼠标释放事件
void mouseReleaseEvent(QMouseEvent *ev);
signals:
};
mybutton.cpp
#include "mybutton.h"
#include
MyButton::MyButton(QWidget *parent)
: QPushButton{parent}
{
}
void MyButton::mousePressEvent(QMouseEvent *ev)
{
qDebug() << "鼠标按下" ;
}
void MyButton::mouseReleaseEvent(QMouseEvent *ev)
{
qDebug() << "鼠标抬起" ;
}
结果如下:
注意:按钮是提升自定义mybutton类。而mybutton又是继承于QPushButton
mybutton.h
#include
#include
#include
#include
class MyButton : public QPushButton
{
Q_OBJECT
public:
explicit MyButton(QWidget *parent = nullptr);
//鼠标点击事件
void mousePressEvent(QMouseEvent *ev);
signals:
};
mybutton.cpp
#include "mybutton.h"
#include
MyButton::MyButton(QWidget *parent)
: QPushButton{parent}
{
}
void MyButton::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
qDebug() << "MyButton鼠标按下的是左键";
ev->accept(); // accept 事件不会再往下传递
}
else // 鼠标的右键按下与鼠标的中间按键往下传递
{
qDebug() << "MyButton鼠标其他键按下";
QPushButton::mousePressEvent(ev);
}
}
myWidget.h
#include \
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr);
~MyWidget();
//鼠标点击事件
void mousePressEvent(QMouseEvent *ev);
private:
Ui::MyWidget *ui;
};
myWidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
qDebug() << "MyWidget鼠标左键按下";
}else {
qDebug() << "MyWidget鼠标其他键按下";
}
}
在myWidget类与mybutton类都有重写鼠标事件。
现在accept()函数
当鼠标的左键按下时的打印:
对鼠标的左键按下不在往下发所以myWidget收不到。
当鼠标的右键按下时的打印:
对鼠标的右键按下往下发所以myWidget能收到。
当代码把accept()函数改成ignore()函数。鼠标左键按下事件继续往下传递。传递给了父组件mywidget
void MyButton::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
qDebug() << "MyWidget鼠标左键按下";
ev->ignore(); //忽略,事件继续往下传递,给谁传递?
//事件传递给了父组件,不是给父类(基类)
}
else // 鼠标的右键按下与鼠标的中间按键往下传递
{
qDebug() << "MyButton鼠标其他键按下";
QPushButton::mousePressEvent(ev);
}
}
以上就是事件的接收与忽略。就是accept()函数与ignore()函数。
函数原型: bool event(QEvent *ev)
event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。
myWidget.h
#include
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr);
~MyWidget();
bool event(QEvent *ev); // 重写event事件
void keyPressEvent(QKeyEvent *event); //键盘按下事件
void keyReleaseEvent(QKeyEvent *event);
private:
Ui::MyWidget *ui;
};
myWidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
}
MyWidget::~MyWidget()
{
delete ui;
}
bool MyWidget::event(QEvent *e)
{
if(e->type() == QEvent::KeyPress) /* 按键按下的分发 */
{
/* 类型转换 */
QKeyEvent *env = static_cast<QKeyEvent *>(e);
if(env->key() == Qt::Key_0)
{
qDebug() << "event Qt::Key_0";
return QWidget::event(e); /* 只处理按键0 */
}
return true; /* 其他按键不分发下去 */
} else {
return QWidget::event(e); /* 其他的事件都分发下去 */
}
}
void MyWidget::keyPressEvent(QKeyEvent *event) //键盘按下事件
{
switch(event->key()) {
case Qt::Key_0:
qDebug() <<"Qt::Key_0";
break;
case Qt::Key_1:
qDebug() <<"Qt::Key_1";
break;
}
}
void MyWidget::keyReleaseEvent(QKeyEvent *event)
{
qDebug() <<"MyWidget::keyReleaseEvent";
}
总结:当按下按键0按下才处理。其他按键不起作用。
按键0按下的结果:
其他按键按下不起作用。只打印按键释放。因为如上return QWidget::event(e); /* 其他的事件都分发下去 */
按键是否是可以打印的。如果改成return true; 就不会打印按键释放。
函数原型如下:virtual bool QObject::eventFilter ( QObject * watched, QEvent * event )
watched:过滤哪个控件 event:过滤哪个事件。
这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,**不想让它继续转发,就返回 true,否则返回 false。**事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。
mybutton.h
#include
class MyButton : public QPushButton
{
Q_OBJECT
public:
explicit MyButton(QWidget *parent = nullptr);
void mouseMoveEvent(QMouseEvent *ev);
signals:
};
mybutton.cpp
#include "mybutton.h"
#include
MyButton::MyButton(QWidget *parent)
: QPushButton{parent}
{
}
void MyButton::mouseMoveEvent(QMouseEvent *ev)
{
qDebug() << "mouseMoveEvent";
QPushButton::mousePressEvent(ev);
}
mywidget.h
#include
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr);
~MyWidget();
//事件过滤器
bool eventFilter(QObject *obj, QEvent *e);
private:
Ui::MyWidget *ui;
};
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
//安装过滤器
ui->pushButton->installEventFilter(this);
ui->pushButton->setMouseTracking(true);/* 按钮追踪 */
}
MyWidget::~MyWidget()
{
delete ui;
}
bool MyWidget::eventFilter(QObject *obj, QEvent *e)
{
if(obj == ui->pushButton)
{
QMouseEvent *env = static_cast<QMouseEvent *>(e);
//判断事件
if(e->type() == QEvent::MouseMove)
{
ui->pushButton->setText(QString("eventFilter:(%1, %2)").arg(env->x()).arg(env->y()));
return true; /* 不分发 */
}
else
{
return QWidget::eventFilter(obj, e);
}
return true;
}
else
{
return QWidget::eventFilter(obj, e);
}
}
如上 return true; /* 不分发 */ 所以 qDebug() << “mouseMoveEvent”;没有打印
如果设置return false; 事件不会过滤。就会打印mouseMoveEvent。
总结:
现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:
上面1, 2,3,点用的多。4, 5不怎么用。
事件过滤器与enevt事件,以及事件的处理函数都是虚函数继承与上层父类。我们在重写时虚函数必须继承上层父类一致。否则就不叫重写。
以上就是今天要讲的内容,QT的事件。主要理解QT从上到下的流程。