Qt-事件处理

一、Qt的事件分发机制

1、QEvent 简介

  • QEvent是所有事件类的基类,所有的事件类(如:鼠标事件、绘图事件、键盘事件等事件类)都继承自该类。其只有一个参数:事件的类型type,主要用来判断所触发事件的类型。
  • Qt 主事件循环是通过exec()函数进行的,它从事件队列中来获取本地的窗口系统事件,并将其转换成QEvent,然后再将转换的事件QEvent发送给具体的对象QObject
  • 一般来说,事件来自底层的窗口系统,此时为主动自发的。但是也有可能通过手动的方式来发送事件,此时是非自发的,具体使用:sendEventpostEvent等方法。
  • 对象通过调用event函数来接收事件,event函数可以被子类重载,进而实现自定义事件处理和添加新的事件类型。
  • 默认情况下,对象通过调用event函数来接收到的事件,会自动被分配到对应的事件处理器,如:mousePressEvent,wheelEvent,paintEvent,timerEvent等。
  • 事件过滤器installEventFilter允许一个对象拦截住发往另一个对象的事件,即对其进行过滤。

2、事件处理过程

  • 对象接收到的所有事件都会发送到event中进行处理,event函数返回值为bool类型,如果bool值为true,则意味着用户将在event函数中自行处理该事件,该事件将不再向下分发传递,否则将会根据事件的类型继续向下分发。默认情况下事件会自动分发到对应的事件处理器中,除非重写event函数来对一些事件进行特殊处理,否则event函数不会对事件进行拦截处理操作
  • 事件过滤器 installEventFilter可以在event之前进行更高层级的过滤操作,让event都接收不到对应的事件。默认情况下对象是直接将事件传递到event中进行处理,若需要更高层级的事件过滤操作,就要先在对应的控件上安装事件过滤器,然后再重写事件过滤函数。
  • 在不重写event函数和事件过滤器 eventFilter函数的情况下,窗口对象在接收到事件之后,都是根据事件的类型,自动转发到对应的事件处理器中进行处理。注:自动转发的前提是要有该特定的事件处理器,若没有将不会自动转发处理。如:鼠标事件有各种对应的鼠标事件处理器(mousePressEvent,mouseMoveEvent,…),而触摸屏事件就没有特定的事件处理器来进行处理,此时就需要自己在event函数中来对该事件自行处理。

在这里插入图片描述
事件处理流程总结:
正常情况下,首先窗口对象会将获取的所有事件都发送给event函数,然后event()根据事件的类型不同来进行分发。常见的事件类型Qt已经定义了对应的事件处理函数,如鼠标事件、键盘事件等为最常用的。其中event()事件的返回值为bool类型,如果它对接受的事件进行了处理,不需要再继续往下传了,就返回true,否则就返回到对应父控件的event事件中继续进行处理。【注意,必须要将未处理的事件返回给父控件进行处理。因为事件类型太多,不可能自己对每种事件都来自行处理。

3、应用示例

(1)、event可拦截事件,使其不再向下分发。如拦截鼠标点击事件

/******** Pr05_QtEvent.cpp *********/
#include "Pr05_QtEvent.h"
#include <QMouseEvent>
#include <QLabel>

Pr05_QtEvent::Pr05_QtEvent(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
}

void Pr05_QtEvent::mousePressEvent(QMouseEvent *event)
{
    //当点击的是鼠标左键时执行
    if (event->button() == Qt::LeftButton)
    {
        ui.label->setText("emit by mousePressEvent function!");
    }
}

//event会拦截住鼠标单击事件向下传递,即mousePressEvent不会接收到鼠标单击事件
bool Pr05_QtEvent::event(QEvent *e)
{
    if (e->type() == QEvent::MouseButtonPress)
    {
        //当单击鼠标时,将其静态转换成鼠标事件
        QMouseEvent *ev = static_cast<QMouseEvent*>(e);
        if (ev->button() == Qt::LeftButton)
        {
            ui.label->setText("emit by event function!");
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return QWidget::event(e);
    }
}

二、常用事件简介

1、鼠标事件

  • enterEvent(QEvent *event):鼠标进入事件,如自定义的QLabel控件中可以实现该功能
  • leaveEvent(QEvent *event):鼠标离开事件
  • mouseMoveEvent(QMouseEvent *event):鼠标移动事件,默认的是鼠标按下时,移动鼠标才可以触发该事件。可以在构造函数中设置 setMouseTracking(bool enable)true,即可在不按鼠标的情况下,就可以追踪到鼠标移动事件。
  • mousePressEvent(QMouseEvent *event):鼠标按下事件
  • mouseReleaseEvent(QMouseEvent *event):鼠标释放事件

2、键盘事件

  • keyPressEvent(QKeyEvent *event):按键事件,可以根据event->key()来获取当前按下的键,并对指定的按键进行处理,过滤等。如按下A键时触发事件的条件为:event->key() == Qt::Key_A
  • keyReleaseEvent(QKeyEvent *event):当松开指定的按键时,才会触发对应的事件。按下时不会触发该事件。
按键事件示例,对指定按键进行特殊处理
void ClassName::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
    //当按键为Esc时进行处理,调用EscStopTest函数
    case Qt::Key_Escape:
        this->EscStopTest();
        break;
    //当按键为A时进行特殊处理
    case Qt::Key_A:
        Debug() << "This is A Key";
        break;
    default:
        QWidget::keyPressEvent(event); //其它按键事件不进行处理。
    }
}

