Hi,
In my previous post i was using QShortcut to get application wide shortcuts. I didn’t like that route because it depends on QWidget, but i didn’t see any other option back then. After a little bit of discussion on how to get the same stuff working without QShortcut (thus no QWidget dependency) i started trying the same using Qt’s event filter system. That turned out to work rather nice! Because there is no more need for QWidget, the somewhat ugly trick to put the QWidget based mainwindow in a global class isn’t needed anymore. The C++ side looks as follows:
shortcut.cpp
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include "shortcut.h" #include <QKeyEvent> #include <QCoreApplication> #include <QDebug> 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()) { 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); } |
And the header, shortcut.h:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#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 |
Making it available in QML is done the same way as in the previous post:
|
1 2 3 |
... qmlRegisterType<Shortcut>("Project", 0, 1, "Shortcut"); ... |
The reason that this works is because of the re-implemented eventFilter function. That filter listens for key events and if it finds one (and if that one matches the one we registered) then a signal is emitted to indicate the application that the registered key has been pressed. It’s really that simple. The filter is registered in the class constructor through qApp->installEventFilter(this); That’s all i needed to do to get is working.
In QML it still looks as simple as this:
|
1 2 3 4 5 6 |
Shortcut { key: "Ctrl+C" onActivated: { console.log("JS: " + key + " pressed.") } } |
Right now this is not part of any KDE QML imports, but i do want to try and get this specific component in QML. It will help other applications as well if they happen to need this functionality.
Cheers,
Mark
Update
Updated the code to reflect all the suggestions from the comments. The QML side still works exactly the same, but has an additional signal that will let you know when the key has been changed. Changed! not triggered! That is still a separate signal. Also added a bool: m_keypressAlreadySend in an attempt to prevent double signals which was happening all the time.

