Qt - 鼠标事件&键盘事件&定时器事件

前言

该篇作为学习Qt事件系统的前导篇,主要介绍最常见的三个事件的使用。

鼠标事件

参考文档:Drag and Drop
参考例程:Draggable Text ExampleDraggable Icons Example

鼠标移动事件只会在按下鼠标按键的情况下才会发生,除非通过显式调用 QWidget::setMouseTracking() 函数来开启鼠标轨迹,这种情况下只要鼠标指针在移动,就会产生一系列的QT鼠标事件。

在这里插入图片描述

.h

#include <QMimeData>
#include <QDrag>
#include <QMouseEvent>

mousePressEvent

当鼠标按下时,该函数会被调用,这里我们获取控件的位置,并创建 QDrag 对象,QDragexec前,一定要设置 QMimeData,否则会弹出警告“QDrag: No mimedata set before starting the drag”。并且不会开始拖拽操作。

QMimeData 可用来保存拖拽操作附带的信息,比如字符串、文件或者图片,同时也可以用来验证其所保存的信息格式,并以此来判断是否可接收。未来再补充 QMimeData 的用法。

另外要注意,在 windows 下,QDrag::exec() 是个同步操作,要在 exec() 返回后,才会继续执行下面的代码。

void Mouseevent::mousePressEvent(QMouseEvent *event)
{
    //childAt 返回QWidget中的x,y处的小部件,若无则返回null
    QLabel *child = qobject_cast<QLabel*>(childAt(event->pos()));
    if (!child)
        return;
    //移动偏移量:起始鼠标位 - 控制原位置
    QPoint hotSpot = event->pos() - child->pos();

    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;

    mimeData->setText(child->text());
    mimeData->setData("hotSpot",
                      QByteArray::number(hotSpot.x()) + ' ' + QByteArray::number(hotSpot.y()));
    drag->setMimeData(mimeData);

    // QDrag::exec()是个同步操作,要在exec()返回后,才会继续执行下面的代码
    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
    //等待鼠标松开后的返回
    if (dropAction == Qt::MoveAction)
        child->close();
}

dragEnterEvent

首先,当需要一个控件接收 dragdrop,就要先调用控件的方法:setAcceptDrops(true)

当鼠标拖拽进入控件时,会触发 dragEnterEvent,如果不做处理,后续将不会接收到 dragMoveEvent 事件和dragLeaveEvent 事件。在 dragEnterEvent 事件中,如果调用了 QDragMoveEvent::accept() 函数,后续将可以收到dragMoveEvent 事件和 dragLeaveEvent 事件。而如果调用 QDragMoveEvent::ignore() 函数,效果相当于不处理,不会接收后续事件。

setAcceptDrops(true);

void Mouseevent::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasText()) {
        if (event->source() == this) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else {
        //忽略
        event->ignore();
    }
}

dropEvent

dragaccept 状态,然后释放鼠标,就会产生 dropEvent。我们可以在这个事件里处理本次拖拽附带的Mime 信息。

void Mouseevent::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasText()) {
        const QMimeData *mime = event->mimeData();
        QString piece = mime->text();
        QPoint position = event->pos();
        QPoint hotSpot;

        //获取位置偏移量
        QByteArrayList hotSpotPos = mime->data("hotSpot").split(' ');
        if (hotSpotPos.size() == 2) {
            hotSpot.setX(hotSpotPos.first().toInt());
            hotSpot.setY(hotSpotPos.last().toInt());
        }

        QLabel *newLabel = new QLabel(piece, this);
        //设置位置:放下时鼠标位 - 偏移量(起始鼠标位-控件原位置)
        newLabel->move(position - hotSpot);
        newLabel->show();

        event->accept();

    }
}

键盘事件

在这里插入图片描述

.h

#include <QKeyEvent>

keyPressEvent

当我们按下按键时,会回调该函数。可在此函数中直接实现按键检测后的逻辑。

void Keyboardevent::keyPressEvent(QKeyEvent *event){
    qDebug() << "keyPressEvent" << event->key();
    
    int x = ui->pushButton->x();
    int y = ui->pushButton->y();

    if(event->key() == Qt::Key_Left)
    {
        x = x - STEPPING;
    }
    if(event->key() == Qt::Key_Right)
    {
        x = x + STEPPING;
    }
    if(event->key() == Qt::Key_Up)
    {
        y = y - STEPPING;
    }
    if(event->key() == Qt::Key_Down)
    {
        y = y + STEPPING;
    }

    ui->pushButton->move(x,y);
}

