QT学习笔记(7) QT中的事件系统

一、QT中的事件

  事件是对各种应用程序需要知道的由应用程序内部或者外部产生的事情或者动作的通称。

  在Qt中使用一个对象来表示一个事件,它继承自QEvent类。

  事件与信号并不相同,比如我们使用鼠标点击了一下界面上的按钮,那么就会产生鼠标事件QMouseEvent(不是按钮产生的),而因为按钮被按下了,所以它会发出clicked()单击信号(是按钮产生的)。这里一般只关心按钮的单击信号,而不用考虑鼠标事件,但是如果要设计一个按钮,或者当鼠标点击按钮时让它产生别的效果,那么就要关心鼠标事件了。可以看到,事件与信号是两个不同层面的东西,它们的发出者不同,作用也不同。在Qt中,任何QObject的子类的实例都可以接收和处理事件。
  常见事件:鼠标事件、键盘事件、定时事件、上下文菜单事件、关闭事件、拖放事件、绘制事件等。

二、事件的处理

  (1)方法一:重新实现部件的paintEvent(),mousePressEvent()等事件处理函数。这是最常用也的一种方法,不过它只能用来处理特定部件的特定事件。例如前一章实现拖放操作,就是用的这种方法。
  (2)方法二:重新实现notify()函数。这个函数功能强大,提供了完全的控制,可以在事件过滤器得到事件之前就获得它们。但是,它一次只能处理一个事件。
  (3)方法三:向QApplication对象上安装事件过滤器。因为一个程序只有一个QApplication对象,所以这样实现的功能与使用notify()函数是相同的,优点是可以同时处理多个事件。
  (4)方法四:重新实现event()函数。QObject类的event()函数可以在事件到达默认的事件处理函数之前获得该事件。
  (5)方法五:在对象上安装事件过滤器。使用事件过滤器可以在一个界面类中同时处理不同子部件的不同事件。

  在实际编程中,最常用的是方法一,其次是方法五。

 

三、事件的处理过程

  在每个程序的main()函数的最后,都会调用QApplication类的exec()函数,它会使Qt应用程序进入事件循环,这样就可以使应用程序在运行时接收发生的各种事件。一旦有事件发生,Qt便会构建一个相应的QEvent子类的对象来表示它,然后将它传递给相应的QObject对象或其子对象。

  事件的传递顺序是这样的:先是事件过滤器,然后是该部件的event()函数,最后是该部件的事件处理函数,如果该部件忽略掉该事件(event->ignore()),那么这个事件就会传递给这个部件的父部件。

这里还要注意,event()函数和事件处理函数,是在该部件内进行重新定义的,而事件过滤器却是在该部件的父部件中进行定义的。

示例代码如下:

mainwindow.h

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3 
 4 #include <QMainWindow>
 5 
 6 namespace Ui {
 7 class MainWindow;
 8 }
 9 
10 class MainWindow : public QMainWindow
11 {
12     Q_OBJECT
13 
14 public:
15     explicit MainWindow(QWidget *parent = 0);
16     ~MainWindow();
17 
18 protected:
19     //键盘按下事件
20     void keyPressEvent(QKeyEvent *event);
21     //定时器事件
22     void timerEvent(QTimerEvent *event);
23     //鼠标按下事件
24     void mousePressEvent(QMouseEvent *event);
25     //关闭事件
26     void closeEvent(QCloseEvent *event);
27     //重写event事件
28     bool event(QEvent *event);
29     //事件过滤器
30     bool eventFilter(QObject *watched, QEvent *event);
31 
32 private:
33     Ui::MainWindow *ui;
34 
35     int timerId;//定时器id
36 };
37 
38 #endif // MAINWINDOW_H

mybutton.h

 1 #ifndef MYBUTTON_H
 2 #define MYBUTTON_H
 3 
 4 #include <QPushButton>
 5 //让MyButton继承自QPushButton
 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

mylabel.h

 1 #ifndef MYLABEL_H
 2 #define MYLABEL_H
 3 
 4 #include <QLabel>
 5 //让MyLabel继承自QLabel
 6 class MyLabel : public QLabel
 7 {
 8     Q_OBJECT
 9 public:
10     explicit MyLabel(QWidget *parent = 0);
11 
12 protected:
13     //虚函数不能直接通过F1来查看,只能通过基类查看
14     //此函数是虚函数,其中的函数名、参数、返回值都不能改变
15     void mousePressEvent(QMouseEvent *ev);//函数有参数,不能自动补全,要删掉括号,再补全。
16     void mouseReleaseEvent(QMouseEvent *ev);
17     void mouseMoveEvent(QMouseEvent *ev);
18 
19 signals:
20 
21 public slots:
22 };
23 
24 #endif // MYLABEL_H

smallwidget.h

#ifndef SMALLWIDGET_H
#define SMALLWIDGET_H

#include <QWidget>

class SmallWidget : public QWidget
{
    Q_OBJECT
public:
    explicit SmallWidget(QWidget *parent = 0);

signals:

public slots:
};

#endif // SMALLWIDGET_H

