qt 消息(事件)机制

1.QEventLoop (即Qt中的事件循环类)

消息循环在QEventLoop类中实现。通过QEventLoop::exec()可以进入一个消息循环的阻塞状态中,也就是不断地PeekMessage、TranslateMessage、DispatchMessage(和windows 消息机制差不多的)。
Application类中,除去启动参数、版本等相关东西后,关键就是维护了一个QEventLoop,Application的exec就是QEventLoop的exec。不过Application中的这个EventLoop,我们称作“主事件循环”Main EventLoop。所有的事件分发、事件处理都从这里开始。

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    //或者QGuiApplication, 或者 QApplication
    ...
    ...
    return app.exec();
}

 在windows平台上,其底层原理也是通过PeekMessage、TranslateMessage、DispatchMessage从消息队列进行捕捉、转换、分发。

2.QEvent

QT将系统产生的消息转化为QT事件,QT事件被封装为对象,所有的QT事件均继承抽象类QEvent。
比如键盘、鼠标产生的keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent)。其他的如QPaintEvent、QCloseEvent。

每一个事件处理函数,都是带有参数的,这个参数是QEvent的子类,携带了各种事件的参数。比如按键事件 void keyPressEvent(QKeyEvent *event) 中的QKeyEvent, 就包括了按下的按键值key、 count等等。


3.事件过滤器

Qt还提供了事件过滤机制,在事件分发之前先过滤一部分事件

a.安装一个事件过滤器.

void QObject::installEventFilter(QObject *filterObj) 

延伸:一个是移除对应的事件过滤器

void QObject::removeEventFilter(QObject *obj)

    QObject* objA = new MyQObjectA;
    QObject* objB = new MyQObjectB;
    // 安装事件过滤器;    
    objA->installEventFilter(objB);
    // 移除事件过滤器;
    objA->removeEventFilter(objB);

使用 installEventFilter方法 给对象objA安装objB的事件过滤器,这样objB对象的eventFilter方法中就可以接收到objA对象的所有事件了,如果objA对象不想objB对象再监听自己的事件了就使用 removeEventFilter方法移除objB对象对事件的监听。

b.重写QObject类的eventFilter函数。
virtual bool eventFilter(QObject *watched, QEvent *event);

  • 如果返回true,表示事件过滤,不会发送到对象本身。
  • 如果返回false,表示事件未过滤,会通过event()方法将事件分发到对象。
  • 返回给基类进行处理,例:return QObject::eventFilter(obj, event)。

一般的操作办法:父窗口类通过重写eventFilter方法来监听子控件的相关事件进行处理

好处就是:不需要通过重写控件的方式获取某些事件。


Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    ui->label->installEventFilter(this);
}
 
Widget::~Widget()
{
    delete ui;
}
 
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
    if(obj == ui->label)
    {
        //鼠标进入的时候
        if (event->type() == QEvent::Enter)
        {
            ui->label->setText("我是红色");
            ui->label->setStyleSheet(redStyle);
            return true;
        }
        else if(event->type() == QEvent::Leave) //鼠标离开
        {
            ui->label->setText("我是黑色");
            ui->label->setStyleSheet(blackStyle);
            return true;
        }
 
        return false;//别的事件会传给label对象
    }
 
    // standard event processing
    return QWidget::eventFilter(obj, event);
}

4.event()函数主要用于事件的分发

通过QObject类event函数处理
virtual bool event(QEvent *event);
event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。如果传入的事件已被识别并且处理,返回 true,否则返回 false。如果返回值是 true,QApplication 会认为这个事件已经处理完毕,会继续处理事件队列中的下一事件;如果返回值是 false,QApplication 会尝试寻找这个事件的下一个处理函数。

//重写event函数
bool MyWidget::event(QEvent *e)
{
    //如果该对象触发的事件类型是键盘按下 将e转成键盘事件并判断是否是tab键 是则处理 否则重新分配给event的默认处理方式
    if (e->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast(e);//强制类型转换
        if (keyEvent->key() == Qt::Key_Tab) {
            qDebug() << "You press tab.";
            return true;
        }
    }

    //重新分配给event的默认处理方式  //必要的,重新处理其他事件
    return QWidget::event(e);  
    
    //若返回的不是再调用QWidget的event函数 而是直接返回false 则表示只能处理tab键 其它键都不能接收了 
    //return false;
    
}

 注意:其实eventFilter和event函数已经在窗口函数中执行的,相当于在窗口回调函数中进行二次处理(事件过滤或拦截)。

