Unsolved Custom gestures and custom gesture events - when and how to properly use the registered gesture type
-
This is a continuation of this previous post of mine and can be viewed as Part 2 of the saga "How to implement custom gestures?".
I have successfully mapped the
Swipe gesture
from my device to the Qt API using theQSwipeGesture
. In addition to that now I am integrating a custom gesture calledAir Wheel
(spinning your finger in mid-air above the device produces this type of gesture). In order to do that I've decided to use a combination of a customQGestureRecognizer
(with an implementation of therecognize(...)
method) which is responsible for updating the state of the gesture and a customQGestureEvent
which delivers the gesture to both the recognizer and my widget (that updates its UI according to the state of the data inside that gesture). It took me a couple of days to get things working and there is still an issue which I am unable to figure out how to solve namely the usage of the registered type of my custom gesture (retrieved when usingregisterRecognizer(...)
). Before I go into detail in the list below I have listed the steps I undertook to map the device's gesture to the Qt Gesture API:- Retrieve data from device and convert it to a more Qt
C++
friendly format (the library used for communicating with the device is written inC
) - Send data to a
GestureManager
via a signal. The purpose of the this manger is to have a dedicated slot for each gesture I want to map inside which it generates a built-in (example:QSwipeGesture
) or generic (QGesture
) gesture object, populates it with data (in case of a custom gesture the genericQGesture
is populated with dynamic property(ies)) and sends a built-in or customQGestureEvent
to a given receiver - Inside the
void GestureManager::slotAirWheelGesture(int counter)
create a genericQGesture
(since I'm usingQGestureRecognizer::recognize(...)
I don't need to subclassQGesture
and implement the required methods for it) and add a dynamic propertycounter
which contains the counter data that is part of theAir Wheel
gesture (spinning your finger/hand clock-/counterclockwise increases/decreases thecounter
) (see code snippet below) - Inside the
void GestureManager::slotMapAirWheelToQGesture(int counter)
create a customQAirWheelGestureEvent
(its type is set toQEvent::User + 1
that is1001
in the class declaration of the event) calledairWheelEvent
, attach the gesture to it and post it usingQCoreApplication::postEvent(receiver, airWheelEvent)
wherereceiver
is set to the widget that will act upon the gesture event
void GestureManager::slotMapAirWheelToQGesture(int counter) { if (receiver == Q_NULLPTR) { cout << "Receiver not set. AirWheel gestures will not be mapped and propagated" << endl; return; } else if (!airWheelEnabled) { cout << "AirWheel is not enabled." << endl; return; } cout << "GestureManager: receiver \"" << receiver->objectName().toStdString() << "\"" << endl; QGesture* gesture = new QGesture(); gesture->setProperty("counter", counter); // cout << gesture->property("counter").value<int>() << endl; QList<QGesture*> gestures; gestures.append(gesture); QAirWheelGestureEvent *airWheelEvent = new QAirWheelGestureEvent(gestures); QCoreApplication::postEvent(receiver, airWheelEvent); }
-
QAirWheelGestureRecognizer
processes the event as well as the widget which owns the recognizer, has registered it and accepts the gesture type theregisterRecognizer(...)
has returned:this->airWheelRecognizer = new QAirWheelGestureRecognizer(); // Create an instance of the custom recognizer Qt::GestureType airWheelGestureType = QGestureRecognizer::registerRecognizer(this->airWheelRecognizer); // Register the custom recognizer and retrieve the automatically generated gesture type this->airWheelRecognizer->setGestureType(airWheelGestureType); // Store the type inside the recognizer for further usage grabGesture(airWheelGestureType); // Enable grabbing of the custom gesture
The custom
QGestureRecognizer::Result QAirWheelGestureRecognizer::recognize(QGesture* state, QObject* watched, QEvent* event)
processes a received event as follows:
QGestureRecognizer::Result QAirWheelGestureRecognizer::recognize(QGesture* state, QObject* watched, QEvent* event) { // If event is not a gesture let it be if(event->type() != QEvent::Gesture) { return Ignore; } // Event is a gesture event so try to cast it to the QAirWheelGestureEvent QAirWheelGestureEvent*ge = dynamic_cast<QAirWheelGestureEvent *>(event); if(!ge) { return Ignore; } // Check if it's a custom gesture; NOTE: ge->gesture(this->gestureType) doesn't work!!! QGesture *airWheelGesture = ge->gesture(Qt::CustomGesture); if(!airWheelGesture) { return Ignore; } Qt::GestureType type = airWheelGesture->gestureType(); if(type == this->gestureType-1) { // THIS IS WHAT'S BOTHERING ME! // ... // Update counter property of `state` using the data from the `event` return FinishGesture; } else { return Ignore; } }
Now back to the issue. The documentation clearly states that
User-defined gestures are registered with the QGestureRecognizer::registerRecognizer() function which generates a custom gesture ID with the Qt::CustomGesture flag set.
I'm obvously using this here as well as in the
event(QEvent *event)
of my widget:bool Widget::event(QEvent* event) { if (event->type() == QEvent::Gesture) { bool res = gestureEvent(static_cast<QGestureEvent*>(event)); // Do not propagate the event further in this case (don't call QWidget::event(event)) return res; } return QWidget::event(event); } bool Widget::gestureEvent(QGestureEvent* gestureEvent) { QGesture* gesture = Q_NULLPTR; if ((gesture = gestureEvent->gesture(Qt::SwipeGesture))) { bool res = gestureSwipeTriggered(static_cast<QSwipeGesture*>(gesture)); return res; } else if ((gesture = gestureEvent->gesture(this->airWheelRecognizer->getGestureType()))) { // THIS HERE WORKS! bool res = gestureAirWheelTriggered(gesture); // Handle air wheel gesture in relation to the UI return res; } return false; }
I've added the
if(type == this->gestureType-1)
inside my recognizer because thetype
(fromQt::GestureType type = airWheelGesture->gestureType();
) is set to256
(which is0x100
as inQt::CustomGesture
) whilethis->gestureType
(member of the custom recognizer) is set to257
(the return value of theregisterRecognizer(...)
.The following questions pop into mind working with the code I've written:
- Why am I unable to use the registered type inside my
recognize(...)
but at the same time using that very same type inside myevent(...)
works? - Is it enough that I'm casting to a
QAirWheelGestureEvent
and the cast doesn't fail to make a solid case that this event indeed contains my gesture with its special dynamic properties? AQGestureEvent
actually contains a list ofQGesture
objects and from what I understand its not forbidden to have multiple different gestures in that list especially if I use the generic implementation of theQGesture
and apply dynamic properties to it. - Do I really need to check for the
type
of the gesture object inside myrecognize(...)
then in order to make sure that the gesture's type is the same as the registered one? If yes, then how do I do that? Usingthis->gestureType - 1
is definitely not a solution since, depending on the order which the various recognizers are registered in, thegestureType
will be different. Obviously I check for the validity of thecounter
property by callingif(airWheelGesture->property("counter").isValid())
which does make sure thatcounter
is present however doesn't exclude the chance that it's another different gesture which coincidentally also contains the given property (along with others or even just that very same one).
I'd like to make this mapping application-independent that is allow for any Qt application that can use gestures and has access to the device I'm mapping to use my custom gestures.
One extra question handling the topic of custom gestures:
The way the recognition for custom gestures works is somewhat of a mystery when it comes to the provided API documentation in the Qt docs. The other path one can take when handling such this particular scenario is to subclass
QGesture
(implementing itsreset()
method). This definitely provides a much better type-checking in terms of what gesture is actually stored inside the list of gestures that the customQAirWheelGestureEvent
delivers. However I have yet to discover when thecreate()
method of the custom recognizer is called. I tried it last week and it appeared that it was called all the time even when I didn't touch the application at all. - Retrieve data from device and convert it to a more Qt