Better_Software_Header_MobileBetter_Software_Header_Web

Find what you need - explore our website and developer resources

Qt Input Method - Virtual Keyboard

Implementing an out-of-process virtual keyboard for text input in Qt apps

#include <QApplication>
#include <QDBusConnection>

#include "keyboard.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    if (!QDBusConnection::sessionBus().registerService("com.kdab.inputmethod")) {
        qFatal("Unable to register at DBus");
        return 1;
    }

    Keyboard keyboard;

    if (!QDBusConnection::sessionBus().registerObject("/VirtualKeyboard", &keyboard, QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllSlots)) {
        qFatal("Unable to register object at DBus");
        return 1;
    }

    return app.exec();
}
class Keyboard : public QWidget
{
    Q_OBJECT

public:
    explicit Keyboard(QWidget *parent = Q_NULLPTR);

public slots:
    void showKeyboard();
    void hideKeyboard();
    bool keyboardVisible() const;

signals:
    void specialKeyClicked(int key);
    void keyClicked(const QString &text);

private slots:
    void buttonClicked(int key);
};
Keyboard::Keyboard(QWidget *parent)
    : QWidget(parent)
{
    setWindowFlags(Qt::WindowDoesNotAcceptFocus | Qt::Tool |
                   Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);

    QGridLayout *gridLayout = new QGridLayout(this);

    QSignalMapper *mapper = new QSignalMapper(this);
    connect(mapper, SIGNAL(mapped(int)), SLOT(buttonClicked(int)));

    int row = 0;
    int column = 0;

    for (int i = 0; i < layoutSize; ++i) {
        if (keyboardLayout[i].key == NEXT_ROW_MARKER) {
            row++;
            column = 0;
            continue;
        }

        QPushButton *button = new QPushButton;
        button->setFixedWidth(40);
        button->setText(QString::fromLatin1(keyboardLayout[i].label));

        mapper->setMapping(button, keyboardLayout[i].key);
        connect(button, SIGNAL(clicked()), mapper, SLOT(map()));

        gridLayout->addWidget(button, row, column);
        column++;
    }
}
#define NEXT_ROW_MARKER 0

struct KeyboardLayoutEntry{
    int key;
    const char *label;
};

KeyboardLayoutEntry keyboardLayout[] = {
    { Qt::Key_1, "1" },
    { Qt::Key_2, "2" },
    { Qt::Key_3, "3" },
    { Qt::Key_4, "4" },
    { Qt::Key_5, "5" },
    { Qt::Key_6, "6" },
    { Qt::Key_7, "7" },
    { Qt::Key_8, "8" },
    { Qt::Key_9, "9" },
    { Qt::Key_0, "0" },
    { Qt::Key_Backspace, "<-" },
    { NEXT_ROW_MARKER, 0 },
    { Qt::Key_Q, "q" },
    { Qt::Key_W, "w" },
    ...
};

const static int layoutSize = (sizeof(keyboardLayout) /
                               sizeof(KeyboardLayoutEntry));
void Keyboard::buttonClicked(int key)
{
    if ((key == Qt::Key_Enter) || (key == Qt::Key_Backspace))
        emit specialKeyClicked(key);
    else
        emit keyClicked(keyToCharacter(key));
}
static QString keyToCharacter(int key)
{
    for (int i = 0; i < layoutSize; ++i) {
        if (keyboardLayout[i].key == key)
            return QString::fromLatin1(keyboardLayout[i].label);
    }

    return QString();
}
void Keyboard::showKeyboard()
{
    QWidget::show();
}

void Keyboard::hideKeyboard()
{
    QWidget::hide();
}

bool Keyboard::keyboardVisible() const
{
    return QWidget::isVisible();
}
class QVkImPlatformInputContextPlugin : public QPlatformInputContextPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPlatformInputContextFactoryInterface" FILE "vkim.json")

public:
    QVkImPlatformInputContext *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
};

QVkImPlatformInputContext *QVkImPlatformInputContextPlugin::create(const QString& system, const QStringList& paramList)
{
    Q_UNUSED(paramList);

    if (system == QLatin1String("vkim")) {
        return new QVkImPlatformInputContext;
    }

    return 0;
}
{
    "Keys": [ "vkim" ]
}
class QVkImPlatformInputContext : public QPlatformInputContext
{
    Q_OBJECT

public:
    QVkImPlatformInputContext();
    ~QVkImPlatformInputContext();

