Qt事件

作者: 仙人掌__ | 来源:发表于2024-02-25 09:30 被阅读0次

    一、事件类型

    1、事件分类

    Qt中事件根据源头分为三种类型:

    • 自发事件:由操作系统产生(例如用户按下鼠标,操作系统会产生一个鼠标事件)然后提交到Qt的event loop

    • Post事件:由应用程序通过postEvent()提交给Qt的event loop的事件

    • Send事件: 由应用程序通过sendEvent()触发的事件,与Post不一样的是他不会提交到Qt的event loop中,是同步执行的。

    2、event loop

    当在main()函数中调用QApplication::exec()时会自动开启一个event loop循环,每一次循环都会按照如下的顺序处理事件

    while (!exit_was_called) {
        while (!posted_event_queue_is_empty) {
            process_next_posted_event();
        }
        while (!spontaneous_event_queue_is_empty) {
            process_next_spontaneous_event();
        }
        while (!posted_event_queue_is_empty) {
            process_next_posted_event();
        }
    }
    

    3、postEvent()和sendEvent()

    大部分情况下的事件(例如鼠标事件、绘制事件,尺寸调整事件)都是由Qt自动帮我们提交到event loop,然后传递给对应的接受者,我们只需要实现对应的事件hander即可。例如QWidget中对应的各种各样的hander

    class MyButton : public QPushButton{
    public:
        MyButton(QWidget *parent = nullptr) : QPushButton(parent)
        {
            setStyleSheet("background-color: #ffffff;");
        }
    
        void mousePressEvent(QMouseEvent *e) override {
            qDebug() << "MyButton::mousePressEvent()";
            QPushButton::mousePressEvent(e);
            e->ignore();
        }
    
        void keyPressEvent(QKeyEvent *) override
        {
            qDebug() << "MyButton::keyPressEvent()";
        }
    
        bool event(QEvent *event) override
        {
            if (event->type() == QEvent::MouseButtonPress) {
                qDebug() << "MyButton::event()";
            }
            return QPushButton::event(event);
        }
    };
    

    其中event(QEvent *event)的默认实现流程大概如下:

    1、根据event->type()的事件类型调用对应的xxxEvent()函数,大部分的系统事件都有对应的hander

    2、没有对应xxxEvent()函数的事件则采用默认实现

    可以通过QApplication::postEvent()或者QApplication::sendEvent()手动发送一个事件,前者将事件提交到event loop,后者直接触发

    // mouseEvent事件必须分配在堆上,内部会持有它,当事件在event loop被使用后,会被自动delete。
    auto *mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, QPoint(50, 50), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QApplication::postEvent(myButton, mouseEvent);
    
    // mouseEvent事件会被myButton立即处理,且内部不持有它,所以需要自己delete;或者将mouseEvent分配在栈上
    auto *mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, QPoint(50, 50), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
    QApplication::sendEvent(myButton, mouseEvent);
    delete mouseEvent;
    

    二、事件派发

    事件响应链

    窗口-->父视图-->子视图1-->子视图2....-->子视图n;越在顶端的子视图则优先处理事件

    image.png

    用户在按钮上点击一下,操作系统会生成一个QMouseEvent事件,添加到Qt的 Event loop,Qt首先找到最终能够响应该鼠标事件的子视图(鼠标点击的位置刚好在子视图上,且该子视图为可见的,处于隐藏状态或者被其它子视图覆盖都算不可见),最后在调用该子视图的event()函数,如果event()返回false,那么事件将传递给事件响应链的上一个视图,否则事件传递终止

    三、事件过滤器

    正常情况下,发送给一个QObject对象的事件首先会调用event()函数来进行处理,如果想抢在event()函数之前处该事件,可以给该对象添加一个事件过滤器对象,使用方法如下:

    class EventFilter:public QObject
    {
    public:
        bool eventFilter(QObject *watched, QEvent *event) override
        {
            if (event->type() == QEvent::MouseButtonPress) {
                qDebug() << "EventFilter::eventFilter()";
                // 截获鼠标按下事件
                return true;
            }
            else if event->type() == QEvent::KeyPress{
                // 不处理键盘事件
                return false;
            }
            return QObject::eventFilter(watched, event);
        }
    }
    
    // 给myWidget对象添加事件过滤器
    auto *myWidget = new MyWidget;
    myWidget->installEventFilter(new EventFilter);
    

    eventFilter()返回true代表事件直接被截获了,false则代表事件会丢回给原先对象的event()函数中去处理

    四、消费事件

    在事件传递的过程中,除了通过事件过滤器(即eventFilter())决定事件是否消费外,还可以通过调用event的accept()函数来消耗这个事件,当事件被消耗后,它将不再继续传递。

    相关文章

      网友评论

          本文标题:Qt事件

          本文链接:https://www.haomeiwen.com/subject/zwqgadtx.html