如何在 QML 中模拟鼠标点击?

Posted

技术标签:

【中文标题】如何在 QML 中模拟鼠标点击?【英文标题】:How to simulate mouse clicks in QML? 【发布时间】:2011-09-22 15:51:00 【问题描述】:

我正在尝试向当前显示的 QML 对象发送QMouseEventQApplication::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();

点击模拟器.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

点击模拟器.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 

【问题讨论】:

您是否也尝试过指定 globalPos 的 MouseEvent? The globalPos() is initialized to QCursor::pos(), which may not be appropriate. Use the other constructor to specify the global position explicitly. 【参考方案1】:

由于您在 app.exec() 之前发送事件,我认为主事件循环尚未启动。您可以尝试postEvent,但如果 exec() 在事件队列开始之前清除它,这也可能会失败。在那种情况下,也许你可以在 exec() 之后的某个地方发布它?

更新:通过查看QDeclarativeMouseArea autotests,它现在可以工作了。缺少的是发布事件。这对我有用:

QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
pressEvent.setScenePos(QPointF(x, y));
pressEvent.setButton(Qt::LeftButton);
pressEvent.setButtons(Qt::LeftButton);
QApplication::sendEvent(m_viewer->scene(), &pressEvent);

QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
releaseEvent.setScenePos(QPointF(x, y));
releaseEvent.setButton(Qt::LeftButton);
releaseEvent.setButtons(Qt::LeftButton);
QApplication::sendEvent(m_viewer->scene(), &releaseEvent);

有点奇怪的是 QML 文件中的 onPressed 在 press 事件之后没有被调用 - 只有在 release 事件也被发送之后。

【讨论】:

关于exec() 的优点。但是,将代码移动到计时器处理程序中也不起作用。我用我当前的测试代码更新了问题。 @foraidt 我已经更新了答案。虽然在 Mac 上进行了测试,但希望在 Windows 上的行为是相同的。【参考方案2】:

接受的答案有效,但有更好的方法。只需使用QGraphicScene's

bool sendEvent(QGraphicsItem* item, QEvent* event)

功能。如果您知道要将事件发送到哪个项目。

【讨论】:

以上是关于如何在 QML 中模拟鼠标点击?的主要内容,如果未能解决你的问题,请参考以下文章

vbs/vb如何模拟鼠标点击

如何在 F# 中模拟鼠标点击和按键

js如何实现鼠标模拟点击

C#怎么模拟鼠标点击

如果无法识别,如何在游戏中模拟硬件鼠标点击?

在javascript中模拟鼠标点击时如何设置目标属性?