    bool isValid() const Q_DECL_OVERRIDE;
    void setFocusObject(QObject *object) Q_DECL_OVERRIDE;

    void showInputPanel() Q_DECL_OVERRIDE;
    void hideInputPanel() Q_DECL_OVERRIDE;
    bool isInputPanelVisible() const Q_DECL_OVERRIDE;

private slots:
    void keyboardSpecialKeyClicked(int key);
    void keyboardKeyClicked(const QString &character);

private:
    QDBusInterface *m_keyboardInterface;

    QObject *m_focusObject;
};
QVkImPlatformInputContext::QVkImPlatformInputContext()
    : m_focusObject(0)
{
    m_keyboardInterface = new QDBusInterface("com.kdab.inputmethod",
                                             "/VirtualKeyboard",
                                             "local.server.Keyboard",
                                             QDBusConnection::sessionBus(),
                                             this);

    connect(m_keyboardInterface, SIGNAL(keyClicked(QString)),
            SLOT(keyboardKeyClicked(QString)));
    connect(m_keyboardInterface, SIGNAL(specialKeyClicked(int)),
            SLOT(keyboardSpecialKeyClicked(int)));
}
bool QVkImPlatformInputContext::isValid() const
{
    return m_keyboardInterface->isValid();
}
void QVkImPlatformInputContext::setFocusObject(QObject *object)
{
    m_focusObject = object;
}
void QVkImPlatformInputContext::showInputPanel()
{
    m_keyboardInterface->call("showKeyboard");
}

void QVkImPlatformInputContext::hideInputPanel()
{
    m_keyboardInterface->call("hideKeyboard");
}
bool QVkImPlatformInputContext::isInputPanelVisible() const
{
    const QDBusReply<bool> reply = m_keyboardInterface->call("keyboardVisible");

    if (reply.isValid())
        return reply.value();
    else
        return false;
}
void QVkImPlatformInputContext::keyboardKeyClicked(const QString &characters)
{
    if (!m_focusObject)
        return;

    QInputMethodEvent event;
    event.setCommitString(characters);

    QGuiApplication::sendEvent(m_focusObject, &event);
}
void QVkImPlatformInputContext::keyboardSpecialKeyClicked(int key)
{
    if (!m_focusObject)
        return;

    if (key == Qt::Key_Enter) {
        QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress,
                                              Qt::Key_Enter,
                                              Qt::NoModifier);

        QGuiApplication::postEvent(m_focusObject, pressEvent);

        QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease,
                                                Qt::Key_Enter,
                                                Qt::NoModifier);

        QGuiApplication::postEvent(m_focusObject, releaseEvent);
    } else if (key == Qt::Key_Backspace) {
        QKeyEvent *pressEvent = new QKeyEvent(QEvent::KeyPress,
                                              Qt::Key_Backspace,
                                              Qt::NoModifier);

        QGuiApplication::postEvent(m_focusObject, pressEvent);

        QKeyEvent *releaseEvent = new QKeyEvent(QEvent::KeyRelease,
                                                Qt::Key_Backspace,
                                                Qt::NoModifier);

        QGuiApplication::postEvent(m_focusObject, releaseEvent);
    }
}
QInputMethodEvent event;
    event.setCommitString("", -1, 1);

    QGuiApplication::sendEvent(m_focusObject, &event);
# start the keyboard UI application
./server

# set the environment variable
export QT_IM_MODULE=vkim

# run any Qt application
designer

22 Comments

7 - Sept - 2015

Daniel

8 - Sept - 2015

Tobias Koenig

9 - Sept - 2015

Daniel

9 - Sept - 2015

Daniel

9 - Sept - 2015

Tobias Koenig

9 - Sept - 2015

Daniel

17 - Sept - 2015

Simon

18 - Sept - 2015

Tobias Koenig

21 - Sept - 2015

Simon

22 - Sept - 2015

Tobias Koenig

22 - Oct - 2015

cristina

26 - Oct - 2015

Tobias Koenig

15 - Mar - 2017

YT

15 - Mar - 2017

Tobias Koenig

16 - Mar - 2017

YT

8 - May - 2017

rober

8 - May - 2017

Tobias Koenig

4 - Oct - 2017

Steven Szczuka

5 - Oct - 2017

Tobias Koenig

21 - Mar - 2018

KCJ

11 - May - 2022

Laszlo Papp

12 - May - 2022

Tobias Koenig

TobiasKoenig

Tobias Koenig

Senior Software Engineer

Learn Modern C++

Learn more