Qt Input Method – Virtual Keyboard Implementing an out-of-process virtual keyboard for text input in Qt apps
In the last episode of this blog series we learned about the overall concepts of input methods in Qt, with a look behind the scenes to see how a key press event from the native windowing system travels through the Qt input stack until it appears on screen inside a QLineEdit. In that context, the input method was used to allow the user to modify the input (e.g. composing a Chinese character sequence from Latin1 input).
Today we want to shed some light on another use case of the input method system, namely how to implement an out-of-process virtual keyboard that can be used to feed text input to arbitrary Qt-based applications.
Why do we need a virtual keyboard?
Believe it or not, there are devices out there which do not have a hardware keyboard attached (you might be holding one in your hand right now, while reading these lines ;)): mobile phones, tablets, kiosk terminals, to name just a few of them. The virtual keyboard (or sometimes called on-screen keyboard), is an application that runs alongside the other applications on the device. Whenever the user wants to input some text for the other applications, the virtual keyboard pops up a window that imitates the keys of a hardware keyboard. The user can now click the key buttons and the virtual keyboard application will convert those to key events, which will be sent to the application window that currently has the input focus. The receiving application processes the events like normal key events, so whether a hardware keyboard or a virtual keyboard is used should be opaque to it.
How do we implement a virtual keyboard with Qt?
There are actually two ways to implement a virtual keyboard with Qt: in-process and out-of-process.
For the in-process approach, the virtual keyboard becomes just another QWidget or QtQuick Item that shows the keyboard buttons, reacts to user interaction and delivers the synthesized QKeyEvents to the application’s event loop. The advantage of this approach is that it is very easy to implement, since no inter-process communication between the keyboard and the application is needed. The disadvantage, however, is that every application has to store its own instance of the virtual keyboard and the state of all these instances (visibility, activity etc.) might somehow need to be synchronized between applications.
The out-of-process approach, on the other hand, allows for a single instance of the virtual keyboard to be shared between all other applications, so no synchronization between multiple virtual keyboard instances is necessary. This requires you to come up with an inter-process communication mechanism between the global virtual keyboard instance and all the applications that want to use it.
In today’s blog post, we’ll go for the out-of-process approach and use DBus as the IPC mechanism, since it requires little code to integrate it with Qt applications and has already proved to work for other input method systems (IBus).
The keyboard application
Our keyboard application is based on Qt Widgets, to minimise the amount of code to write, but the concepts can be adopted to a QtQuick based UI wholesale. The actual UI is really basic, just a QGridLayout with a couple of buttons inside. There is one button for each number, one for each Latin character and a Backspace and Enter button. For simplicity we omit Shift and CapsLock keys (and therefore the functionality of entering upper case characters), implementing that is left as an exercise for the reader ;).
The source code of the main.cpp looks like that:
#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(); }
In the first three lines we include the headers for the classes we want to use: QApplication for the event loop, QDBusConnection to make this application accessible via DBus and keyboard.h, which provides the keyboard UI. In line 11 we try to register our application at the session bus, under the unique name ‘com.kdab.inputmethod’. If that fails, then something is wrong with DBus, so there is no point in running the application, because it won’t be reachable by other applications. In line 16 we create an instance of our keyboard UI, which is encapsulated inside the Keyboard class. Note that we don’t call show() on the Keyboard object here, so after startup, the application won’t show up any window. In line 18 we publish the Keyboard object under the path ‘/VirtualKeyboard’ and make all its public slots and signals accessible. That is all that needs to be done in main.cpp, now let’s have a look at keyboard.h:
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); };
Our Keyboard class inherits from QWidget, so that it can be shown as a window on the screen. Except from a constructor, where all the UI initialization is done, it provides three public slots to show up the keyboard UI, hide it again and query its current visibility. Additionally there are two signals keyClicked() and specialKeyClicked(), which are emitted whenever the user clicked a number or letter button (keyClicked()) or the Backspace or Enter button (specialKeyClicked()).
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++; } }
To ensure that interacting with the keyboard window does not take away the focus from the application window that uses the virtual keyboard, we have to set the Qt::WindowDoesNotAcceptFocus flag. From line 8 on we assemble the UI by creating the QGridLayout and filling it with the keyboard buttons. To avoid code duplication, we store the properties of the buttons (key code and label) in an array and iterate over it in the for-loop in line 16. For each entry we create a QPushButton with a fixed width and set the key label as button text. Since all button clicks should be handled in a central place, we use the QSignalMapper to invoke the private buttonClicked(int key) slot, whenever one of the QPushButtons is clicked. The ‘key’ parameter is the corresponding Qt::Key as defined in the ‘keyboardLayout’ array, which is defined as the following:
#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));
So every entry has the Qt::Key code and the label that should be used. Additionally there are some special marker entries with key code ‘NEXT_ROW_MARKER’, which just ensure that the following buttons will end up on the next line in our QGridLayout.
Now what happens if the user clicks on one of the QPushButtons? The buttonClicked() slot is invoked:
void Keyboard::buttonClicked(int key) { if ((key == Qt::Key_Enter) || (key == Qt::Key_Backspace)) emit specialKeyClicked(key); else emit keyClicked(keyToCharacter(key)); }
If the pressed key is Backspace or Enter, the specialKeyClicked() signal is emitted, otherwise the key code is mapped to a character and the keyClicked() signal is emitted with that character as parameter. To do the mapping, the helper method keyToCharacter() is invoked, which is defined as follows:
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(); }
In this simple keyboard implementation, we just return the label of the button (so a lower-case character or a number). In a more advanced version, you would have to take pressed modifier keys into consideration to map to upper-case or other special characters.
The implementation for the remaining public slots is straightforward. They simply forward the requests to the corresponding methods in the QWidget base class.
void Keyboard::showKeyboard() { QWidget::show(); } void Keyboard::hideKeyboard() { QWidget::hide(); } bool Keyboard::keyboardVisible() const { return QWidget::isVisible(); }
To summarize: We now have a keyboard UI application that is accessible via DBus through the name “com.kdab.inputmethod/VirtualKeyboard”. It provides methods to show it on screen, hide it from screen and ask for its current visibility. Additionally, that application emits the two signals specialKeyClicked() and keyClicked() (also via DBus) whenever the user clicks on one of the QPushButtons.
A simple test as to whether the keyboard UI works as expected, can be done by starting the application and then using qdbusviewer to inspect its DBus interface, invoke the exported slots and log the signal emissions.
Now that we have the virtual keyboard, let’s have a look at the corresponding input method implementation…
The Qt Input Method plugin
In the previous post [add link] of this series we learned that the communication between the Qt text input components and the input method system is done through the public front end class QInputMethod and the private back end class QPlatformInputContext. If we want to provide our own input method, we have to do two things:
- Implement a custom version of QPlatformInputContext subclass
- Implement a QPlatformInputContextPlugin, which provides that subclass to Qt
Let’s start with the latter by having a look at the main.cpp of the plugin:
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; }
We declare a new class QVkImPlatformInputContextPlugin which inherits from QPlatformInputContextPlugin and we reimplement the virtual method create(). The plugin meta data are stored in the external file ‘vkim.json’, which just contains the following lines:
{ "Keys": [ "vkim" ] }
The term ‘vkim’ is short for ‘virtual keyboard input method’ and is used as an identifier for our input method plugin. We will see later on where it is needed.
In the implementation of QVkImPlatformInputContextPlugin::create() we check if the requested object has that identifier and return a new instance of our custom class QVkImPlatformInputContext in this case. QVkImPlatformInputContext is our custom reimplementation of QPlatformInputContext with the following declaration:
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; };
It reimplements five virtual methods from QPlatformInputContext that have the following functionality:
- isValid() is called by QPlatformInputContextFactory (the class that loads the plugin and invokes the create() method) to check if the input method is ready to use
- setFocusObject() is called by QGuiApplication to inform the QPlatformInputContext about the object that currently has input focus
- showInputPanel() is called by QInputMethod::show() to call up the keyboard UI if a text input field retrieves the focus
- hideInputPanel() is called by QInputMethod::hide() to hide the keyboard UI if the current text input field loses focus
- isInputPanelVisible() is called by QInputMethod::isVisible() to inform the application about the visibility state of the keyboard UI
Additionally there are the two private slots, keyboardSpecialKeyClicked() and keyboardKeyClicked(), which are invoked whenever we receive the corresponding signals from our keyboard UI application via DBus. The two member variables store a pointer to the DBus interface and a pointer to the current focus object in the application.
Now let’s have a look at the implementation of that class, where all the magic happens:
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))); }
Inside the constructor, the connection to the keyboard UI DBus interface is established. Here we have to specify the “com.kdab.inputmethod” application ID and “/VirtualKeyboard” path to point to the exported Keyboard object. Since our class needs to be informed about the buttons that the user clicked on the keyboard UI, we connect the two signals keyClicked() and specialKeyClicked() against the two private slots.
bool QVkImPlatformInputContext::isValid() const { return m_keyboardInterface->isValid(); }
Inside the isValid() method we just return whether the DBus interface object is valid. If we couldn’t connect to the keyboard UI application, then our input method wouldn’t be of any use, so this test is sufficient.
void QVkImPlatformInputContext::setFocusObject(QObject *object) { m_focusObject = object; }
In case the QGuiApplication informs us about a new focus object, we store the pointer to it in the member variable, since we need it later on (see below).
void QVkImPlatformInputContext::showInputPanel() { m_keyboardInterface->call("showKeyboard"); } void QVkImPlatformInputContext::hideInputPanel() { m_keyboardInterface->call("hideKeyboard"); }
When QInputMethod::show() or QInputMethod::hide() is invoked by the text input widgets (e.g. QLineEdit or QTextEdit), the showInputPanel() and hideInputPanel() methods are called, which forward these requests via the DBus interface to the keyboard UI process:
bool QVkImPlatformInputContext::isInputPanelVisible() const { const QDBusReply<bool> reply = m_keyboardInterface->call("keyboardVisible"); if (reply.isValid()) return reply.value(); else return false; }
The very same approach is taken for QInputMethod::isVisible(), which invokes isInputPanelVisible(). In that case we also invoke the remote function in the keyboard UI process via DBus, but this time, we have to process the return value as well:
void QVkImPlatformInputContext::keyboardKeyClicked(const QString &characters) { if (!m_focusObject) return; QInputMethodEvent event; event.setCommitString(characters); QGuiApplication::sendEvent(m_focusObject, &event); }
Now we come to the part where our custom QPlatformInputContext forwards the key clicks, as received from the keyboard UI application, to the user application. For textual input that is done with the QInputMethodEvent. So if there is a text input widget that currently has the input focus (m_focusObject != null) we set the typed in characters as commit string on the event and send it to the object.
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); } }
For every non-textual input, in our case the Backspace and Enter key, we synthesize QKeyEvents for the press and release event and post them to the focus object through the event loop. While using a QKeyEvent to deliver, the Enter key is required. The Backspace functionality (removing the character on the left side of the input cursor) could have been implemented with a QInputMethodEvent as well:
QInputMethodEvent event; event.setCommitString("", -1, 1); QGuiApplication::sendEvent(m_focusObject, &event);
Since we are passing values unequal to 0 to the second and third parameter of setCommitString(), the QInputEvent won’t append the given characters to the content of the input widget, but will replace 1 character (replaceLength in 3rd parameter) at position -1 (replaceFrom in 2nd parameter), which means 1 character left of the input cursor position, with an empty string (first parameter).
Bringing it all together
Now that we have the virtual keyboard and the input method plugin that communicates with it, let’s have a look at how to use it with a real Qt application. By default, the used QPA plugin provides an input method for the system, so if we want to use our own one, we have to tell Qt about that explicitly, by setting the environment variable QT_IM_MODULE to the identifier of the input method. In our case that identifier is ‘vkim’, the very same value we stored inside vkim.json as meta data for the input method plugin.
To use Qt Designer with our virtual keyboard just execute the following commands on the Linux command line:
# start the keyboard UI application ./server # set the environment variable export QT_IM_MODULE=vkim # run any Qt application designer
Now as soon as you click in a text input field in Qt Designer, the keyboard UI will show up:
Further improvements
As mentioned before, this example implementation is really basic, to show you the concepts behind that technique without getting lost in details. For a real virtual keyboard that can be used in a production system, you might want to add the following functionality:
- multiple keyboard layouts That basically means showing different labels on the keyboard buttons, depending on the configured layout, and returning these characters in the keyToCharacter() function
- positioning of keyboard window In the current implementation, the position of the keyboard window on screen is selected by the window manager, which normally prevents windows from overlapping. For the virtual keyboard, however, we might want to open it near (below or above) the text input widget, so that we don’t have to move the mouse cursor across the whole screen. With QWidget::mapToGlobal() we can determine the position of the focus widget on the screen and place the UI accordingly, by adding a new ‘void setPosition(int x, int y)’ method to the DBus interface of the Keyboard class.
Resources
The source code of the example can be found at https://github.com/KDAB/virtual-keyboard-demo.
Yes, widgets are still alive and kicking. In the many years we have worked with them, we have gathered quite a number of tips and tricks that we’d love to share with you.
Populating a Combobox from Qt Designer
Using Clang-Format to Ensure Style Guidelines
Un-sorting Headers in QTableView
Using GammaRay to Find the Class of an UI Element
Understanding Contents Margins in Qt Layouts
Starting the Application in GammaRay from Qt Creator
Communicating between a View/Delegate and a Model
Visualizing the Model Stack in GammaRay
Traversing Proxy Models to Get to the Source Model at the Bottom
Changing the Font to Jetbrains Mono in Qt Creator
Using Custom Types with Model/View
Top 7 Shortcuts in Qt Creator
Avoiding QVariant::fromValue around your Own Types
Document Templates in Qt Creator - Part 1
Document Templates in Qt Creator - Part 2
Document Templates in Qt Creator - Part 3
Document Templates in Qt Creator - Part 4
Adapting Column Content to Size
Converting Enums to and from Strings
Qt Designer Plugins (Part 1) - Widgets Promotion
Qt Designer Plugins (Part 2) - Initializing Classes when Using Qt Designer
I think you article is very interesting, and also important for me. I have heavy problems to be able to use a im_module inside Linux Mint for Qt5 programs. You use Qt5 for compile your program ? I tried it, and later i use a test program compiled with Qt5, with only one QLineEdit at center of a QWidget, but without success. A keyboard not appear after run “server” , export and set to vkim and then execute my program with QLineEdit. Have you some idea ? Have used also Qt ? Thanks for any help. Daniel
Hej Daniel,
yes, the linked code is supposed to be compiled with Qt5. Is your IM module installed in the right path? Have a look at http://doc.qt.io/qt-5/deployment-plugins.html#debugging-plugins for further ideas how to debug plugin loading (e.g. QT_DEBUG_PLUGINS), that should give you a hint if the plugin can be found/loaded at all.
Hi Tobias (sprichst Du Deutsch ?).
Thanks for your quick answer. What i do now is from a clean environment, install Qt (from VirtualBox) and see what will happen, because i have too many directories. Previous i use a release version and also check QT_PLUGIN_PATH, .. but without success. I am writing already from a virtual Ubuntu. Daniel
I tell you about the exactly steps what i do :
1) install Ubuntu
2) install Qt on /home/daniel/Qt
3) download virtual-keyboard-demo
4) install “build-essential” and “libgl1-mesa-dev”
5) now i can compile “plugin” and “server” from Qt Creator, which i do with release-mode
6) i check, plugin is released on folder /home/daniel/Qt/5.5/gcc_64/plugins/platforminputcontexts/
7) start “./server”
8) set environment variables. I see at begin, many environment variables are not set except QT_IM_MODULE which is set to ibus. I set :
export QTDIR=/home/daniel/Qt/5.5/gcc_64/
export QT_PLUGIN_PATH=$QTDIR”plugins” (a echo return /home/daniel/Qt/5.5/gcc_64/plugins)
export QT_IM_MODULE=vkim (“echo $QT_IM_MODULE” return “vkim”)
9) i start designer with :
$QTDIR”bin/designer”
Something is wrong, if i click on a QLineEdit, a keyboard not appear, i only can see it with
$QTDIR”bin/qdbusviewer” and playing with :
– click on “com.kdab.inputmethod”
– click on “Virtualkeyboard” at right
– local.server.Keyboard
– double click at Method:showKeyboard (keyboard appear)
– Signal:keyClicked
and every signal is received pushing buttons of this keyboard
Hej Daniel,
you have been right, the code didn’t work with latest Qt 5.5, because the IID for the plugin meta data has changed from “org.qt-project.Qt.QPlatformInputContextFactoryInterface” to “org.qt-project.Qt.QPlatformInputContextFactoryInterface.5.1”.
I have fixed the code in the repository now, instead of hard-coding the string literal, it uses the QPlatformInputContextFactoryInterface_iid define now.
Thank you very much for spotting it! 🙂
Hi Tobias.
I am very happy, because now is working. This will give me hope to make working also “gcin” input method, which we always use.
Very much thanks for your help.
Daniel
Thanks for the interesting article. I am in the process of converting a Qt4 application to run on Qt5 and now have everything working except the On-Screen-Keyboard. As our software runs on proprietary devices and is the only running application that has any user interaction via the screen, I am interested in the in-process approach which you say in your introduction “… is very easy to implement”. I’m struggling to find documentation anywhere on how to do this and wondered if you could point me at any or explain how your implementation would be adapted? I already have the OSK from my Qt4 app so just being able to hook it up would be really good – especially as I was given 1 day to do the whole migration and I’ve already taken 2 (which, all things considered is not too bad considering it has over 400,000 lines of code) 🙂 The OSK is now the only stumbling block. Thanks again.
Hej Simon,
the in-process OSK can be implemented by just creating corresponding QKeyEvent objects, whenever the user presses/releases on one of the OSK button, and deliver them to the current focus object of the application, e.g. QGuiApplication::sendEvent(QGuiApplication::focusObject(), event). You just have to make sure that clicking the button on the OSK won’t let it take the focus (see how it is done on the external OSK in the example in the git repo).
Thanks Tobias – I hope you won’t mind if I ask one more question: In my Qt4 application, after instantiating my own class which inherited from QInputContext all I had to do was call qApp->setInputContext and this ensured that when I touched a UI input element my OSK would pop-up. Is there something similar I can do in Qt5 with my QPlatformInputContext derived class or do I need to hook all of that up manually too?
Thanks again.
Hej Simon,
the QGuiApplication::setInputContext() method has been removed in Qt5. To use a custom input method, you have to implement and install your own input method plugin and make the application using it by setting the QT_IM_MODULE environment variable.
Is there any way you can show how to do the virtual keyboard by doing the in process approach? I am confused how it would be different than the out of process approach.
Hej Christina,
for the in-process version, you’d simply put the virtual keyboard widget into the main application and let it communicate directly with the QVkImPlatformInputContext, so the separate server process and dbus communication is left out.
Hello
It’s very valuable information for me. However I have an issue to build ‘plugin’ project.
When I try ‘qmake’ with Qt 5.7, it has totally no problem, but with Qt 5.8 I always get:
“Project ERROR: Could not find feature reduce_exports”.
From some forums in the internet, I now know it was caused by “load(qt_plugin)”, but I don’t know how to fix it.
Do you have any idea on it?
Thank you.
Hej,
I just pushed a fix to the git repository, which fixes compilation with Qt 5.8, please re-pull.
Great thanks, it works.
BTW, I think there is a description about this kind of tricky issue.
I’m reading http://doc.qt.io/qt-5/plugins-howto.html and related, but I can’t find it.
If I’m right, where could I find such document?
hi:
The virtualkeyboard run well,but When I transplat it to the platform of arm ,it has a vital problem as follows:
QObject::connect: No such signal QDBusAbstractInterface::keyClicked(QString)
Unable to register at DBus
Aborted
Hej,
the error message says it all: ‘Unable to register at DBus’, so your system seems to miss a running DBus daemon where the virtual keyboard could connect to.
Hi Tobias
Thanks so much for a very informative post.
I was able to make it work “in-process” by putting the keyboard class in the plug-in and it’s been working so far. Is there any problem with instantiating widgets in a plug-in?
Thanks
Steve
Hej Steve,
instantiating a widget from a plug-in is perfectly fine (that’s how QtCreator creates most of its UI for example), just make sure that do you not unload the plugin as long a such a widget instance is still around.
To get the server to build on Debian Stretch I had to install `qtbase5-private-dev`.
Hi, nice article. Thanks for sharing.
My only issue with the way you build the keyboard using QGridLayout is that it is fairly limiting to mimic a real keyboard virtually. This is because hardware keyboards do not seem to map well into a QGridLayout. They seem to be more freestyle with alignments.
Do you feel that setGeometry on the QPushButtons is a more practical way to go? That is how I implemented it at least and that resembles a real keyboard (or even other virtual keyboards) closer.
Hej Laszlo,
the article concentrated more about what happens behind the scenes, not so much about making a pretty UI 🙂 So sure, in an actual product software the keyboard UI should adapt to real keyboard layouts and support switching between various ones.