Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Capture Keypresses in C++, perform logic, then send update to QML
Forum Updated to NodeBB v4.3 + New Features

Capture Keypresses in C++, perform logic, then send update to QML

Scheduled Pinned Locked Moved Solved QML and Qt Quick
4 Posts 3 Posters 2.6k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • LyneRL Offline
    LyneRL Offline
    LyneR
    wrote on last edited by
    #1

    I have a QtQuick based embedded application where key codes are being sent to my embedded device from a non-keyboard peripheral to communicate changes in temperature. For instance a sequence of B,1,3,0,E would indicate a temperature reading of 130. I am trying to get something simple to work first so I am trying to toggle some text based on seeing an F key. I want to capture the key-presses on the C++ side so that I can perform some logic on the key presses and then pass signals to the qml for visual indication of changes. I have tried to follow several examples but can't seem to get it all connected and working.

    Here is the code I have so far, although it isn't working, all I get is the program has unexpectedly finished.

    keypresshandler.h

    #ifndef KEYPRESSHANDLER_H
    #define KEYPRESSHANDLER_H
    
    #include <QObject>
    #include <QQmlApplicationEngine>
    #include <QKeyEvent>
    
    class QEvent;
    
    class KeyPressHandler : public QObject
    {
        Q_OBJECT
        // create getter and notify for text that can be changed
        Q_PROPERTY(QString textContent READ textContent NOTIFY textContentChanged)
    
    public:
        explicit KeyPressHandler (QObject *parent = 0);
        ~KeyPressHandler ();
    //    void KeyPressInit (); // don't know what to initialize
    
        QString textContent(); // the getter
    
    signals:
    
        void textContentChanged(); // the notifier for the QML side
    
    public slots:
    
    protected:
        // capture key presses here so I can do some business logic and
        // then send a signal to QML when I have performed the logic driving
        // a change to QML
        void keyPressEvent (QKeyEvent *event);
    
    private:
        QQmlApplicationEngine *m_engine;
        QString m_text = textOff;
        const QString textOn = "The text is now ON";
        const QString textOff = "The text is now OFF";
    };
    
    #endif // KEYEVENTHANDLER_H
    
    

    keypresshandler.cpp

    #include <QKeyEvent>
    #include <QDebug>
    #include "keypresshandler.h"
    
    KeyPressHandler::KeyPressHandler (QObject *parent) : QObject (parent)
    {
        m_engine = (QQmlApplicationEngine *)parent;
    }
    
    KeyPressHandler::~KeyPressHandler()
    {
    }
    
    QString KeyPressHandler::textContent()
    {
        qDebug() << "called text getter";
        return m_text;
    }
    
    
    // how can I get this to handle the keypress events for my application?
    void KeyPressHandler::keyPressEvent(QKeyEvent *event)
    {
        switch (event->key())
        {
        case Qt::Key_F:
            qDebug() << "F key pressed";
            if (textOff == m_text)
                m_text = textOn;
            else
                m_text = textOff;
            emit textContentChanged();
            break;
        default:
            break;
        }
    }
    
    

    main.cpp

    #include "keypresshandler.h"
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QtQml>
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        qmlRegisterType<KeyPressHandler>("com.ais.text", 1, 0, "KeyPressHandler");
    
        KeyPressHandler *kph = new KeyPressHandler (&engine);
    //    kph->KeyPressInit();
    
        engine.load (QUrl(QStringLiteral("qrc:///main.qml")));
        engine.rootContext()->setContextProperty("kph", kph);
    
        return app.exec();
    }
    

    main.qml

    import QtQuick 2.6
    import com.ais.text 1.0
    
    Rectangle {
        id: root
        visible: true
        width: 800
        height: 480
    
        Text {
            id: mytext
    
            anchors {
                left: parent.left
                right: parent.right
                verticalCenter: parent.verticalCenter
            }
    
            font { pointSize: 24 }
    
            horizontalAlignment: Text.AlignHCenter
    
            // want to use the getter KeyPressHandler textContent returning a QString
            text: textContent()
    
            // want to set the text to the changed text when a key is pressed
            onTextContentChanged: {
                mytext.text = textContent();
            }
        }
    
        // Do I need to create an object from the class like this?
        KeyPressHandler {
            id: kph
            onTextContentChanged: {
                mytext.text = kph.textContent;
            }
        }
    
    }
    

    Please suggest a different approach if this doesn't make sense.

    1 Reply Last reply
    1
    • p3c0P Offline
      p3c0P Offline
      p3c0
      Moderators
      wrote on last edited by
      #2

      Hi @LyneR,
      QQmlApplicationEngine can only load Window and ApplicationWindow whereas you have you have used Rectangle as root element.

      Do I need to create an object from the class like this?

      Yes you have to instantiate the registered object.

      Since you have use QQmlApplicationEngine another way would be to subclass this and create your own class where you can watch for events by adding an eventFilter. Here is a small demo example:

      //MyEngine based on QQmlApplicationEngine
      
      MyEngine::MyEngine()
      {
          connect(this, &MyEngine::objectCreated, [=](QObject *object, const QUrl &url){
              object->installEventFilter(this);
          });
      }
      
      bool MyEngine::eventFilter(QObject *object, QEvent *event)
      {
          if (event->type() == QEvent::KeyPress) {
              QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
              qDebug() << "key" << keyEvent->key() << " press on " << object;
              return true;
          }
          return false;
      }
      
      //Use this engine
      int main(int argc, char *argv[])
      {
          QGuiApplication app(argc, argv);
      
          MyEngine engine;
          engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
      
          return app.exec();
      }
      
      //main.qml
      import QtQuick 2.6
      import QtQuick.Window 2.2
      
      Window {
          visible: true
          width: 640
          height: 480
      
          Text {
              text: "Hello World"
              anchors.centerIn: parent
          }
      }
      

      Now try keypressing on the Window and watch for events.
      Hope this helps you...

      157

      GrecKoG LyneRL 2 Replies Last reply
      1
      • p3c0P p3c0

        Hi @LyneR,
        QQmlApplicationEngine can only load Window and ApplicationWindow whereas you have you have used Rectangle as root element.

        Do I need to create an object from the class like this?

        Yes you have to instantiate the registered object.

        Since you have use QQmlApplicationEngine another way would be to subclass this and create your own class where you can watch for events by adding an eventFilter. Here is a small demo example:

        //MyEngine based on QQmlApplicationEngine
        
        MyEngine::MyEngine()
        {
            connect(this, &MyEngine::objectCreated, [=](QObject *object, const QUrl &url){
                object->installEventFilter(this);
            });
        }
        
        bool MyEngine::eventFilter(QObject *object, QEvent *event)
        {
            if (event->type() == QEvent::KeyPress) {
                QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                qDebug() << "key" << keyEvent->key() << " press on " << object;
                return true;
            }
            return false;
        }
        
        //Use this engine
        int main(int argc, char *argv[])
        {
            QGuiApplication app(argc, argv);
        
            MyEngine engine;
            engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        
            return app.exec();
        }
        
        //main.qml
        import QtQuick 2.6
        import QtQuick.Window 2.2
        
        Window {
            visible: true
            width: 640
            height: 480
        
            Text {
                text: "Hello World"
                anchors.centerIn: parent
            }
        }
        

        Now try keypressing on the Window and watch for events.
        Hope this helps you...

        GrecKoG Offline
        GrecKoG Offline
        GrecKo
        Qt Champions 2018
        wrote on last edited by GrecKo
        #3

        @p3c0 said in Capture Keypresses in C++, perform logic, then send update to QML:

        Hi @LyneR,
        QQmlApplicationEngine can only load Window and ApplicationWindow whereas you have you have used Rectangle as root element.

        Not exactly true, but you do need to use a *Window to actually display a Window.

        Since you have use QQmlApplicationEngine another way would be to subclass this and create your own class where you can watch for events by adding an eventFilter.

        A better and non intrusive way would be to use installEventFilter on the QGuiApplication's instance

        An even better solution would be to use the ready-made Shortcut component like this :

        property bool on: true
        Text {
            text: on ?  "The text is now ON" : "The text is now OFF"
        }
        Shortcut {
            sequence: "F"
            context: Qt.ApplicationShortcut
            onActivated: on = !on
        }
        
        1 Reply Last reply
        0
        • p3c0P p3c0

          Hi @LyneR,
          QQmlApplicationEngine can only load Window and ApplicationWindow whereas you have you have used Rectangle as root element.

          Do I need to create an object from the class like this?

          Yes you have to instantiate the registered object.

          Since you have use QQmlApplicationEngine another way would be to subclass this and create your own class where you can watch for events by adding an eventFilter. Here is a small demo example:

          //MyEngine based on QQmlApplicationEngine
          
          MyEngine::MyEngine()
          {
              connect(this, &MyEngine::objectCreated, [=](QObject *object, const QUrl &url){
                  object->installEventFilter(this);
              });
          }
          
          bool MyEngine::eventFilter(QObject *object, QEvent *event)
          {
              if (event->type() == QEvent::KeyPress) {
                  QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
                  qDebug() << "key" << keyEvent->key() << " press on " << object;
                  return true;
              }
              return false;
          }
          
          //Use this engine
          int main(int argc, char *argv[])
          {
              QGuiApplication app(argc, argv);
          
              MyEngine engine;
              engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
          
              return app.exec();
          }
          
          //main.qml
          import QtQuick 2.6
          import QtQuick.Window 2.2
          
          Window {
              visible: true
              width: 640
              height: 480
          
              Text {
                  text: "Hello World"
                  anchors.centerIn: parent
              }
          }
          

          Now try keypressing on the Window and watch for events.
          Hope this helps you...

          LyneRL Offline
          LyneRL Offline
          LyneR
          wrote on last edited by
          #4

          @p3c0
          Thank you very much! This solution worked great for me. Grecko's solution would only work for the very simple case. I will need to do much more logic involving storing up multiple keys before changing the window. So I think the event filter is the best way to go for me. I was able to quickly move past this issue with your working example. Thanks so much!

          1 Reply Last reply
          0

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved