Assigning Keyboard Shortcuts to QML Components
Question
I am deep into building a Desktop Application with QML and Qt Creator and I am currently researching keyboard handling and how it works with QML elements. I am already aware of the lack of proper QML replacements for Desktop Widgets.
My current problem is that I wish to assign some global keyboard shortcuts to some particular QML components (like assigning keyboard shortcuts to buttons on the GUI) which should activate them. The best I could manage is to use FocusScopes and Key Navigation to be able to just navigate the GUI via keyboards, but this isn't the same thing.
Can anyone suggest what to do in this scenario? Is there any such feature coming in with Qt 5? I couldn't find any information on this on the Internet.
Solution
Answering my own question as the Shortcuts are now possible to implement in Qt 5.1.1.
Shortcuts can be easily bound to QtQuick
controls like Button
, ToolButtons
and MenuItem
using the QML Action
item. e.g. :
ApplicationWindow {
...
ToolButton { action: openAction } // Add a tool button in a ToolBar
...
Action {
id: openAction
text: "&Open"
shortcut: "Ctrl+O"
onTriggered: // Do some action
tooltip: "Open an image"
}
}
Pressing Ctrl+O will execute the action specified in the onTriggered section.
Refer to Qt Quick Controls Gallery example
OTHER TIPS
You can totally use shortcut in QML by using EventFilter in C++(Qt).
You can do by below steps:
1. Create a Shortcut class by C++.
2. Register QML Type for Shortcut class
3. Import Shortcut to QML file and handle it.
#ifndef SHORTCUT_H
#define SHORTCUT_H
#include <QDeclarativeItem>
class Shortcut : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant key READ key WRITE setKey NOTIFY keyChanged)
public:
explicit Shortcut(QObject *parent = 0);
void setKey(QVariant key);
QVariant key() { return m_keySequence; }
bool eventFilter(QObject *obj, QEvent *e);
signals:
void keyChanged();
void activated();
void pressedAndHold();
public slots:
private:
QKeySequence m_keySequence;
bool m_keypressAlreadySend;
};
#endif // SHORTCUT_H
#include "shortcut.h"
#include <QKeyEvent>
#include <QCoreApplication>
#include <QDebug>
#include <QLineEdit>
#include <QGraphicsScene>
Shortcut::Shortcut(QObject *parent)
: QObject(parent)
, m_keySequence()
, m_keypressAlreadySend(false)
{
qApp->installEventFilter(this);
}
void Shortcut::setKey(QVariant key)
{
QKeySequence newKey = key.value<QKeySequence>();
if(m_keySequence != newKey) {
m_keySequence = key.value<QKeySequence>();
emit keyChanged();
}
}
bool Shortcut::eventFilter(QObject *obj, QEvent *e)
{
if(e->type() == QEvent::KeyPress && !m_keySequence.isEmpty()) {
//If you want some Key event was not filtered, add conditions to here
if ((dynamic_cast<QGraphicsScene*>(obj)) || (obj->objectName() == "blockShortcut") || (dynamic_cast<QLineEdit*>(obj)) ){
return QObject::eventFilter(obj, e);
}
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(e);
// Just mod keys is not enough for a shortcut, block them just by returning.
if (keyEvent->key() >= Qt::Key_Shift && keyEvent->key() <= Qt::Key_Alt) {
return QObject::eventFilter(obj, e);
}
int keyInt = keyEvent->modifiers() + keyEvent->key();
if(!m_keypressAlreadySend && QKeySequence(keyInt) == m_keySequence) {
m_keypressAlreadySend = true;
emit activated();
}
}
else if(e->type() == QEvent::KeyRelease) {
m_keypressAlreadySend = false;
}
return QObject::eventFilter(obj, e);
}
qmlRegisterType<Shortcut>("Project", 0, 1, "Shortcut");
import Project 0.1
Rectangle {
.................
.................
Shortcut {
key: "Ctrl+C"
onActivated: {
container.clicked()
console.log("JS: " + key + " pressed.")
}
}
}
Starting from Qt 5.9 the desired behavior is even included:
import QtQuick 2.9
Item {
Shortcut {
context: Qt.ApplicationShortcut
sequences: [StandardKey.Close, "Ctrl+W"]
onActivated: {
container.clicked()
console.log("JS: Shortcut activated.")
}
}
}
If you omit the context, it will only work for currently active windows, otherwise for the entire application, see the documentation.
So assuming you are calling a function on that button click event like this,
Button {
...
MouseArea {
anchor.fill: parent
onClicked: callThisFunction();
}
}
Then you can assign assign global keyboard shortcuts in this way. But the limitation is the Global QML element (a parent element which holds all other QML elements) should have the focus. Ex. :
Rectangle {
id: parentWindow
...
...
Button {
...
MouseArea {
anchor.fill: parent
onClicked: callThisFunction();
}
}
Keys.onSelectPressed: callThisFunction()
}
This is not exactly what you want but it may help.