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



  • 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.


  • Moderators

    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...



  • @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
    }
    


  • @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!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.