Bootstrap

Qt 5.14.2 学习记录 —— 십구 事件


1、事件的概念

用户进行操作时会产生事件,事件可以关联处理函数。Qt封装了操作系统的事件机制,然后进一步封装得到了信号槽。事件是信号槽的底层机制。如果一个行为在Qt中没有对应的信号,那么就需要重写事件处理函数。事件处理函数都是虚函数,这样才能被重写。关于事件处理函数,看Qt文档。

在这里插入图片描述

QEvent就是事件,下面则是各种子事件。一部分常见事件:

在这里插入图片描述

事件分发,事件过滤属于Qt事件机制底层的逻辑,Qt对此给程序员提供了一些API可用。

事件分发重写event函数,能获取所有事件,但这样程序员有可能因此出现大错误。

事件过滤顾名思义,它是事件分发的其中一步操作,它针对的是某一类型的时间。

2、处理事件

处理也就是写处理函数。重写事件处理函数,也就利用了多态机制。创建子类,继承自Qt自带的类,在子类中重写父类的事件处理函数。

处理鼠标进入和鼠标离开事件

创建一个QWidget项目,放一个label到界面,接下来的程序在鼠标进入label区域时label文本显示鼠标进入,离开时显示鼠标离开。可以把label的frameShape属性改为Box,方便观察。

在当前项目下,创建一个C++类,继承自QLabel。

在这里插入图片描述

在label.h文件中引入QLabel头文件。

// label.h

#include <QWidget>
#include <QLabel>

class Label : public QLabel
{
    Q_OBJECT
public:
    Label(QWidget* parent);
    
    void enterEvent(QEvent* event);
    void leaveEvent(QEvent* event);
};


// label.cpp

#include "label.h"
#include <QDebug>

Label::Label(QWidget* parent) : QLabel(parent)
{

}

void Label::enterEvent(QEvent *event)
{
    (void) event;
    qDebug() << "enterEvent";
}

void Label::leaveEvent(QEvent *event)
{
    (void) event;
    qDebug() << "leaveEvent";
}

自定义的类是Label,但是界面中的label是QLabel的,所以现在这些处理函数还不起作用。解决办法就是右键label,提升为,按照自定义的类名和头文件名来填写,不选全局包含;点击添加,选中新添加的项,提升即可。

也可以不单独创建一个类,直接在Widget文件中写逻辑,那么此时就不是在label里事件发生时有处理,而是在Qt整个窗口内触发事件时都会被处理。

3、鼠标事件

在QWidget项目下,创建自定义类Label,还是用QLabel作为父类,在界面中创建一个label,提升为Label类的。

1、鼠标单击和双击

// label.h

#include <QWidget>
#include <QMouseEvent>

class Label : public QLabel
{
    Q_OBJECT
public:
    Label(QWidget* parent);
    
    // 鼠标左键右键滚轮触发
    void mousePressEvent(QMouseEvent* event);
};

// label.cpp

#include "label.h"
#include <QDebug>

Label::Label(QWidget* parent) : QLabel(parent)
{

}

void Label::mousePressEvent(QMouseEvent *event)
{
    // 两个方式
    // 以label对象左上角为原点
    qDebug() << event->x() << ", " << event->y();
    // 以屏幕左上角为原点
    qDebug() << event->globalX() << ", " << event->globalY();

    // Qt罗列了一些按键
    if (event->button() == Qt::LeftButton)
        qDebug() << "按下左键";
    else if (event->button() == Qt::RightButton)
        qDebug() << "按下右键";
}

抬起和双击

// label.h

void mouseReleaseEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);

// label.cpp

void Label::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        qDebug() << "释放左键";
    else if (event->button() == Qt::RightButton)
        qDebug() << "释放右键";
}

void Label::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        qDebug() << "双击左键";
    else if (event->button() == Qt::RightButton)
        qDebug() << "双击右键";
}

2、鼠标移动

// label.h

void mouseMoveEvent(QMouseEvent* event);

// label.cpp

void Label::mouseMoveEvent(QMouseEvent *event)
{
    qDebug() << event->x() << event->y();
}

但是实际运行后,不会打印,是因为鼠标只要一移动,会触发大量的鼠标移动事件,那么为了程序运行流畅,Qt默认不追踪这个事件,所以不会触发。解决办法就是解开这个限制即可

Label::Label(QWidget* parent) : QLabel(parent)
{
    this->setMouseTracking(true);
}

3、鼠标滚轮滚动

// label.h

// 通过delta()来获取这次鼠标滚轮滚了多远
#include <QWheelEvent>
public:
	void wheelEvent(QWheelEvent* event);
private:
	int total;

// label.cpp

Label::Label(QWidget* parent) : QLabel(parent)
{
    total = 0;
}

void Label::wheelEvent(QWheelEvent *event)
{
    //qDebug() << event->delta();
    // 计算总的滚动距离
    total += event->delta();
    qDebug() << total;
}

4、键盘事件

QWidget项目,这次不创建子类,直接在widget文件中写

// widget.h

#include <QWidget>
#include <QKeyEvent>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void keyPressEvent(QKeyEvent *event);

private:
    Ui::Widget *ui;
};

// widget.cpp
#include <QDebug>

void Widget::keyPressEvent(QKeyEvent *event)
{
    // 组合键, 用修饰符modifiers来获取
    // ctrl + a
    if (event->key() == Qt::Key_A && event->modifiers() == Qt::ControlModifier)
        qDebug() << "组合键ctrl + A";
    else if (event->key() == Qt::Key_A)
        qDebug() << "按下了A键";
    else qDebug() << event->key();
}

5、定时器事件

QTimer实现定时器功能,它的基础是QTimerEvent定时器事件。

QWidget项目,放一个LCD Numbers到界面中

// widget.h

void timerEvent(QTimerEvent* event);
int timerId;

// widget.cpp

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 开启定时器事件
    // timerId身份标识, 类似于Linux文件标识符
    timerId = this->startTimer(1000);
}

void Widget::timerEvent(QTimerEvent *event)
{
    // 如果一个程序存在多个定时器, 那么每个定时器都会触发timerEvent函数
    if (event->timerId() != this->timerId) return ;
    int value = ui->lcdNumber->intValue();
    if (value <= 0)
    {
        this->killTimer(this->timerId);
        return ;
    }
    value -= 1;
    ui->lcdNumber->display(value);
}

6、窗口移动和大小改变事件

// widget.h

#include <QWidget>
#include <QMoveEvent>
#include <QResizeEvent>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void moveEvent(QMoveEvent* event);
    void resizeEvent(QResizeEvent* event);

private:
    Ui::Widget *ui;
};

// widget.cpp

#include <QDebug>

void Widget::moveEvent(QMoveEvent *event)
{
    qDebug() << "之前的位置: " << event->oldPos();
    qDebug() << "现在的位置: " << event->pos();
}

void Widget::resizeEvent(QResizeEvent *event)
{
    qDebug() << event->size();
}

结束。

;