Qt 之 消息机制和事件讲解

1、图形界面应用程序的消息处理模型

特点:
基于操作系统才能运行;
GUI应用程序提供的功能必须由用户触发;
用户操作界面时操作系统是第一个感知的 ;
系统内核的消息通过事件处理转变成QT的信号。

2. Qt中的事件处理

(1)在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 事件处理的核心包括事件产生、分发、接受和处理。

①事件的产生
问题:谁来产生事件?
最容易想到的是我们的输入设备,比如键盘、鼠标产生的keyPressEvent,keyReleaseEvent,mousePressEvent,mouseReleaseEvent事件(他们被封装成QMouseEvent和QKeyEvent)。

②Qt中事件的分发

问题:谁来负责分发事件?
对于non-GUI的Qt程序,是由QCoreApplication负责将QEvent分发给QObject的子类-Receiver.;对于Qt GUI程序,由QApplication来负责。

③事件的接受和处理
问题:谁来接受和处理事件?
答案是QObject。类是整个Qt对象模型的心脏,事件处理机制是QObject三大职责(内存管理、内省(intropection)与事件处理制)之一。任何一个想要接受并处理事件的对象均须继承自QObject,可以选择重载QObject::event()函数或事件的处理权转给父类。

(2)Qt平台将系统产生的消息转变成Qt事件

A. Qt事件是一个QEvent(或子类)的对象;
B. 有时一个事件包含多个事件类型,比如鼠标事件又可以分为鼠标按下、双击、和移动多种操作事件类型由QEvent类的枚举型QEvent::Type来表示,可由帮助文档进行查询;
C. Qt事件用于描述程序内部或外部发生的对应动作(描述的是操作系统发生来的消息,一个系统消息对应着一个消息事件);
D. 任意QObject对象都具备时间处理的能力.
Qt 之 消息机制和事件讲解_第1张图片
note:QEvent子类可以表示一个事件,但并不能处理这个事件

Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。如上所述,event()函数主要用于事件的分发

(3)GUI应用程序的事件处理方式

A. Qt事件产生后会立即被分发到QWidget对象(QObject的子类,如按键QPushButton对象等);
B. QWidget对象其内部会有一个event(QEVent*)函数被调用,进行事件处理;
C. event()根据事件类型调用不同的事件处理函数(默认的子函数);
D. 在事件处理函数中发送Qt中预定义的信号;
E. 调用信号关联的槽函数。

(4) QPushButton事件处理分析

Qt 之 消息机制和事件讲解_第2张图片

①接收到鼠标事件

②QApplication调用QObject::event(QEvent*)成员函数来处理,进行事件的分派。

③调用QPushButton的mouseReleaseEvent(QMouseEvent*)成员函数

④QPushButton调用click()成员函数

⑤触发信号SIGNAL(clicked())

(5)实例

实例一:自定义事件处理函数

鼠标左键右键

wigth.h

#ifndef WIDGET_H
#define WIDGET_H#include class Widget : public QWidget
{
    Q_OBJECT
protected:
    void mousePressEvent(QMouseEvent *event);
public:
    Widget(QWidget *parent = 0);~Widget();
};#endif // WIDGET_H

widgth.cpp

#include "widget.h"
#include 
#include 
#include 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
}void Widget::mousePressEvent(QMouseEvent *event)
{
    if(event->button()==Qt::LeftButton)
    {
        qDebug()<<"LeftButton clicked!";
    }
    else if(event->button()==Qt::RightButton)
    {
        qDebug()<<"RightButton clicked!";
        
    }}
​
Widget::~Widget()
{
    
}

main.cpp

#include 
#include "widget.h"int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    
    return a.exec();
}

实例二:自定义事件处理函数

QMyPushButton.h

#ifndef _QMYPUSHBUTTON_H_
#define _QMYPUSHBUTTON_H_

#include 

typedef void (QButtonListener)(QObject*,QMouseEvent*);

class QMyPushButton : public QPushButton
{
    Q_OBJECT

protected:
    QButtonListener* m_listener;

    //重写QPushButton的事件处理函数 就有可能不会产生clicked信号
    void mouseReleaseEvent(QMouseEvent *e);
public:
    explicit QMyPushButton(QWidget* parent = 0, QButtonListener* listener = 0);
};

#endif // _QMYPUSHBUTTON_H_

QMyPushButton.cpp

#include "QMyPushButton.h"
#include 

QMyPushButton::QMyPushButton(QWidget* parent, QButtonListener* listener):QPushButton(parent)
{
    m_listener = listener;
}

//重写改写事件处理函数,会改变程序的行为。
void QMyPushButton::mouseReleaseEvent(QMouseEvent *e)
{
    if(m_listener != NULL)
    {
        //调用自定义的事件处理函数,尽管按钮的clicked信号被连接到onMyButtonClicked槽函数,
        //但因自定义的m_listener函数里并不触发clicked信号,从而槽函数不会被调用。
        m_listener(this, e);
        e->accept();//事件被接收,就不再传递到父QWidget

        setDown(false); //按钮设置为“弹起”状态
    }
    else
    {
        //父类的mouseReleaseEvent会去调用clicked(),并触发SIGNAL(clicked())
        //从而调用到连接到该信号的槽函数(本例为onMyButtonClicked())
        QPushButton::mouseReleaseEvent(e); //调用父类
    }
}

Widget.h

#ifndef _WIDGET_H_
#define _WIDGET_H_

#include 
#include "QMyPushButton.h"

class Widget : public QWidget
{
    Q_OBJECT
    QMyPushButton myButton;

protected slots:
    void onMyButtonClicked();

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // _WIDGET_H_

Widget.cpp

#include "Widget.h"
#include 

//自定义事件处理函数
void onMyButtonMouseRelease(QObject* sender, QMouseEvent* e)
{
    qDebug() << "onMyButtonMouseRelease(QObject* sender, QMouseEvent* e)";
}

Widget::Widget(QWidget *parent)
    : QWidget(parent),myButton(this, onMyButtonMouseRelease) //实验2:myButton(this, 0)
{
    myButton.setText("QMyPushButton");

    connect(&myButton, SIGNAL(clicked()), this, SLOT(onMyButtonClicked()));
}

//槽函数,用于接收按钮的clicked信号
void Widget::onMyButtonClicked()
{
    qDebug() << "onMyButtonClicked()" ;
}

Widget::~Widget()
{
}

main.cpp

#include "Widget.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

(6)事件(QEvent)和信号(SIGNAL)的不同

Qt 之 消息机制和事件讲解_第3张图片
例如:单击界面上的按钮,那么就会产生鼠标事件QMouseEvent(不是按钮产生的),由于按钮被按下了,所以他会发出一个单击信号clicked()信号(是按钮产生的),这里只考虑单击信号而不用考虑鼠标事件,但如果要设计一个按钮,或者当单击按钮时让它产生别的效果,此时就要考虑鼠标事件了,由此,信号和事件是两个不同层面的东西,发出者不同,作用不同。Qt中,所有的QObject的子类实例均可对事件接收和处理!

3. 小结

(1)Qt中的事件和信号不同

(2)事件由QObject对象进行处理

(3)信号由QObject对象触发

(4)重写事件处理函数可能改变程序行为

(5)信号的触发不会对程序行为造成影响

(6)事件处理是在实际工程开发中应用非常普遍的

你可能感兴趣的:(QT)