QT实现键盘复用:单击、双击、长按

来源:互联网 发布:mysql数据库默认密码 编辑:程序博客网 时间:2024/05/29 12:25

由于项目需求,需要实现基于键盘按键的复用,查了很多资料都不满足我的需求,其中Mango的吐槽一下Qt的按键消息响应对我启发很大,他阐述了关于按键长按的问题,我的测试结果和他有些出入,但总体思路是一样的,也欢迎大家指正。下面来说一下具体的实现过程。

键盘按键单击、双击

首先键盘按键的单击、双击实现,没错!就是用的QTimer,一说到这估计大部分人都知道怎么回事了,但这里也有个误区,那就是如何区分单击和双击的问题,这也是我实现过程中遇到的问题。我最开始的做法是根据按下和释放的时间间隔来区分的,现在看来这显然是不对的,但当时脑袋可能蒙圈了(>_<),这样是无法准确的区分单击和双击的,应该用两次按键的时间间隔来区分,这里在按下、或释放里实现都是可以的,我最后选择在释放里实现,后面再说原因。如果谁不清楚按下、释放什么意思,自己去查qt帮助文档吧。。。
头文件里定义几个相关变量

//MyClass.h    QTimer*                     timer_;    bool                        LongPress_;//后面用到    int                         ClickCount_;//按键计数    KeyFlag                     KeyFlag_;//枚举
enum KeyFlag{    kKey_NULL,    kKey_A,    kKey_S,    kKey_D,    kKey_F,    kKey_J,    kKey_K};

构造函数连接timeout信号到单击函数

//MyClass.cpp    timer_ = new QTimer(this);    connect(timer_, SIGNAL(timeout()), this, SLOT(KeyOneClick()));    ClickCount_ = 0;    LongPress_ = false;    KeyFlag_ = kKey_NULL;

重写void keyReleaseEvent(QKeyEvent *event)

void MyClass::keyReleaseEvent(QKeyEvent *event) {    switch (event->key())    {    case Qt::Key_A:        if (!timer_->isActive()) {//计数期间,如果QTimer已开始,则不重新开始            timer_->start(400);//400ms是判断双击的时间间隔,不唯一,可根据实际情况改变            KeyFlag_ = kKey_A;//单击函数的按键标识        }        ClickCount_++;//点击计数,在400ms内如果点击两次认为是双击        if (ClickCount_ == 2){            timer_->stop();//停止计时            ClickCount_ = 0;//计数清零            cout << "this is A double click" << endl;            DoDoubleThings();//执行按键A双击动作        }        break;    case Qt::Key_S://注意到没?我实现的都是字母键,其他键是不一样的        break;    case Qt::Key_D:        break;    case Qt::Key_F:        break;    case Qt::Key_J:        break;    case Qt::Key_K:        break;    default:        break;    }}

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

void MyClass::KeyOneClick() {    switch (KeyFlag_)//判断点击的是哪个按键    {    case kKey_A:                ClickCount_ = 0;//计数清零        timer_->stop();//停止计时        cout << "this is A ckick" << endl;        DoSigalClickThings();//单击A的动作        break;    case kKey_S:        break;    case kKey_D:        break;    case kKey_F:        break;    case kKey_J:        break;    case kKey_K:        break;    default:        break;    }}

键盘按键长按

至此实现键盘单击和双击复用,那么我们再来看一下长按怎么处理呢?
先看一下按键长按的过程分析,我们知道一按一松实现一次click动作,那我们测试一下qt键盘长按的具体过程,重写void keyPressEvent(QKeyEvent *event)、void keyReleaseEvent(QKeyEvent *event)函数。

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

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

为了方便表示我定义

  • P:press动作
  • R:release动作
  • T:isAutoRepeat()返回true
  • F:isAutoRepeat()返回false

下面看一下长按会发生什么吧。

void MyClass::keyPressEvent(QKeyEvent *event) {    switch (event->key())    {    case Qt::Key_A:             if (!event->isAutoRepeat()) {            //非长按输出press、not repeat等关键词            cout << "this is A press: not Auto Repeat" << endl;        }        else{                   //长按输出press、is repeat等关键词               cout << "this is A press: is Auto Repeat" << endl;         }        break;    default:        break;    }}
void MyClass::keyReleaseEvent(QKeyEvent *event) {    switch (event->key())    {    case Qt::Key_A:             if (!event->isAutoRepeat()) {            //非长按输出release、not repeat等关键词            cout << "this is A release: not Auto Repeat" << endl;        }        else{                   //非长按输出release、is repeat等关键词            cout << "this is A release: is Auto Repeat" << endl;         }        break;    default:        break;    }}

运行结果用我的方式表示为:P(F)R(T)P(T)R(T)…P(T)R(T)P(T)R(F),也就是当你长按时会循环发生press和release动作,

  1. 第一次执行press动作,此时QKeyEvent 不认为你在长按,而在release时,QKeyEvent 已经开始认为你在长按了;
  2. 第二次到倒数第二次QKeyEvent 认为你都在长按;
  3. 最后一次,press动作依然为长按,但release却变成非长按了;

也就是不管你按多久最开始的press肯定为非长按状态,最后的release肯定为非长按状态。结合这些特性,我们来实现键盘按键的复用,即同时实现单击双击和长按三个动作。

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

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

再来看看在void keyReleaseEvent(QKeyEvent *event),如果长按,它第一次就是R(T)了,那就可以通过判断isAutoRepeat()的状态来区分长按和非长按了。

还有一个问题就是,虽然可以判断长按了,但是长按时是会循环执行的,如不控制,岂不会执行n次长按要实现的动作,因此还要加一个flag来控制,让它只执行一次。

最后,还要讨论一下长按的最后一次release动作,它和非长按的release是相同的R(F),为了避免这种情况,我们正好利用控制长按的flag来进行区分。

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

void MyClass::keyReleaseEvent(QKeyEvent *event) {    switch (event->key())    {    case Qt::Key_A:        //是否是长按可以从release中直接判断        if (!event->isAutoRepeat()) {            //LongPress_初始值为false,如果非长按执行单击或双击动作判断            //如果长按会在长按里将其置true,在最后的R(F)里就不会执行单击、双击判断的动作            if (!LongPress_) {                if (!timer_->isActive()) {                    timer_->start(400);                    KeyFlag_ = kKey_A;                }                ClickCount_++;                if (ClickCount_ == 2){                    timer_->stop();                    ClickCount_ = 0;                    cout << "this is A doubleclick" << endl;                    DoDoubleThings();//执行按键A双击动作                }            }            LongPress_ = false;//置false        }        else{                       if (!LongPress_) {                 cout << "this is longpress" << endl;                 //限定长按只执行一次,最后会在R(F)里将LongPress_重新置false                LongPress_ = true;                DoLongPressThings();            }                   }        break;    case Qt::Key_S:        break;    case Qt::Key_D:        break;    case Qt::Key_F:        break;    case Qt::Key_J:        break;    case Qt::Key_K:        break;    default:        break;    }   }

亲测有效,如有不同方法,欢迎讨论,或是有更好的方法,也请不吝分享!

阅读全文
1 0
原创粉丝点击