zoukankan      html  css  js  c++  java
  • Qt事件与常用事件处理、过滤

    转载:

      https://blog.csdn.net/apollon_krj/article/category/6939539

      https://blog.csdn.net/qq_41072190/article/category/7593738

    在Qt中我们可以应用信号与槽对一些鼠标点击的操作进行处理,如: 
    QPushbutton::clicked 
    QPushbutton::realsead 
    QPushbutton::pressed 
    而信号与槽的处理属于事件的一种,产生一个信号可以认为是一个信号事件,而槽函数就是对于该信号事件进行处理的回调函数。由于信号与槽属于事件,也就是说信号很强大,但是事件更强大。那么我们就有必要好好总结一下Qt的常用的一些事件了。

    1、首先明确事件处理过程: 
    事件(event)是由系统或者Qt本身在不同时刻发出的。当用户按下鼠标、敲下键盘,或者其它情况时候都会发出一个相应的事件。一些事件在对用户操作做出相应时发出,如键盘事件等;另外一些则是由系统自动发出,如计时事件等。Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件,当事件发生时,Qt将创建一个事件对象。Qt中所有事件类都继承自QEvent。在事件对象创建完毕之后,Qt将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给指定的事件处理函数(event handler)进行处理。

    2、常用事件(事件处理函数):

     1 keyPressEvent()             //键盘按下事件
     2 keyReleaseEvent()           //键盘释放事件
     3 mouseDoubleClickEvent()     //鼠标双击事件
     4 mouseMoveEvent()            //鼠标移动事件
     5 mousePressEvent()           //鼠标按下事件
     6 mouseReleaseEvent()         //鼠标释放事件
     7 timerEvent()                //定时器事件
     8 dragEnterEvent()            //拖拽进入当前窗口事件
     9 dragLeaveEvent()            //拖拽离开当前窗口事件
    10 dragMoveEvent()             //拖拽移动事件
    11 enterEvent()                //进入窗口区域事件
    12 leaveEvent()                //离开窗口区域事件
    13 closeEvent()                //关闭窗口事件

    以上的事件是比较常用的一些事件。这些事件的回调函数都是虚函数,其成员属性为”protected function”,在其基类中声明,再到具体的派生类中进行父类虚函数的覆写,以实现不同类中对于同一事件的不同处理,以上的虚函数在QWidget中基本都已声明,我们在具体使用时只需要继承QWidget,然后在QWidget的派生类中具体实现即可。

    3、QTimerEvent定时器事件: 
    测试main()函数不变(基类QWidget,MyWidget继承自QWidget):

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

    比如,我们采用timerEvent()实现两个计数器:其中一个计数器的事件触发的时间间隔为1s(计数到23s停止),另一个为0.5s(一直计数不停止)。分别显示在不同Label上。

    /*myWidget.h*/
    #ifndef MYWIDGET_H
    #define MYWIDGET_H
     
    #include <QWidget>
     
    namespace Ui {
    class MyWidget;
    }
     
    class MyWidget : public QWidget
    {
        Q_OBJECT
    public:
        explicit MyWidget(QWidget *parent = 0);
        ~MyWidget();
    protected:
        //定时器事件
        void timerEvent(QTimerEvent *);
    private:
        Ui::MyWidget *ui;
        int timerID_fast;        //区分不同定时器,类似于文件描述符
        int timerID_slow;
    };
     
    #endif // MYWIDGET_H
    /*myWidget.cpp*/
    #include "mywidget.h"
    #include "ui_mywidget.h"
     
    MyWidget::MyWidget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::MyWidget)
    {
        ui->setupUi(this);
        timerID_fast = this->startTimer(1000);//以毫秒为单位,每隔1秒触发一次定时器
        timerID_slow = this->startTimer(500);
    }
    void MyWidget::timerEvent(QTimerEvent *ev)
    {
    //如果触发定时器的是编号为timerID_fast,则处理,否则处理timerID_slow对应的代码
        if(ev->timerId() == timerID_fast){
            static int sec = 0;
            if(sec == 20){  //定时到20s时停止计时
                killTimer(timerID_fast);
            }
            ui->labelCoordinate->setText(QString("<center><h1>Timer out:%1</h1></center>").arg(sec++));
            //<center><h1>Timer out:%1</h1></center>为HTML的写法,中间(center)标题大小(h1)显示
        }
        else if(ev->timerId() == timerID_slow){
            static int sec = 0;
            ui->label->setText(QString("<center><h1>Timer out:%1</h1></center>").arg(sec++));
        }
    }
    MyWidget::~MyWidget()
    {
        delete ui;
    }

    结果如下:

    对于定时器的操作就是设定了每个一段时间产生一个中断,然后去执行我们在派生类中重新覆写的虚函数(这里的虚函数是覆写基类中的,所以其返回值、参数、函数名都和基类的函数名是相同的),显然这是一个回调函数。

    4、QMouseEvent鼠标事件: 
    鼠标事件应该算得上是GUI编程中用的最多的一个事件了,比如扫雷、棋类游戏等都对于鼠标事件的使用是相当频繁的。而鼠标事件我们就拿press、release、move、enter、leave等几个事件来说说:

    QWidget.cpp以及QWidget.h必须要修改,保持创建QWidget时即可,添加Class文件为MyLabel。在标签上显示鼠标move的坐标、鼠标位于标签内还是标签外(当鼠标位于标签内时,会显示move的坐标,所以测试鼠标leave与enter时应注销move的setText()),在qDebug中显示鼠标按键是左键/右键/中键,

    /*myLabel.h*/
    #ifndef MYLABEL_H
    #define MYLABEL_H
    //需要对标签进行提升,提升原本的类QLabel为MyLabel
    #include <QWidget>
    #include <QLabel>
    class MyLabel : public QLabel
    {
        Q_OBJECT
    public:
        explicit MyLabel(QWidget *parent = 0);
    protected:
        //鼠标点击事件
        void mousePressEvent(QMouseEvent *ev);
        //鼠标释放事件
        void mouseReleaseEvent(QMouseEvent *ev);
        //鼠标移动事件
        void mouseMoveEvent(QMouseEvent *ev);
        //进入窗口(Label)区域
        void enterEvent(QEvent *);
        //离开窗口(Label)区域
        void leaveEvent(QEvent *);
    signals:
     
    public slots:
    };
     
    #endif // MYLABEL_H
    /*myLabel.cpp*/
    #include "mylabel.h"
    #include <QMouseEvent>
    #include <qDebug>
    MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
    {
        //设定追踪鼠标,一开始运行就追踪而不是需要先按下键才会追踪
        this->setMouseTracking(true);
    }
     
    //鼠标按下事件处理函数
    void MyLabel::mousePressEvent(QMouseEvent *ev)
    {
        if(ev->button() == Qt::LeftButton){
            qDebug() << "Left";
        }else if(ev->button() == Qt::RightButton){
            qDebug() << "Right";
        }else if(ev->button() == Qt::MidButton){
            qDebug() << "Middle";
        }
        /*
         * QString str = QString("abc %1 ^_^ %2").arg(123456).arg("ABCDEF");
         * str = abc 123456 ^_^ ABCDEF
        */
        int i = ev->x();
        int j = ev->y();
        QString str = QString("<center><h1>Mouse Press:(%1, %2)</h1></center>").arg(i).arg(j);
        this->setText(str);
    }
    //鼠标抬起事件处理函数
    void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
    {
        QString str = QString("<center><h1>Mouse Release:(%1, %2)</h1></center>").arg(ev->x()).arg(ev->y());
        this->setText(str);
    }
    //鼠标移动事件处理函数
    void MyLabel::mouseMoveEvent(QMouseEvent *ev)
    {
        QString str = QString("<center><h1>Mouse Move:(%1, %2)</h1></center>").arg(ev->x()).arg(ev->y());
        //this->setText(str);
    }
    //鼠标位于标签内
    void MyLabel::enterEvent(QEvent * ev)
    {
        QString str = QString("<center><h1>Mouse Enter</h1></center>");
        this->setText(str);
    }
    //鼠标位于标签外
    void MyLabel::leaveEvent(QEvent *ev)
    {
        QString str = QString("<center><h1>Mouse Leave</h1></center>");
        this->setText(str);
    }

    5、QCloseEvent事件与QMessageBox使用: 
    在上例中QWidget加上鼠标关闭事件,点击右上角关闭弹出QMessageBox::question窗口选择是否关闭,该功能用到的accept()与ignore()两个事件处理函数,第一个accept()是接收事件,之后事件就不再向下传递了,而ignore()则是忽略事件不做处理事件会传递给父组件(而不是基类)

    void MyWidget::closeEvent(QCloseEvent *ev)
    {
        int ret = QMessageBox::question(this,"question","Close the Windows?");
        if(ret == QMessageBox::Yes){
            //关闭窗口
            //接收事件,事件不再向下传递
            ev->accept();
        }else{
            //不关闭窗口
            //忽略事件,事件继续给父组件传递,由于父组件没有做关于close的接收操作
            //所以不做关闭操作
            ev->ignore();
        }
    }

    6、QKeyEvent键盘事件: 
    ev作为传递事件的参数其参数key()可以获取引发事件(中断)的是哪一个按键,与Qt的枚举变量进行比对,来进行相应的操作:

    void MyWidget::keyPressEvent(QKeyEvent *ev)
    {
        if(ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z){
            qDebug() << (char)ev->key();
        }
        else{
            qDebug() << ev->key();
        }
    }

    该键盘事件处理函数是对A~Z的字母进行识别并打印(不打印ASCII码,而打印对应字符),而对于非A~Z的字符则打印ASCII码: 

    常用的一些键的枚举常量如下所示(我们不必要去记忆这些东西,只需要在帮助文档查一下即可,但是还是有必要浏览一遍的):

    Qt::Key_Escape  0x01000000   
    Qt::Key_Tab 0x01000001   
    Qt::Key_Backtab 0x01000002   
    Qt::Key_Backspace   0x01000003   
    Qt::Key_Return  0x01000004   
    Qt::Key_Enter   0x01000005  Typically located on the keypad.
    Qt::Key_Insert  0x01000006   
    Qt::Key_Delete  0x01000007   
    Qt::Key_Pause   0x01000008  The Pause/Break key (Note: Not related to pausing media)
    Qt::Key_Print   0x01000009   
    Qt::Key_SysReq  0x0100000a   
    Qt::Key_Clear   0x0100000b   
    Qt::Key_Home    0x01000010   
    Qt::Key_End 0x01000011   
    Qt::Key_Left    0x01000012   
    Qt::Key_Up  0x01000013   
    Qt::Key_Right   0x01000014   
    Qt::Key_Down    0x01000015   
    Qt::Key_PageUp  0x01000016   
    Qt::Key_PageDown    0x01000017   
    Qt::Key_Shift   0x01000020   
    Qt::Key_Control 0x01000021  On OS X, this corresponds to the Command keys.
    Qt::Key_Meta    0x01000022  On OS X, this corresponds to the Control keys. On Windows keyboards, this key is mapped to the Windows key.
    Qt::Key_Alt 0x01000023   
    Qt::Key_AltGr   0x01001103  On Windows, when the KeyDown event for this key is sent, the Ctrl+Alt modifiers are also set.
    Qt::Key_CapsLock    0x01000024   
    Qt::Key_NumLock 0x01000025   
    Qt::Key_ScrollLock  0x01000026   
    Qt::Key_F1  0x01000030   
    Qt::Key_F2  0x01000031   
    Qt::Key_F3  0x01000032   
    Qt::Key_F4  0x01000033   
    Qt::Key_F5  0x01000034   
    Qt::Key_F6  0x01000035   
    Qt::Key_F7  0x01000036   
    Qt::Key_F8  0x01000037   
    Qt::Key_F9  0x01000038   
    Qt::Key_F10 0x01000039   
    Qt::Key_F11 0x0100003a   
    Qt::Key_F12 0x0100003b   
     
    Qt::Key_0   0x30     
    Qt::Key_1   0x31     
    Qt::Key_2   0x32     
    Qt::Key_3   0x33     
    Qt::Key_4   0x34     
    Qt::Key_5   0x35     
    Qt::Key_6   0x36     
    Qt::Key_7   0x37     
    Qt::Key_8   0x38     
    Qt::Key_9   0x39
     
    Qt::Key_A   0x41     
    Qt::Key_B   0x42     
    Qt::Key_C   0x43     
    Qt::Key_D   0x44     
    Qt::Key_E   0x45     
    Qt::Key_F   0x46     
    Qt::Key_G   0x47     
    Qt::Key_H   0x48     
    Qt::Key_I   0x49     
    Qt::Key_J   0x4a     
    Qt::Key_K   0x4b     
    Qt::Key_L   0x4c     
    Qt::Key_M   0x4d     
    Qt::Key_N   0x4e     
    Qt::Key_O   0x4f     
    Qt::Key_P   0x50     
    Qt::Key_Q   0x51     
    Qt::Key_R   0x52     
    Qt::Key_S   0x53     
    Qt::Key_T   0x54     
    Qt::Key_U   0x55     
    Qt::Key_V   0x56     
    Qt::Key_W   0x57     
    Qt::Key_X   0x58     
    Qt::Key_Y   0x59     
    Qt::Key_Z   0x5a

    7、event()事件处理器与eventFilter()事件过滤器: 这两个函数也是虚函数,均声明在基类QObject中。

     1 virtual bool event(QEvent * e)

    2 virtual bool eventFilter(QObject * watched, QEvent * event) 

    有时候,我们需要对某个事件进行屏蔽,可以通过在其事件处理的中断函数中进行操作,也可以在event()事件处理函数中进行操作,还可以在eventFilter()事件过滤器中进行操作,以上三种操作:eventFilter()事件过滤器中操作是比较方便的,可以说是指哪打哪,可以随意过滤掉某一个类中的某一个事件。而event()也可以做到,但是event()函数主要功能并不是直接处理事件,而是按照事件对象的类型分派给指定的事件处理函数(event handler)进行处理。 
    这种关系可以表示为: 

    event()是一个根据不同事件做分类处理的功能,具体如下(就类似于switch…case语句或者if..else..语句),这是对原有event()虚函数的覆写(屏蔽定时器,屏蔽除了Y按键的其它按键):

    //事件处理器event()只在本控件有效,我们这里通过event()进行事件过滤
    bool MyWidget::event(QEvent *ev)
    {
    //switch的写法
        //对于定时器进行功能进行覆写,对键盘的Y键保留中断,其他键忽略。其它时间仍按默认处理
        //默认的是在event()的分发层(下一级)进行处理,而定时器的处理直接放在了event()函数中处理
        //到了下一层定时器的处理发现return的是true就不再处理,键盘按键也是同理
        switch(ev->type()){
            case QEvent::Timer: //如果是定时器事件,返回true停止时间传播,忽略定时器中断
                //timerEvent(static_cast<QTimerEvent*>(ev));       //不处理直接返回就是干掉了定时器
                //取消注释,则是将定时器事件提到event()函数中直接处理
                return true;
                break;
            case QEvent::KeyPress:
                if(static_cast<QKeyEvent *>(ev)->key() == Qt::Key_Y){
                    return QWidget::event(ev);
                }
                return true;
                break;
            default:
            //默认的则是保留原有event()的处理方式
                return QWidget::event(ev);
                break;
        }
    }

    当然,用switch能写出来用if…else…也可以,应根据需求进行选择(当分支较多时switch是一个空间换取时间的做法(switch是随机访问会为每个case的指令块生成一个起始地址标记,自然存在一个跳跃表的空间),而if…else…是一个时间效率不及switch的做法,但节省空间。而一般要用event屏蔽事件分支都比较少,二者就无太大区别)。关于if…else…与switch()case的效率比较参考blog:switch与ifelse的效率问题

    //if...else...的写法
    bool MyWidget::event(QEvent *ev)
    {
        if(ev->type() == QEvent::Timer){        //定时器特殊处理:忽略
            return true;
        }
        else if(ev->type() == QEvent::KeyPress){    //键盘处理:保留Y键,其它键忽略
            if(static_cast<QKeyEvent *>(ev)->key() == Qt::Key_Y){
                return QWidget::event(ev);
            }
            return true;
        }
        else{
            return QWidget::event(ev);  //其它事件按原有方式处理
            //注意这里一定要把不特殊处理的按原有方式处理
        }
    }

    除了在event()向每个具体的类对象分发事件以前,我们可以用event()进行事件的提前处理,而到达底层具体事件处理时,先查看event()对事件的返回值的状态(true/false),再确定处理方式。而在event()之上,我们也可以通过eventFilter()来完成事件的过滤(提前处理),因为这(eventFilter())就是事件过滤器。说到底无论是事件分发还是事件过滤,都是对事件的提前处理,并向底层返回一个处理状态。只是如果多个类中要对同一事件进行相同的提前处理,需要再多个类中实现多次event()的覆写,eventFilter()则需要一次即可。下例中是对label标签页的事件提前处理/事件过滤:

    //ui->label->installEventFilter(this);//在构造函数中需要安装过滤器,这是不同于event的一个地方
    //对于不同的类要分别安装过滤器
    bool MyWidget::eventFilter(QObject *obj, QEvent *ev)
    {
        //对标签label进行事件过滤
        if(obj == ui->label){
            QMouseEvent * env = static_cast<QMouseEvent *>(ev);
            if(ev->type() == QEvent::MouseMove){
                ui->label->setText(QString("<center><h1>Mouse Move:(%1, %2)"
                                           "</h1></center>").arg(env->x()).arg(env->y()));
                return true;    //结束传播
            }
            else if(ev->type() == QEvent::MouseButtonPress){
                ui->label->setText(QString("<center><h1>Mouse pressed</h1></center>"));
                return true;    //结束传播
            }
            else if(ev->type() == QEvent::MouseButtonRelease){
                ui->label->setText(QString("<center><h1>Mouse released</h1></center>"));
                return true;    //结束传播
            }
        }
        //其余的事件按照默认(未覆写)的处理方式处理
            return QWidget::eventFilter(obj,ev);
    }

    注意:event()、eventFilter()虽然可以用来屏蔽某些事件,但是我们一般不会去修改这两个函数,而是直接在具体的事件处理函数中进行处理操作。



  • 相关阅读:
    Hadoop常用命令介绍
    hadoop异常: java.io.EOFException: Unexpected end of input stream
    Python操作MySQL
    Python常用模块安装
    pyenv激活虚拟环境失败
    Jarvis OJ A Piece Of Cake
    JarvisOJ BASIC 德军的密码
    JarvisOJ BASIC -.-字符串
    HDU 1003 Max Sum
    洛谷 P2119 魔法阵
  • 原文地址:https://www.cnblogs.com/Dana-gx/p/10475127.html
Copyright © 2011-2022 走看看