PDA

View Full Version : Synthetic mouse events got "stuck" at position of firstly generated event



yurumi
20th August 2015, 22:54
Hi all,

I'm trying to generate mouse events using QGuiApplication::postEvent() and pass them to a QML scene. While the first event is processed correctly, following events are somehow stuck at the position of the first event. For example assume there are two MouseAreas left and right. When the first event is created inside the left MouseArea, onPressed() and onReleased() of this area are triggered. When the next event is created inside the right MouseArea, still the onPressed() and onReleased() of the left MouseArea are triggered. If I create an event with a real mouse the event occurs on the position of the last synthetic event. Anyhow, (solely) the next synthetic event is correct again. Also, after redrawing the window the next synthetic event is correct.

Besides postEvent() I tried sendEvent() and notify() in combination with flush(), sendPostedEvents() and sync() (although the events seem to be delivered - just at the wrong position). I also send the events to a receiver class derived from QObject with an overridden event() method: the coords of the events were correct.

Here is some sample code implementing the setup described above. Mouse events are generated alternating left and right every 3s. After a press event, a release event is created 0.3s later. If a MouseArea is pressed it turns green otherwise it is red. Real mouse events or moving the mouse over the rects cause the next synthetic event to be at the correct position.

mouseeventgenerator.h:


#ifndef MOUSEEVENTGENERATOR_H
#define MOUSEEVENTGENERATOR_H

#include <QtCore>

class QQuickView;

class MouseEventGenerator : public QObject
{
Q_OBJECT
public:
explicit MouseEventGenerator(QQuickView *viewer = 0);

public slots:
void generateMouseEvent();
void generateReleaseEventLeft();
void generateReleaseEventRight();

protected:
void postEvent(int x, int y, int gesture_code);

QQuickView* m_viewer;
QString m_lastSide;
};

#endif // MOUSEEVENTGENERATOR_H


mousegenerator.cpp:


#include "mouseeventgenerator.h"
#include <QtCore>
#include <QGuiApplication>
#include <QApplication>
#include <QtQuick/QQuickView>

MouseEventGenerator::MouseEventGenerator(QQuickVie w *viewer)
: QObject(),
m_viewer(viewer),
m_lastSide("")
{
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(generateMouseEvent()));
timer->start(3000);
}

void MouseEventGenerator::generateMouseEvent(){
if(m_lastSide == "right"){
postEvent(220, 220, 1);
QTimer::singleShot(300, this, SLOT(generateReleaseEventLeft()));
m_lastSide = "left";
}else{
postEvent(420, 220, 1);
QTimer::singleShot(300, this, SLOT(generateReleaseEventRight()));
m_lastSide = "right";
}
}

void MouseEventGenerator::generateReleaseEventLeft(){
postEvent(220, 220, 0);
}

void MouseEventGenerator::generateReleaseEventRight(){
postEvent(420, 220, 0);
}

void MouseEventGenerator::postEvent(int x, int y, int gesture_code){
if(gesture_code == 1){
QMouseEvent *pressEvent = new QMouseEvent(QEvent::MouseButtonPress, QPoint(x, y),
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QGuiApplication::postEvent(m_viewer, pressEvent);
}
else if(gesture_code == 0){
QMouseEvent *releaseEvent = new QMouseEvent(QEvent::MouseButtonRelease, QPoint(x,y),
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QGuiApplication::postEvent(m_viewer, releaseEvent);
}
}


main.qml:


import QtQuick 2.4

Item {
width: 640
height: 480
visible: true

Rectangle {
id: background
anchors.fill: parent
color: "black"
}

Rectangle {
id: rectLeft
x: 200
y: 200
width: 100; height: 100
color: "red"

MouseArea {
anchors.fill: parent
onPressed: {
parent.color = "green"
}
onReleased: {
parent.color = "red"
}
}
}

Rectangle {
id: rectRight
x: 400
y: 200
width: 100; height: 100
color: "red"

MouseArea {
anchors.fill: parent

onPressed: {
parent.color = "green"
}
onReleased: {
parent.color = "red"
}
}
}

}


main.cpp:


#include <QGuiApplication>
#include <QtQuick/QQuickView>
#include <QQmlContext>
#include "mouseeventgenerator.h"

int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;

MouseEventGenerator *generator = new MouseEventGenerator(&view);

view.setResizeMode(QQuickView::SizeRootObjectToVie w);
view.setSource(QUrl("qrc:/main.qml"));
view.show();

return app.exec();
}


Any help is highly appreciated!

Cheers,
Thomas

PS: I'm using Qt 5.4.2 and gcc 5.1.
PPS: Maybe this is somehow connected? [URL="http://http://www.qtcentre.org/threads/61989-Stop-mouse-event-and-start-on-another-widget"]

anda_skoa
21st August 2015, 10:31
Maybe you need move events?

Cheers,
_

yurumi
21st August 2015, 12:08
Thanks for the suggestion. I tried adding move events after each press and release event – to no avail.

But somehow you could be right. I examined the mouse.x and mouse.y properties for the MouseArea::onPressed(). They are relative to the receiving item. For the synthetic events they also seem to be relative to the first item that was clicked(?). For example:

qml: PRESSED MouseArea RIGHT: 20::20 <-- simulated press event in the right MouseArea
qml: PRESSED MouseArea RIGHT: -180::20 <-- simulated press event in the left MouseArea

In this case all events are received by the right MouseArea as if there is some kind of active focus for mouse events. Subsequently, I examined the MouseArea::entered() and exited() signals which seemed to be send correctly (press event --> onEntered triggered, release event --> onExited triggered).

Thanks for any clue,
Thomas

anda_skoa
21st August 2015, 13:14
You can install an event filter on the application object, it will "see" all events that happen in the application.

Cheers,
_

yurumi
21st August 2015, 15:47
Thanks for the quick reply! In fact you were so fast that you overtook me while I was editing my previous post (please have a look above).

In the meantime I installed a custom event filter and compared the real mouse movement events with my generated events. The only differences for the real input are quite alot of MouseMove events and some CursorChange events when the mouse passes the MouseAreas/Rects. I recreated the MouseMove events more sophisticated, so that there is a trail of events from/to the origin before/after each press/release event. I even created CursorChange events everytime the origin is reached. Still doesn't work :-(

When comparing the real mouse movement with the simulated one, there is another difference: When the simulated move events enter/exit the MouseArea which was previously clicked, onEntered() / onExited() are triggered (not so for the other MouseArea). This also suggests that there is some kind of unreleased focus. I stumbled upon grabMouse() and releaseMouse() for QWidget. The pendant for QQuickItems seems to be ungrabMouse(). I tried creating QEvent::UngrabMouse events which somehow sounds promising. Also no luck :crying:

Maybe it would be better to create QTouchEvent, but this seems to be quite difficult: https://forum.qt.io/topic/46140/generate-qtouchevents-to-simulate-touches/12

yurumi
24th August 2015, 21:20
...just to close this topic: Although not fully satisfying, I figured out a workaround for the above mentioned problem.

For every MouseArea add:


...
onReleased: {
...
parent.visible = false
parent.visible = true
}

so the mouse event somehow gets "reset"...

velorums
30th December 2015, 11:36
I've found that you can get the item that grabbed the mouse via QQuickWindow::mouseGrabberItem(). Then you have to do the bound checking and call QQuickItem::ungrabMouse() when needed.

It's probably somehow related to the implementation of dragging.