qt - QML:来自动态鼠标区域的 'Steal' 事件

标签 qt qml

我目前正在尝试在 QML 中实现一个拖动创建机制,但是我遇到了一个问题,我需要新创建的 MouseArea 来成为鼠标事件的目标,即使原始 MouseArea 还没有鼠标按钮释放事件。

Window {
    id: window
    width: 300
    height: 300

    Rectangle {
        id: base
        width: 20
        height: 20

        color: "red"

        MouseArea {
            anchors.fill: parent

            property var lastPoint
            property var draggedObj: null

            function vecLength( vec ) {
                return Math.abs( Math.sqrt( Math.pow( vec.x, 2 ) +
                                            Math.pow( vec.y, 2 ) ) );
            }

            onPressed: lastPoint = Qt.point( mouse.x, mouse.y )
            onPositionChanged: {
                if ( !draggedObj ) {
                    var diff = Qt.point( mouse.x - lastPoint.x,
                                         mouse.y - lastPoint.y );
                    if ( vecLength( diff ) > 4 ) {
                        draggedObj = dragObj.createObject( window );
                    }
                }

                mouse.accepted = !draggedObj;
            }
        }
    }

    Component {
        id: dragObj

        Rectangle {
            width: 20
            height: 20

            color: "blue"

            Drag.active: dragArea.drag.active
            Drag.hotSpot.x: 10
            Drag.hotSpot.y: 10

            MouseArea {
                id: dragArea
                anchors.fill: parent

                drag.target: parent
            }
        }
    }
}

如果您运行此代码并进行尝试,您将看到在红色 Rectangle 中拖动会导致创建可拖动的蓝色 Rectangle,但它不会跟随鼠标,因为红色 MouseArea 仍在接收鼠标事件,尽管蓝色 MouseArea 位于其上方。

有什么方法可以强制蓝色的 MouseArea 接收鼠标事件吗?

最佳答案

我以前遇到过这种情况,并在我的阁楼上开始了解决方案。

这里的技巧是调用 QQuickItem::grabMouse()并向新创建的对象发送鼠标按下事件。 不幸的是,我认为这只能通过 C++ 完成。

然后我创建了一个辅助类来向 qml 公开此功能:

鼠标抓取器.h

#ifndef MOUSEGRABBER
#define MOUSEGRABBER

#include <QObject>
#include <QQuickItem>
#include <QGuiApplication>
#include <QMouseEvent>

class MouseGrabber : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)
    Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)

public:
    explicit MouseGrabber(QObject *parent = 0) : QObject(parent), m_target(nullptr), m_active(true) {  }
    QQuickItem* target() const { return m_target; }
    bool active() const { return m_active;}


signals:
    void targetChanged();
    void activeChanged();

public slots:
    void setTarget(QQuickItem* target)
    {
        if (m_target == target)
            return;
        ungrabMouse(m_target);
        if (m_active)
            grabMouse(target);
        m_target = target;
        emit targetChanged();
    }
    void setActive(bool arg)
    {
        if (m_active == arg)
            return;
        m_active = arg;

        if (m_active)
            grabMouse(m_target);
        else
            ungrabMouse(m_target);

        emit activeChanged();
    }

private:
    static void grabMouse(QQuickItem* target)
    {
        if (target)
        {
            target->grabMouse();
            QMouseEvent event(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton,  QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
            QGuiApplication::sendEvent(target, &event);
        }
    }

    static void ungrabMouse(QQuickItem* target)
    {
        if (target)
            target->ungrabMouse();
    }

    QQuickItem* m_target;
    bool m_active;
};

#endif // MOUSEGRABBER

通过直接调用插槽而不是操纵属性可以使这更方便,但这就是我的库存。例如一个名为 grabMouseUntilRelease(QQuickItem* item) 的插槽,它为该项目抓取鼠标,使用 installEventFilter 监听鼠标释放事件并自动取消抓取。


注册该类,以便它可以在代码中的某处使用 qmlRegisterType 在 QML 中实例化:

qmlRegisterType<MouseGrabber>("com.mycompany.qmlcomponents", 1, 0, "MouseGrabber");

之后,您可以在 QML 中实例化 MouseGrabber 并通过修改其属性(targetactive)来使用它:

QML

import com.mycompany.qmlcomponents 1.0

Window {
    id: window
    width: 300
    height: 300

    Rectangle {
        id: base
        width: 20
        height: 20

        color: "red"

        MouseArea {
            anchors.fill: parent

            property var lastPoint
            property var draggedObj: null

            function vecLength( vec ) {
                return Math.abs( Math.sqrt( Math.pow( vec.x, 2 ) +
                                           Math.pow( vec.y, 2 ) ) );
            }

            onPressed: lastPoint = Qt.point( mouse.x, mouse.y )
            onPositionChanged: {
                if ( !draggedObj ) {
                    var diff = Qt.point( mouse.x - lastPoint.x,
                                        mouse.y - lastPoint.y );
                    if ( vecLength( diff ) > 4 ) {
                        draggedObj = dragObj.createObject( window );
                        grabber.target = draggedObj.dragArea; // grab the mouse
                    }
                }

                mouse.accepted = !draggedObj;
            }
        }
    }
    MouseGrabber {
        id: grabber
    }

    Component {
        id: dragObj

        Rectangle {
            property alias dragArea: dragArea
            width: 20
            height: 20

            color: "blue"

            Drag.active: dragArea.drag.active
            Drag.hotSpot.x: 10
            Drag.hotSpot.y: 10

            MouseArea {
                id: dragArea
                anchors.fill: parent
                drag.target: parent
                onReleased: {
                    if (grabber.target === this)
                        grabber.target = null; // ungrab the mouse
                }
            }
        }
    }
}

关于qt - QML:来自动态鼠标区域的 'Steal' 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29777230/

相关文章:

javascript - QWebEngineView 中未定义的 qt.webChannelTransport

c++ - 使用原始数据将 QImage 转换为 cv::Mat

python - Qt C++项目中如何调用python matplotlib?

javascript - 是否可以从 qml 访问 QScriptValue 中持有 QVariantMap 的任何内容?

javascript - QML StackView 替换无内容推送

qt - QListWidget:更改选择而不触发selectionChanged

qt - 为什么设置 QLabel 的像素图不起作用?

c++ - 在 C++ 中添加自定义字体并在 QML 中按其姓氏调用它们

javascript - 在 QML 中委托(delegate)一个数组

QML:如何将项目属性重置为默认值