mainwindow.cpp

  1 #include "mainwindow.h"
  2 #include "ui_mainwindow.h"
  3 #include <QKeyEvent>
  4 #include <QTimerEvent>
  5 #include <QCloseEvent>
  6 #include <QMouseEvent>
  7 #include <QMessageBox>
  8 #include <QEvent>
  9 #include <QDebug>
 10 
 11 MainWindow::MainWindow(QWidget *parent) :
 12     QMainWindow(parent),
 13     ui(new Ui::MainWindow)
 14 {
 15     ui->setupUi(this);
 16 
 17     //启动定时器
 18     //从2秒开始,每个一秒调用一次定时器
 19     timerId = this->startTimer(2000);//毫秒为单位
 20 
 21     connect(ui->pushButton,&MyButton::clicked,
 22             [=]()
 23             {
 24                 qDebug() << QString::fromLocal8Bit("MyButton按钮信号被处理");
 25             }
 26             );
 27     //安装过滤器
 28     ui->label->installEventFilter(this);
 29 }
 30 
 31 MainWindow::~MainWindow()
 32 {
 33     delete ui;
 34 }
 35 void MainWindow::keyPressEvent(QKeyEvent *event)
 36 {
 37     qDebug() << QString::fromLocal8Bit("%1").arg(event->key());
 38 }
 39 void MainWindow::timerEvent(QTimerEvent *event)
 40 {
 41     static int sec = 0;
 42     ui->label->setText(QString("%1").arg(sec++));
 43 
 44     if(5 == sec)
 45     {
 46         this->killTimer(timerId);//关闭定时器
 47     }
 48 
 49 }
 50 
 51 void MainWindow::mousePressEvent(QMouseEvent *event)
 52 {
 53     qDebug() << QString::fromLocal8Bit("主窗口鼠标被按下");
 54 }
 55 
 56 void MainWindow::closeEvent(QCloseEvent *event)
 57 {
 58     int ret = QMessageBox::question(this,"question",QString::fromLocal8Bit("是否关闭窗口"));
 59     if(ret == QMessageBox::Yes)
 60     {
 61         //关闭窗口
 62         //处理关闭窗口事件,接收事件,事件就不会再往下传递
 63         event->accept();
 64     }
 65     else
 66     {
 67         //不关闭窗口
 68         //忽略事件,事件继续给父组件传递
 69         event->ignore();
 70     }
 71 }
 72 
 73 bool MainWindow::event(QEvent *event)
 74 {
 75     //事件分发
 76 //    switch(event->type())
 77 //    {
 78 //    case QEvent::Close:
 79 //        closeEvent(e);
 80 //        break;
 81 //    }
 82 
 83 
 84     if(event->type() == QEvent::Timer)
 85     {
 86         //将QEvent类型的对象event强制类型转化成QTimerEvent
 87         QTimerEvent * env = static_cast<QTimerEvent *>(event);
 88         timerEvent(env);
 89         //干掉定时器
 90         //如果返回true,事件停止传播
 91         //return true;
 92     }
 93     else
 94     {
 95         //事件继续传播
 96         return QWidget::event(event);
 97     }
 98 }
 99 
100 bool MainWindow::eventFilter(QObject *watched, QEvent *event)
101 {
102     if(watched == ui->label)
103     {
104         QMouseEvent *e = static_cast<QMouseEvent *>(event);
105         //判断事件
106         if(event->type()==QEvent::MouseMove)
107         {
108             ui->label->setText(QString("Mouse move:(%1,%2)").arg(e->x()).arg(e->y()));
109             return true;
110         }
111         else
112         {
113             return QWidget::eventFilter(watched,event);
114         }
115     }
116     else
117     {
118         return QWidget::eventFilter(watched,event);
119     }
120 }

mybutton.cpp

 1 #include "mybutton.h"
 2 #include <QDebug>
 3 #include <QMouseEvent>
 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() << QString::fromLocal8Bit("MyButton左键按下事件");
16         //事件接收后,就不会往下传递
17     }
18     else
19     {
20         //不作处理
21         QPushButton::mousePressEvent(e);
22     }
23     //事件忽略,会继续往下传递
24     QPushButton::mousePressEvent(e);
25 }

mylabel.cpp

 1 #include "mylabel.h"
 2 #include <QLabel>
 3 #include <QMouseEvent>//鼠标移动类
 4 #include <QDebug>
 5 
 6 MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
 7 {
 8     //在已进入控件,就开始追踪鼠标
 9     this->setMouseTracking(true);
10 }
11 
12 void MyLabel::mousePressEvent(QMouseEvent *ev)
13 {
14     int x = ev->x();
15     int y = ev->y();
16     //组串
17     QString text = QString("%1, %2").arg(x).arg(y);
18     this->setText(text);
19 
20     if(ev->button() == Qt::LeftButton)
21     {
22         qDebug() << QString::fromLocal8Bit("左键被按下");
23     }
24 }
25 void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
26 {
27 
28 }
29 void MyLabel::mouseMoveEvent(QMouseEvent *ev)
30 {
31 
32     int x = ev->x();
33     int y = ev->y();
34     //组串
35     QString text = QString("%1, %2").arg(x).arg(y);
36     this->setText(text);
37 }

smallwidget.cpp

 1 #include "smallwidget.h"
 2 #include <QSpinBox>//微调器
 3 #include <QSlider>//滑条
 4 #include <QHBoxLayout>//水平布局
 5 
 6 //自定义控件
 7 SmallWidget::SmallWidget(QWidget *parent) : QWidget(parent)
 8 {
 9     //声明并实例化控件
10     QSpinBox *spin = new QSpinBox(this);
11     QSlider *slider = new QSlider(Qt::Horizontal,this);
12     //设置布局
13     QHBoxLayout *hLayout = new QHBoxLayout;
14     //把控件添加到布局中
15     hLayout->addWidget(spin);
16     hLayout->addWidget(slider);
17 
18     setLayout(hLayout);
19     //valueChanged有重载,我们需要进行强制类型转换
20     connect(spin,static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
21             slider,&QSlider::setValue);
22 
23     connect(slider,&QSlider::sliderMoved,spin,&QSpinBox::setValue);
24 
25 }