QT事件系统

本文详细介绍了Qt事件的传递和处理过程,包括事件的构造、QApplication的事件过滤器、事件派发以及自定义事件的发送。事件处理流程涉及notify、filter和event处理函数。同时,讨论了如何安装和使用事件过滤器,以及自定义事件的处理方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事件的传递和处理

当一个事件发生时,Qt通过构造一个QEvent对象来表示发生的事件,然后通过调用event()函数,将该事件对象发送给特定的QObject(或其子类)对象。

此函数不会对事件本身进行处理, 而是首先检查所接受到的事件类型, 然后根据事件类型来调用相应的事件处理程序,事件处理程序在处理完事件之后会返回一个bool值表示该事件是被接受,还是被忽略了。
具体流程:
一个事件发生(如按键被按下),先是检查QApplication的事件过滤器,然后进入QApplication::notify中,过滤或合并一些事件,之后进入receiver中。
进入receiver之后,也是先进入receiver自身的filter,有的事件被filter处理了,有的被filter忽略了,那么就会执行它的父类的事件处理函数(若父类也忽略,继续往上,直到最上层)。直至最后,还被filter未处理的事件,会传递到event中,event根据事件类型进行派发,将事件派发到特定的事件处理函数中。
其实事件的处理可以分为:先进入QApplication的notify=(若有)继承自QApplication的自定义类的notify->QApplication的filter->receiver的filter->receiver的event->具体的event handler。

自定义事件

//继承QEvent,包含一系列需要的数据,并要提供给其特定的event type
class MyEvent : public QEvent
{
public:
    MyEvent();
    MyEvent(int x, int y, int z);

    static const Type type;

    int x;
    int y;
    int z;
};
const QEvent::Type MyEvent::type = (QEvent::Type)QEvent::registerEventType()//注册自定义事件,后续判断就用这个返回的type
//对于自定义事件,必须要重写event或其他对其处理

发送事件

先说明QT的事件循环机制,就是在main函数中的QApplication.exec()执行时开始,不断执行processEvent获取事件,处理并删除事件(使用的是delete)。
1 send
直接发送到指定对象,不放入事件循环队列中,直接调用notify函数将事件派发,send的event对象必须分配在stack上(局部变量),因为send不会自动删除事件

QKeyEvent event(QEvent::KeyPress, Key_X, 'X', 0);//可以是qt自带事件,也可以是自定义事件
QApplication::sendEvent(mainWin, &event);//绕过事件循环,直接派发到指定对象,返回一个bool表示事件是否处理完毕

2 post
直接添加到事件循环队列中,并自动删除事件,但event对象必须是手动new出来的

QApplication::postEvent(object, new MyEvent(QEvent::registerEventType(2048)));//将事件添加到事件循环队列中,并立即返回

3 sendpost
将事件循环队列中的指定type的事件,立刻马上派发给指定对象
4 accept和ignore
一般只用于处理事件的最终的具体的handler中

void MyFancyWidget::keyPressEvent(QKeyEvent *event)
    {
        if (event->key() == Key_Escape) {
            ...
            event->accept();//告诉QT此事件已处理完毕,不必再派发
        } else {
            event->ignore();//若ignore,则qt会继续往上(父类)中寻找事件的handler
        }
    }
    //但accept和ignore最好是用filter等更高级的方法来代替,如filter中return true和false
    //一般event都默认为accepted(即不显示调用accept也会停止派发),所以一般不显示调用accept,而只是在需要忽略时调用ignore,或是不ignore而转向执行其他处理函数
    //而且QWidget中所有handler都是默认ignore的,所以忽略时也可以直接转向QWidget的默认handler

事件派发与处理

  • notify