5.事件处理

在QWidget子类中,通过重写keyPressEvent、keyReleaseEvent等等事件处理函数,做一些自定义的事件处理。下面为常见的事件处理函数:

    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);
    virtual void mouseDoubleClickEvent(QMouseEvent *event);
    virtual void mouseMoveEvent(QMouseEvent *event);
#if QT_CONFIG(wheelevent)
    virtual void wheelEvent(QWheelEvent *event);
#endif
    virtual void keyPressEvent(QKeyEvent *event);
    virtual void keyReleaseEvent(QKeyEvent *event);
    virtual void focusInEvent(QFocusEvent *event);
    virtual void focusOutEvent(QFocusEvent *event);
    virtual void enterEvent(QEvent *event);
    virtual void leaveEvent(QEvent *event);
    virtual void paintEvent(QPaintEvent *event);
    virtual void moveEvent(QMoveEvent *event);
    virtual void resizeEvent(QResizeEvent *event);
    virtual void closeEvent(QCloseEvent *event);
#ifndef QT_NO_CONTEXTMENU
    virtual void contextMenuEvent(QContextMenuEvent *event);
#endif
#if QT_CONFIG(tabletevent)
    virtual void tabletEvent(QTabletEvent *event);
#endif
#ifndef QT_NO_ACTION
    virtual void actionEvent(QActionEvent *event);
#endif

#if QT_CONFIG(draganddrop)
    virtual void dragEnterEvent(QDragEnterEvent *event);
    virtual void dragMoveEvent(QDragMoveEvent *event);
    virtual void dragLeaveEvent(QDragLeaveEvent *event);
    virtual void dropEvent(QDropEvent *event);
#endif

    virtual void showEvent(QShowEvent *event);
    virtual void hideEvent(QHideEvent *event);
    virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);

    // Misc. protected functions
    virtual void changeEvent(QEvent *);

延伸补充:

QT事件产生

(1)操作系统事件

  操作系统将获取的事件,比如鼠标按键,键盘按键等keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件, 放入系统的消息队列中,Qt事件循环的时候读取消息队列中的消息,转化为QEvent并被分发到相应的QWidget对象,相应的QWidget中的event(QEvent *)进行事件处理会对事件进行处理,event(QEvent *)会根据事件类型调用不同的事件处理函数,在事件处理函数中发送QT预定义的信号,最终调用信号关联的槽函数。

  GUI应用程序的事件处理:

  A、QT事件产生后会被立即发送到相应的QWidget对象

  B、相应的QWidget中的event(QEvent *)进行事件处理

  C、event(QEvent *)根据事件类型调用不同的事件处理函数

  D、在事件处理函数中发送QT预定义的信号

  E、调用信号关联的槽函数

(2)Qt应用程序自己产生

  程序产生事件有两种方式, 一种是调用QApplication::postEvent(), 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用 QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理;

  另一种方式是调用sendEvent()函数,事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是阻塞型的。

     sendEvent()中事件对象的生命期由Qt程序管理,支持分配在栈上和堆上的事件对象;postEvent()中事件对象的生命期由Qt平台管理,只支持分配在堆上的事件对象,事件被处理后由Qt平台销毁。

 QT提供了五种不同级别的事件处理和过滤:

     A、重写特定事件处理函数.

     最常见的事件处理办法就是重写mousePressEvent(), keyPressEvent(), paintEvent() 等特定事件处理函数。

   B、重写event()函数.

     重写event()函数时, 需要调用父类的event()函数来处理不需要处理或是不清楚如何处理的事件。

     return QWidget::event(event);

     C、在Qt对象上安装事件过滤器

     安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)

     首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数,所有发往B的事件都将先由A的eventFilter()处理。然后, A要重写QObject::eventFilter()函数, 在eventFilter() 中对事件进行处理。

     D、给QAppliction对象安装事件过滤器

如果给QApplication对象装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前eventFilter()。在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃。

     E、继承QApplication类,并重载notify()函数

     Qt是用QApplication::notify()函数来分发事件的,要在任何事件过滤器查看任何事件之前先得到这些事件,重写notify()函数是唯一的办法。通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类,而且可以给QApplication对象安装任意个数的事件过滤器。

你可能感兴趣的:(qt,qt,开发语言)