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.
-
Hi @LyneR,
QQmlApplicationEngine
can only loadWindow
andApplicationWindow
whereas you have you have usedRectangle
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 loadWindow
andApplicationWindow
whereas you have you have usedRectangle
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 theQGuiApplication
'sinstance
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!