keyReleaseEvent

当我们松开按键时,会回调该函数。可在此函数中直接实现按键检测后的逻辑。

void Keyboardevent::keyReleaseEvent(QKeyEvent* event){

    qDebug() << "keyReleaseEvent" << event->key();

    int x = ui->pushButton->x();
    int y = ui->pushButton->y();

    if(event->key() == Qt::Key_Left)
    {
        x = x - STEPPING;
    }
    if(event->key() == Qt::Key_Right)
    {
        x = x + STEPPING;
    }
    if(event->key() == Qt::Key_Up)
    {
        y = y - STEPPING;
    }
    if(event->key() == Qt::Key_Down)
    {
        y = y + STEPPING;
    }

    ui->pushButton->move(x,y);
}

焦点问题

测试时发现,若检测方向键,焦点很容易被UI控件抢占,导致 ```keyPressEvent`` 未被调用。

    //解决方向键焦点问题,否则方向键不触发keyPressEvent
    ui->pushButton->setFocusPolicy(Qt::NoFocus);

多按键检测

原理

上面的代码只能实现单按键的检测。若先按下 再按下 ,其先调用的 keyPressEvent(左),再调用keyPressEvent(上),此后不断调用 keyReleaseEvent(上)keyPressEvent(上),直到按键 退出,调用keyReleaseEvent(左)。发现了吗,我们可以通过记录 keyPressEvent()keyReleaseEvent() 的执行过程去判断当前那些按键被按下了。

实现

QList<int> mList;

void Keyboardevent::keyPressEvent(QKeyEvent *event){
    qDebug() << "keyPressEvent" << event->key();

    mList.push_back(event->key());

    movePushButton();
}

void Keyboardevent::keyReleaseEvent(QKeyEvent* event){

    qDebug() << "keyReleaseEvent" << event->key();

    for(int i = 0;i < mList.count(); i++){
        if(mList[i] == event->key()){
            mList.removeAt(i);
        }
    }
}

void Keyboardevent::movePushButton(){

    int x = ui->pushButton->x();
    int y = ui->pushButton->y();

    for(int i = 0;i < mList.count(); i++){

        if(mList[i] == Qt::Key_Left)
        {
            x = x - STEPPING;
        }
        if(mList[i] == Qt::Key_Right)
        {
            x = x + STEPPING;
        }
        if(mList[i] == Qt::Key_Up)
        {
            y = y - STEPPING;
        }
        if(mList[i] == Qt::Key_Down)
        {
            y = y + STEPPING;
        }
    }

    ui->pushButton->move(x,y);
}

改进

  • QList 存储可更换为 HashMap 等以提高效率;由于这里仅需要保存四个按键,这里还可以使用一个int keystate的位保存按键状态。
  • 由于我们是在按下按键时进行移动操作,既按下按键上,再按住按键左,先释放按键左,则按键上的keyPressEvent 将不会再此被调用。解决方法也很简单,keyPressEvent()keyReleaseEvent() 单纯用来维护按键状态,使用定时器进行定时检查,在其中进行移动操作。

定时器事件

与上面的两个事件不同,这个事件的消息是系统产生的。

#include "Timeoutevent.h"
#include "ui_Timeoutevent.h"
#include <QDebug>
#include <QTime>
#include <ctime>

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

    mtimer = new QTimer;

    connect(mtimer,&QTimer::timeout,this,&Timeoutevent::slot_timeout);
}

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

void Timeoutevent:: slot_timeout(){
    qDebug() << "slot_timeout";

    QTime time;
    time= QTime::currentTime();
    qsrand(time.msec()+time.second()*1000);
    int n = qrand() % 5;

    qDebug() << "伪随机数 qrand()" << n;
}

void Timeoutevent::on_pushButton_clicked()
{
    if(!mtimer->isActive()){
        mtimer->start(1000);
    }
}

void Timeoutevent::on_pushButton_2_clicked()
{
    mtimer->stop();
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值