Qt 中的事件处理

目录

1、事件和信号

2、事件处理的顺序

3、Qt中的事件过滤器 

4、Qt中的拖放事件  

5、发送预定义事件

6、自定义事件对象 


1、事件和信号

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

系统内核将用户操作翻译成对应的程序消息 ,GUI程序在运行时会创建一个消息队列 ,程序在运行过程中需要实时处理队列中的消息 ,当队列中没有消息时,程序将处于停滞状态 

操作系统发送的消息如何转变成Qt信号?

Qt平台将系统产生的消息转换为Qt事件

         - Qt事件是一个QEvent的对象,Qt事件用于描述程序内部或外部发生的动作 

         - 任意的QObject对象都具备事件处理的能力 :输入事件,拖拽事件,操作系统绘制GUI事件,关闭事件,计时器事件 ...

                                 

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

       1. Qt事件产生后立即被分发到QWdget对象 ,QWidget中的 event(QEvent*) 函数进行事件处理 

       2. event(QEvent*) 根据事件类型调用不同的事件处理函数 ,在事件处理函数中发送Qt中预定义的信号 

       3. 调用信号关联的槽函数

场景分析:按钮点击 

 

QPushButton事件处理分析 

       1、接收到鼠标点击事件 

       2、调用event(QEvent*)成员函数 

       3、调用mouseReleaseEvent(QMouseEvent*)成员函数 

       4、调用click()成员函数 

       5、触发信号SIGNAL(clicked()) 

QMyPushButton.h

#ifndef _QMYPUSHBUTTON_H_
#define _QMYPUSHBUTTON_H_

#include <QPushButton>

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

class QMyPushButton : public QPushButton
{
    Q_OBJECT
protected:
    QButtonListener* m_listener;

    //重写鼠标释放事件处理函数,事件分配后会被调用
    void mouseReleaseEvent(QMouseEvent *e);
public:
    explicit QMyPushButton(QWidget* parent = 0, QButtonListener* listener = 0);
    
signals:
    
public slots:
    
};

#endif // _QMYPUSHBUTTON_H_

QMyPushButton.cpp

#include "QMyPushButton.h"
#include <QMouseEvent>

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

//按钮被点击时事件传过来时会调用这个重写的事件处理函数
void QMyPushButton::mouseReleaseEvent(QMouseEvent *e)
{
    if( m_listener != NULL )
    {
        m_listener(this, e); // onMyButtonMouseRelease,没有像预定义的那样发送一个信号

        e->accept();   // 标记当前事件已经被处理

        setDown(false);// 更新UI,按钮弹起(否则按钮会一直显示被按着状态)
    }
    else
    {
        // 调用默认处理函数,会发送预定义的信号,对应槽函数正常调用
        QPushButton::mouseReleaseEvent(e);
    }
}

Widget.h

#ifndef _WIDGET_H_
#define _WIDGET_H_

#include <QtGui/QWidget>
#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 <QDebug>

void onMyButtonMouseRelease(QObject* sender, QMouseEvent* e)
{
    qDebug() << "onMyButtonMouseRelease(QObject* sender, QMouseEvent* e)";
}

