Qt事件系统:键盘事件

一、简介

QKeyEvent 类用来描述一个键盘事件。当键盘按键被按下或者被释放时,键盘事件便会被发送给拥有键盘输人焦点的部件。

QKeyEvent 的 key() 函数可以获取具体的按键,对于 Qt 中给定的所有按键,可以在帮助中查看 Qt: :Key 关键字。需要特别说明的是,回车键在这里是 Qt::Key_Return;键盘上的一些修饰键,比如 Ctrl 和 Shift 等, 这里需要使用 QKeyEvent 的 modifiers() 函数来获取,可以在帮助中使用 Qt:: KeyboardModifier 关键字来査看所有的修饰键。

QKeyEvent 有两个键盘事件成员函数,在头文件.h中进行声明:

#include <QKeyEvent>

protected:
    void keyPressEvent(QKeyEvent *event); //键盘按下事件
    void keyReleaseEvent(QKeyEvent *event); //键盘松开事件

二、常用操作

下面是些常用操作:

// 键盘按下事件
void Widget::keyPressEvent(QKeyEvent * event)
{
    // 普通键
    switch (event->key())
    {
        // ESC键
        case Qt::Key_Escape:
            qDebug() <<"ESC";
        break;
        // 回车键
        case Qt::Key_Return:
            qDebug() <<"Enter";
        break;
        // F1键
        case Qt::Key_F1:
            qDebug() <<"F1";
        break;
    }

    // 两键组合
    if(event->modifiers() == Qt::ControlModifier) { // 如果按下了CTRL键
        if(event->key() == Qt::Key_M){
           qDebug()<<"CTRL + M";
        }
    }

    if(event->modifiers() == Qt::AltModifier) { // 如果按下了ALT键
       if(event->key() == Qt::Key_M)
           qDebug()<<"ALT + M";
    }

    if(event->modifiers() == Qt::ShiftModifier){ // 如果按下了Shift键
           if(event->key() == Qt::Key_M)
               qDebug()<<"Shift + M";
    }

    // 三键组合Shift + Ctrl + A的实现
    if (event->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier) && event->key() == Qt::Key_A) {
        qDebug() << "CTRL + Shift + A";
    }
}

// 键盘释放事件
void Widget::keyReleaseEvent(QKeyEvent *event)
{
    // 方向UP键
    if(event->key() == Qt::Key_Up)
    {
        qDebug() << "release: "<< "up";
    }
}

分别按下 ESC、Enter、CTRL + M、ALT + M 等键,“应用程序输出”窗口输出如下:

ESC
Enter
CTRL + M
ALT + M
release:  up

下图列出了所有的修饰键:

三、按键与自动重复

自动重复是指按下键盘上的键(修饰键除外)不放时,会不断重复的发送键按下事件,Qt 默认是启用自动重复的,若要实现类似按键 A+D 之类的快捷键,就需要关闭自动重复。可使用如下方法来关闭自动重复:

// 若自动重复则什么也不做
if(QKeyEvent::isAutoRepeat()) 
    return;

Qt 的键盘事件整体表现为,按住一个键时:
1、第一次触发 keyPressEvent(),isAutoRepeat() 返回 false

2、没有触发 keyReleaseEvent(),停顿一会

3、再一次触发 keyPressEvent(),isAutoRepeat() 返回true

4、触发 keyReleaseEvent()

5、若没松开按键,isAutoRepeat() 返回 true,返回第 3 步;若松开按键,isAutoRepeat() 返回 false

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓

四、键盘捕获

可以只指定窗口中的某个控件捕获键盘事件,使其他控件无法获得键盘事件,示例如下。

MyButton.h:

#ifndef MYBUTTON_H
#define MYBUTTON_H

#include <QWidget>
#include <QPushButton>
#include <QKeyEvent>
#include <QDebug>

class MyButton : public QPushButton
{
    Q_OBJECT
public:
    explicit MyButton(QWidget *parent = nullptr);

protected:
    void keyPressEvent(QKeyEvent *event);
};

#endif // MYBUTTON_H

MyButton.cpp

#include "MyButton.h"

MyButton::MyButton(QWidget *parent) : QPushButton(parent)
{

}

void MyButton::keyPressEvent(QKeyEvent *event)
{
    qDebug() << "button键盘按键事件:" << this->objectName();
    QWidget *ww = keyboardGrabber(); // 返回正在捕获键盘输入的部件,若没有则返回 0
    qDebug() << "正在捕获的控件:" << ww;

    if(event->key() == Qt::Key_Q) {
        qDebug() << "按下了Q键";
        this->releaseKeyboard();   // 释放捕获的键盘输入
     }
}

Widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDebug>
#include <QKeyEvent>
#include "MyButton.h"

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);

protected:
    void keyPressEvent(QKeyEvent *event);

private:
    MyButton* m_pBtn1;
    MyButton* m_pBtn2;
};
#endif // WIDGET_H

Widget.cpp:

#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->resize(300,400);

    // 初始化按钮1
    m_pBtn1=new MyButton();
    m_pBtn1->setParent(this);
    m_pBtn1->setText("AAA");
    m_pBtn1->move(10,10);
    m_pBtn1->resize(100,100);
    m_pBtn1->setObjectName("aaa");
    // 初始化按钮2
    m_pBtn2=new MyButton();
    m_pBtn2->setParent(this);
    m_pBtn2->setText("BBB");
    m_pBtn2->move(150,10);
    m_pBtn2->resize(100,100);
    m_pBtn2->setObjectName("bbb");

    // 指定控件捕获键盘
    m_pBtn1->grabKeyboard();
    /*使按钮 AAA 捕获键盘,此时产生的键盘事件都只会发送给按钮 AAA,也就是说
        其他部件无法获得键盘事件。
        只有可见的部件才能捕获键盘输入,若 isVisible()返回 false,则该部件不能调用grabKeyboard()函数
    */
}

void Widget::keyPressEvent(QKeyEvent *event)
{
    Q_UNUSED(event);

    qDebug()<<"Widget发生键盘事件";
}

按下任何键,发现只有按钮 AAA 才能获得键盘事件,按钮 BBB 无法获得键盘事件。

五、键盘按键单击、双击

首先键盘按键的单击、双击实现用的 QTimer,一说到这估计大部分人都知道怎么回事了,但这里也有个误区,那就是如何区分单击和双击的问题。这里使用两次按键的时间间隔来区分,这里在按下、或释放里实现都是可以的,这里我最后选择在释放里实现,后面再说原因。

头文件里定义几个相关变量:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QKeyEvent>
#include <QTimer>
#include <QDebug>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);

protected:
    void keyReleaseEvent(QKeyEvent *event);

private:
    QTimer* m_pTimer; // Ctrl双击定时器
    int m_nClickCnt = 0; // 点击次数
    bool m_bLongPress = false; // 是否为长按    
};
#endif // WIDGET_H

再看键盘按键单击、双击实现:

#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    // 定时器
    m_pTimer = new QTimer(this);
    connect(m_pTimer, &QTimer::timeout, [=]{
        m_nClickCnt = 0; // 计数清零
        m_pTimer->stop(); // 停止计时
        qDebug() << "键盘单击";
    });
}

void Widget::keyReleaseEvent(QKeyEvent *event)
{
    Q_UNUSED(event);

    if(event->key() == Qt::Key_A) {
        // 计数期间,如果QTimer已开始,则不重新开始
        if(!m_pTimer->isActive())
            m_pTimer->start(500); // 500ms是判断双击的时间间隔,不唯一,可根据实际情况改变

        m_nClickCnt++; // 点击计数,在500ms内如果点击两次认为是双击
        if(m_nClickCnt >= 2) {
            m_nClickCnt = 0; // 计数清零
            m_pTimer->stop(); // 停止计时
            qDebug() << "键盘双击";
        }
    }
}

单击,在 500ms 内未达到双击次数,也就是未执行 timer_->stop(); 时间耗尽触发 timeout 信号,执行单击动作。这里提一下 stop() 函数,QTimer 执行 start(n) 后,如果不 stop(),那它会循环执行。

六、键盘按键长按

至此实现键盘单击和双击复用,那么我们再来看一下长按怎么处理呢?

为了区分是否是长按,QKeyEvent 提供了一个 isAutoRepeat() 函数自动检测按键是否长按

  • 长按返回 true
  • 非长按返回 false

前面提到单击和双击的区分,其实在void keyPressEvent(QKeyEvent *event)、void keyReleaseEvent(QKeyEvent *event)函数里都可以,反正都是记录时间差,press-press 或 release-release 没分别,那最后为什么选择在按键释放函数里实现呢?

问题就在还得同时实现长按功能,刚刚分析得出无论你长按还是非长按,第一次的 press 动作他都是按下非长按的,如果在void keyPressEvent(QKeyEvent *event)里实现,那长按必然会附加一次单击,这当然不是我们想要的;

至此分析完毕,我想我们该开始写代码了。

#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    // 定时器
    m_pTimer = new QTimer(this);
    connect(m_pTimer, &QTimer::timeout, [=]{
        m_nClickCnt = 0; // 计数清零
        m_pTimer->stop(); // 停止计时
        qDebug() << "键盘单击";
    });
}

void Widget::keyReleaseEvent(QKeyEvent *event)
{
    Q_UNUSED(event);

    if(event->key() == Qt::Key_A) { // Qt::Key_Control经实测,长按永远不会使isAutoRepeat()为true
        // 是否是长按可以从release中直接判断
        if (!event->isAutoRepeat()) {
            // LongPress_初始值为false,如果非长按执行单击或双击动作判断
            // 如果长按会在长按里将其置true,在最后的Relese(非长按)里就不会执行单击、双击判断的动作
            if (!m_bLongPress) {
                if (!m_pTimer->isActive()) {
                    m_pTimer->start(500);
                }
                m_nClickCnt++;
                if (m_nClickCnt == 2){
                    m_nClickCnt = 0; // 计数清零
                    m_pTimer->stop(); // 停止计时
                    qDebug() << "键盘双击";
                }
            }
            m_bLongPress = false; // 置false
        }
        else{
            if (!m_bLongPress) {
                qDebug() << "键盘长按";
                // 限定长按只执行一次,最后会在Relese(非长按)里将LongPress_重新置false
                m_bLongPress = true;
            }
        }
    }
}

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓ 

猜你喜欢

转载自blog.csdn.net/QtCompany/article/details/131743524