Qt深入浅出(十一)事件处理机制与画笔

事件处理机制

​ 什么是事件,比如当我们打开一个窗口, 如果我们不动鼠标或者键盘, 那这个窗口就永远静静的躺着, 只有我们使用鼠标点击,或者键盘按下键的时候, 窗口才会有对应的反应。那么这个过程中就是用户向窗口发送了事件。

​ 在 Qt 的界面应用程序都是事件驱动的,程序的每个动作也都是由某个事件所触发。在Qt 中事件抽象成QEvent类。​ Qt中的事件有其对应的事件处理函数,事件先由QApplication::exec()消息循环接收,然后传递给对应窗口。

1 窗口事件处理函数event

​ Qt中所有的事件都是继承至QEvent, QEvent继承至QObject。每个窗口会收到各种事件,每个窗口也都有一个总的事件处理函数,用来处理事件或者进一步分派事件。

​ 窗口的这个事件处理函数为QWidget::event()。


   
   
  1. [virtual protected] bool QWidget::event(QEvent *event);

​ 需要注意它是一个保护权限的虚函数,那么我们一般通过派生QWidget对象,重写这个虚函数,来自定义事件处理。

Widget.h


   
   
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include <QDialog>
  5. class Widget : public QWidget
  6. {
  7.    Q_OBJECT
  8. public:
  9.    Widget(QWidget *parent = 0);
  10.    bool event(QEvent *event);
  11.    ~Widget();
  12. };
  13. #endif // WIDGET_H

Widget.cpp


   
   
  1. #include "widget.h"
  2. #include <QEvent>
  3. #include <QDebug>
  4. Widget::Widget(QWidget *parent)
  5.   : QWidget(parent)
  6. {
  7. }
  8. bool Widget::event(QEvent *ev)
  9. {
  10. /*只对鼠标点击事件进行处理,其它的调用基类的event函数*/
  11.    if(ev->type() == QEvent::MouseButtonPress ||
  12.            ev->type() == QEvent::MouseButtonDblClick)
  13.   {
  14.        qDebug() << __FUNCTION__ << endl;
  15.     //event->ignore(); //代表我无视这个消息
  16.     event->accept(); //代表我接收了这个消息
  17.     //return false; //这个消息我没有处理.
  18.        return true; //这个消息在我已经处理过了
  19.        
  20.   }
  21.    return QWidget::event(ev);
  22. }
  23. Widget::~Widget()
  24. {
  25. }

​ 需要注意的是:只有event->accept()以及return true时,才不往父窗口传递,否则都会传递。

2 鼠标事件

2.1 鼠标点击事件

​ Qt中的鼠标事件为QMouseEvent,  鼠标按下事件对应的事件处理函数为:


   
   
  1. [virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);

​ 这个是QWidget里面实现的鼠标按下事件处理函数, 它也是保护类型的虚函数,那么,我们一般是去覆写这个虚函数来实现我们想要的处理函数.

  • 示例代码:

    Widget.h

    
         
         
    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include <QWidget>
    4. #include <QDialog>
    5. class Widget : public QWidget
    6. {
    7.    Q_OBJECT
    8. public:
    9.    Widget(QWidget *parent = 0);
    10.    void mousePressEvent(QMouseEvent*); //重写虚函数
    11.    ~Widget();
    12. };
    13. #endif // WIDGET_H

    Widget.cpp

    
         
         
    1. #include "widget.h"
    2. #include <QMouseEvent>
    3. #include <QDebug>
    4. Widget::Widget(QWidget *parent)
    5.   : QWidget(parent)
    6. {
    7. }
    8. void Widget::mousePressEvent(QMouseEvent* ev)
    9. {
    10.    qDebug() << __FUNCTION__ << endl;
    11.    if(ev->button() == Qt::LeftButton)
    12.       {
    13.        qDebug() << "left button press" << endl;
    14.   }
    15.    else if(ev->button() == Qt::RightButton)
    16.       {
    17.        qDebug() << "right button press" << endl;
    18.   }
    19.    else if(ev->button() == Qt::MidButton)
    20.       {
    21.        qDebug() << "mid button press" << endl;
    22.   }
    23.    qDebug() << ev->pos() << endl;
    24. }

    普通鼠标有左右中三个键,可通过QMouseEvent::button来判断是哪个键:

    
         
         
    1. Qt::MouseButton QMouseEvent::button() const;
    2. /*
    3. *返回值为枚举类型:Qt::LeftButton 左键、Qt::RightButton 右键、Qt::MidButton 中键、Qt::NoButton没有键按下。
    4. */

    鼠标的点击位置可以通过QMouseEvent::pos来获取:

    
         
         
    1. QPoint QMouseEvent::pos() const