QApplication的notify作为最高层,在事件触发时,第一个接到事件对象并对其处理,是全局的。
但作为内置类,它的notify不能被改写,如果我们需要在第一时间处理事件,就可以对QApplication安装filter(但仍晚于notify),或是继承它并重写notify,然后使用这个自定义类作为QApplication的替代,在main函数中创建整个应用程序。
步骤:①继承QApplication,仅重写其notify函数(若其他也要自定义则自行修改)
②在main函数中,一般创建应用程序都使用的是QApplication,但重写后要使用我们自定义的类来替代
补充:窗口并非就是应用程序,它们是父子关系,一个程序可以有多个窗口,但每个窗口都属于这一个程序。

  • filter

为何使用filter:由于若要自定义某事件的处理函数,那一般的做法就是继承父类(如QLabel)
从事件的传递和处理顺序,我们知道了事件过滤触发的时机,但它的具体作用如下:
每个特定的对象,都有一个指定的事件过滤器(filter),这个filter通过它自身的eventFilter()函数来进行过滤(说是过滤,其实就是第一层处理)。当事件发生时,首先就是进入QObject的eventFilter中,对特定类型的事件作出处理,然后再按照顺序往下传递。

//一个典型的filter
bool MyFilter::eventFilter(QObject *obj, QEvent *event)
{
    if(obj == ui->label)//obj为事件所触发的对象(不是filter!如btn1被按住时,obj就是btn1)
    {
        if (event->type() == QEvent::Enter)//通过event参数的type来判断触发的事件
        {
            ui->label->setText("我是红色");
            ui->label->setStyleSheet(redStyle);
            return true;//eventFilter返回true,表示filter对事件已作出处理,事件不再向下传递
        }
        else if(event->type() == QEvent::Leave)
        {
            ui->label->setText("我是黑色");
            ui->label->setStyleSheet(blackStyle);
            return true;
        }
 
        return false;//返回false,事件继续向下传递(传递到receiver的event函数中)
    }
 
    return QWidget::eventFilter(obj, event);//或是这样写,让其他类(一般都是父类)来对事件进行处理
}

安装filter

MyFilter f;
Watched w;

w.installEventFilter(f);//为w安装f作为filter,每次有事件传递到w对象时,会先进入到f的eventFilter函数中
w.removeEventFilter(f);//解除filter的监听

一般使用方式:① 特定对象使用特定filter
② 对QApplication安装自定义filter,实现全局事件过滤(最先处理,如实现软件中的全局快捷键)
③ 对于包含多个组件的父窗口类,定义一个eventFilter函数来处理多个组件的事件,或是指定特定的fitler作为这多个组件的filter(不然就需要每个组件都要继承并重写自身的事件处理函数)
其他:① 当一个对象安装有多个filter时,会逐个执行,但顺序为先安装的后调用
③ 也可以在一个对象中定义eventFilter函数,然后指定对象自身为它自己的filter

  • event

最简单粗暴,也是优先级最低的方法,就是继承一个类并重写它的event函数
其大致类似于filter,算是优先级更低版本的filter

//一个典型的event
bool MyWidget::event(QEvent *e)//此参数为待派发的事件对象
{
	//关于return:事件的处理是一个一个挨着来的,第一个event处理完后return true表示event1处理完毕
	//然后event2再进来,return false表示未处理,继续向下传递
	//然后event3进来,return一个函数,则表示传递给其他对象来处理
    if (e->type() == QEvent::KeyPress)//同样可以通过type判断触发的事件
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);//若要使用event对象,通常需要cast
        if (keyEvent->key() == Qt::Key_Tab) {
            qDebug() << "You press tab.";
            return true;//返回true,表示此事件处理完毕,不会再往下派发
        }
    }
    return QWidget::event(e);//每个event函数都必须要写,不然会出现事件无法处理的情况


}

若还要更粗暴,就是直接继承并重写event handler

//一个典型的handler
void myLabel::mousePressEvent(QMouseEvent *event)//因为已经是最后一层,event的类型已然明了
{
	//作最终的事件处理
    if(event->Buttons == LeftButton)
    {
        //do sth
    }
    else if(event->Buttons == RightButton)
    {
        //do sth
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值