Qt 事件处理机制

1. 什么是事件

在这里插入图片描述

2. 如何处理事件

在这里插入图片描述

  • 所有事件对象类型都继承自抽象类QEvent, 鼠标事件QMouseEvent, 绘图事件QPaintEvent 都是 QEvent的子类
  • 因为QWidget 继承了基类 QObject, 又覆盖了event()虚函数,所以当事件发生时会调用QWidget类中的事件处理函数。
  • 所有的事件处理函数都是虚函数,可以被QWidget的子类覆盖。
  • 可以继承QWidget或者其子类,QDialog、QMainWindow。然后在自定义的窗口子类中重写事件处理函数,当相应的事件被触发时,会利用多态的语法机制,所执行到的事件处理函数将是子类中重写的版本。

3. 绘图事件

在这里插入图片描述

3.1 案例:图片浏览器

3.1.1 构建好框架

在这里插入图片描述
在这里插入图片描述
右键按钮,点击Go slots , 可以直接在头文件和源文件中生成槽函数
在这里插入图片描述
这里自动生成的槽函数名是不可以改的
在这里插入图片描述

3.1.2 导入图片资源

把图片文件夹拷贝到工程目录下
右键工程名然后添加文件,再选择Qt资源
在这里插入图片描述

给prefix为/, 再add所有图片
在这里插入图片描述

3.1.3 代码示例

showimagedialog.h

#ifndef SHOWIMAGEDIALOG_H
#define SHOWIMAGEDIALOG_H

#include <QDialog>
#include <QPainter> // 画家类, 绘制图形
#include <QImage>	// 图片类, 加载图片

namespace Ui {
class ShowImageDialog;
}

class ShowImageDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ShowImageDialog(QWidget *parent = 0);
    ~ShowImageDialog();

private slots:
	// prev按钮对应的槽函数
    void on_m_btnPrev_clicked();
	// next按钮对应的槽函数
    void on_m_btnNext_clicked();

private:
	// 绘图事件处理函数(), 虚函数
    void paintEvent(QPaintEvent *);
private:
    Ui::ShowImageDialog *ui;
    int m_index;	// 图片索引
};

#endif // SHOWIMAGEDIALOG_H

showimagedialog.cpp

#include "showimagedialog.h"
#include "ui_showimagedialog.h"

ShowImageDialog::ShowImageDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ShowImageDialog)
{
    ui->setupUi(this);
    m_index = 1;
}

ShowImageDialog::~ShowImageDialog()
{
    delete ui;
}

void ShowImageDialog::on_m_btnPrev_clicked()
{
    if(--m_index < 1){
        m_index = 3;
    }
    update(); // 触发绘图事件
}

void ShowImageDialog::on_m_btnNext_clicked()
{
    if(++m_index > 4){
        m_index = 1;
    }
    update(); // 触发绘图事件
}

// 事件处理函数
void ShowImageDialog::paintEvent(QPaintEvent *){
	// 1. 创建画家对象
    QPainter painter(this);
    // 2. 获取绘图矩形所在区域
    QRect rect = ui->frame->frameRect();
    // 3. 坐标平移,让rect和painter使用相同坐标系
    rect.translate(ui->frame->pos());
    // 4. 构建要绘制的图形对象
    QImage image(":/image/"+ QString::number(m_index)+".jpg");
    // 5. 使用painter将image图片画到rect
    painter.drawImage(rect, image);
}

在这里插入图片描述

4. 定时器事件

在这里插入图片描述

  • timeEvent是定时器事件处理函数,是一个虚函数,可以在QObject的子类中重写这个函数,达到自定义事件处理。为了满足虚函数覆盖条件,这个参数是不能少的。

4.1 案例:用 定时器事件 实现摇奖机

4.1.1 构建框架

在这里插入图片描述

4.1.2 导入图片资源

  • 直接把图片问价夹 image 放入工程目录下
    在这里插入图片描述

4.1.3 代码示例

erinedialog.h

#ifndef ERINEDIALOG_H
#define ERINEDIALOG_H

