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.

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top