QML 全局鍵盤 監控


開發環境

  • windows
  • QtCreator 4.10.2(Community)
  • C++和QML混合編輯

應用場景

應用程序分為登錄界面和主界面2個窗口。要求在主界面有全局鍵盤監控的功能,比如按ESC時,確認后退回到登錄窗口。

QML中的按鍵事件處理

三要素:

  • focus :true            //組件必須獲得焦點,只有在獲得焦點時,該組件的Key事件才有效
  • Key.enabled:true  //使能Key功能
  • Key.onPress:{}        //重寫Key按下的處理時間

  舉個例子:我們假想這個Rect是主界面最外部的包圍框(root),即所有的界面元素都在這個Rect中。

Rectangle{
     id: root
     width:  512
     height: 512
     color: "gray"
 
     focus: true
     Keys.enabled: true
     Keys.onPressed: {
             switch (event.key){
             case Qt.Key_Left:
                 //todo
                 break;
             case Qt.Key_Right:
                  //todo
                 break;
             case Qt.Key_Up:
                  //todo
                 break;
             case Qt.Key_Down:
                  //todo
                 break;
             default:
                 return;
             }
             event.accepted = true;//表示這些列出的按鍵事件已經處理,    
                                              //不再往下傳遞
         }
}    
View Code

  按這個方法,假如主界面一開始加載完成時,獲得焦點的是這個Rect,那么當然可以實現我們的需求。但是用戶一旦在界面上操作一些獲取焦點的組件,使得這個Rect失去了焦點。那么這個全局監控鍵盤得到功能就無法實現了。如果想反復設置Rect的focus屬性,顯然不是一個好辦法。

Qt的事件過濾器

  QML不方便完成的事情,我們自然想到了C++了。原則上講:QML負責界面邏輯,C++負責數據處理和功能實現。這邊博客給了我基本的思路:https://blog.csdn.net/weixin_34354945/article/details/92973701

  思路:實現一個過濾器類。這個類只要是繼承自QObject或其子類,它就有一個虛函數eventFilter。我們只要實現它,在其中捕獲按鈕事件,進行處理。其他事件放行。捕獲到按鍵事件時,C++發出信號,QML處理信號。只要把這個過濾器類安裝我們的主窗口就可以實現需求。

 過濾器類

 filterevent.h 

#ifndef FILTEREVENT_H
#define FILTEREVENT_H

#include <QObject>
#include <QDebug>

class FilterEvent : public QObject
{
    Q_OBJECT
public:
    explicit FilterEvent(QObject *parent = nullptr);
protected:
protected:
      bool eventFilter(QObject *obj, QEvent *ev) override;

signals:
      void myExited();

public slots:
};

#endif // FILTEREVENT_H
View Code

 filterevent.cpp 

#include "filterevent.h"

#include <qevent.h>

FilterEvent::FilterEvent(QObject *parent) : QObject(parent)
{

}

bool FilterEvent::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        switch (keyEvent->key()) {
        case Qt::Key_Escape:
            qDebug()<<"pressed Key_Escape";
            emit myExited();
            return true;
        case Qt::Key_Enter:
            qDebug()<<"pressed Key_Enter";
            return true;
        }
        qDebug() << "Ate key press" << keyEvent->key();
    }
    return false;
}
View Code

安裝過濾器

  在這里,我遇到了一個問題:這個過濾器的實例對象應該放在哪里?放在 main.cpp 的main函數中,我們是可以通過調用 installEventFilter 安裝到主窗口上,但是按鍵事件處理中的信號該怎么處理呢,信號的處理地點是要放在QML中的。

參照其它博客的寫法試了試,最后QML會報錯過濾器實例對象can not find(雖然名字是變藍色了...)。最后結合自己的經歷,想了另一個辦法。

  • filterEvent實現在QML端,采用on+信號的形式處理按鍵事件中發出的信號
  • main.cpp 中通過查找objectName的方式,得到主窗口和filterEvent 的指針
  • 主窗口調用installEventFilter 函數安裝上filterEvent

  首先要將過濾器類注冊到QML的元象樹系統中。第一個參數是自己定義的包名,要改成自己的,用到的時候QML端import。

qmlRegisterType<FilterEvent>("an.qt.UserDefine", 1, 0, "FilterEvent");

  然后安裝

//安裝事件過濾器
    QObject* mainRootItem = engine.rootObjects().at(1);
    QObject* filterEvent = mainRootItem->findChild<QObject*>("filterEvent");
    mainRootItem->installEventFilter(filterEvent);

  QML端

//事件過濾器
    FilterEvent{
        objectName: "filterEvent"  //objectName用以元象樹查找元素
        onMyExited: {
            console.log("filterEvent trigged");
            //todo
        }
    }

補充說明

  這里要補充說明的是:QML的對象是以樹的形式進行管理的。可以這樣理解:最外圍的最大的組件可以看成是根節點,它包含的組件呢,是根節點的分支。子節點再包含一些組件,就再分支。(個人對元象樹的理解,錯誤請指出)。然后我們可以通過 findChild<QObject*>("objectName") 的方式得到我們想要找的對象。比如可以進行一些信號與槽函數綁定

  安裝過濾器中的 mainRootItem 也是這樣獲得的。之所以是 at(1) 是因為我第一步加載的是登錄界面(0)。

  信號與槽函數綁定舉例

    QObject* logInRootItem = engine.rootObjects().at(0);
    QObject* logInButton = logInRootItem->findChild<QObject*>("logInButton");//根據qml中的objectName找到這個對象,獲取到它的指針
    if(logInButton)
    {
        //連接QML元素中的信號到C++的槽函數
        QObject::connect(logInButton,SIGNAL(logIn(QString)),&control,SLOT(showWindow(QString)));
    }

 


免責聲明!

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



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