【Qt开发】事件与信号/事件过滤器

事件

Qt系统的图形化窗口是由事件驱动的。,点击鼠标、按键,改变窗口大小、最小化窗口、关闭窗口等都会产生相应的事件。
QWidget类的所有界面组件类的基类,QWdiget类定义了大量的处理事件相关的数据类型和接口函数。

一、事件的产生

当应用程序发生变化,如点击鼠标、按下键盘等,就会产生事件。
事件是对象,是QEvent类或其派生类的实例。

Qt的主事件循环(QCoreApplication::exec())从事件队列中获取本地窗口系统事件,将它们转换为QEvent,并将转换后的事件发送给QObject。QObject类调用函数QObject::event()来处理事件。

事件可以划分为3类:

  • 自主事件:是由窗口系统产生的事件。例如,QKeyEvent事件、QMouseEvent事件。自主事件会进入系统事件队列,然后被应用程序的事件循环逐个处理
  • 发布事件:是由Qt或应用程序产生的事件。例如,QTime定时器发送定时溢出时,Qt会自动QTimeEvent事件。应用程序使用静态的QCoreApplication::postEvent()产生发布事件。发布事件会进入Qt事件队列,然后被应用程序的事件循环逐个处理
  • 发送事件:是由Qt或应用程序定向发送给某个对象的事件。应用程序使用静态的QCoreApplication::sendEvent()产生发送事件,由对象的event()函数直接处理

自主事件和发布事件是异步的,也就是事件进入队列后系统去处理,程序不会在产生事件的地方停止进行等待

QCoreApplication::postEvent
(
	QObject *receiver
	,QEvent *event
	,int priority = Qt::NromalEventPriority
)
  • receiver:事件的接收者
    event:事件对象
    priority :事件的优先级
  • 发布事件之后,该函数会立即退出,不会等待事件处理完,发布事件是异步的
QCoreApplication::sendEvent
(
	QObject *receiver
	,QEvent *event
)
  • receiver:事件的接收者
    event:事件对象
  • 发送事件之后,需要等待事件处理完,发送事件是同步的

二、事件的派发

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();//事件循环
}

函数QApplication::exec()的主要功能就是不断的检查系统队列和Qt事件队列中是否有未处理的自主事件和发布事件。
如果有事件就派发给接收事件的对象去处理,事件循环还可以把相同的事件进行合并发送给接收事件的对象去处理。

注意,应用程序的事件循环只会处理自主事件和发布事件,而不会处理发送事件,因为发送事件是以同步模式运行的

学习两个派发事件的函数:

在事件队列未能及时被处理,导致用户界面出现卡顿无响应,
可以使用这个函数来派发事件队列中为处理的事件
static void QCoreApplication::processEvents
(
QEventLoop::ProcessEventFlags flags=QEventLoop::AllEvents
)
  • QEventLoop::AllEvents:处理所有事件
  • QEventLoop::ExcludeUserInputEvents:排除用户输入事件
  • QEventLoop::ExcludeSockstNoitEvents:排除网络socket的通知事件
  • QEventLoop::WaitForMoreEvents:如果没有未处理的事件,等待更多事件
static void QCoreApplication::sendPostEvents
(
	QObject *receiver=nullptr //接收者对象
	,int event_type=0 //事件类型
)

把QCoreApplication::postEvent()发送到Qt事件队列里的事件立刻派发出去。如果没指定事件类型,就派发所有给这个接收者的事件。如果接收者和事件类型都不指定,就派发给所有使用QCoreApplication::postEvent()发布的事件

三、事件类和事件类型

事件是QEvent或其派生类的实例。QEvent是所有事件类的基类,但是它不是抽象函数。它可以自己创建事件,其主要的几个函数如下:

函数描述
void accept()接收事件
void ignore()忽略事件
bool isAccepted()是否接收事件
bool isInputEvent()事件对象是不是QInputEvent或其派生类实例
bool isPointerEvent()事件对象是不是QPointerEvent或其派生类实例
bool isSinglePointEvent()事件对象是不是QSinglePointEvent或其派生类实例
void spontaneous()是不是自主事件,也就是窗口系统事件
QEvent::Type type()事件类型

调用ignore(),事件会派发给其父组件。

每一个事件都有唯一的的事件类型,也有对应的事件类,有的事件类可以处理多种类型的事件。

在这里插入图片描述

四、事件的处理

有对象接收到应用程序派发过来的事件后,会调用函数event()处理事件,event()是QObject类中定义的一个虚函数,原型如下:

bool QObject::event(QEvent *e);

参数e就是事件对象,通过e->type()来判断事件的类型,调用对应的处理函数。
例如:事件类是QEvent::Paint(需要重新绘制事件类型),就调用paintEvent()处理函数。我们只需要重写函数paintEvent()就能达到自己的目标。

QWidget类是所有组件类的基类,它重新实现了event(),并针对一写典型类型的事件定义了专门的事件处理函数。

在这里插入图片描述
如果需要处理的事件在QWidget中没有对应的事件处理函数,需要重写event(),判断事件类型后调用自己定义的事件处理函数。

事件与信号

事件和信号的区别在于,事件通常是窗口系统和应用程序产生的。信号则是由Qt定义或用户自定义的。Qt为界面组件定义的信号通常都是对事件的封装。

现在需要编写一个例子:设计一个标签类TMyLabel,该类继承QLabel,重写event()函数
,并且对QEvent::HoverEnter和QEvent::HoverLeave类型的事件进行处理。当光标移动到组件上,组件上文字的颜色为红色,离开组件上的文字为黑色。

	 tmylabel.h