#include <QDialog>
#include <QTimer>
#include <QPainter>
#include <QDir>
#include <QTime>
#include <QVector>
#include <QImage>
#include <QDebug>

namespace Ui {
class ErineDialog;
}

class ErineDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ErineDialog(QWidget *parent = 0);
    ~ErineDialog();

private slots:
	// Start按钮对应的槽函数
    void on_pushButton_clicked();
private:
	// 加载图片到容器
    void loadPhotos(const QString& path);
    // 定时器事件处理函数(虚函数)
    void timerEvent(QTimerEvent *);
    // 绘图事件处理函数(虚函数)
    void paintEvent(QPaintEvent *);
private:
    Ui::ErineDialog *ui;
    QVector<QImage>m_vecPhotos; // 保存图片的容器
    int m_index;				// 图片载容器索引
    int m_timer;				// 定时器ID	
    bool isStarted;				// 标记:true正在摇奖,false停止摇奖
};

#endif // ERINEDIALOG_H

erinedialog.cpp

#include "erinedialog.h"
#include "ui_erinedialog.h"

ErineDialog::ErineDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ErineDialog)
{
    ui->setupUi(this);
    m_index = 0;
    isStarted = false;
    // 设置随机种子
    qsrand(QTime::currentTime().msec());
    // 加载"./image"中所有图片到容器
    loadPhotos("./image");
    qDebug() << "load image munber: " << m_vecPhotos.size();
}

ErineDialog::~ErineDialog()
{
    delete ui;
}
	
	// Start按钮对应的槽函数
	void ErineDialog::on_pushButton_clicked()
	{
	    if(isStarted == false){
	        isStarted = true;	// 摇奖开始
	        m_timer = startTimer(50);
	        ui->pushButton->setText("Stop");
	    }
	    else{
	        isStarted = false;	// 摇奖结束
	        killTimer(m_timer);
	        ui->pushButton->setText("Start");
	    }
	}


void ErineDialog::loadPhotos(const QString& path)
{
	// 遍历当前目录所有图片
	// 创建一个目录对象
    QDir dir(path);
    // 获取目录下所有文件名
    QStringList list1 = dir.entryList(QDir::Files);
    for(int i = 0; i < list1.size(); i++){
        QImage image(path + "/" + list1.at(i));
        m_vecPhotos << image;
    }
    // 递归遍历所有子目录中的图片
    // 获取目录下所有目录名,不要 . 和 ..
    QStringList list2 = dir.entryList(QDir::Dirs|QDir::NoDotAndDotDot);
    for(int i=0; i<list2.size(); i++){
        loadPhotos(path + "/" + list2.at(i));
    }
}
// 定时器事件处理函数(虚函数)
void ErineDialog::timerEvent(QTimerEvent *){
    m_index = qrand() % m_vecPhotos.size();
    update();
}
// 绘图事件处理函数(虚函数)
void ErineDialog::paintEvent(QPaintEvent *){
    // 1. 创建画家对象
    QPainter painter(this);
    // 2. 获取绘图矩形所在区域
    QRect rect = ui->frame->frameRect();
    // 3. 坐标平移,让rect和painter使用相同坐标系
    rect.translate(ui->frame->pos());
    // 4. 使用painter将image图片画到rect
    painter.drawImage(rect, m_vecPhotos[m_index]);
}

4.2 案例: 用 定时器信号 实现摇奖机

  • 把定时器的操作封装成成员函数的形式, 这种更灵活一点,每个Qtime对象就可以表达一个唯一的定时器,适合一个程序里有多个定时器的场景.

4.2.1 代码示例

erinedialog.h

#ifndef ERINEDIALOG_H
#define ERINEDIALOG_H

#include <QDialog>
#include <QTimer>
#include <QPainter>
#include <QDir>
#include <QTime>
#include <QVector>
#include <QImage>
#include <QDebug>

namespace Ui {
class ErineDialog;
}

class ErineDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ErineDialog(QWidget *parent = 0);
    ~ErineDialog();

private slots:
    // 定时器到时以后要执行的槽函数
    void onTimeout(void);
    // Start按钮对应的槽函数
    void on_pushButton_clicked();
