QML MouseArea: onExited не срабатывает после программного перемещения мыши в MouseArea

Эта проблема возникает в Windows, но не в Linux. Другие платформы не пробовал.

У меня есть собственный класс (код ниже), который использует QCursor для установки положения мыши.

Проблема связана со следующим кодом (repo):

import QtQuick 2.15
import QtQuick.Window 2.15

// Custom C++ class, implementation below
import io.github.myProject.utilities.mousehelper 1.0

Window {
    visible: true
    width: 800
    height: 600

    MouseHelper { id: mouseHelper }

    MouseArea {
        id: mouseArea
        hoverEnabled: true
        anchors.fill: parent
        property var p

        onPressed: {
            p = mouseArea.mapToGlobal(
                mouseArea.width * 0.5, mouseArea.height * 0.5);
            mouseHelper.setCursorPosition(0, 0);
        }

        onReleased: {
            mouseHelper.setCursorPosition(p.x, p.y);
        }

        onExited: {
            console.log('This should happen twice, but it only happens once.');
        }
    }
}

Действия по воспроизведению проблемы:

  1. Наведите курсор мыши на окно. Курсор переместится в верхний левый угол экрана, и сработает onExited.
  2. Отпустите кнопку мыши. Курсор переместится в середину окна.
  3. Переместите мышь из окна.

onExited должен срабатывать во второй раз, когда пользователь перемещает мышь из окна, но этого не происходит. Могу ли я каким-то образом

  1. заставить его загореться или
  2. иначе обнаружит, что мышь вышла из области мыши?

onPositionChanged все еще срабатывает, но я могу использовать это только для определения того, когда мышь находится близко к краю MouseArea, а не когда она ушла.

Я попытался наложить глобальный MouseArea сверху и передать все события как способ выполнить некоторую ручную проверку положения в особом случае, но я не мог передать события наведения.


Класс для установки положения мыши:

#ifndef MOUSEHELPER_H
#define MOUSEHELPER_H

#include <QObject>
#include <QCursor>

class MouseHelper : public QObject {
    Q_OBJECT
public:
    explicit MouseHelper(QObject *parent = nullptr);

    Q_INVOKABLE void setCursorPosition(int x, int y);

signals:

public slots:
};

#endif // MOUSEHELPER_H
#include "mousehelper.h"
#include <QGuiApplication>

MouseHelper::MouseHelper(QObject *parent) : QObject(parent) {}

void MouseHelper::setCursorPosition(int x, int y) {
    QCursor::setPos(x, y);
}

Я регистрирую этот класс как тип QML в моей основной функции:

int main(int argc, char *argv[]) {
    // ...
    qmlRegisterType<MouseHelper>("io.github.myProject.utilities.mousehelper",
                                 1, 0, "MouseHelper");
}

Затем я могу импортировать его в QML и использовать.