Wait, does this mean that there is no standard out-of-the-box way to use shortcuts in qml? What’s the point of this technology then? Just an experiment from Nokia with KDE as the main (free) tester? Awesome business they are doing there.. *sigh*
I don’t know.. I only needed this feature and it wasn’t there. So i made it :)
Is there any chance this makes its way into the official Qt repository?
Ask the Qt developers :)
The way it works now, i.e. with the Open Governance in place and Qt being developed cooperatively at qt-project.org, is that developers with working additions can upload changes to a review system and eventually get those changes merged into Qt proper.
@kevin I know, but i don’t plan on adding something in Qt. I just don’t have the time for it anymore. (new job since the beginning of last monday). I keep developing my projects, but going through the progress of getting anything in Qt is something i gladly leave to those that have a bit more time.
If anyone else wants to do that, feel free to do so. The code is as free as it can get. No licence at all, just free for whatever one wants to do with it.
As for this component ending up in QML.. I’m not _that_ sure if that’s a good idea. Sure, something of “application based shortcuts” should be available in QML, but if that’s done then it should be done right with the addition of mouse shortcuts as well. So that shortcuts like “CTRL + Left Mouse Button” become possible.
There is a UiAction class within the “UI Helpers” project repository, but I’ve no idea if it serves EXACTLY the same purposes. (I’m a clueless old man.) However, it clearly can support the multi-platform nature of the QKeySequence::StandardKeys — something which your Class does not attempt to do.
- – - -
Now, WRT your statement: “If anyone else wants to do that, feel free to do so…..”
I’ll proceed to investigate as a Qt change, but I can’t submit something which created. The Classes must be Copyright by both of us, if I expand your work to also handle a “MouseSequence” or “MultiSequence”. It would probably be best for you to sign up as a contributor and submit this on your own.
Preliminary thinking: A “MouseSequence” would be a single mouse event, and would occur upon either Button Release, Button Press, or Button DoubleClick. The MouseSequence would support held modifiers on the keyboard, and also held mouse buttons (LeftButton, RightButton, or MiddleButton). The Qt::MouseButton of the MouseSequence cannot be present among the Qt::MouseButtons (held buttons) flags. But for example, Release of Qt::XButton1 (i.e., the “Back” button) while holding down Qt::LeftButton is a _different_ MouseSequence than Click(Release) of Qt::BackButton by itself. And DoubleClick of Qt::RightButton+Alt is different than DoubleClick of Qt::RightButton without the keyboard modifier.
But all of those examples are single events, quite easy for me to check the moment after they happened. In contrast, a “MultiSequence” would be a MouseSequence, followed by a KeySequence (EMacs Style, not simultaneous). In order to recognize the MultiSequence, the shortcut must first satisfy the mouseEvent, set another flag (perhaps “m_MouseSequenceWasDone”), which can become a conditional to allow testing of the next keyPress to try for a full match (and emit of the shortcut signal).
Feel free to EM, anytime.
@rickst29
As for StandardKey support. That’s not as easy as it sounds. Even if i let my class inherit from QKeySequence as well and add Q_ENUMS(StandardKey) even then i can’t access it in QML like so: Shortcut. … It does work if i add the StandardKey enum to my class. So to get that working some casting to different enums would be needed i guess. Not something i’d like to do.
As for the rest. Adding mouse support is certainly not easy. For keys and keys alone it’s relatively easy. All information that i need to know is available in one event in the QKeyEvent object. If i (or you) want to add in mouse support then that will go over multiple events (a key event and a mouse event) so there would have to be some mechanism to temporary store one of the two and see if a full “sequence” can be somposed when the other event comes in.. That’s tricky stuff! Actually, doing this would be a close to re-creating the private QShortcutMap class.
nokia? you are a bit out of date, no?
Why, it was them who started this qml story. Let’s hope the new owner is smarter.
Stop here! I don’t want this to turn into a “QML sucks” discussion. Keep on the subject which is application wide shortcuts in QML.
One thing that gets easily overlooked is that QML is just a declarative language with an engine capable of handling documents written in it.
It can be used with any form of component set, UI or non-UI alike. The one we are most likely dealing with here, QtQuick 1, is just a basic set demonstrating the power of QML for QGraphicsView based UI.
Actual product component sets like Plasma Components, the N9 UI components or BlackBerry10′s Cascades components usually come with all facilities needed for their respective use case.
Mark demonstrates here nicely that any such component set can easily be extended or augmented, by library developers as well as application developers
Yeah, QtQuick 2 has a lot more power :) Can’t wait to actually use it! (i know, the RC is released).
Yet it still also lacks a lot!
- Advanced drag/drop support is nowhere to be found
- There is very poor integration with existing C++ models that are assigned to QML. For example, it doesn’t play well with add/delete animations. That only works well if you use the QML build in models. When i need C++ models + animations i just “hack” around it.. But it certainly could be improved a lot. And it doesn’t look like Qt5 is doing any better here though i still have to try it :)
- .. just keeping it to two otherwise i’m complaining too much :)
doesn’t that crash like crazy? you are static casting all events to a qkeyevent, looks wrong
Nope, works just fine. The example of listening for keys like this in the eventFilter function actually comes from the Qt documentation http://qt-project.org/doc/qt-4.8/eventsandfilters.html#event-filters I changed it only slightly, but certainly not the casting.
You realize the example checks the event type before doing the casting, right?
oops, i did have the “e->type() == QEvent::KeyPress” in the first if, but apparently removed it as well at some point in time. Will add that back :)
It seems you moved it down to the emit
You should call QDeclarativeItem::eventFilter() not QObject::eventFilter() inside your own reimplementation, otherwise every custom event handling in QDeclarativeItem would be ignored. And as Albert pointed out correctly, check the event type before casting it! ;)
Some additional suggestions:
*include QCoreApplication so you don’t have to change it when eventually porting to Qt5 (QApplication is part of Qt5′s QtWidgets module)
* for consistency, change the return value of the property getter to match the type of the property and setter argument
* Since this is a QML item, you really should have a NOTIFY signal for the property and emit it from setKey() when appropriate
* You don’t have to check canConvert, if value() encounters anything other than a QKeySequence it will return a default constructed one. so it already does what you are doing here internally
* Since this item does not need any UI itself, you could consider subclassing QObject
Fixed.
Fixed.
Fixed.
Fixed.
Fixed. I didn’t know that QObject only inheritance was enough for an item to be available in QML like:
Shortcut {
…
}
Thanks a lot for telling me! Guess i have to change some other classes as well :p
I will update my main post in a minute to reflect all the fixes.
And since we are being pedantic your key() method would be better as const :-)
hehehe :p nitpicking huh ;)
I “might” add that in.. Depends on if i remember it.
By application wide, you mean accessible only from the qml application or
global wide (global shortcut in the KDE desktop ?)
If this is not for global shortcuts can you provide some links about how could this be done for a clean qml plasmoid?