Как симулировать щелчки мышью в QML? - PullRequest
14 голосов
/ 22 сентября 2011

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

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

Фон

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

Обновление

  1. Как отметил fejd, код клика был выполнен до QApplication::Exec(), поэтому я переместил его в обработчик таймера, который будет срабатывать, пока exec() работает.
  2. Добавлен специфичный для Windows код, который работает как положено.
  3. Добавлено еще несколько попыток в 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 

Ответы [ 2 ]

10 голосов
/ 30 сентября 2011

Поскольку вы отправляете событие до 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 не вызывался после события press - только после отправки события release.

0 голосов
/ 09 октября 2014

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

bool sendEvent(QGraphicsItem* item, QEvent* event)

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

...