private:
    // 加载图片到容器
    void loadPhotos(const QString& path);
//    // 定时器事件处理函数(虚函数)
//    void timerEvent(QTimerEvent *);
    // 绘图事件处理函数(虚函数)
    void paintEvent(QPaintEvent *);
private:
    Ui::ErineDialog *ui;
    QVector<QImage>m_vecPhotos; // 保存图片的容器
    int m_index;				// 图片载容器索引
//    int m_timer;				// 定时器ID
    QTimer m_timer;             // 定时器
    bool isStarted;				// 标记:true正在摇奖,false停止摇奖
};

#endif // ERINEDIALOG_H

erinedialog.cpp

#include "erinedialog.h"
#include "ui_erinedialog.h"

ErineDialog::ErineDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ErineDialog)
{
    ui->setupUi(this);
    m_index = 0;
    isStarted = false;
    // 设置随机种子
    qsrand(QTime::currentTime().msec());
    // 加载"./image"中所有图片到容器
    loadPhotos("./image");
    qDebug() << "load image munber: " << m_vecPhotos.size();
    //
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
}

ErineDialog::~ErineDialog()
{
    delete ui;
}

// Start按钮对应的槽函数
void ErineDialog::on_pushButton_clicked()
{
    if(isStarted == false){
        isStarted = true;	// 摇奖开始
//        m_timer = startTimer(50);
        m_timer.start(50);
        ui->pushButton->setText("Stop");
    }
    else{
        isStarted = false;	// 摇奖结束
//        killTimer(m_timer);
        m_timer.stop();
        ui->pushButton->setText("Start");
    }
}


void ErineDialog::loadPhotos(const QString& path)
{
    // 遍历当前目录所有图片
    // 创建一个目录对象
    QDir dir(path);
    // 获取目录下所有文件名
    QStringList list1 = dir.entryList(QDir::Files);
    for(int i = 0; i < list1.size(); i++){
        QImage image(path + "/" + list1.at(i));
        m_vecPhotos << image;
    }
    // 递归遍历所有子目录中的图片
    // 获取目录下所有目录名,不要 . 和 ..
    QStringList list2 = dir.entryList(QDir::Dirs|QDir::NoDotAndDotDot);
    for(int i=0; i<list2.size(); i++){
        loadPhotos(path + "/" + list2.at(i));
    }
}

//void ErineDialog::timerEvent(QTimerEvent *){
//    m_index = qrand() % m_vecPhotos.size();
//    update();
//}

// 定时器到时以后要执行的槽函数
void ErineDialog::onTimeout(void){
    m_index = qrand() % m_vecPhotos.size();
    update();
}

// 绘图事件处理函数(虚函数)
void ErineDialog::paintEvent(QPaintEvent *){
    // 1. 创建画家对象
    QPainter painter(this);
    // 2. 获取绘图矩形所在区域
    QRect rect = ui->frame->frameRect();
    // 3. 坐标平移,让rect和painter使用相同坐标系
    rect.translate(ui->frame->pos());
    // 4. 使用painter将image图片画到rect
    painter.drawImage(rect, m_vecPhotos[m_index]);
}

5. 鼠标事件

在这里插入图片描述

5.1 案例:实现鼠标左键拖拽label方块移动

在这里插入图片描述

5.1.1 构建框架

  • styleSheet选择label颜色
    在这里插入图片描述

5.1.2 代码示例

QRect(x, y, w, h), 描述一个矩形区域,有 位置 长宽
QPoint(x, y) 描述一个像素点,只有位置,支持加减法运算
QSize(w, h) 描述一个区域大小

mousedialog.h

#ifndef MOUSEDIALOG_H
#define MOUSEDIALOG_H

#include <QDialog>
#include <QMouseEvent> //鼠标事件类
namespace Ui {
class MouseDialog;
}

