QML:从动态 MouseArea 中“窃取”事件

Posted

技术标签:

【中文标题】QML:从动态 MouseArea 中“窃取”事件【英文标题】:QML: 'Steal' events from dynamic MouseArea 【发布时间】:2015-04-21 15:53:08 【问题描述】:

我目前正在尝试在 QML 中实现 drag-to-create 机制,但我遇到了一个问题,我需要新创建的 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接收鼠标事件?

【问题讨论】:

【参考方案1】:

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

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

然后我创建了一个帮助类来将此功能公开给 qml:

MouseGrabber.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 监听鼠标释放事件并自动取消抓取。


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

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
                
            
        
    

【讨论】:

谢谢!我不知道grabMouse(),但它看起来正是我所需要的。【参考方案2】:

我的另一个答案是过度设计。 在您的情况下无需窃取鼠标事件,您只需在 onPositionChanged 处理程序中更新拖动的蓝色矩形的位置(或使用Binding 或直接在Rectangle 组件内)。

在您的MouseArea 中写下这个就足够了:

onPositionChanged: 
    if ( !draggedObj ) 
        var diff = Qt.point( mouse.x - lastPoint.x,
                             mouse.y - lastPoint.y );
        if ( vecLength( diff ) > 4 ) 
            draggedObj = dragObj.createObject( window );
        
     else  //update the position of the dragged rectangle
        draggedObj.x = mouse.x - draggedObj.width/2;
        draggedObj.y = mouse.y - draggedObj.height/2;
    


onReleased: draggedObj = null

【讨论】:

以上是关于QML:从动态 MouseArea 中“窃取”事件的主要内容,如果未能解决你的问题,请参考以下文章

MouseArea 窃取 QQuickItem 的鼠标事件

QML MouseArea:如何将鼠标事件传播到其他鼠标区域?

qt qml。 MouseArea 可以看到事件,但将它们全部传递给父级而不影响它们吗?

QML事件处理 八

qml 按钮不触发,与 MouseArea 相同

qml mousearea overlab 在悬停模式下不起作用