如何在QML中模拟鼠标点击?
我正在尝试将 QMouseEvent 发送到当前显示的 QML 对象。 QApplication::sendEvent()
总是返回 false
,这意味着我的事件没有得到处理;但我不明白为什么。
也许我将事件发送给了错误的对象?我应该将其发送到哪里? 我也尝试过使用 QGraphicsSceneMouseEvent 而不是 QMouseEvent,但也没有运气。
我尝试使用调试器单步执行事件代码,但它太复杂了,我无法看出它为什么不起作用。
背景
我正在开发一款通过简单的触摸屏进行控制的软件。我通过以太网获取触摸事件,我想从中合成鼠标单击事件。 这样,软件将在目标设备上以与开发人员 PC 上相同的方式进行控制。
更新
- 正如 fejd 所指出的,点击代码是在 QApplication::Exec() 之前执行的,因此我将其移至计时器处理程序中,该处理程序将在
exec()
执行时触发跑步。 - 添加了按预期工作的特定于 Windows 的代码。
- 在 Qt 中添加了更多尝试,无论
sendEvent()
返回true
还是false
,这些尝试都不起作用。
到目前为止我有这个:
main.cpp
#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"
#include "clicksimulator.h"
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/qmlClickSimulator/main.qml"));
viewer.showMaximized();
ClickSimulator sim(&viewer);
QTimer timer;
sim.connect(&timer, SIGNAL(timeout()), SLOT(click()));
timer.start(100);
return app.exec();
}
clicksimulator.h
#ifndef CLICKSIMULATOR_H
#define CLICKSIMULATOR_H
#include <QObject>
class QmlApplicationViewer;
class ClickSimulator : public QObject
{
Q_OBJECT
QmlApplicationViewer* m_viewer;
public:
explicit ClickSimulator(QmlApplicationViewer* viewer, QObject *parent = 0);
public slots:
void click();
};
#endif // CLICKSIMULATOR_H
clicksimulator.cpp
#include "clicksimulator.h"
#include "qmlapplicationviewer.h"
#include <QMouseEvent>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QApplication>
#include <QGraphicsScene>
#include <QTest>
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501
#include "Windows.h"
ClickSimulator::ClickSimulator(QmlApplicationViewer* viewer, QObject *parent) :
QObject(parent)
, m_viewer(viewer)
{
}
void ClickSimulator::click()
{
if (NULL != m_viewer)
{
const int x = qrand() % 500 + 100, y = qrand() % 500 + 100;
{
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(x, y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
// QMouseEvent pressEvent(
// QEvent::MouseButtonPress,
// QPoint(x, y),
// Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
const bool isSent = QApplication::sendEvent(m_viewer->scene(), &pressEvent);
qDebug() << "'Press' at (" << x << "," << y << ") successful? " << isSent;
}
{
QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
pressEvent.setScenePos(QPointF(x, y));
pressEvent.setButton(Qt::LeftButton);
pressEvent.setButtons(Qt::LeftButton);
QGraphicsItem* item = m_viewer->itemAt(x, y);
const bool isSent = m_viewer->scene()->sendEvent(item, &pressEvent);
//const bool isSent = QApplication::sendEvent(m_viewer->scene(), &pressEvent);
qDebug() << "'Press' at (" << x << "," << y << ") successful? " << isSent;
}
// This platform specific code works...
{
const double fScreenWidth = ::GetSystemMetrics( SM_CXSCREEN )-1;
const double fScreenHeight = ::GetSystemMetrics( SM_CYSCREEN )-1;
const double fx = x*(65535.0f/fScreenWidth);
const double fy = y*(65535.0f/fScreenHeight);
INPUT inp[3];
inp[0].type = INPUT_MOUSE;
MOUSEINPUT & mi = inp[0].mi;
mi.dx = fx;
mi.dy = fy;
mi.mouseData = 0;
mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
mi.time = 0;
mi.dwExtraInfo = 0;
inp[1] = inp[0];
inp[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
inp[2] = inp[0];
inp[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(3, inp, sizeof(INPUT));
}
}
}
main.qml
import QtQuick 1.0
Rectangle {
width: 360
height: 360
Text {
id: text1
text: qsTr("This text is placed at the click coordinates")
}
MouseArea {
id: mousearea1
anchors.fill: parent
onClicked: {
console.log("click at " + mouse.x + ", " + mouse.y);
text1.pos.x = mouse.x;
text1.pos.y = mouse.y;
}
}
}
输出
'Press' at ( 147 , 244 ) successful? false
'Press' at ( 147 , 244 ) successful? true
I'm trying to send a QMouseEvent
to the QML objects being currently displayed.
The QApplication::sendEvent()
always returns false
meaning that my event did not get handled; but I don't see why.
Maybe I'm sending the event to the wrong object? Where should I send it to?
I also played around with QGraphicsSceneMouseEvent
instead of QMouseEvent
but had no luck either.
I tried stepping through the event code with the debugger but it is too complex for me to see why it's not working.
Background
I'm working on a piece of software that will be controlled via a simple touch screen. I get the touch events via ethernet and I want to synthesize mouse click events from them.
This way the software will be controlled on the target device in the same way as on a developer PC.
Update
- As noted by fejd, the click code was executed before
QApplication::Exec()
, so I moved it into a timer handler that will be triggered whileexec()
is running. - Added Windows-specific code that works as expected.
- Added some more attempts in Qt none of which works no matter whether
sendEvent()
returnstrue
orfalse
.
So far I have this:
main.cpp
#include <QtGui/QApplication>
#include "qmlapplicationviewer.h"
#include "clicksimulator.h"
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QmlApplicationViewer viewer;
viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
viewer.setMainQmlFile(QLatin1String("qml/qmlClickSimulator/main.qml"));
viewer.showMaximized();
ClickSimulator sim(&viewer);
QTimer timer;
sim.connect(&timer, SIGNAL(timeout()), SLOT(click()));
timer.start(100);
return app.exec();
}
clicksimulator.h
#ifndef CLICKSIMULATOR_H
#define CLICKSIMULATOR_H
#include <QObject>
class QmlApplicationViewer;
class ClickSimulator : public QObject
{
Q_OBJECT
QmlApplicationViewer* m_viewer;
public:
explicit ClickSimulator(QmlApplicationViewer* viewer, QObject *parent = 0);
public slots:
void click();
};
#endif // CLICKSIMULATOR_H
clicksimulator.cpp
#include "clicksimulator.h"
#include "qmlapplicationviewer.h"
#include <QMouseEvent>
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QApplication>
#include <QGraphicsScene>
#include <QTest>
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501
#include "Windows.h"
ClickSimulator::ClickSimulator(QmlApplicationViewer* viewer, QObject *parent) :
QObject(parent)
, m_viewer(viewer)
{
}
void ClickSimulator::click()
{
if (NULL != m_viewer)
{
const int x = qrand() % 500 + 100, y = qrand() % 500 + 100;
{
QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(x, y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
// QMouseEvent pressEvent(
// QEvent::MouseButtonPress,
// QPoint(x, y),
// Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
const bool isSent = QApplication::sendEvent(m_viewer->scene(), &pressEvent);
qDebug() << "'Press' at (" << x << "," << y << ") successful? " << isSent;
}
{
QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
pressEvent.setScenePos(QPointF(x, y));
pressEvent.setButton(Qt::LeftButton);
pressEvent.setButtons(Qt::LeftButton);
QGraphicsItem* item = m_viewer->itemAt(x, y);
const bool isSent = m_viewer->scene()->sendEvent(item, &pressEvent);
//const bool isSent = QApplication::sendEvent(m_viewer->scene(), &pressEvent);
qDebug() << "'Press' at (" << x << "," << y << ") successful? " << isSent;
}
// This platform specific code works...
{
const double fScreenWidth = ::GetSystemMetrics( SM_CXSCREEN )-1;
const double fScreenHeight = ::GetSystemMetrics( SM_CYSCREEN )-1;
const double fx = x*(65535.0f/fScreenWidth);
const double fy = y*(65535.0f/fScreenHeight);
INPUT inp[3];
inp[0].type = INPUT_MOUSE;
MOUSEINPUT & mi = inp[0].mi;
mi.dx = fx;
mi.dy = fy;
mi.mouseData = 0;
mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
mi.time = 0;
mi.dwExtraInfo = 0;
inp[1] = inp[0];
inp[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
inp[2] = inp[0];
inp[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(3, inp, sizeof(INPUT));
}
}
}
main.qml
import QtQuick 1.0
Rectangle {
width: 360
height: 360
Text {
id: text1
text: qsTr("This text is placed at the click coordinates")
}
MouseArea {
id: mousearea1
anchors.fill: parent
onClicked: {
console.log("click at " + mouse.x + ", " + mouse.y);
text1.pos.x = mouse.x;
text1.pos.y = mouse.y;
}
}
}
output
'Press' at ( 147 , 244 ) successful? false
'Press' at ( 147 , 244 ) successful? true
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
由于您在 app.exec() 之前发送事件,因此我认为主事件循环尚未启动。您可以尝试 postEvent 相反,尽管如果 exec( ) 在开始之前清除事件队列。在这种情况下,也许您可以将其发布在 exec() 之后的某个地方?
更新:通过查看QDeclarativeMouseArea 自动测试。缺少的是发布活动。这对我有用:
有点奇怪的是 QML 文件中的 onPressed 在新闻事件之后没有被调用 - 只有在发布事件也被发送之后。
Since you send the event before app.exec(), I don't think the main event loop has been started . You could try postEvent instead, though that might fail as well if exec() clears the event queue before it starts. In that case, perhaps you can post it somewhere after exec()?
Update: Got it working now by looking at the QDeclarativeMouseArea autotests. What was missing was the release event. This worked for me:
What was a bit odd was that onPressed in the QML file did not get called after the press event - only after the release event was sent as well.
接受的答案有效,但还有更好的方法。只需使用
QGraphicScene
的函数即可。如果您知道将事件发送到哪个项目。
The accepted answer works, but there is a better way. Just use
QGraphicScene
'sfunction. If you know which item to send the event to.