2.2 鼠标释放事件

​ Qt中的鼠标释放事件也是一个QMouseEvent对象,  鼠标按下事件对应的事件处理函数为:


   
   
  1. [virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);

​ 使用方式,与鼠标点击事件类似,不再复述。

2.3 鼠标移动事件

​ Qt中的鼠标移动事件为也是一个QMouseEvent对象,  鼠标移动事件对应的事件处理函数为:


   
   
  1. [virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
  • 默认情况下, 鼠标移动事件触发条件必须是鼠标至少一个按键按下状态, 再去移动鼠标,但是鼠标QMouseEvent::Button()还是Qt::NoButton。


   
   
  1. void Widget::mouseMoveEvent(QMouseEvent*  ev)
  2. {
  3. qDebug() << ev->pos() << endl;
  4.    qDebug() << ev->button() << endl; //不能用这个判断到底是哪个键按下
  5. }

  • 如果想要不按下鼠标按键,就触发鼠标移动事件可以调用QWidget::setMouseTracking


   
   
  1. void QWidget::setMouseTracking(bool enable);

​ 你可以参数在Widget中构造函数中调用该函数,enable参数为true代表开启鼠标跟踪,那么鼠标不需要按下任何键都能触发鼠标移动事件。

2.4 鼠标滚轮事件

​ 鼠标滚轮事件为QWheelEvent, 继承自QObject,对应的事件处理函数为:


   
   
  1. [virtual protected] void QWidget::wheelEvent(QWheelEvent *event);

​ 例如:


   
   
  1. void Widget::wheelEvent(QWheelEvent* ev)
  2. {
  3.    qDebug() << ev->angleDelta() << endl;
  4. }

2.5 其它鼠标事件

  • 鼠标双击事件对应的事件处理函数


   
   
  1. [virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event)
  • 鼠标进入窗口对应的事件处理函数


   
   
  1. [virtual protected] void QWidget::enterEvent(QEvent *event)
  • 鼠标离开窗口对应的事件处理函数


   
   
  1. [virtual protected] void QWidget::leaveEvent(QEvent *event)

3 键盘事件

​ 在Qt中键盘事件是QKeyEvent,继承自QObject。

3.1 键盘按下事件

​ 按下的事件对应的处理函数为:


   
   
  1. [virtual protected] void QWidget::keyPressEvent(QKeyEvent *event)

​ 例如:

​ widget.h


   
   
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. class Widget : public QWidget
  5. {
  6.    Q_OBJECT
  7. public:
  8.    Widget(QWidget *parent = 0);
  9.    ~Widget();
  10.    void keyPressEvent(QKeyEvent* ev);
  11. };
  12. #endif // WIDGET_H

​ widget.cpp


   
   
  1. #include "widget.h"
  2. #include <QKeyEvent>
  3. #include <QDebug>
  4. Widget::Widget(QWidget *parent)
  5.   : QWidget(parent)
  6. {
  7.      
  8. }
  9. void Widget::keyPressEvent(QKeyEvent* ev)
  10. {
  11.    switch(ev->key())
  12.   {
  13.        case Qt::Key_A:
  14.            qDebug() << "key a pressed" << endl;
  15.            break;
  16.        case Qt::Key_B:
  17.            qDebug() << "key b pressed" << endl;
  18.            break;
  19.        default:
  20.            break;
  21.   }
  22. }

​ 通过QKeyEvent::key()函数获取到具体按下的某个键值:


   
   
  1. int QKeyEvent::key() const;
  2. /*
  3. * 返回值的具体键值可以查看Qt帮助文档
  4. */

3.2 键盘释放事件

​ 鼠标按下事件对应的事件处理函数为:


   
   
  1. [virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event);

​ 使用方法与键盘按下事件类似,不再复述。

3.3 设置焦点窗口

​ 我们知道键盘事件是发送给当前激活状态的窗口,当只有一个窗口的时候,键盘事件定然是传递给这个窗口的。但是如果这个窗口上面有多个子窗口的时候,我们需要指定一个焦点窗口用来接收键盘事件。

​ 焦点窗口可以通过tab键、鼠标按键点击等方式切换,也可以通过代码的方式来切换:


   
   
  1. void QWidget::setFocus(Qt::FocusReason reason);
  2. /*
  3. *Qt::FocusReason reason 告诉Qt焦点切换的原因,一般填个Qt::OtherFocusReason即可
  4. */

3.4 判断按键大小写

​ 通过QKeyEvent的text()函数:


   
   
  1. QString QKeyEvent::text() const
  2. /*
  3. *返回一个QString类型,这个是键盘产生的字符串
  4. */

​ 那么我们可以这样判断大小写:


   
   
  1. void Widget::keyPressEvent(QKeyEvent* e)
  2. {
  3.      if( e->text() >= "A" && e->text() <= "Z" )
  4.     {
  5.          qDebug() << "uppercase" << endl;
  6.     }
  7.      else if( e->text() >= "a" && e->text() <= "z" )
  8.     {
  9.          qDebug() << "lowcase" << endl;
  10.     }
  11. }

4 事件过滤器

​ 上面我们看到,事件都只能在自己类中处理,如果其它对象想知道某个窗口的事件,可以通过给窗口安装事件过滤器,对应的函数为:


   
   
  1. void QObject::installEventFilter(QObject *filterObj);
  2. /*
  3. *这个函数谁调用,谁被监控
  4. *QObject *filterObj 监控者
  5. */

​ 事件过滤器的处理函数为:


   
   
  1. [virtual] bool QObject::eventFilter(QObject *watched, QEvent *event);
  2. /*
  3. * QObject *watched,被监控对象
  4. * QEvent *event 具体哪些事件
  5. * 返回值 true 代表过滤事件, false 代表不过滤
  6. */

​ 例如:

Widget.h


   
   
  1. Widget.h
  2. #ifndef WIDGET_H
  3. #define WIDGET_H
  4. #include <QWidget>
  5. class Widget : public QWidget
  6. {
  7.    Q_OBJECT
  8. public:
  9.    Widget(QWidget *parent = 0);
  10.    ~Widget();
  11.    bool eventFilter(QObject *watched, QEvent *event);
  12. };
  13. #endif // WIDGET_H

Widget.c


   
   
  1. #include "widget.h"
  2. #include <QDebug>
  3. #include <QHBoxLayout>
  4. #include <QPushButton>
  5. #include <QEvent>
  6. #include <mybutton.h>
  7. Widget::Widget(QWidget *parent) : QWidget(parent)
  8. {
  9.    this->resize(400, 300);
  10.    QHBoxLayout* hBox = new QHBoxLayout;
  11.    MyButton* pb = new MyButton("pb");
  12.    hBox->addWidget(pb);
  13.    this->setLayout(hBox);
  14.    pb->installEventFilter(this); //给pb对象安装事件过滤器
  15. }
  16. bool Widget::eventFilter(QObject *watched, QEvent *event)
  17. {
  18.    if(event->type() == QEvent::MouseButtonPress
  19.            || event->type() ==  QEvent::MouseButtonDblClick)
  20.   {
  21.        qDebug() << "eventFilter" << endl;
  22.        return true; //过滤掉点击事件
  23.   }
  24.    return QWidget::eventFilter(watched, event);
  25. }
  26. Widget::~Widget()
  27. {
  28.  
  29. }

mybutton.h


   
   
  1. #ifndef MYBUTTON_H
  2. #define MYBUTTON_H
  3. #include <QPushButton>
  4. #include <QKeyEvent>
  5. class MyButton : public QPushButton
  6. {
  7. public:
  8.    MyButton(const QString & text, QWidget * parent = 0);
  9.    void mousePressEvent(QMouseEvent* ev);
  10. };
  11. #endif // MYBUTTON_H

mybutton.cpp


   
   
  1. #include "mybutton.h"
  2. #include <QDebug>
  3. MyButton::MyButton(const QString & text, QWidget * parent)
  4.   : QPushButton(text, parent)
  5. {
  6. }
  7. void MyButton::mousePressEvent(QMouseEvent* ev)
  8. {
  9.    static int i = 0;
  10.    qDebug() << __FUNCTION__ << i++ << endl;
  11. }

5 事件传递过程

​ QApplication的exec从桌面系统中获取事件,然后分发给对应的窗口事件处理函数bool event(QEvent),然后该函数再分发给具体的某个事件处理函数,如鼠标事件、键盘事件等。

6 事件处理函数是如何被调用的

​   我们重定义事件处理函数后,并没有显示的调用,那为什么,事件能够进入到我们的事件处理函数中呢?

  

   这是其实是因为,我们的事件处理函数都是虚函数,我们override了基类的事件处理函数, 而Qt在底层实现了使用基类的指针指向我们的派生类对象, 并且在事件触发时,使用基类指针来调用我们override的事件处理函数。

​ 简单来说,就是应用了c++多态。

7 绘图事件

​ 在桌面系统中所有的窗口都是绘制出来的, 那么在窗口形态发生变化时, 就会产生一个绘画事件, 在Qt中叫作QPaintEvent, 其对应的事件处理函数是:


   
   
  1. [virtual protected] void QWidget::paintEvent(QPaintEvent *event);

7.1 QPainter画家

​ 我们知道画图一般需要一个画家来画,画家想要画图首先要给它一张画纸,然后画家需要一把画笔来画轮廓, 一把笔刷来填充颜色。

​ 那么在Qt中我们的画家叫做QPainter, 画纸可以是任何QPaintDevice的派生类对象,

​ QWidget本身也继承至QPaintDevice,所以也能用来当作画纸。继承至QPaintDevice的类还有QPixmap、QPicture、QImage。

    QPainter有QPen画笔, QBrush笔刷。

    注意:当画纸为QWidget的派生类对象时, QPainter只能在QWidget的painteEvent事件处理函数中使用.

 

   

7.2 在窗口上绘图

​ 设置画笔笔刷, 并且绘制出线,文本,矩形等等:

​ widet.h


   
   
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. class Widget : public QWidget
  5. {
  6.    Q_OBJECT
  7. public:
  8.    Widget(QWidget *parent = 0);
  9.    ~Widget();
  10.    void paintEvent(QPaintEvent* event);
  11. };
  12. #endif // WIDGET_H

​ widget.cpp


   
   
  1. #include "widget.h"
  2. #include <QDebug>
  3. #include <QPaintEvent>
  4. #include <QPainter>
  5. Widget::Widget(QWidget *parent) : QWidget(parent)
  6. {
  7.    this->resize(400, 300);
  8.    QFont font = this->font(); //这个可以用来设置字体的样式
  9.    font.setPixelSize(10);
  10.    this->setFont(font);
  11. }
  12. void Widget::paintEvent(QPaintEvent* event)
  13. {
  14.    qDebug() << "paintEvent" << endl;
  15.    QPainter painter;
  16.    painter.begin(this);
  17.    painter.setPen(Qt::red);
  18. // painter.setPen(QPen(QBrush(Qt::red), 20, Qt::DashLine)); //画笔的高级设置
  19.    painter.setBrush(Qt::blue); //设置
  20. // painter.setBrush(QBrush(Qt::blue, Qt::Dense3Pattern); //笔刷的高级设置
  21.    painter.drawText(100, 100, "huangwl"); //画字
  22.    painter.drawLine(0, 0, 100, 100); //画线
  23. painter.drawRect(100, 100, 200, 200); //画矩形
  24. painter.drawEllipse(100, 0, 100, 200); //画椭圆
  25.    painter.end();
  26. }
  27. Widget::~Widget()
  28. {
  29. }

7.3 转变窗口坐标系

  • 更改绘图坐标系的坐标原点


   
   
  1. void QPainter::translate(constQPointF &offset)

 

  • 更改绘图坐标系的角度


   
   
  1. void QPainter::rotate(qreal angle)

 

  • 缩放坐标系单位


   
   
  1. void QPainter::scale(qreal sx, qreal sy)

​ Widget.h


   
   
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. class Widget : public QWidget
  5. {
  6.    Q_OBJECT
  7. public:
  8.    Widget(QWidget *parent = 0);
  9.    ~Widget();
  10.    void paintEvent(QPaintEvent* event);
  11. };
  12. #endif // WIDGET_H

​ Widget.cpp


   
   
  1. #include "widget.h"
  2. #include <QDebug>
  3. #include <QPaintEvent>
  4. #include <QPainter>
  5. Widget::Widget(QWidget *parent) : QWidget(parent)
  6. {
  7.    this->resize(600, 480);
  8. }
  9. void Widget::paintEvent(QPaintEvent* event)
  10. {
  11.    qDebug() << "paintEvent" << endl;
  12.    QPainter painter;
  13.    painter.begin(this);
  14.    painter.setPen(Qt::red);
  15.    painter.setBrush(Qt::blue);
  16.    painter.translate(100, 0); //坐标原点转换到(100,0)位置
  17.    painter.rotate(45);  //顺时针旋转45度
  18.    painter.scale(0.5, 0.5); //坐标系的单位变为原来的0.5
  19.    painter.drawRect(0, 0, 100, 100); //画矩形
  20.    painter.end();
  21. }
  22. Widget::~Widget()
  23. {
  24. }

7.4 保存与还原QPainter设置

  • 保存之前QPainter所有的设置


   
   
  1. void QPainter::save()

  • 还原之前QPainter的设置.


   
   
  1. void QPainter::restore()

7.5 绘制事件触发的时机

  1. 窗口失去焦点, 窗口缩放.

  2. 窗口被覆盖, 或者窗口被显示出来

  3. 在代码中可以用函数repaint()和update()

  • repaint() 立即重绘,并且可以指定重绘区域

    
         
         
    1. [slot] void QWidget::repaint()

  • update()  不是马上重绘,加入到消息队列中,由消息循环来调度,有时多个update()会合并成一个重绘事件。

    
         
         
    1. [slot] void QWidget::update()

​ 例如:

​ widget.h


   
   
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. class Widget : public QWidget
  5. {
  6.    Q_OBJECT
  7. public:
  8.    Widget(QWidget *parent = 0);
  9.    ~Widget();
  10.    void paintEvent(QPaintEvent* event);
  11. public slots:
  12.    void doRepaint();
  13.    void doUpdate();
  14. private:
  15.    QColor _color;
  16. };
  17. #endif // WIDGET_H

​ Widget.cpp


   
   
  1. #include "widget.h"
  2. #include <QDebug>
  3. #include <QPaintEvent>
  4. #include <QPainter>
  5. #include <QDialog>
  6. #include <QPushButton>
  7. #include <QHBoxLayout>
  8. Widget::Widget(QWidget *parent) : QWidget(parent), _color(Qt::red)
  9. {
  10.    QDialog * dialog = new QDialog(this);
  11.    QPushButton * pb0 = new QPushButton("update", this);
  12.    QPushButton * pb1 = new QPushButton("repaint", this);
  13.    QHBoxLayout * hBox = new QHBoxLayout(this);
  14.    hBox->addWidget(pb0);
  15.    hBox->addWidget(pb1);
  16.    dialog->setLayout(hBox);
  17.    this->resize(600, 480);
  18.    dialog->resize(400, 300);
  19.    dialog->show();
  20.    connect(pb0, SIGNAL(clicked()), this, SLOT(doUpdate()));
  21.    connect(pb1, SIGNAL(clicked()), this, SLOT(doRepaint()));
  22. }
  23. void Widget::doUpdate()
  24. {
  25.    _color.setRgb(0, 0, 255);
  26.    this->update();
  27. }
  28. void Widget::doRepaint()
发布了207 篇原创文章 · 获赞 66 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/qq_38025219/article/details/104956527