3、触摸屏事件

  • event():触控屏事件没有专有的函数来进行处理,所以需要在event() 函数中进行直接处理
//处理触控屏示例
bool TouchLabel::event(QEvent *e)
{ 
//对指定的事件进行处理,相当于过滤。若成功处理就返回true,否则返回false,并传给父控件处理
    if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate)
    {
        QTouchEvent *eTouch = static_cast<QTouchEvent*>(e);     
        emit FuncWork(eTouch);
        return true;
    }
    else if (e->type() == QEvent::TouchEnd)
    {
        QTouchEvent *eTouch = static_cast<QTouchEvent*>(e);     
        emit FuncEnd(eTouch);
        return true;
    }
    //未处理的事件将其返回给对应父控件来进行处理(必须)
    return QLabel::event(e);
}

4、其它一些常用事件

  • paintEvent(QPaintEvent *event):绘图事件,所有的绘图操作都需要在该函数中进行重新实现
  • changeEvent(QEvent *event):改变事件,该函数主要通过重新实现来处理状态的改变。如切换语言等操作可以在该函数中实现,来进行响应。
  • QApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority):添加事件到接受对象的事件队列中去,由于事件必须要分配到堆中去,所以待添加的事件要用new来进行生成。然后用 QObject::customEvent(QEvent *event) 来接受该事件并进行处理。应用详解
  • QCoreApplication::sendEvent(QObject *receiver, QEvent *event):直接将事件发送到接受方,并返回处理的结果。该方法与postEvent 不同,该方式是同步的形式来进行的,发送之后不会立即删除,一直等到处理结果返回,其事件是创建在栈上 的。而postEvent 事件是异步的,创建在堆上的事件被放在队列中后,就立即返回了。

三、事件过滤器

当一个窗口中有很多控件,且有多个控件需要过滤事件时,使用event函数将变的繁琐,因为每个控件都要重写event来进行过滤对应的事件。此时,可以通过安装事件过滤器来进行便捷的操作。
如下图每个红色方框表示一个控件时,若要对每个控件进行事件过滤,就需要对每个控件的event函数进行重写。此时通过安装事件过滤器将是最好的方式。

在这里插入图片描述

1、首先在需要过滤事件的控件对象上安装事件过滤器installEventFilter(QObject *filterObj)

该函数的原型为:void QObject::installEventFilter(QObject *filterObj),参数为事件过滤器对象。将事件过滤器对象安装到指定的控件上,事件过滤器对象为要进行的过滤操作。

  • 事件过滤器对象(filterObj)接受所有发送到当前控件上的事件
  • filterObj 通过eventFilter()来接受事件,并进行过滤操作
2、过滤事件bool QObject::eventFilter(QObject *watched, QEvent *event)
  • watched:为要进行过滤事件的控件,即所追踪的控件
  • event:为要进行过滤的事件类型,即对什么样的事件进行过滤操作

指定控件在安装了事件过滤器后,事件就会首先进入eventFilter函数,重写该函数就可以实现对指定的事件进行过滤处理。如果成功进行过滤处理,就返回true,否则就返回false,并且该事件不再继续向后传递。注意必须要将未处理的事件返回给父控件
【注】事件过滤器对象和被安装过滤器的控件必须要在同一线程下,否则事件过滤器将不起作用。如果两个控件进入到不同的线程中,只有等这两个控件重新进入到同一线程下事件过滤器才能起作用。

3、应用示例
通过事件过滤器对鼠标点击进行过滤操作
主程序如下:

Pr05_QtEvent::Pr05_QtEvent(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    
    ui.label->installEventFilter(this); //安装事件过滤器
}

//重写事件过滤函数
bool Pr05_QtEvent::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == ui.label)
    {
        if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
        {
            QMouseEvent *ev = static_cast<QMouseEvent*>(event);
            if (ev->button() == Qt::LeftButton)
            {
                ui.label->setText("emit by eventFilter function!");
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    else
    {
        return QWidget::eventFilter(watched, event);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值