事件
事件(event)是由系统或者 Qt 本身在不同的时刻发出的。
- 当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。
- 一些事件在对用户操作做出响应时发出,如键盘事件等;
- 另一些事件则是由系统自动发出,如计时器事件。
Qt 程序需要在 main()函数创建一个 QApplication对象,然后调用它的 exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于 QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给 QObject 的 event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(eventhandler)
【注意】
- 这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数
通过QLabel了解事件的操作
- 修改基类名称
- 提升控件
- 重写虚函数
设置label属性
如果重写QLabel事件 需要在自定义的label控件中实现 事件的重写
- 【新建类】
- 修改要继承的类名称
提升label控件为mylabel,这样label控件才能使用mylabel重写的事件。
总结:
如果想重写 某个类的事件
一般情况 就要自定义一个类
继承于该控件的类型然后将控件的类 提升成 自定义的类
这样我们就可以在自定义类中 重写控件类型事件函数
mylabel.h【声明虚函数,修改继承的基类】
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>//修改
//mylabel继承于QLabel,重写事件
class mylabel : public QLabel//修改
{
Q_OBJECT
public:
explicit mylabel(QWidget *parent = 0);
//重新写鼠标进入事件
virtual void enterEvent(QEvent * event);
//重新写鼠标离开事件
virtual void leaveEvent(QEvent * event);
virtual void mouseMoveEvent(QMouseEvent * event);
virtual void mousePressEvent(QMouseEvent * event);
signals:
public slots:
};
#endif // MYLABEL_H
mylabel.cpp【修改继承的基类,重写虚函数,设置鼠标跟踪,在构造函数中增加此属性】
#include "mylabel.h"
#include<QDebug>
#include<QMouseEvent>
mylabel::mylabel(QWidget *parent) :
QLabel(parent)//继承qlabel
{
//设置鼠标跟踪功能
//在构造函数中增加此属性
this->setMouseTracking(true);
}
void mylabel::enterEvent(QEvent *event)
{
qDebug()<<"鼠标进入"<<endl;
}
void mylabel::leaveEvent(QEvent *event)
{
qDebug()<<"鼠标离开"<<endl;
}
void mylabel::mouseMoveEvent(QMouseEvent *event)//事件对象
{
// if(event->button()==Qt::LeftButton)//【有bug,先按下,就不能执行移动】
{
qDebug()<<"鼠标左键移动 x="
<<event->x()<<",y="<<event->y()<<endl;
}
// else if(event->button()==Qt::RightButton)
{
qDebug()<<"鼠标右键移动x="
<<event->x()<<",y="<<event->y()<<endl;
}
}
void mylabel::mousePressEvent(QMouseEvent *event)
{
//ev包含了我们所需要的坐标信息
if(event->button()==Qt::LeftButton)
{
qDebug()<<"鼠标左键按下x="
<<event->x()<<",y="<<event->y()<<endl;
}
else if(event->button()==Qt::RightButton)
{
qDebug()<<"鼠标右键按下x="
<<event->x()<<",y="<<event->y()<<endl;
}
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
程序运行:
事件分发器(了解)
事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。event()函数主要用于事件的分发
如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。
- 如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
- 在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播。
- 记得不关心的事件 记得用父类的事件分发器处理。
案例:在事件分发器中 处理鼠标单击事件
1、重写 分发器事件(MyL abel类中)
virtual bool event(QEvent *e)
2、判断出事鼠标单击:QEvent::MouseButtonPress
【静态类型转换】
//事件分发器
bool mylabel::event(QEvent *e)
{
//只关心鼠标按下事件
if(e->type()==QEvent::MouseButtonPress)
{
qDebug()<<"在事件分发器中鼠标单击了"<<endl;
//注意QEvent类没有x y,但是QMouseEvent
QMouseEvent*ev=static_cast<QMouseEvent*>(e);
qDebug()<<"x= "<<ev->x()<<",y="<<ev->y()<<endl;
return true;//当前已经处理 不需要想下分
}
//请调用父类qlabel的事件分发器处理其他shija
return QLabel::event(e);
}
程序运行结果:
事件过滤器(了解)
Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器
在QObject类中Public Functions找到:
1、在MyLabel.cpp的构造函数中 加载 事件过滤器
mylabel::mylabel(QWidget *parent) :
QLabel(parent)//继承qlabel
{
//设置鼠标跟踪功能
//在构造函数中增加此属性
this->setMouseTracking(true);
//加载事件过滤器
this->installEventFilter(this);
}
2、重写事件过滤器
头文件声明
//声明事件过滤器函数并实现事件过滤器函数
//watched:事件发生的控件 event;控件产生的具体事件
virtual bool eventFilter(QObject *watched, QEvent *event);
实现
bool mylabel::eventFilter(QObject *watched, QEvent *event)
{
if(watched==this)//当前控件
{
if(event->type()==QEvent::MouseButtonPress)
{
qDebug()<<"事件过滤器 试标大姐了"<<endl;
QMouseEvent*ev=static_cast<QMouseEvent*>(ev);
qDebug()<<"x="<<ev->x()<<"y="<<ev->y()<<endl;
return true;//自己处理
}
}
//对于其他控件事件,记得统统交给父类处理
return QLabel::eventFilter(watched,event);
}
程序运行结果:
【注意】
1、这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管很强大,但这种行为会严重降低整个应用程序的事件分发效率。
2、事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。