Proxy mouse/touch events
-
There has been some prior discussion, e.g.
"fake events":http://stackoverflow.com/questions/18849663/generating-simulating-fake-mouse-events-in-qt
but I am trying to get this working correctly with Qt5.4 and QQuickView. The simple project below demonstrates. A remote or proxy cursor moves within the main window and if it coincides with an UI element such as a button, a MouseEvent is generated that fakes a users action. The position of the proxy click can be anywhere but the event generated uses the position of the proxy cursor. So the button gets pressed.
The ClickSimulator class creates an event and sends it to an UI element. This appears to work but the event is never received by the QML id realbutton.
I am using prebuilt Qt5.4 and I am targeting multiple platforms. This is a genrally useful utility so I hope someone has done this and can spot some problem in my code.
main.cpp
@#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include "clicksimulator.h"int main(int argc, char *argv[])
{
QApplication app(argc, argv);QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); QObject *topLevel = engine.rootObjects().value(0); QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel); QQuickView *view = new QQuickView; view->setParent(window); // Setup the finger click handler ClickSimulator sim(window); sim.connect(window, SIGNAL(newfingerpos(int, int)), SLOT(proxyClick(int, int))); QObject::connect(view->engine(), SIGNAL(quit()), view, SLOT(close())); return app.exec();
}
@main.qml
@import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
visible: truesignal newfingerpos(int x, int y) // This is the overlay that catches the finger clicks Button { objectName: "overlaybutton" anchors.fill: parent opacity: 0.3 z: 2 MouseArea { anchors.fill: parent propagateComposedEvents: false onClicked: { mouse.accepted = true console.log("overlay tapped") newfingerpos(50, 50) } } } // This is the 'real' button but it will only be pressed from the newfingerpos signal Button { id: button objectName: "realbutton" width: 100; height: 100 x:25; y:25 text: qsTr("Press Me") z: 1 MouseArea { anchors.fill: parent onClicked: { console.log("button pressed by proxy event") console.log("Mouse x: ", mouseX, "Mouse y: ", mouseY) } } }
}
@clicksimulator.h
@#pragma once#include <QObject>
#include <QQuickView>class ClickSimulator : public QObject
{
Q_OBJECTpublic:
explicit ClickSimulator(QQuickWindow* viewer, QObject *parent = 0);public slots:
void proxyClick(int, int);private:
QQuickWindow* _viewport;};
@clicksimulator.cpp
@#include "clicksimulator.h"#include <QMouseEvent>
#include <QQuickItem>ClickSimulator::ClickSimulator(QQuickWindow* viewport, QObject *parent) :
QObject(parent),
_viewport(viewport)
{
}void ClickSimulator::proxyClick(int x, int y)
{
if (_viewport != NULL) {
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(x, y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QQuickItem* controlarea = _viewport->findChild<QQuickItem*>("realbutton");
const bool isSent = _viewport->sendEvent(controlarea, &pressEvent);
qDebug() << "'Press' at (" << x << "," << y << ") successful? " << isSent;QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint(x, y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); const bool isRlseSent = _viewport->sendEvent(controlarea, &releaseEvent); qDebug() << "'Release' at (" << x << "," << y << ") successful? " << isRlseSent; _viewport->update(); }
}
@mousetest.pro
@TEMPLATE = appQT += qml quick widgets
SOURCES += main.cpp
clicksimulator.cppRESOURCES += qml.qrc
Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
Default rules for deployment.
include(deployment.pri)
HEADERS +=
clicksimulator.h
@