1.Qt的键盘事件:
void keyReleaseEvent(QKeyEvent *); //按键释放事件
void keyPressEvent(QKeyEvent *); //按键按下事件
2.Qt可以响应的按键事件:
单个按键、组合键(例如:crtl+c)
注意:
- 当单个按键按下释放后,按键事件还会停顿一下,所以我们不能在按键事件里面直接处理逻辑。--------借助定时器。
- 如果我们想响应多个按键同时按下的效果,单凭这键盘事件是没办法实现的。-------借助容器
3.借助定时器和容器,实现多个按键按下的同时响应
-
以WASD四按键为例,实现QLabel控件的丝滑移动。首先准备一个容器用于存放按下按键的key
QList<int> keys; //头文件中添加成员
-
定义一个定时器用于遍历容器
QTimer* keyRespondTimer; //头文件中添加成员 keyRespondTimer = new QTimer(this); //构造函数中创建定时器对象,并连接信号槽 connect(keyRespondTimer, &QTimer::timeout, this, &Widget::slotTimeOut);
-
处理按键按下事件
void Widget::keyPressEvent(QKeyEvent *event){ if(!event->isAutoRepeat()) //判断如果不是长按时自动触发的按下,就将key值加入容器 keys.append(event->key()); if(!keyRespondTimer->isActive()) //如果定时器不在运行,就启动一下 keyRespondTimer->start(4); }
-
处理按键释放事件
void Widget::keyReleaseEvent(QKeyEvent *event){ if(!event->isAutoRepeat()) //判断如果不是长按时自动触发的释放,就将key值从容器中删除 keys.removeAll(event->key()); if(keys.isEmpty()) //容器空了,关闭定时器 keyRespondTimer->stop(); }
注意点:if(!event->isAutoRepeat())
,增加这一判断是为了避免在按键长按期间一直触发按键事件,对同一键值一直做容器追加和删除操作。这显然是没必要的耗时操作,我们只需要保证在长按期间,那个按键的键值key一直在容器里就好了。
-
遍历key值容器,实现多个按键的同时响应
void Widget::slotTimeOut(){ foreach (int key, keys) { switch (key) { case Qt::Key_W: ui->label->move(ui->label->x(), ui->label->y() - 1); break; case Qt::Key_A: ui->label->move(ui->label->x() - 1, ui->label->y()); break; case Qt::Key_S: ui->label->move(ui->label->x(), ui->label->y() + 1); break; case Qt::Key_D: ui->label->move(ui->label->x() + 1, ui->label->y()); break; default: break; } } }
4.效果图
5.源码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
#include <QKeyEvent>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
private:
Ui::Widget *ui;
QList<int> keys;
QTimer* keyRespondTimer;
void slotTimeOut();
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){
ui->setupUi(this);
keyRespondTimer = new QTimer(this);
connect(keyRespondTimer, &QTimer::timeout, this, &Widget::slotTimeOut);
}
Widget::~Widget(){
delete keyRespondTimer;
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event){
if(!event->isAutoRepeat()) //判断如果不是长按时自动触发的按下,就将key值加入容器
keys.append(event->key());
if(!keyRespondTimer->isActive()) //如果定时器不在运行,就启动一下
keyRespondTimer->start(4);
}
void Widget::keyReleaseEvent(QKeyEvent *event){
if(!event->isAutoRepeat()) //判断如果不是长按时自动触发的释放,就将key值从容器中删除
keys.removeAll(event->key());
if(keys.isEmpty()) //容器空了,关闭定时器
keyRespondTimer->stop();
}
void Widget::slotTimeOut(){
foreach (int key, keys) {
switch (key) {
case Qt::Key_W:
ui->label->move(ui->label->x(), ui->label->y() - 1);
break;
case Qt::Key_A:
ui->label->move(ui->label->x() - 1, ui->label->y());
break;
case Qt::Key_S:
ui->label->move(ui->label->x(), ui->label->y() + 1);
break;
case Qt::Key_D:
ui->label->move(ui->label->x() + 1, ui->label->y());
break;
default:
break;
}
}
}