#include <QWidget>
#include <QLabel>
#include <QEvent>
class TMyLabel : public QLabel
{
    Q_OBJECT
public:
    TMyLabel(QWidget *parten=nullptr);
private:
	//重写event
    bool event(QEvent *e);
	//重写鼠标双事件处理的函数
    void mouseDoubleClickEvent(QMouseEvent *event);
signals:
	//自定义信号
    void doubleClicked();
};
	tmyLabel.cpp
#include "tmylabel.h"

TMyLabel::TMyLabel(QWidget *parten):QLabel(parten)
{
    this->setAttribute(Qt::WA_Hover,true);
}

bool TMyLabel::event(QEvent *e)
{
    QPalette plet=this->palette();
    if(e->type()==QEvent::HoverEnter)
    {
        this->setText("鼠标进入");
        plet.setColor(QPalette::WindowText,Qt::red);
        this->setPalette(plet);
    }
    else if(e->type()==QEvent::HoverLeave)
    {
        this->setText("鼠标离开");
        plet.setColor(QPalette::WindowText,Qt::black);
        this->setPalette(plet);
    }
    //其他事件类型继续交给父类处理
    return QLabel::event(e);
}

void TMyLabel::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    emit doubleClicked();
}

事件过滤器

从事件与信号章节得知,我们需要重新处理事件需要重新定义一个新类,在新类中去重新函数event()或对应的事件处理函数。
QObject还提供了另一种方式来处理事件:事件过滤器。它可以将一个对象的事件委托给另一个对象来监视并处理。
例如:一个窗口可以作为其界面上的QLabel组件的事件过滤器,派发给QLabel组件的事件由窗口去处理,这样就不需要重新定义一个标签类啦。

实现事件过滤器需要用到两个函数:

安装事件过滤器:将自己注册给监视对象
void QObject::installEventFilter(QObject *filterObj);
参数 filterObj:监视对象

监视对象需要重新实现eventFilter(),对监视到的事件进行处理
bool QObject::eventFilter(QObject *watched, QEvent *event);
参数watched:被监视对象
参数event:事件对象
返回值:
	true(事件就不会在传播给其他对象)
	false(事件会继续传播给事件接收者做进一步处理)

代码例子:
widget.h

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private:
    bool eventFilter(QObject *watched,QEvent *event);
private:
    Ui::Widget *ui;
};
	widget.cpp
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->label->installEventFilter(this);
    ui->label_2->installEventFilter(this);
}

Widget::~Widget()
{
    delete ui;
}

bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    if(watched==ui->label)
    {
        if(event->type()==QEvent::Enter)
        {
            ui->label->setStyleSheet("background-color:rgb(170,255,255)");
            ui->label->setText("走开,我不喜欢");
        }
        else if(event->type()==QEvent::Leave)
        {
            ui->label->setText("靠近我呀");
        }
    }
    else if(watched==ui->label_2)
    {
        if(event->type()==QEvent::Enter)
        {
            ui->label_2->setStyleSheet("background-color:rgb(85,255,127)");
            ui->label_2->setText("走开,我不喜欢");
        }
        else if(event->type()==QEvent::Leave)
        {
            ui->label_2->setText("靠近我呀");
        }
    }
    我们只处理了少数事件,其他的事情传播给父类处理(如QEvent::Paint事件没有处理),
    所以需要返回父类的eventFilter()
    如果直接返回true,界面上就不会显示文字啦
    return QWidget::eventFilter(watched,event);
}
  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Qt 中,QApplication 类是整个应用程序的核心类,它管理着应用程序的主事件循环,同时负责分发和处理各种事件。其中,QApplication 的 notify 函数是用于处理应用程序接收到事件后的通知的函数。 当应用程序接收到一个事件时,Qt 会自动调用 QApplication 的 notify 函数。该函数会传递一个 QObject 对象和一个 QEvent 对象,分别表示事件的接收者和事件本身。在 notify 函数中,可以根据事件类型和接收者对象进行处理,并进行一些额外的操作,比如更新应用程序的状态或者发送信号。 除了重写 QApplication 的 notify 函数,Qt 还提供了事件过滤器事件监听机制,可以方便地对应用程序中的事件进行处理。 事件过滤器是通过 QObject::installEventFilter() 函数设置的。当事件到达对象时,会先调用事件过滤器,并将事件对象传递给过滤器进行处理。如果过滤器返回 true,则表示事件已经被过滤,不会再传递给接收者;如果过滤器返回 false,则事件将继续传递给接收者。事件过滤器一般用于在不改变接收者的情况下,对事件进行一些预处理或者后处理。 事件监听机制是通过重写 QObject 的事件处理函数来实现的。当一个对象接收到一个事件时,Qt 会自动调用该对象的事件处理函数。如果需要处理其他对象的事件,则可以使用事件过滤器。在事件处理函数中,可以根据事件类型进行处理,并进行一些额外的操作,比如更新对象的状态或者发送信号。 需要注意的是,在使用事件过滤器事件监听机制时,一定要注意不要阻止事件传递,否则可能会导致应用程序无法正常工作。同时,如果同时使用事件过滤器事件监听机制,一定要注意它们之间的优先级关系,以避免出现不必要的问题。 总的来说,Qt事件处理机制非常灵活,可以通过多种方式对应用程序中的事件进行处理。在实际开发中,需要根据具体的需求选择合适的方式来处理事件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世_生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值