1

I'm implementing a customized virtual keyboard using QML. My purpose is simulating a physical key press signal of real keyboard when I click a button in virtual keyboard. I have followed the tutorial in Qt Virtual Keyboard and have successfully built and run the example code.

The problem is that the example code employs QCoreApplication::sendEvent() function in a C++ class to send key press event to focused QObject. It works well when I import QtQuick.Controls 1.3 in main.qml as the guide, but takes no effect when I change to QtQuick.Controls 2.2, which is essential in my application. Here is the core of the example code:

void KeyEmitter::emitKey(Qt::Key key)
{
    QQuickItem* receiver = qobject_cast<QQuickItem*>(QGuiApplication::focusObject());
    if(!receiver) {
        return;
    }
    QKeyEvent pressEvent = QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier, QKeySequence(key).toString());
    QKeyEvent releaseEvent = QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
    QCoreApplication::sendEvent(receiver, &pressEvent);
    QCoreApplication::sendEvent(receiver, &releaseEvent);
}

So how can I send the key press event to my application?

AAEM
  • 1,837
  • 2
  • 18
  • 26
Dan Do
  • 79
  • 1
  • 8
  • Be more specific please. Using QML Controls 2 does your emitKey method is called? Or sendEvent has no effect? Place a breakpoint to the beginning of the emitKey method and see what happens. – Ponzifex May 27 '20 at 19:05
  • Yes the emitKey() method has been called, but it did not take effect – Dan Do May 28 '20 at 03:23

2 Answers2

2

I think the focusObject is the actual clicked button, so sending an QKeyEvent to that instead of the TextField is pointless.

How about passing the actual receiver object's pointer instead of asking QGuiApplication for the focusObject.

Try this:

keyemitter.h file (header only, you don't need cpp file):

#ifndef KEYEMITTER_H
#define KEYEMITTER_H
#include <QObject>
#include <QCoreApplication>
#include <QKeyEvent>
class KeyEmitter : public QObject
{
    Q_OBJECT
public:
    KeyEmitter(QObject* parent=nullptr) : QObject(parent) {}
    Q_INVOKABLE void keyPressed(QObject* tf, Qt::Key k) {
        QKeyEvent keyPressEvent = QKeyEvent(QEvent::Type::KeyPress, k, Qt::NoModifier, QKeySequence(k).toString());
        QCoreApplication::sendEvent(tf, &keyPressEvent);
    }
};
#endif // KEYEMITTER_H

main.cpp file:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQmlContext>
#include "keyemitter.h"
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQuickView view;
    KeyEmitter keyEmitter;
    view.rootContext()->setContextProperty("keyEmitter", &keyEmitter);
    view.setSource(QStringLiteral("qrc:/main.qml"));
    view.show();
    return app.exec();
}

main.qml file:

import QtQuick 2.12
import QtQuick.Controls 2.12
Rectangle {
    anchors.fill: parent
    color: "red"
    Column{
        Row {
            TextField {
                id: tf
                Component.onCompleted: { console.log(tf); }
                text: "123"
            }
        }
        Row {
            Button {
                text: "1"
                onClicked: keyEmitter.keyPressed(tf, Qt.Key_1)
            }
            Button {
                text: "2"
                onClicked: keyEmitter.keyPressed(tf, Qt.Key_2)
            }
            Button {
                text: "3"
                onClicked: keyEmitter.keyPressed(tf, Qt.Key_3)
            }
        }
        Row {
            Button {
                text: "DEL"
                onClicked: keyEmitter.keyPressed(tf, Qt.Key_Backspace)
            }
            Button {
                text: "OK"
                onClicked: keyEmitter.keyPressed(tf, Qt.Key_Enter)
            }
            Button {
                text: "ESC"
                onClicked: keyEmitter.keyPressed(tf, Qt.Key_Escape)
            }
        }
    }
}

AAEM
  • 1,837
  • 2
  • 18
  • 26
Ponzifex
  • 555
  • 5
  • 20
1

Thank @Ponzifex,

You are right. When I click the button in my customized keyboard, the focus object immediately changes to the clicked button instead of the textfield as I want.

Simply, changing focusPolicy of the button to NoFocus fixes my problem.

Button {
    id: btnOne                
    focusPolicy: Qt.NoFocus
    text: qsTr("1")
    onClicked: {
        keyEmitter.emitKey( Qt.Key_1 )
    }
}
Dan Do
  • 79
  • 1
  • 8