QT實現鍵盤復用:單擊、雙擊、長按


轉自:https://blog.csdn.net/qq_27450255/article/details/78780959

 

由於項目需求,需要實現基於鍵盤按鍵的復用,查了很多資料都不滿足我的需求,其中Mango的吐槽一下Qt的按鍵消息響應對我啟發很大,他闡述了關於按鍵長按的問題,我的測試結果和他有些出入,但總體思路是一樣的,也歡迎大家指正。下面來說一下具體的實現過程。

鍵盤按鍵單擊、雙擊

首先鍵盤按鍵的單擊、雙擊實現,沒錯!就是用的QTimer,一說到這估計大部分人都知道怎么回事了,但這里也有個誤區,那就是如何區分單擊和雙擊的問題,這也是我實現過程中遇到的問題。我最開始的做法是根據按下和釋放的時間間隔來區分的,現在看來這顯然是不對的,但當時腦袋可能蒙圈了(>_<),這樣是無法准確的區分單擊和雙擊的,應該用兩次按鍵的時間間隔來區分,這里在按下、或釋放里實現都是可以的,我最后選擇在釋放里實現,后面再說原因。如果誰不清楚按下、釋放什么意思,自己去查qt幫助文檔吧。。。
頭文件里定義幾個相關變量

1 //MyClass.h
2     QTimer*                     timer_;
3     bool                        LongPress_;//后面用到
4     int                         ClickCount_;//按鍵計數
5     KeyFlag                     KeyFlag_;//枚舉
 1 enum KeyFlag
 2 {
 3     kKey_NULL,
 4     kKey_A,
 5     kKey_S,
 6     kKey_D,
 7     kKey_F,
 8     kKey_J,
 9     kKey_K
10 };

構造函數連接timeout信號到單擊函數

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

重寫void keyReleaseEvent(QKeyEvent *event)

 1 void MyClass::keyReleaseEvent(QKeyEvent *event) {
 2     switch (event->key())
 3     {
 4     case Qt::Key_A:
 5         if (!timer_->isActive()) {//計數期間,如果QTimer已開始,則不重新開始
 6             timer_->start(400);//400ms是判斷雙擊的時間間隔,不唯一,可根據實際情況改變
 7             KeyFlag_ = kKey_A;//單擊函數的按鍵標識
 8         }
 9         ClickCount_++;//點擊計數,在400ms內如果點擊兩次認為是雙擊
10         if (ClickCount_ == 2){
11             timer_->stop();//停止計時
12             ClickCount_ = 0;//計數清零
13             cout << "this is A double click" << endl;
14             DoDoubleThings();//執行按鍵A雙擊動作
15         }
16         break;
17 
18     case Qt::Key_S://注意到沒?我實現的都是字母鍵,其他鍵是不一樣的
19         break;
20     case Qt::Key_D:
21         break;
22     case Qt::Key_F:
23         break;
24     case Qt::Key_J:
25         break;
26     case Qt::Key_K:
27         break;
28     default:
29         break;
30     }
31 
32 }

單擊函數,在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

下面看一下長按會發生什么吧。

 1 void MyClass::keyPressEvent(QKeyEvent *event) {
 2     switch (event->key())
 3     {
 4     case Qt::Key_A:     
 5         if (!event->isAutoRepeat()) {
 6             //非長按輸出press、not repeat等關鍵詞
 7             cout << "this is A press: not Auto Repeat" << endl;
 8         }
 9         else{       
10             //長按輸出press、is repeat等關鍵詞   
11             cout << "this is A press: is Auto Repeat" << endl; 
12         }
13         break;
14     default:
15         break;
16     }
17 
18 }
19 
20 void MyClass::keyReleaseEvent(QKeyEvent *event) {
21     switch (event->key())
22     {
23     case Qt::Key_A:     
24         if (!event->isAutoRepeat()) {
25             //非長按輸出release、not repeat等關鍵詞
26             cout << "this is A release: not Auto Repeat" << endl;
27         }
28         else{       
29             //非長按輸出release、is repeat等關鍵詞
30             cout << "this is A release: is Auto Repeat" << endl; 
31         }
32         break;
33     default:
34         break;
35     }
36 
37 }

運行結果用我的方式表示為: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來進行區分。

至此分析完畢,我想我們該開始寫代碼了。

 1 void MyClass::keyReleaseEvent(QKeyEvent *event) {
 2     switch (event->key())
 3     {
 4     case Qt::Key_A:
 5         //是否是長按可以從release中直接判斷
 6         if (!event->isAutoRepeat()) {
 7             //LongPress_初始值為false,如果非長按執行單擊或雙擊動作判斷
 8             //如果長按會在長按里將其置true,在最后的R(F)里就不會執行單擊、雙擊判斷的動作
 9             if (!LongPress_) {
10                 if (!timer_->isActive()) {
11                     timer_->start(400);
12                     KeyFlag_ = kKey_A;
13                 }
14                 ClickCount_++;
15                 if (ClickCount_ == 2){
16                     timer_->stop();
17                     ClickCount_ = 0;
18                     cout << "this is A doubleclick" << endl;
19                     DoDoubleThings();//執行按鍵A雙擊動作
20                 }
21             }
22             LongPress_ = false;//置false
23         }
24         else{           
25             if (!LongPress_) { 
26                 cout << "this is longpress" << endl; 
27                 //限定長按只執行一次,最后會在R(F)里將LongPress_重新置false
28                 LongPress_ = true;
29                 DoLongPressThings();
30             }           
31         }
32         break;
33 
34     case Qt::Key_S:
35         break;
36     case Qt::Key_D:
37         break;
38     case Qt::Key_F:
39         break;
40     case Qt::Key_J:
41         break;
42     case Qt::Key_K:
43         break;
44     default:
45         break;
46     }   
47 }

親測有效,如有不同方法,歡迎討論,或是有更好的方法,也請不吝分享!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM