LINUX.ORG.RU

[Qt] Послать искусственный QMouseEvent?


0

0

Демо код тут: http://gist.github.com/517445

Пытаюсь отправить событие типа «левые клик» виджету, а он его не принимает =( Настоящей мышкой работает как надо.

Вот код моего виджета, который просто в себе содержит QCalendarWidget и QPushButton.

#include "calendar.h"

Calendar::Calendar(QWidget *parent) :
    QWidget(parent)
{
    qCal = new QCalendarWidget;
    qBtn = new QPushButton(tr("Press me"));

    connect(qBtn, SIGNAL(clicked()), this, SLOT(testCustomClick()));

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(qCal);
    layout->addWidget(qBtn);

    setLayout(layout);
    qDebug() << "Date:" << QDate::currentDate();
}

Calendar::~Calendar()
{
}

void Calendar::testCustomClick()
{
    QMouseEvent qm2(QEvent::MouseButtonPress, QPoint(qCal->width()/2,qCal->height()/2), Qt::LeftButton , Qt::LeftButton, Qt::NoModifier);
    QApplication::sendEvent(qCal, &qm2);
}


void Calendar::mousePressEvent(QMouseEvent* ev)
{
    qDebug() << "mouse event: " << ev << "x=" << ev->x() <<" y=" << ev->y();
    QWidget::mousePressEvent(ev);
}

При нажатии на кнопку пытаюсь сгенерировать клик в центре виджета-календаря, но вместо того что б выбралась там дата, событие все-равно приходит в мой виджет (срабатывает mousePressEvent, видно по выводу qDebug() ) при этом если настоящей мышкой ткнуть в календарь, то Calender::mousePressEvent не срабатывает, что логично, т.к. событие обрабатывается календарем и наверх не посылается.

Посылаю событие через QApplication::sendEvent прямо на виджет календаря, но no luck =( Может у кого идеи будут?

судя по коду, QCalendarWidget отфильтровывает события нажатия мыши на себе. Посылать нужно конкретно на дочерний view, а он недоступен извне. Интересная ситуация. Кстати просто press недостаточно - нужен именно клик, press+release.

alex_custov ★★★★★ ()
Ответ на: комментарий от alex_custov

Вот в том то и дело, что отфильтровывает. Точнее не отфильтровывает (eventFilter тут не причем), а просто футболит события своим родителям, пока оно не доберется до QAbstractScrollArea, где event благополучно возвращает false на всех мышко-событиях, а раз виджет вернул false из event, то Qt передает его вешстоящему виджету, где я его уже вижу но мне от этого не легче.

Вопрос на миллион - как же черт побери оно тогда с обычной мышкой работает?

redbaron ★★ ()

Используй postEvent с целью QApplication::focusWidget(). Позаботься, чтобы твой виджет имел фокус - если пытаться послать event в виджет без фокуса, возможен сегфолт

annulen ★★★★★ ()
Ответ на: комментарий от annulen
    qCal->setFocus();
    qDebug() << "Focus: " << QApplication::focusWidget();

    QMouseEvent* qm2 = new QMouseEvent(QEvent::MouseButtonPress, QPoint(5,5), Qt::LeftButton , Qt::LeftButton, Qt::NoModifier);
    QApplication::postEvent(QApplication::focusWidget(), qm2, 0);
    QMouseEvent* qm = new QMouseEvent(QEvent::MouseButtonRelease, QPoint(5,5), Qt::LeftButton , Qt::LeftButton, Qt::NoModifier);
    QApplication::postEvent(QApplication::focusWidget(), qm, 0);

    QApplication::processEvents();

Дебаг вывод подтверждает, что QCalendarWidget в фокусе, но ничего не взлетает =(

redbaron ★★ ()
Ответ на: комментарий от redbaron

странно, у меня с QKeyEvent'ами все отлично пашет

annulen ★★★★★ ()

а тебе точно это нужно? Если для тестирования, можно QtTest заюзать, там все удобнее. Если для штатной работы приложения, то это костыль

annulen ★★★★★ ()
Ответ на: комментарий от annulen

Точно нужно. Я хочу иметь возможность с минимальными модификациями встраивать QT приложения в 3D движок, т.е. мне в любом случае надо как то насильно пропихивать евенты в Qt

redbaron ★★ ()
Ответ на: комментарий от annulen

Пока план такой: расположить прозрачный виджет поверх всего, ловить в нем евенты, определять какой конкретно виджет под курсором и слать евент непосредственно ему, т.е. не QCalendarWidget, а тем виджетам из которых он сам состоит. Т.е. как бы эмулировать работу хардварной мышки

redbaron ★★ ()
Ответ на: комментарий от redbaron

Я хочу иметь возможность с минимальными модификациями встраивать QT приложения в 3D движок, т.е. мне в любом случае надо как то насильно пропихивать евенты в Qt

не, что-то тут не так, зачем Вам дважды user input по одной и той же области обрабатывать?

shty ★★★★★ ()

и да, не знаю в этом ли дело, но попробуйте так:

void Calendar::mousePressEvent(QMouseEvent* ev) 
{ 
    qDebug() << "mouse event: " << ev << "x=" << ev->x() <<" y=" << ev->y(); 
    QWidget::mousePressEvent(ev); 
    ev->accept();
}
shty ★★★★★ ()
Ответ на: комментарий от shty

Не вижу где дважды? Есть 3д движок (не мой, не на qt), хочется иметь возможность показывать браузер и видео прямо внутри игрового мира, фича типа render-to-texture в движке есть, надо только зарегистрировать новый тип текстуры и отдавать pixmap когда спросят. При этом мы можем детектить когда курсор находится над текстурой и в каком месте текстуры конкретно, если пользователь нажмет левую кнопку мышки хотелось бы передать в Qt инфу, что мол вот тут у тебя случился клик.

Раз уж есть видео и браузер, то почему б не иметь возможность встраивать вообще любое произвольное qt приложение (при наличии исходников)? И тут упираемся, что некоторые наследники QWidget посылают подальше евенты переданные им.

redbaron ★★ ()
Ответ на: комментарий от shty

Я подозревал, что Calendar::mousePressEvent может что-то портить, по-этому проверил и с полностью закоментированным методом, все равно не работает.

redbaron ★★ ()

Решено! Корректный код.

void Calendar::testCustomClick()
{
    QPoint pos(qCal->width()/2,qCal->height()/2);
    QWidget *w = qApp->widgetAt(qCal->mapToGlobal(pos)); //finds exact widget under cursor
    qDebug() << "widget under point of click: " << w;

    {
    QMouseEvent qm2(QEvent::MouseButtonPress, pos, Qt::LeftButton , Qt::LeftButton, Qt::NoModifier);
    QApplication::sendEvent(w, &qm2);
    }
    {
    QMouseEvent qm2(QEvent::MouseButtonRelease, pos, Qt::LeftButton , Qt::LeftButton, Qt::NoModifier);
    QApplication::sendEvent(w, &qm2);
    }
}

Смысл в том, что надо детектить виджет под курсором и слать евент непосредственно ему. В коде выше pos содержит координаты относительно виджета, куда мы хотим ткнуть, затем widget->mapToGlobal(pos) вычисляет глобальные координаты на экране (вызывать надо именно метод того виджета относительно которого содержатся координаты в pos) и qApp->widgetAt(const QPoint & point) вычисляет виджет под кликом, после чего мы шлем ему евенты. Ура! =)

redbaron ★★ ()
Ответ на: комментарий от redbaron

Можно даже проще, что будет работать всегда, даже когда Qt окна и в помине нет.

    QPoint pos(qCal->width()/2,qCal->height()/2);
    QWidget *w = qCal->childAt(pos);
redbaron ★★ ()
Ответ на: комментарий от redbaron

если пользователь нажмет левую кнопку мышки хотелось бы передать в Qt инфу, что мол вот тут у тебя случился клик

так и передавай, кто мешает? :)

дёргай обработчик mouse click'а и подсовывай ему координаты или возьми заверни в QMouseEvent и передавай в обработчик который обрабатывает event'ы, зачем сам event-то бросать?

Раз уж есть видео и браузер, то почему б не иметь возможность встраивать вообще любое произвольное qt приложение (при наличии исходников)? И тут упираемся, что некоторые наследники QWidget посылают подальше евенты переданные им.

да всё встраиваеся как показывает практика, только надо аккуратно

shty ★★★★★ ()
Ответ на: комментарий от redbaron

Так, поспешил :) Когда мы нашли нужный виджет и отправляем событие надо и координаты сконвертировать уже для того самого виджета.

   QPoint pos(qCal->width()/2,qCal->height()/2); 
   QWidget *w = qCal->childAt(pos); 
   pos = w->mapFrom(qCal,pos); //конвертим координаты клика
redbaron ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.