Подтвердить что ты не робот

Как имитировать щелчки мыши в QML?

Я пытаюсь отправить QMouseEvent на объекты QML, отображаемые в настоящее время. QApplication::sendEvent() всегда возвращает false, что означает, что мое событие не обработано; но я не понимаю, почему. Может быть, я отправил событие на неправильный объект? Куда мне отправить? Я также играл с QGraphicsSceneMouseEvent вместо QMouseEvent, но также не повезло.

Я попытался пропустить код события с помощью отладчика, но мне слишком сложно понять, почему он не работает.

Фон

Я работаю над программным обеспечением, которое будет контролироваться с помощью простого сенсорного экрана. Я получаю события касания через ethernet, и я хочу синтезировать события щелчка мыши из них. Таким образом, программное обеспечение будет контролироваться на целевом устройстве так же, как на ПК разработчика.

Update

  • Как отмечено 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 
4b9b3361

Ответ 1

Поскольку вы отправляете событие перед app.exec(), я не думаю, что основной цикл событий был запущен. Вы могли бы попробовать postEvent, хотя это может закончиться неудачно, если exec() очистит очередь событий до ее запуска. В этом случае, возможно, вы можете отправить его где-нибудь после exec()?

Обновление:. Теперь он работает, глядя на автотесты QDeclarativeMouseArea. То, что не хватало, было событием релиза. Это сработало для меня:

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);

Что было немного странно, так это то, что onPressed в QML файле не вызывался после события пресса - только после того, как было отправлено событие релиза.

Ответ 2

Принятый ответ работает, но есть лучший способ. Просто используйте QGraphicScene

bool sendEvent(QGraphicsItem* item, QEvent* event)

функция. Если вы знаете, какой элемент отправить событие.