zoukankan      html  css  js  c++  java
  • 界面编程之QT的事件20180727

    /*******************************************************************************************/

    一、事件

    1.含义

    事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,

    或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,

    如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。

    2.事件的产生与处理流程

    在前面我们也曾经简单提到,Qt 程序需要在main()函数创建一个QApplication对象,

    然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,

    程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。

    Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,

    Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,

    而是按照事件对象的类型分派给特定的事件处理函数(event handler),

    关于这一点,会在后边详细说明。

    3.事件处理函数(event handler)

    在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如

         keyPressEvent()

         keyReleaseEvent()

         mouseDoubleClickEvent()

         mouseMoveEvent()

         mousePressEvent()

         mouseReleaseEvent() 等。

    这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。

    4.自定义的事件处理函数

    自己实现了自己需要的某一个控件的事件处理函数,则这个控件就不是原来的控件了,是自定义的控件,这时候

    就需要在ui中使用提升的方法来使用这个自定义的控件。

    void MyLabel::mousePressEvent(QMouseEvent *ev)//QMouseEvent表示鼠标是左键还是右键或者是中间键按下

    {

        int i = ev->x();//相对上一层控件的左上角的坐标位置

        int j = ev->y();

        //sprinf

        /*qt中的格式化字符串打印:

         * QString str = QString("abc %1 ^_^ %2").arg(123).arg("mike");

         * str = abc 123 ^_^ mike

        */

        if(ev->button() == Qt::LeftButton)

        {

            qDebug() << "left";

        }

        else if(ev->button() == Qt::RightButton)

        {

            qDebug() << "right";

        }

        else if(ev->button() == Qt::MidButton)

        {

            qDebug() << "mid";

        }

        QString text = QString("<center><h1>Mouse Press: (%1, %2)</h1></center>")

                .arg(i).arg(j);

        this->setText(text);

    }

    MyLabel::MyLabel(QWidget *parent) : QLabel(parent)

    {

        //设置追踪鼠标

        this->setMouseTracking(true);//这样一开始鼠标放到控件上就会触发移动事件,否则除非点击之类的操作后才会触发。        

    }

    //e->modifiers()获取特殊键,)e->key()获取一般键,两个组合起来用可以实现常见的快捷键,具体见《Qt复合快捷键.txt》

    void MyWidget::keyPressEvent(QKeyEvent *e)//QKeyEvent表示到底是哪个按键被按下

    {

        qDebug() << (char)e->key();

        if(e->key() == Qt::Key_A)//QKeyEvent表示到底是哪个按键被按下

        {

            qDebug() << "Qt::Key_A";

        }

    }

    //定时器事件

    //设置定时器,每隔1s产生一个定时器事件,然后调用相应的事件处理函数

    timerId = this->startTimer(1000); //毫秒为单位 每隔1s触发一次定时器

    void MyWidget::timerEvent(QTimerEvent *e)

    {

        if(e->timerId() == this->timerId)//多个定时器时,区分具体是哪个定时器

        {

            static int sec = 0;

            ui->label->setText(QString("<center><h1>timer out: %1</h1></center>").arg(sec++));

            if(5 == sec)

            {

                //停止定时器,需要id 表示停止的是哪个定时器

                this->killTimer(this->timerId);

            }

        }

        else if(e->timerId() == this->timeId2)

        {

            static int sec = 0;

            ui->label_2->setText(QString("<center><h1>timer out: %1</h1></center>").arg(sec++));

        }

    }

    /*******************************************************************************************/

    二、事件的接收和忽略

    当一个事件产生后,首先传递给的是事件处理函数,

    如果事件处理函数内只是自己处理了,没有往下继续传递,则后面的就接收不到这个事件,也就是槽函数不会

      被调用,这就是事件的接收

    如果事件处理函数内有继续往下传递,则后面就可以接收到这个事件,也就是槽函数会被调用,

      这就是事件的忽略

    void MyButton::mousePressEvent(QMouseEvent *e)

    {

        if(e->button() == Qt::LeftButton)

        {

            //如果是左键按下

            qDebug() << "按下的是左键";

                       //处理后没有往下传递,这时槽函数就不会被调用,事件的接收          

            //事件接收后,就不会往下传递

                       //事件的忽略:

            //e->ignore(); //事件忽略函数,事件继续往下传递,但是

                 //事件传递给了父组(控)件(父窗口(MyWidget)),不是给父类(基类),这时按钮的槽函数就不会被调用

                                //ignore函数一般在关闭事件(closeEvent)中使用

             }

        else

        {

            //不做处理//事件的忽略,

            QPushButton::mousePressEvent(e);//事件继续往下传递,这时槽函数就会被调用

            //所以最好要加上上面这句话,自己不处理的事件,不要影响别人需要处理的

        }

    }

    void MyWidget::closeEvent(QCloseEvent *e)

    {

        int ret = QMessageBox::question(this, "question", "是否需要关闭窗口");

        if(ret == QMessageBox::Yes)

        {

            //关闭窗口

            //处理关闭窗口事件(处理逻辑是qt自己实现的),接收事件,事件就不会再往下传递

            e->accept();

        }

        else

        {

            //不关闭窗口

            //不处理关闭窗口事件,忽略事件,事件继续给父组件传递

            e->ignore();

        }

    }

    /*******************************************************************************************/

    三、event()函数

    event()函数主要用于消息分发的

    Qwidget中有event函数,所以每个继承QWidget的控件都有这个函数,由于这个函数是进行分发的,

    所以可以在这个函数里面进行消息的过滤等操作。

    可以复写event()函数来实现自己想要的功能。

      函数返回值是 bool 类型。

         如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。

        如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,

             而是会继续处理事件队列中的下一事件。

         在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播。

    bool MyWidget::event(QEvent *e)

    {

    //    //事件分发机制

    //    switch( e->type() )//获取事件类型来进行比较

    //    {

    //        case QEvent::Close:

    //            closeEvent(e);//所以其实可以不用像前面那样靠实现这个函数进行处理,

    //        break;

    //    case QEvent::MouseMove:

    //        mouseMoveEvent(e);//也可以在当前这个event函数里(也就是具体事件处理函数的上一层)直接处理,

    //        break;

    //        /*

    //         *  ……

    //        */

    //    }

        if(e->type() == QEvent::Timer)

        {

            //干掉定时器

            //如果返回true,事件停止传播

            //QTimerEvent *env = static_cast<QTimerEvent *>(e);

            //timerEvent(env);

            return true;

        }

        else if(e->type() == QEvent::KeyPress)

        {

            //类型转换

            QKeyEvent *env = static_cast<QKeyEvent *>(e);

            if(env->key() == Qt::Key_B)

            {

                return QWidget::event(e);

            }

            return true;

        }

        else

        {

            return QWidget::event(e);//调用父类也就是原先的处理方式

            //return false;//此时当前控件对象的事件处理函数都不会被调用,影响很大,所以,

                         //event函数尽量不要复写。

        }

    }

    /*******************************************************************************************/

    四、事件过滤器_事件总结

    前面event函数也可以过滤事件,但是那个过滤是过滤当前控件对象的事件,过滤不了其他控件对象。也不能

    把每个控件对象的event函数都复写一遍,所以要用事件过滤器。

    app.exec()中监测并产生事件,事件产生后先经过事件过滤器,然后再经过各个控件对象的event函数,event函数再分发给各个具体的事件处理函数

    具体见图1

     

    1.事件过滤器:

    QObject有一个eventFilter()函数,用于建立事件过滤器。函数原型如下:

    virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );

    这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以理解成一种过滤代码。

    事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。

    这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false。

    事件过滤器的调用时间是目标对象(也就是参数里面的watched对象)接收到事件对象之前。

    也就是说,如果你在事件过滤器中停止了某个事件,那么,

    watched对象以及以后所有的事件过滤器根本不会知道这么一个事件。

    一般我们是在最主要的窗口,也就是最外层的窗口,复写eventFilter()函数,来过滤各个控件对象的某个事件

    //安装过滤器

    ui->label_2->installEventFilter(this);//建立过滤器前,还需要安装过滤器

    bool MyWidget::eventFilter(QObject *obj, QEvent *e)//建立过滤器

    {//QObject *obj 控件对象, QEvent *e 事件

        if(obj == ui->label_2)

        {

            QMouseEvent *env = static_cast<QMouseEvent *>(e);

            //判断事件

            if(e->type() == QEvent::MouseMove)

            {

                ui->label_2->setText(QString("Mouse move:(%1, %2)").arg(env->x()).arg(env->y()));

                return true;

            }

            if(e->type() == QEvent::MouseButtonPress)

            {

                ui->label_2->setText(QString("Mouse press:(%1, %2)").arg(env->x()).arg(env->y()));

                return true;

            }

            if(e->type() == QEvent::MouseButtonRelease)

            {

                ui->label_2->setText(QString("Mouse release:(%1, %2)").arg(env->x()).arg(env->y()));

                return true;

            }

            else

            {

                return QWidget::eventFilter(obj, e);

            }

        }

        else

        {

            return QWidget::eventFilter(obj, e);

        }

       

    //    if(obj == ui->pushButton)

    //    {

           

    //    }

    }

    事件过滤器的强大之处在于,我们可以为整个应用程序添加一个事件过滤器。记得,installEventFilter()函数是QObject的函数,

    QApplication或者QCoreApplication对象都是QObject的子类,因此,我们可以向QApplication或者QCoreApplication添加事件过滤器。

    这种全局的事件过滤器将会在所有其它特性对象的事件过滤器之前调用。尽管很强大,但这种行为会严重降低整个应用程序的事件分发效率。

    因此,除非是不得不使用的情况,否则的话我们不应该这么做

    注意,

    事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。

    另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,

    只有等到二者重新回到同一线程的时候过滤器才会有效。

    上述代码具体见《QEvent》

     1 #include "mywidget.h"
     2 #include <QApplication>
     3 
     4 int main(int argc, char *argv[])
     5 {
     6     QApplication a(argc, argv);
     7     MyWidget w;
     8     w.show();
     9 
    10     return a.exec();
    11 }
    main.cpp
     1 #ifndef MYBUTTON_H
     2 #define MYBUTTON_H
     3 
     4 #include <QPushButton>
     5 
     6 class MyButton : public QPushButton
     7 {
     8     Q_OBJECT
     9 public:
    10     explicit MyButton(QWidget *parent = 0);
    11 
    12 protected:
    13     void mousePressEvent(QMouseEvent *e);
    14 
    15 signals:
    16 
    17 public slots:
    18 };
    19 
    20 #endif // MYBUTTON_H
    mybutton.h
     1 #include "mybutton.h"
     2 #include <QMouseEvent>
     3 #include <QDebug>
     4 
     5 MyButton::MyButton(QWidget *parent) : QPushButton(parent)
     6 {
     7 
     8 }
     9 
    10 void MyButton::mousePressEvent(QMouseEvent *e)
    11 {
    12     if(e->button() == Qt::LeftButton)
    13     {
    14         //如果是左键按下
    15         qDebug() << "按下的是左键";
    16         //事件接收后,就会往下传递
    17 
    18 
    19         //e->ignore(); //忽略,事件继续往下传递,给谁传递?
    20                      //事件传递给了父组件,不是给父类(基类)
    21     }
    22     else
    23     {
    24         //不做处理
    25         QPushButton::mousePressEvent(e);
    26         //事件的忽略,事件继续往下传递
    27     }
    28 }
    mybutton.cpp
     1 #ifndef MYLABEL_H
     2 #define MYLABEL_H
     3 
     4 #include <QLabel>
     5 
     6 class MyLabel : public QLabel
     7 {
     8     Q_OBJECT
     9 public:
    10     explicit MyLabel(QWidget *parent = 0);
    11 
    12 protected:
    13     //鼠标点击事件
    14     void mousePressEvent(QMouseEvent *ev);
    15     //鼠标释放事件
    16     void mouseReleaseEvent(QMouseEvent *ev);
    17     //鼠标移动事件
    18     void mouseMoveEvent(QMouseEvent *ev);
    19     //进入窗口区域
    20     void enterEvent(QEvent *);
    21     //离开窗口区域
    22     void leaveEvent(QEvent *);
    23 
    24 signals:
    25 
    26 public slots:
    27 };
    28 
    29 #endif // MYLABEL_H
    mylabel.h
    #include "mylabel.h"
    #include <QMouseEvent>
    #include <QDebug>
    
    MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
    {
        //设置追踪鼠标
        this->setMouseTracking(true);
    }
    
    void MyLabel::mousePressEvent(QMouseEvent *ev)
    {
        int i = ev->x();
        int j = ev->y();
        //sprinf
        /*
         * QString str = QString("abc %1 ^_^ %2").arg(123).arg("mike");
         * str = abc 123 ^_^ mike
        */
    
        if(ev->button() == Qt::LeftButton)
        {
            qDebug() << "left";
        }
        else if(ev->button() == Qt::RightButton)
        {
            qDebug() << "right";
        }
        else if(ev->button() == Qt::MidButton)
        {
            qDebug() << "mid";
        }
    
        QString text = QString("<center><h1>Mouse Press: (%1, %2)</h1></center>")
                .arg(i).arg(j);
    
        this->setText(text);
    }
    
    void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
    {
        QString text = QString("<center><h1>Mouse Release: (%1, %2)</h1></center>")
                .arg( ev->x() ).arg( ev->y() );
    
        this->setText(text);
    }
    
    void MyLabel::mouseMoveEvent(QMouseEvent *ev)
    {
        QString text = QString("<center><h1>Mouse move: (%1, %2)</h1></center>")
                .arg( ev->x() ).arg( ev->y() );
    
        //this->setText(text);
    }
    
    void MyLabel::enterEvent(QEvent *e)
    {
        QString text = QString("<center><h1>Mouse enter</h1></center>");
    
        this->setText(text);
    }
    
    void MyLabel::leaveEvent(QEvent *e)
    {
        QString text = QString("<center><h1>Mouse leave</h1></center>");
    
        this->setText(text);
    }
    mylabel.cpp
     1 #ifndef MYWIDGET_H
     2 #define MYWIDGET_H
     3 
     4 #include <QWidget>
     5 
     6 namespace Ui {
     7 class MyWidget;
     8 }
     9 
    10 class MyWidget : public QWidget
    11 {
    12     Q_OBJECT
    13 
    14 public:
    15     explicit MyWidget(QWidget *parent = 0);
    16     ~MyWidget();
    17 
    18 protected:
    19     //键盘按下事件
    20     void keyPressEvent(QKeyEvent *);
    21     //计时器事件
    22     void timerEvent(QTimerEvent *);
    23     //鼠标点击事件
    24     void mousePressEvent(QMouseEvent *);
    25     //关闭事件
    26     void closeEvent(QCloseEvent *);
    27 
    28     // 重写event事件
    29     bool event(QEvent *);
    30 
    31     //事件过滤器
    32     bool eventFilter(QObject *obj, QEvent *e);
    33 
    34 private:
    35     Ui::MyWidget *ui;
    36 
    37     int timerId;
    38     int timeId2;
    39 };
    40 
    41 #endif // MYWIDGET_H
    mywidget.h
      1 #include "mywidget.h"
      2 #include "ui_mywidget.h"
      3 #include <QDebug>
      4 #include <QKeyEvent>
      5 #include <QCloseEvent>
      6 #include <QMessageBox>
      7 #include <QEvent>
      8 
      9 MyWidget::MyWidget(QWidget *parent) :
     10     QWidget(parent),
     11     ui(new Ui::MyWidget)
     12 {
     13     ui->setupUi(this);
     14 
     15     timerId = this->startTimer(1000); //毫秒为单位 每隔1s触发一次定时器
     16     this->timeId2 = this->startTimer(500);
     17 
     18 
     19     connect(ui->pushButton, &MyButton::clicked,
     20             [=]()
     21             {
     22                 qDebug() << "按钮被按下";
     23             }
     24 
     25             );
     26 
     27     //安装过滤器
     28     ui->label_2->installEventFilter(this);
     29     ui->label_2->setMouseTracking(true);
     30 }
     31 
     32 MyWidget::~MyWidget()
     33 {
     34     delete ui;
     35 }
     36 
     37 void MyWidget::keyPressEvent(QKeyEvent *e)
     38 {
     39     qDebug() << (char)e->key();
     40 
     41     if(e->key() == Qt::Key_A)
     42     {
     43         qDebug() << "Qt::Key_A";
     44     }
     45 }
     46 
     47 void MyWidget::timerEvent(QTimerEvent *e)
     48 {
     49     if(e->timerId() == this->timerId)
     50     {
     51         static int sec = 0;
     52         ui->label->setText(
     53              QString("<center><h1>timer out: %1</h1></center>").arg(sec++)
     54 
     55                     );
     56 
     57         if(5 == sec)
     58         {
     59             //停止定时器
     60             this->killTimer(this->timerId);
     61         }
     62     }
     63     else if(e->timerId() == this->timeId2)
     64     {
     65         static int sec = 0;
     66         ui->label_2->setText(
     67              QString("<center><h1>timer out: %1</h1></center>").arg(sec++)
     68 
     69                     );
     70     }
     71 }
     72 
     73 void MyWidget::mousePressEvent(QMouseEvent *e)
     74 {
     75     qDebug() << "+++++++++++++++++++++++";
     76 }
     77 
     78 void MyWidget::closeEvent(QCloseEvent *e)
     79 {
     80     int ret = QMessageBox::question(this, "question", "是否需要关闭窗口");
     81     if(ret == QMessageBox::Yes)
     82     {
     83         //关闭窗口
     84         //处理关闭窗口事件,接收事件,事件就不会再往下传递
     85         e->accept();
     86     }
     87     else
     88     {
     89         //不关闭窗口
     90         //忽略事件,事件继续给父组件传递
     91         e->ignore();
     92     }
     93 
     94 }
     95 
     96 bool MyWidget::event(QEvent *e)
     97 {
     98 //    //事件分发
     99 //    switch( e->type() )
    100 //    {
    101 //        case QEvent::Close:
    102 //            closeEvent(e);
    103 //        break;
    104 //    case QEvent::MouseMove:
    105 //        mouseMoveEvent(e);
    106 //        break;
    107 //        /*
    108 //         *  ……
    109 //        */
    110 //    }
    111 
    112     if(e->type() == QEvent::Timer)
    113     {
    114         //干掉定时器
    115         //如果返回true,事件停止传播
    116         //QTimerEvent *env = static_cast<QTimerEvent *>(e);
    117         //timerEvent(env);
    118         return true;
    119     }
    120     else if(e->type() == QEvent::KeyPress)
    121     {
    122         //类型转换
    123         QKeyEvent *env = static_cast<QKeyEvent *>(e);
    124         if(env->key() == Qt::Key_B)
    125         {
    126             return QWidget::event(e);
    127         }
    128         return true;
    129 
    130     }
    131     else
    132     {
    133         return QWidget::event(e);
    134         //return false;
    135     }
    136 
    137 }
    138 
    139 bool MyWidget::eventFilter(QObject *obj, QEvent *e)
    140 {
    141     if(obj == ui->label_2)
    142     {
    143         QMouseEvent *env = static_cast<QMouseEvent *>(e);
    144         //判断事件
    145         if(e->type() == QEvent::MouseMove)
    146         {
    147             ui->label_2->setText(QString("Mouse move:(%1, %2)").arg(env->x()).arg(env->y()));
    148             return true;
    149         }
    150         if(e->type() == QEvent::MouseButtonPress)
    151         {
    152             ui->label_2->setText(QString("Mouse press:(%1, %2)").arg(env->x()).arg(env->y()));
    153             return true;
    154         }
    155         if(e->type() == QEvent::MouseButtonRelease)
    156         {
    157             ui->label_2->setText(QString("Mouse release:(%1, %2)").arg(env->x()).arg(env->y()));
    158             return true;
    159         }
    160         else
    161         {
    162             return QWidget::eventFilter(obj, e);
    163         }
    164     }
    165     else
    166     {
    167         return QWidget::eventFilter(obj, e);
    168     }
    169     
    170 //    if(obj == ui->pushButton)
    171 //    {
    172         
    173 //    }
    174 }
    mywidget.cpp

    2.event()函数和事件过滤器之间的比较与选择

    Qt 具有这么多种事件处理函数,肯定有一个地方对其进行分发,否则,Qt 怎么知道哪一种事件调用哪一个事件处理函数呢?这个分发的函数,

    就是event()。显然,当QMouseEvent产生之后,event()函数将其分发给mouseEvent()事件处理器进行处理。

    event()函数会有两个问题:

    I、event()函数是一个 protected 的函数,这意味着我们要想重写event(),必须继承一个已有的类。试想,我的程序根本不想要鼠标事件,

       程序中所有组件都不允许处理鼠标事件,是不是我得继承所有组件,一一重写其event()函数?protected 函数带来的另外一个问题是,

       如果我基于第三方库进行开发,而对方没有提供源代码,只有一个链接库,其它都是封装好的。我怎么去继承这种库中的组件呢?

    II、event()函数的确有一定的控制,不过有时候我的需求更严格一些:我希望那些组件根本看不到这种事件。event()函数虽然可以拦截,

        但其实也是接收到了QMouseEvent对象。我连让它收都收不到。这样做的好处是,模拟一种系统根本没有那个事件的效果,

        所以其它组件根本不会收到这个事件,也就无需修改自己的事件处理函数。这种需求怎么办呢?

    这两个问题是event()函数无法处理的。于是,Qt 提供了另外一种解决方案:事件过滤器。事件过滤器给我们一种能力,让我们能够完全移除某种事件。

    事件过滤器可以安装到任意QObject类型上面,并且可以安装多个。如果要实现全局的事件过滤器,则可以安装到QApplication或者QCoreApplication上面。

    这里需要注意的是,如果使用installEventFilter()函数给一个对象安装事件过滤器,那么该事件过滤器只对该对象有效,

    只有这个对象的事件需要先传递给事件过滤器的eventFilter()函数进行过滤,其它对象不受影响。

    如果给QApplication对象安装事件过滤器,那么该过滤器对程序中的每一个对象都有效,任何对象的事件都是先传给eventFilter()函数。

    事件过滤器可以解决刚刚我们提出的event()函数的两点不足:

    I、首先,事件过滤器不是 protected 的,因此我们可以向任何QObject子类安装事件过滤器;

    II、其次,事件过滤器在目标对象接收到事件之前进行处理,如果我们将事件过滤掉,目标对象根本不会见到这个事件。

    3.notify()函数

    事实上,还有一种方法,Qt 事件的调用最终都会追溯到QCoreApplication::notify()函数(最后一层),

    因此,最大的控制权实际上是重写QCoreApplication::notify()。这个函数的声明是:

    virtual bool QCoreApplication::notify ( QObject * receiver,

                                            QEvent * event );

    该函数会将event发送给receiver,也就是调用receiver->event(event),

    其返回值就是来自receiver的事件处理器。注意,这个函数为任意线程的任意对象的任意事件调用,

    因此,它不存在事件过滤器的线程的问题。不过我们并不推荐这么做,因为notify()函数只有一个,

    而事件过滤器要灵活得多

    4.事件总结

    Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂。说它复杂,更多是因为它涉及到的函数众多,而处理方法也很多,

    有时候让人难以选择。现在我们简单总结一下 Qt 中的事件机制。

    Qt 中有很多种事件:鼠标事件、键盘事件、大小改变的事件、位置移动的事件等等。如何处理这些事件,实际有两种选择:

    I、所有事件对应一个事件处理函数,在这个事件处理函数中用一个很大的分支语句进行选择,其代表作就是 win32 API 的WndProc()函数

    在这个函数中,我们需要使用switch语句,选择message参数的类型进行处理

    II、每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制

    现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次(前面主要讲的是前三层,前面三个常用,后面两个基本用不上):

    1).重写paintEvent()、mousePressEvent()等事件处理函数。这是最普通、最简单的形式,同时功能也最简单。

    2).重写event()函数。event()函数是所有对象的事件入口,QObject和QWidget中的实现,

      默认是把事件传递给特定的事件处理函数。

    3).在特定对象上面安装事件过滤器。该过滤器仅过滤该对象接收到的事件。

    4).在QCoreApplication::instance()上面安装事件过滤器。

      该过滤器将过滤所有对象的所有事件,因此和notify()函数一样强大,

      但是它更灵活,因为可以安装多个过滤器。

      全局的事件过滤器可以看到 disabled 组件上面发出的鼠标事件。

      全局过滤器有一个问题:只能用在主线程。

    5).重写QCoreApplication::notify()函数。这是最强大的,和全局事件过滤器一样提供完全控制,

      并且不受线程的限制。但是全局范围内只能有一个被使用(因为QCoreApplication是单例的)。

    /*******************************************************************************************/

    五、QT实例___猜数字答案游戏

    //开始游戏

    void MyWidget::on_pushButtonStart_clicked()

    {

        //获取下拉框的时间 .toInt():字符串转换为 int

        gameTime = ui->comboBox->currentText().toInt();

        qDebug() << gameTime << "s";

        //切换到游戏界面

        //ui->stackedWidget->setCurrentIndex(1);

        ui->stackedWidget->setCurrentWidget(ui->pageGame);//通过容器切换页面

        int num;

        //以从0时0分0秒到现在的秒数为种子,如果没有给种子则产生的随机数可能都是一样的。

        qsrand( QTime(0,0,0).secsTo( QTime::currentTime() ) );

        //调用全局的qrand()函数生成随机数,对10000取余,保证位于10000的范围内

        while( ( num = qrand()%10000 ) < 999 );//< 999代表找到的随机数必须是四位数的

        randStr = QString::number(num);

        qDebug() << "randNum = " << randStr;

        //设置进度条

        ui->progressBar->setMinimum(0);//最小值

        ui->progressBar->setMaximum(gameTime);//最大值

        ui->progressBar->setValue(gameTime); //当前值

        gameTimerId = 0;

        //启动定时器

        gameTimerId = startTimer(1000); //以 1000 毫秒(即1s)作为时间间隔

        resultStr.clear();

        ui->textEdit->clear();

    }

    上述代码具体见《GuessNumAns》

     1 #include "mywidget.h"
     2 #include <QApplication>
     3 
     4 int main(int argc, char *argv[])
     5 {
     6     QApplication a(argc, argv);
     7     MyWidget w;
     8     w.show();
     9 
    10     return a.exec();
    11 }
    main.cpp
     1 #ifndef MYWIDGET_H
     2 #define MYWIDGET_H
     3 
     4 #include <QWidget>
     5 #include <QMovie>
     6 
     7 namespace Ui {
     8 class MyWidget;
     9 }
    10 
    11 class MyWidget : public QWidget
    12 {
    13     Q_OBJECT
    14 
    15 public:
    16     explicit MyWidget(QWidget *parent = 0);
    17     ~MyWidget();
    18 
    19     //自定义槽函数
    20     void dealNum();
    21 
    22 private slots:
    23     void on_pushButtonStart_clicked();
    24 
    25     void on_pushButtonEnd_clicked();
    26 
    27     void on_pushButtonDel_clicked();
    28 
    29     void on_pushButtonEnter_clicked();
    30 
    31 protected:
    32     //定时器事件,此为虚函数
    33     void timerEvent(QTimerEvent *e);
    34 
    35 private:
    36     Ui::MyWidget *ui;
    37     int gameTime;       //游戏时间
    38     QString randStr;    //随机数
    39     int gameTimerId;    //游戏时间定时器ID
    40     int overTimerId;    //失败动画定时器ID
    41     int winTimerId;     //成功动画定时器ID
    42     QString resultStr;  //结果数
    43 
    44     QMovie overMovie;   //失败动画
    45     QMovie winMovie;    //胜利动画
    46 
    47 
    48 };
    49 
    50 #endif // MYWIDGET_H
    mywidget.h
      1 #include "mywidget.h"
      2 #include "ui_mywidget.h"
      3 #include <QDebug>
      4 #include <QTime>
      5 #include <QMessageBox>
      6 #include <QString>
      7 
      8 MyWidget::MyWidget(QWidget *parent) :
      9     QWidget(parent),
     10     ui(new Ui::MyWidget)
     11 {
     12     ui->setupUi(this);
     13 
     14     //显示第一个页面(设置页面)
     15     //ui->stackedWidget->setCurrentIndex(0);
     16     ui->stackedWidget->setCurrentWidget(ui->pageSet);
     17 
     18     //初始化数据
     19     //失败动画
     20     overMovie.setFileName(":/new/prefix1/Image/over.gif");
     21     ui->labelOver->setMovie(&overMovie);//给标签设置动画
     22     ui->labelOver->setScaledContents(true);//让动画自动适应标签大小
     23 
     24     //胜利动画
     25     winMovie.setFileName(":/new/prefix1/Image/win.gif");
     26     ui->labelWin->setMovie(&winMovie);
     27     ui->labelWin->setScaledContents(true);
     28 
     29     //猜数字游戏界面相应设置
     30     //数字按钮都连接同一个槽
     31     connect(ui->pushButton0, &QPushButton::clicked, this, &MyWidget::dealNum);
     32     connect(ui->pushButton1, &QPushButton::clicked, this, &MyWidget::dealNum);
     33     connect(ui->pushButton2, &QPushButton::clicked, this, &MyWidget::dealNum);
     34     connect(ui->pushButton3, &QPushButton::clicked, this, &MyWidget::dealNum);
     35     connect(ui->pushButton4, &QPushButton::clicked, this, &MyWidget::dealNum);
     36     connect(ui->pushButton5, &QPushButton::clicked, this, &MyWidget::dealNum);
     37     connect(ui->pushButton6, &QPushButton::clicked, this, &MyWidget::dealNum);
     38     connect(ui->pushButton7, &QPushButton::clicked, this, &MyWidget::dealNum);
     39     connect(ui->pushButton8, &QPushButton::clicked, this, &MyWidget::dealNum);
     40     connect(ui->pushButton9, &QPushButton::clicked, this, &MyWidget::dealNum);
     41 }
     42 
     43 MyWidget::~MyWidget()
     44 {
     45     delete ui;
     46 }
     47 
     48 //开始游戏
     49 void MyWidget::on_pushButtonStart_clicked()
     50 {
     51     //获取下拉框的时间 .toInt():字符串转换为 int
     52     gameTime = ui->comboBox->currentText().toInt();
     53     qDebug() << gameTime << "s";
     54 
     55     //切换到游戏界面
     56     //ui->stackedWidget->setCurrentIndex(1);
     57     ui->stackedWidget->setCurrentWidget(ui->pageGame);
     58 
     59 
     60     int num;
     61     //以从0时0分0秒到现在的秒数为种子
     62     qsrand( QTime(0,0,0).secsTo( QTime::currentTime() ) );
     63     //调用全局的qrand()函数生成随机数,对10000取余,保证位于10000的范围内
     64     while( ( num = qrand()%10000 ) < 999 );
     65     randStr = QString::number(num);
     66     qDebug() << "randNum = " << randStr;
     67 
     68     //设置进度条
     69     ui->progressBar->setMinimum(0);//最小值
     70     ui->progressBar->setMaximum(gameTime);//最大值
     71     ui->progressBar->setValue(gameTime); //当前值
     72 
     73     gameTimerId = 0;
     74     //启动定时器
     75     gameTimerId = startTimer(1000); //以 1000 毫秒(即1s)作为时间间隔
     76 
     77     resultStr.clear();
     78     ui->textEdit->clear();
     79 
     80 }
     81 
     82 //退出游戏
     83 void MyWidget::on_pushButtonEnd_clicked()
     84 {
     85     this->close(); //关闭窗口
     86 }
     87 
     88 void MyWidget::timerEvent(QTimerEvent *e)
     89 {
     90     if(e->timerId() == gameTimerId)//游戏时间
     91     {
     92         gameTime--;
     93 
     94         //设置进度条
     95         ui->progressBar->setValue(gameTime); //当前值
     96 
     97         if(0 == gameTime)//时间到
     98         {
     99             //关闭定时器
    100             killTimer(gameTimerId);
    101 
    102             QMessageBox::information(this, "失败", "时间到了啊!!!");
    103 
    104 
    105             overMovie.start();//启动动画
    106 
    107             //切换失败动画页面
    108             //ui->stackedWidget->setCurrentIndex(2);
    109             ui->stackedWidget->setCurrentWidget(ui->pageOver);
    110 
    111             overTimerId = startTimer(5000); //启动定时器
    112 
    113         }
    114 
    115     }
    116     else if(e->timerId() == overTimerId)//失败动画时间
    117     {
    118         //停止动画,停止定时器,回到游戏设置页面
    119         overMovie.stop();//停止动画
    120         killTimer(overTimerId);  //停止定时器
    121 
    122         //切换到游戏设置页面
    123         ui->stackedWidget->setCurrentWidget(ui->pageSet);
    124 
    125     }
    126     else if(e->timerId() == winTimerId)//胜利动画时间
    127     {
    128 
    129         winMovie.stop();//停止动画
    130         killTimer(winTimerId);  //停止定时器
    131 
    132         //切换到游戏设置页面
    133         ui->stackedWidget->setCurrentWidget(ui->pageSet);
    134 
    135     }
    136 }
    137 
    138 //数字键处理
    139 void MyWidget::dealNum()
    140 {
    141     //获取信号接收者
    142     QObject * mySender = sender();
    143     //转换为按钮类型
    144     QPushButton *p = (QPushButton *)mySender;
    145     if(NULL != p)
    146     {
    147         //获取按钮的内容
    148         QString numStr = p->text();
    149         resultStr += numStr;
    150 
    151         //数字不能以0开始
    152         if(resultStr.size() == 1 && resultStr == "0")
    153         {
    154             resultStr.clear();
    155         }
    156 
    157         if( resultStr.size() <= 4) //保证显示结果为4位
    158         {
    159              ui->textEdit->setText( resultStr );
    160 
    161              if(resultStr.size() == 4) //数字到第4位时
    162              {
    163                  if(resultStr > randStr)
    164                  {
    165                      ui->textEdit->append("数字大了点!!!");
    166                  }
    167                  else if(resultStr < randStr)
    168                  {
    169                      ui->textEdit->append("数字小了点!!!");
    170                  }
    171                  else
    172                  {
    173                      ui->textEdit->append("恭喜你猜对了!!!");
    174 
    175                      //停止定时器
    176                      //关闭定时器
    177                      killTimer(gameTimerId);
    178 
    179                      QMessageBox::information(this, "胜利", "恭喜你猜对了!!!");
    180 
    181                      //切换到成功动画
    182                      winMovie.start();
    183                      ui->stackedWidget->setCurrentWidget(ui->pageWin);
    184 
    185                      //启动定时器
    186                      winTimerId = startTimer(5000); //5s
    187                  }
    188 
    189                  //初始化字符串结果,清空
    190                  resultStr.clear();
    191              }
    192         }
    193 
    194     }
    195 
    196 }
    197 
    198 //退格按钮
    199 void MyWidget::on_pushButtonDel_clicked()
    200 {
    201 
    202     if(resultStr.size() == 1)
    203     {
    204         resultStr.clear();
    205         ui->textEdit->clear();
    206     }
    207     else
    208     {
    209         resultStr.chop(1); //截断最后一位字符
    210         ui->textEdit->setText(resultStr);
    211     }
    212 
    213 }
    214 
    215 //提示按钮
    216 void MyWidget::on_pushButtonEnter_clicked()
    217 {
    218     resultStr.clear();
    219     QString str = "随机数为:" + randStr;
    220     ui->textEdit->setText( str );
    221 }
    mywidget.cpp

    更正一点:

    //数字键处理

    void MyWidget::dealNum()

    {

        //获取信号接收者 //应该是信号的发出者(产生者)

        QObject * mySender = sender();//即得到具体是哪一个按钮发出来的信号

        //转换为按钮类型

        QPushButton *p = (QPushButton *)mySender;

  • 相关阅读:
    洛谷 P1040 加分二叉树
    洛谷 P1892 团伙
    洛谷 P2024 食物链
    洛谷 P1196 银河英雄传说
    并查集--算法,优化,变种
    洛谷 P1801 黑匣子_NOI导刊2010提高(06)
    洛谷 P3370 【模板】字符串哈希
    洛谷 P1090 合并果子
    洛谷 P1219 八皇后
    线的缩放效果
  • 原文地址:https://www.cnblogs.com/yuweifeng/p/9377263.html
Copyright © 2011-2022 走看看