person Joshua Wade    schedule 22.08.2020    source источник
comment
Код, который вы показываете, программно входит в область 2, а не выходит. Я должен что-то упустить.   -  person JarMan    schedule 22.08.2020
comment
@JarMan Прости! Он программно входит в area2. Проблема в том, что у меня нет способа определить, когда пользователь перемещает мышь из area2, потому что событие onExited срабатывает только в том случае, если пользователь перемещает мышь в area2, как обычно - я думаю, что перемещение ее программно не считается, и поэтому оно не знает, как отслеживать onExited?.   -  person Joshua Wade    schedule 24.08.2020
comment
@JarMan Я отредактировал вопрос, чтобы, надеюсь, сделать его более понятным.   -  person Joshua Wade    schedule 24.08.2020
comment
@JoshuaWade Объясните лучше, ваш вопрос сбивает с толку, поскольку они уже указали вам на него (в нем также была опечатка). Не могли бы вы подробно указать, что должно произойти. Согласно вашему коду, когда нажимается область 1, курсор переходит в середину области 2, поэтому в соответствии с этим кодом он не покидает область 2, а из области 1 в область 2.   -  person eyllanesc    schedule 24.08.2020
comment
@eyllanesc Приношу свои извинения, я отредактировал вопрос, указав шаги, которые могут вызвать проблему. 1) Нажмите на area1, 2) переместите мышь из area2. onExited должен срабатывать на area2, но не срабатывает.   -  person Joshua Wade    schedule 24.08.2020
comment
@JoshuaWade Использование Qt 5.15 в Linux вызывает onExited.   -  person eyllanesc    schedule 24.08.2020
comment
@JoshuaWade Примечание: измените mouseHelper.setCursorPosition(area2.x + area2.width * 0.5, area2.height * 0.5); на var p = area2.mapToGlobal(area2.width * .5, area2.height * 0.5) mouseHelper.setCursorPosition(p.x, p.y);, поскольку QCursor использует глобальные позиции.   -  person eyllanesc    schedule 24.08.2020
comment
@eyllanesc Я должен извиниться перед тобой. Я не совсем понял свою производственную проблему. Я должен был протестировать свой пример, но я этого не сделал, и это потратило ваше время. Я купил тебе кофе... надеюсь, это хоть немного компенсирует это.   -  person Joshua Wade    schedule 24.08.2020
comment
Я обновил свой пример, чтобы воспроизвести проблему, с которой я столкнулся в рабочей среде, и это фактическая копия-вставка из моего примерного проекта (что я должен был сделать в первую очередь).   -  person Joshua Wade    schedule 24.08.2020
comment
Вот репозиторий с кодом: github.com/SecondFlight/mousearea-test   -  person Joshua Wade    schedule 24.08.2020
comment
Я попробовал ваш код: 1> Я щелкнул окно (800x600), когда щелкнул, оно переместилось в верхний левый угол, а onExited сработало. 2> когда я отпускаю мышь, курсор оказывается в центре окна (800x600) 3> когда я перемещаю курсор за пределы окна, onExited снова срабатывает. Так в чем проблема? Объяснение такого рода проблем с помощью gif также может быть лучше   -  person Yunus Temurlenk    schedule 26.08.2020
comment
@YunusTemurlenk Я добавлю гифку, когда вернусь домой. То, что вы описали, верно. Я на Windows ... может быть, это проблема конкретной платформы?   -  person Joshua Wade    schedule 26.08.2020
comment
@JoshuaWade Да, в Linux нет проблем, я пробовал и в Linux, и в Windows, происходит только в Windows. Я проверю больше об этом. Также onEntered не работает после клика   -  person Yunus Temurlenk    schedule 26.08.2020
comment
Спасибо за проверку! Я обновлю вопрос.   -  person Joshua Wade    schedule 26.08.2020
comment
После копания это кажется ошибкой. containsMouse также возвращает false после выпущенного события. Возможно, вы можете найти хитрость, как ответили ниже, чтобы решить эту проблему.   -  person Yunus Temurlenk    schedule 27.08.2020
comment
Я не могу воспроизвести эту проблему в Windows, используя Qt 5.12.4. Так что это похоже на регрессионную ошибку. Рассматривали ли вы возможность создания отчета об ошибках?   -  person m7913d    schedule 28.08.2020
comment
Ах, я на 5.15.0 и не подумал проверить регрессии. Скоро сделаю отчет.   -  person Joshua Wade    schedule 28.08.2020
comment
Пытаюсь найти первую версию с регрессом. Я больше не могу воспроизвести это ни в одной версии Qt. Я так невероятно смущен. Я думаю, что я перезагрузился один раз на прошлой неделе ?? Я сдаюсь. Я сделаю отчет, если я могу воспроизвести его снова.   -  person Joshua Wade    schedule 02.09.2020


Ответы (1)


В качестве обходного пути для вашей проблемы вы можете использовать таймер для сброса положения курсора мыши.

Либо в QML:

MouseArea {
...
    Timer {
        id: timer
        interval: 10
        repeat: false
        onTriggered: {
            mouseHelper.setCursorPosition(mouseArea.p.x, mouseArea.p.y)
        }
    }
    
    onReleased: {
        timer.start()
    }
...
}

Или в вашем классе MouseHelper:

#include <QTimer>
...
void MouseHelper::setCursorPosition(int x, int y) {
    QTimer::singleShot(10, this, [x, y]() { QCursor::setPos(x, y); });
}

Это работает для меня, если интервал таймера не слишком мал.

person ptr    schedule 24.08.2020
comment
Этот метод кажется ненадежным для маленьких MouseArea. Если я нажму, а затем передвину мышь, затем отпущу, а затем снова передвину мышь, второй onRelease не сработает, если я буду слишком быстрым. Мои MouseArea имеют размер 20 на 20 пикселей. - person Joshua Wade; 25.08.2020