class MouseDialog : public QDialog
{
    Q_OBJECT

public:
    explicit MouseDialog(QWidget *parent = 0);
    ~MouseDialog();

private:
    // 按下
    void mousePressEvent(QMouseEvent *);
    // 抬起
    void mouseReleaseEvent(QMouseEvent *);
    // 移动
    void mouseMoveEvent(QMouseEvent *);

private:
    Ui::MouseDialog *ui;
    // 拖拽标记,鼠标再label内,并且左键点下时,标记为true
    bool m_drag;
    // m_pose 记录鼠标点下去那一刻,鼠标和labe的相对位置(这里控件的位置是控件左上角的位置)
    QPoint m_pos;
};

#endif // MOUSEDIALOG_H

mousedialog.cpp

#include "mousedialog.h"
#include "ui_mousedialog.h"

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

MouseDialog::~MouseDialog()
{
    delete ui;
}

// 按下, 通过event来知道当前是什么鼠标事件
void MouseDialog::mousePressEvent(QMouseEvent *event)
{
    // 是否鼠标左键	
    if(event->button() == Qt::LeftButton){
        // 获取label所在矩形区域
        // rect对象描述的位置的坐标圆心应该是label控件的左上角,
        // 而鼠标点击时所在的坐标圆心应该是窗口的左上角
        // 为了保持一致,让rect的和当前窗口保持相同的坐标系, 要对rect的坐标进行平移。
        QRect rect = ui->label->frameRect();
        // 坐标平移,让rect和鼠标使用相同的坐标系
        rect.translate(ui->label->pos());
        // 判断鼠标点击的位置是否在rect矩形区域当中
        if(rect.contains(event->pos())){
        	// 可以拖拽
            m_drag = true;
            // 获取鼠标和label的相对位置
            m_pos = ui->label->pos() - event->pos();
        }
    }
}

// 抬起
void MouseDialog::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton){
    	// 左键抬起就不能拖拽了
        m_drag = false;
    }
}
//
void MouseDialog::mouseMoveEvent(QMouseEvent *event)
{
    if(m_drag){
        // 计算label要移动的位置
        QPoint newPos = event->pos() + m_pos;
        QSize s1 = size(); // 父窗口位置
        QSize s2 = ui->label->size(); // 获取label位置
        //x∈(0, 窗口宽-label宽)
        if(newPos.x() < 0){
            newPos.setX(0);
        }
        else if(newPos.x() > s1.width() - s2.width()){
            newPos.setX(s1.width() - s2.width());
        }
        //y∈(0, 窗口高-label高)
        if(newPos.y() < 0){
            newPos.setY(0);
        }
        else if(newPos.y() > s1.height() - s2.height()){
            newPos.setY(s1.height() - s2.height());
        }
		// 移动label到新位置
        ui->label->move(newPos);
    }
}

6. 键盘事件

在这里插入图片描述

6.1 案例:键盘方向键控制label方块移动

在这里插入图片描述

6.1.1 代码示例

keyboarddialog.h

#ifndef KEYBOARDDIALOG_H
#define KEYBOARDDIALOG_H

#include <QDialog>
#include <QKeyEvent> // 键盘事件类

namespace Ui {
class KeyboardDialog;
}

class KeyboardDialog : public QDialog
{
    Q_OBJECT

public:
    explicit KeyboardDialog(QWidget *parent = 0);
    ~KeyboardDialog();
    // 按下
    void keyPressEvent(QKeyEvent*);


private:
    Ui::KeyboardDialog *ui;
};

#endif // KEYBOARDDIALOG_H

keyboarddialog.cpp

#include "keyboarddialog.h"
#include "ui_keyboarddialog.h"

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

KeyboardDialog::~KeyboardDialog()
{
    delete ui;
}

// 按下,通过event知道当前是那个按键
void KeyboardDialog::keyPressEvent(QKeyEvent* event){
    int x = ui->label->pos().x();
    int y = ui->label->pos().y();
    if(event->key() == Qt::Key_Up){
        ui->label->move(x, y-10);
    }
    else if(event->key() == Qt::Key_Down){
        ui->label->move(x, y+10);
    }
    else if(event->key() == Qt::Key_Left){
        ui->label->move(x-10, y);
    }
    else if(event->key() == Qt::Key_Right){
        ui->label->move(x+10, y);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值