// 初始化列表中用onMyButtonMouseRelease初始化m_listener
Widget::Widget(QWidget *parent) : QWidget(parent), myButton(this, onMyButtonMouseRelease)
{
    myButton.setText("QMyPushButton");

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

void Widget::onMyButtonClicked()
{
    qDebug() << "onMyButtonClicked()";
}


Widget::~Widget()
{

}

main.cpp

#include <QtGui/QApplication>
#include "Widget.h"

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

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

   -事件由具体对象进行处理 ,信号由具体对象主动产生 

   -改写事件处理函数可能导致程序行为发生改变,信号是否存在对应的槽函数不会改变程序行为

   -一般而言,信号在具体的事件处理函数中产生 

2、事件处理的顺序

事件被组件对象处理后可能传递到其父组件对象 

QEvent中的关键成员函数 

   -void ignore()  :接收者忽略当前事件,事件可能传递给父组件

   -void accept() :接收者期望处理当前事件 

   -bool isAccepted() :判断当前事件是否被处理 

 

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include "MyLineEdit.h"

class Widget : public QWidget
{
    Q_OBJECT
    
    MyLineEdit myLineEdit;
public:
    Widget(QWidget* parent = 0);
    bool event(QEvent* e);
    void keyPressEvent(QKeyEvent* e);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QDebug>
#include <QEvent>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{

}
// Qt事件被event函数处理
bool Widget::event(QEvent* e)
{
    // 当处理的事件是按键事件
    if( e->type() == QEvent::KeyPress )
    {
        qDebug() << "Widget::event";
    }

    // 直接调用父类事件处理函数
    return QWidget::event(e); // 根据事件的类型调用keyPressEvent()事件处理函数处理
}

void Widget::keyPressEvent(QKeyEvent* e)
{
    qDebug() << "Widget::keyPressEvent";

    // 调用父类的事件处理函数
    QWidget::keyPressEvent(e);
}

Widget::~Widget()
{

}

MyLineEdit.h

#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H

#include <QLineEdit>

class MyLineEdit : public QLineEdit
{
    Q_OBJECT
public:
    explicit MyLineEdit(QWidget *parent = 0);
    bool event(QEvent* e);
    void keyPressEvent(QKeyEvent* e);
signals:
    
public slots:
    
};

#endif // MYLINEEDIT_H

MyLineEdit.cpp

#include "MyLineEdit.h"
#include <QDebug>
#include <QEvent>
#include <QKeyEvent>

MyLineEdit::MyLineEdit(QWidget *parent) :
    QLineEdit(parent)
{
}

bool MyLineEdit::event(QEvent* e)
{
    if( e->type() == QEvent::KeyPress )
    {
        qDebug() << "MyLineEdit::event";
    }

    return QLineEdit::event(e);
}

void MyLineEdit::keyPressEvent(QKeyEvent* e)
{
    qDebug() << "MyLineEdit::keyPressEvent";

    QLineEdit::keyPressEvent(e);

    // e->ignore();告诉Qt平台事件没有被处理,会调用父组件event函数处理
}

main.cpp

#include <QtGui/QApplication>
#include "Widget.h"

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

Qt事件被分配给MyLineEdit,调用自身event函数进行事件处理,根据事件类型不同调用不同的事件处理函数

当取消 e->ignore(); 的注释, 还会调用了父组件的event函数处理事件,即事件又被传给了父组件

 

3、Qt中的事件过滤器 

Qt中的事件过滤器 

   -事件过滤器可以对其他组件接收到的事件进行监控(例如可以没收事件)

   -组件通过installEventFilter()函数安装事件过滤器 (即组件被事件过滤器监控着)

   -任意的QObject对象都可以作为事件过滤器使用,事件过滤器对象需要重写eventFilter()函数 

   -事件过滤器对象在组件之前接收到事件 ,事件过滤器能够决定是否将事件转发到组件对象 

沿用上面实验的代码改进

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "MyLineEdit.h"

class Widget : public QWidget
{
    Q_OBJECT
    
    MyLineEdit myLineEdit;
public:
    Widget(QWidget* parent = 0);

    bool event(QEvent* e);
    void keyPressEvent(QKeyEvent* e);
    
    //重写eventFilter, 所以当前类对象是事件过滤器对象
    bool eventFilter(QObject* obj, QEvent* e);
    
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QDebug>
#include <QEvent>
#include <QKeyEvent>

Widget::Widget(QWidget *parent)
    : QWidget(parent),myLineEdit(this)
{
    myLineEdit.installEventFilter(this); // 事件过滤器对象(Widget)安装到感兴趣的组件对象
}

bool Widget::event(QEvent* e)
{
    if( e->type() == QEvent::KeyPress )
    {
        qDebug() << "Widget::event";
    }

    return QWidget::event(e);
}

void Widget::keyPressEvent(QKeyEvent* e)
{
    qDebug() << "Widget::keyPressEvent";

    QWidget::keyPressEvent(e);
}

//事件过滤对象重写eventFilter函数
bool Widget::eventFilter(QObject* obj, QEvent* e)
{
    bool ret = true; // 返回值为true,表示事件已经处理不会传递给obj了

    // 当被监控组件为myLineEdit且事件为按下按键
    if( (obj == &myLineEdit) && (e->type() == QEvent::KeyPress) )
    {
        qDebug() << "Widget::eventFilter";

        QKeyEvent* evt = dynamic_cast<QKeyEvent*>(e);

        switch(evt->key()) // 当键入数字时,事件被myLineEdit正常接收,其它都被没收
        {
        case Qt::Key_0:
        case Qt::Key_1:
        case Qt::Key_2:
        case Qt::Key_3:
        case Qt::Key_4:
        case Qt::Key_5:
        case Qt::Key_6:
        case Qt::Key_7:
        case Qt::Key_8:
        case Qt::Key_9:
            ret = false; // false事件正常传递给obj
            break;
        default:
            break;
        }
    }
    else
    {
        // 直接调用父类eventFilter函数
        ret = QWidget::eventFilter(obj, e);
    }

    return ret;
}

Widget::~Widget()
{

}

当按下除数字以外的键时

发现字母等都被过滤,MyLineEdit对象没有接收到事件

当按下数字键时

 

4、Qt中的拖放事件  

拖放一个文件进入窗口时将触发拖放事件 ,每一个QWidget对象都能够处理拖放事件 

拖放事件的处理函数为 

      - void dragEnterEvent(QDragEnterEvent* e); 

      - void dropEvent(QDropEvent* e);  

拖放事件中的QMimeData 

      - QMimeData是Qt中的多媒体数据类 ,QMimeData支持多种不同类型的多媒体数据 

      - 拖放事件通过QMimeData对象传递数据 

常用MIME类型数据处理函数 

自定义拖放事件的步骤 

       1. 对接收拖放事件的对象调用 setAcceptDrops 成员函数 

       2. 重写 dragEnterEvent 函数并判断MIME类型 

                期望数据 : e- > acceptProposedAction ()

                其它数据 : e- > ignore(); 

       3. 重写dropEvent函数并判断MIME类型 

                期望数据:从事件对象中获取MIME数据并处理 

                其它数据: e- > ignore(); 

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>

class Widget : public QWidget
{
    Q_OBJECT
protected:
    void dragEnterEvent(QDragEnterEvent* e);
    void dropEvent(QDropEvent* e);
public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QDebug>
#include <QList>
#include <QMimeData>
#include <QUrl>

Widget::Widget(QWidget *parent) : QWidget(parent)
{
    setAcceptDrops(true); // 当前对象可以接受拖放事件
}

// 重写托的事件处理函数
void Widget::dragEnterEvent(QDragEnterEvent* e)
{
    if( e->mimeData()->hasUrls() ) // 判断MIME数据是路径类型
    {
        e->acceptProposedAction();
    }
    else
    {
        e->ignore(); // 交给父组件对象处理
    }
}

// 重写放的事件处理函数
void Widget::dropEvent(QDropEvent* e)
{
    if( e->mimeData()->hasUrls() )
    {
        QList<QUrl> list = e->mimeData()->urls();

        for(int i=0; i<list.count(); i++)
        {
            qDebug() << list[i].toLocalFile(); // 将当前url转换为本地路径
        }
    }
    else
    {
        e->ignore();
    }
}

Widget::~Widget()
{
    
}

 

 

5、发送预定义事件

Qt中可以在程序中自主发送事件 

     -阻塞型事件发送 :事件发送后需要等待事件处理完成 

     -非阻塞型事件发送 :事件发送后立即返回 ,事件被发送到事件队列中等待处理 

QApplication类提供了支持事件发送的静态成员函数 

     -阻塞型发送函数: bool sendEvent(QObject* receiver,  QEvent* event); 

     -非阻塞型发送函数: bool postEvent(QObject* receiver,  QEvent* event);

     -sendEvent中事件对象的生命期由Qt程序管理 ,同时支持栈事件对象堆事件对象的发送 

     -postEvent中事件对象的生命期由Qt平台管理 ,只能发送堆事件对象 ,事件被处理后由Qt平台销毁 

使用sendEvent发送事件对象 

消息发送过程可以理解为:在sendEvent()函数内部直接调用Qt对象的event()事件处理函数。 

这样event函数return,sendEvent才能return

使用postEvent发送事件对象 

Widgte.cpp

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include <QPushButton>

class Widget : public QWidget
{
    Q_OBJECT
    
    QPushButton m_pushButton;

    void testSendEvent();
    void testPostEvent();
protected slots:
    void onButtonClicked();
public:
    Widget(QWidget *parent = 0);
    bool event(QEvent* evt);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include <QMouseEvent>
#include <QApplication>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_pushButton.setParent(this);
    m_pushButton.setText("Test");

    connect(&m_pushButton, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}

void Widget::onButtonClicked()
{
    // testSendEvent();
    // testPostEvent();
}

void Widget::testSendEvent()
{
    // 0,0位置鼠标左键双击事件,没有按键盘任意键
    QMouseEvent dbcEvt(QEvent::MouseButtonDblClick, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);

    qDebug() << "Before sendEvent()";

    QApplication::sendEvent(this, &dbcEvt); // 将事件发送给当前Widget对象

    qDebug() << "After sendEvent()";
}

void Widget::testPostEvent()
{
    QMouseEvent* dbcEvt = new QMouseEvent(QEvent::MouseButtonDblClick, QPoint(0, 0), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);

    qDebug() << "Before postEvent()";

    QApplication::postEvent(this, dbcEvt);

    // 在点击按钮后会产生一个点击事件,在onButtonClicked()槽函数内部调用testPostEvent()
    // 产生一个新事件,当然不会立即处理,会放入事件队列,等点击事件处理完后再处理

    qDebug() << "After postEvent()";
}

bool Widget::event(QEvent* evt)//重写event()函数
{
    if( evt->type() == QEvent::MouseButtonDblClick )
    {
        qDebug() << "event(): " << evt;
    }

    return QWidget::event(evt);
}

Widget::~Widget()
{

}

6、自定义事件对象 

Qt可以自定义新的事件类 

     -自定义的事件类必须继承自QEvent ,必须拥有全局唯一的Type值

     -程序中必须提供处理自定义事件对象的方法 

class StringEvent : public QEvent //自定义事件类
{
    QString m_data;
public:
    const static Type TYPE = static_cast<Type>(QEvent::User + 0xFF);
    // enum QEvent::Type   This enum type defines the valid event types in Qt.    
};

Qt中事件的Type值 

     -每个事件类都拥有全局唯一的Type值,自定义事件类的Type值也需要自定义 

     -自定义事件类使用QEvent::User之后的值作为Type值,程序中保证QEvent::User + VALUE全局唯一即可 

处理自定义事件对象的方法 

     -1. 将事件过滤器安装到目标对象:在eventFilter()函数中编写自定义事件的处理逻辑 

     -2. 在目标对象的类中重写事件处理函数:在event()函数中编写自定义事件的处理逻辑 

StringEvent.h

#ifndef _STRINGEVENT_H_
#define _STRINGEVENT_H_

#include <QEvent>
#include <QString>

class StringEvent : public QEvent //自定义事件类
{
    QString m_data;
public:
    const static Type TYPE = static_cast<Type>(QEvent::User + 0xFF);

    explicit StringEvent(QString data = "");
    QString data();
    
};

#endif // _STRINGEVENT_H_

StringEvent.cpp

#include "StringEvent.h"

StringEvent::StringEvent(QString data) : QEvent(TYPE)
{
    m_data = data;
}

QString StringEvent::data()
{
    return m_data;
}

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include <QLineEdit>

class Widget : public QWidget
{
    Q_OBJECT
    
    QLineEdit m_edit;
public:
    Widget(QWidget *parent = 0);
    bool event(QEvent* evt); 
    bool eventFilter(QObject* obj, QEvent* evt); // 将当前窗口作为事件过滤器
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp

#include "Widget.h"
#include "StringEvent.h"
#include <QMouseEvent>
#include <QDebug>
#include <QApplication>

Widget::Widget(QWidget *parent)
    : QWidget(parent), m_edit(this)
{
    m_edit.installEventFilter(this);//监控文本框对象
}

bool Widget::event(QEvent* evt)//重写event事件处理函数
{
    if( evt->type() == QMouseEvent::MouseButtonDblClick ) // 双击窗口任意空白区,双击事件
    {
        qDebug() << "event: Before sentEvent";

        StringEvent e("DT");

        QApplication::sendEvent(&m_edit, &e); // 发送自定义事件到文本框对象

        qDebug() << "event: After sentEvent";
    }

    return QWidget::event(evt);
}
 
bool Widget::eventFilter(QObject* obj, QEvent* evt) 
{
    if( (obj == &m_edit) && (evt->type() == StringEvent::TYPE) )
    {
        StringEvent* se = dynamic_cast<StringEvent*>(evt);

        qDebug() << "Receive: " << se->data();

        m_edit.insert(se->data());

        return true; // 返回true,代表当前事件已被处理
    }

    return QWidget::eventFilter(obj, evt);
}

Widget::~Widget()
{
    
}

 

 

 

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了使更多的Qt初学者能尽快入门Qt,也为了QtQt Creator的快速普及,我们花费大量精力写出了这一系列教程。虽然教程的知识可能很浅显,虽然教程的语言可能不规范,但是它却被数十万网友所认可。我们会将这一系列教程一直写下去,它将涉及Qt的方方面面 一、Qt Creator的安装和hello world程序的编写 二、Qt Creator编写多窗口程序 三、Qt Creator登录对话框 四、Qt Creator添加菜单图标 五、Qt Creator布局管理器的使用 六、Qt Creator实现文本编辑 七、Qt Creator实现文本查找 八、Qt Creator实现状态栏显示 九、Qt Creator鼠标键盘事件的处理实现自定义鼠标指针 十、Qt Creator实现定时器和产生随机数 十一、Qt 2D绘图(一)绘制简单图形 十二、Qt 2D绘图(二)渐变填充 十三、Qt 2D绘图(三)绘制文字 十四、Qt 2D绘图(四)绘制路径 十五、Qt 2D绘图(五)显示图片 十六、Qt 2D绘图(六)坐标系统 十七、Qt 2D绘图(七)Qt坐标系统深入 十八、Qt 2D绘图(八)涂鸦板 十九、Qt 2D绘图(九)双缓冲绘图简介 二十、Qt 2D绘图(十)图形视图框架简介 二十一、Qt数据库(一)简介 二十二、Qt数据库(二)添加MySQL数据库驱动插件 二十三、Qt数据库(三)利用QSqlQuery类执行SQL语句(一) 二十四、Qt数据库(四)利用QSqlQuery类执行SQL语句(二) 二十五、Qt数据库(五)QSqlQueryModel 二十六、Qt数据库(六)QSqlTableModel 二十七、Qt数据库(七)QSqlRelationalTableModel 二十八、Qt数据库(八)XML(一) 二十九、Qt数据库(九)XML(二) 三十、Qt数据库(十)XML(三) 三十一、Qt 4.7.0及Qt Creator 2.0 beta版安装全程图解 三十二、第一个Qt Quick程序(QML程序) 三十三、体验QML演示程序 三十四、Qt Quick Designer介绍 三十五、QML组件 三十六、QML项目之Image和BorderImage 三十七、Flipable、Flickable和状态与动画 三十八、QML视图 三十九、QtDeclarative模块 四十、使用Nokia Qt SDK开发Symbian和Maemo终端软件 四十一、Qt网络(一)简介 四十二、Qt网络(二)HTTP编程 四十三、Qt网络(三)FTP(一) 四十四、Qt网络(四)FTP(二) 四十五、Qt网络(五)获取本机网络信息 四十六、Qt网络(六)UDP 四十七、Qt网络(七)TCP(一) 四十八、Qt网络(八)TCP(二)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值