• Qml-跨窗口拖动图片、物体


    本文使用的是QtCreater,qml的版本是5.15.2.
    
    • 1

    1、窗口内拖动一个box

    首先建一个box,设置为鲜艳的红色,随后使用MouseArea的属性,注意这里要设置MouseArea的大小,可以用x,y,width,height;也可以使用锚定布局元素anchors.fill=parent表示填充成其父物体的形状。

    随后使用drag.target: box 表示鼠标的拖拽绑定在box这个物体上,随后就可以拖拽动这个物体了。

    核心代码:

    我这里的文件名:drag_page.qml

    Item {
        Text
        {
            x: 49
            y: 49
            width: 131
            height: 24
            text: qsTr("window_2")
            font.styleName: "Regular"
            font.pixelSize: 18
            color: "#000000"
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
        Rectangle
        {
            id:box
            x:200
            y:60
            width:50
            height: 50
            color:"#FF0000"
    
            //Drag.active: mouse.drag.active
        }
        MouseArea
        {
            id:mouse
            //hoverEnabled: true
            anchors.fill: parent
            drag.target: box
            //drag.axis: Drag.XAndYAxis
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2、跨窗口拖动box

    我这里使用两个loader加载两个窗口,形状如下:
    在这里插入图片描述左右的窗口是目前创建的,就是客户端常有的界面。右侧可以继续绘制一些窗口这里给出代码:
    这个文件名叫做:menuBar.qml

    import QtQuick 2.0
    import QtQuick.Controls 2.15
    import QtQuick.Layouts 1.15
    Item {
        MenuBar
        {
            Menu
            {
                title: qsTr("&File")
                Action { text: qsTr("&New...") }
                Action { text: qsTr("&Open...") }
                Action { text: qsTr("&Save") }
                Action { text: qsTr("Save &As...") }
                MenuSeparator { }
                Action { text: qsTr("&Quit") }
            }
            Menu
            {
                title: qsTr("&Edit")
                Action { text: qsTr("Cu&t") }
                Action { text: qsTr("&Copy") }
                Action { text: qsTr("&Paste") }
            }
            Menu
            {
                title: qsTr("&Help")
                Action { text: qsTr("&About") }
            }
        }
    
        Text
        {
            x: 49
            y: 49
            width: 131
            height: 24
            text: qsTr("window_1")
            font.styleName: "Regular"
            font.pixelSize: 18
            color: "#000000"
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    结合前面的drag_page.qml,现在就有了上面的两个窗口。如此便可拖动,但是还有是有问题,请继续看下面。

    3、丝滑跨窗口拖动box

    现在来拖动。发现当鼠标在window_2的时候,是可以拖动红色box的,但是当鼠标出了window_2的时候,红色box是不能被拖动的,这是因为鼠标的作用域不对了。

    所以,长按住物体,能跨窗口拖动,但是拖回来有bug,这个时候我们可以考虑在这两个窗口的上面增加一个蒙层,在蒙层上面拖动box,如此就能实现丝滑的跨窗口拖动了。

    本质:让box和mouseArea都在一个区域,就能丝滑了。

    在这里插入图片描述

    这部分代码就写在main.qml里面:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    
    Window
    {
        title: qsTr("Hello World")
        width: 1920
        height: 1080
        visible: true
        Rectangle
        {
            id:left_control
            x:0
            y:0
            width: 320
            height: parent.height
            border.color:"blue"
            //visible: true
            Loader
            {
                id: loader
                width: parent.width
                height:parent.height
                focus: true
                source:"qrc:/menuBar.qml"
            }
        }
        Rectangle
        {
            id:show_window
            x:left_control.width + 5
            y:0
            width:1000
            height: parent.height
            color:"#EDF1F2"
            border.color:"blue"
    
            Loader
            {
                width: parent.width
                height:parent.height
                source: "qrc:/drag_page.qml"
            }
         }
    
    		//就是下面这两个{}
        Rectangle
        {
            id:box
            x:200
            y:60
            width:50
            height: 50
            color:"#FF0000"
    
            //Drag.active: mouse.drag.active
        }
        MouseArea
        {
            id:mask
            anchors.fill:parent
            drag.target: box
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    4、鼠标事件遮挡

    细心的朋友发现上面还是有问题,这样导致main.qml的鼠标事件 ,会把下面窗口比如drag_page.qml的鼠标事件给遮挡住,如此就不妙了。

    没有关系,我们可以进行鼠标事件穿透

    核心代码:

    
    propagateComposedEvents: true
    mouse.accepted = false
    
    
    • 1
    • 2
    • 3
    • 4

    举例如下

    MouseArea
    {
        id:mask
        anchors.fill:parent
        drag.target: box
    
        propagateComposedEvents: true
        onClicked:
        {
            mouse.accepted = false
            console.log("this is main's mouse clicked")
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    main.qml文件代码:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    
    //import "menuBar.qml"
    Window
    {
        title: qsTr("Hello World")
        width: 1920
        height: 1080
        visible: true
        Rectangle
        {
            id:left_control
            x:0
            y:0
            width: 320
            height: parent.height
            border.color:"blue"
            //visible: true
            Loader
            {
                id: loader
                width: parent.width
                height:parent.height
                focus: true
                source:"qrc:/menuBar.qml"
            }
        }
        Rectangle
        {
            id:show_window
            x:left_control.width + 5
            y:0
            width:1000
            height: parent.height
            color:"#EDF1F2"
            border.color:"blue"
    
            Loader
            {
                width: parent.width
                height:parent.height
                source: "qrc:/drag_page.qml"
            }
         }
    
        Rectangle
        {
            id:box
            x:200
            y:60
            width:50
            height: 50
            color:"#FF0000"
    
            //Drag.active: mouse.drag.active
        }
        MouseArea
        {
            id:mask
            anchors.fill:parent
            drag.target: box
    
            propagateComposedEvents: true
            onClicked:
            {
                mouse.accepted = false
                console.log("this is main's mouse clicked")
            }
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    5、不够完美

    再深入,可以发现,鼠标穿透事件只能穿透三个函数:只有clicked, doubleClicked and pressAndHold这三个函数才能穿透。这就很不舒服了,那我的positionChanged怎么办,其他的一些函数怎么办?

    其实还是有解决的方法的!!!

    使用dropArea.

    我们在一个窗口绘制box,再另一个窗口设置dopArea区域,这样,当这个box进入到这个dropArea区域的时候,就能被响应,从而完成一些事件。

    下面这里就是,boxwindow_2里面,而dropAreawindow1里面,当box进入绿色区域,能引发响应,比如onEntered,onPositionChanged等等。
    在这里插入图片描述核心代码:

    下面这段写在drop_page.qml

    Rectangle
        {
            id:box
            x:200
            y:60
            width:50
            height: 50
            color:"#FF0000"
    
    		//不要忘记这行哟,设置active为ID为mouse的鼠标
            Drag.active: mouse.drag.active
        }
        MouseArea
        {
            id:mouse
            //hoverEnabled: true
            anchors.fill: parent
            drag.target: box
            //drag.axis: Drag.XAndYAxis
    
    		//这里需要自己调用一些函数,有些函数是自动调用的
            onReleased: dropArea_id.Drag.drop()
            onClicked:
            {
                //console.log("this is drag_page's mouse clicked")
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    下面这段写在menuBar.qml里面:

        Rectangle
        {
            id:dropArea_id
            border.width: 5
            x:50
            y:100
            z:0
            width: 200
            height: 300
            border.color:"#00FF00"
    
            DropArea{
                anchors.fill: parent
                onEntered: console.info('enter')
                onExited: console.info('exit')
                onDropped: console.info('dropped')
                onPositionChanged: console.info('positon changed')
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    6、另有拖动妙计

    我们可以在蒙层上面绘制一个box,然后在需要控制的窗口去控制它,让他随鼠标移动到window_1,因为我们发现,只要鼠标不放,物体一定可以被跟着走。

    因为box是在蒙层上面的,而当前控制物体的鼠标是在window_2的,所以,这里就有一个子窗口鼠标坐标向父窗口鼠标坐标转换的一个过程。

    怎么转换呢?借助onPositionChanged函数,知道父子窗口的鼠标偏差,其实就是一个相对位置的问题。子窗口的0,0点是在子窗口的左上角,父窗口一样。鼠标在子窗口的鼠标位置转换到父窗口,就是要加上:子窗口原点的x,y分别距离父窗口原点x,y的距离。

    核心代码:

    这段写在window_2窗口,用于控制

        MouseArea
        {
            id:mouse
            anchors.fill: parent
    
            //if hoverEnabled: true
            //not press can be get mouse position change
            hoverEnabled: true
            onPositionChanged:
            {
                //use event_bus_id for sending singal
                //you should add the pos *** from children window to father
                //it means: event_bus_id.sendMousePos(mouse.x + *** ,mouse.y + *** )
                event_bus_id.sendMousePos(mouse.x,mouse.y)
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    下面这段写在main.qml

    另外EventBus.qml加上:signal sendMousePos(double x,double y)

    // 全局事件通信
    EventBus
    {
        id: event_bus_id
    }
    Connections
        {
            target:event_bus_id
            function onSendMousePos(x,y)
            {
    
                box.x = x
                box.y = y
                console.log(box.x,box.y)
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    7、终极大招——改变鼠标样式

    以下借鉴:https://blog.csdn.net/ebthon/article/details/84112389

    myCursor.h
    
    • 1
    #include  
    #include 
    #include 
    #include 
    #include 
    
    class MyCursor : public QObject {
        
        Q_OBJECT
    public:
        MyCursor();
        ~MyCursor();
    
        Q_INVOKABLE void setMyCursor(QObject *obj,QString source,int width,int height);
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    myCursor.cpp
    
    • 1
    #include "myCursor.h"
    
    MyCursor::MyCursor(){
        
    }
    MyCursor::~MyCursor(){
        
    }
    void MyCursor::setMyCursor(QObject *obj_mouse,QString image_source,int width,int height){
        
        if(nullptr==obj_mouse){
        
            return;
        }
        // 需要将 Qml对象转换为 QQuickItem对象才能 setCursor()
        QQuickItem *itemObj = qobject_cast<QQuickItem*>(obj_mouse);
        if(nullptr==obj_mouse){
        
            return;
        }
        //示例
        //itemObj->setCursor(QCursor(QPixmap(":///resource/image/simulation_page/car.png")));
        itemObj->setCursor(QCursor(QPixmap(image_source).scaled(width, height)));  //, Qt::KeepAspectRatio
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    然后在main.cpp里面导入上面两个文件,加上这句代码:

    //myCursor是以后用来调用的名字
    engine.rootContext()->setContextProperty("myCursor", new MyCursor());
    
    • 1
    • 2

    最后在需要改变mouse样式的mouseArea里面传参就行。

    //修改鼠标形状,以及大小
    image_source = ":///resource/image/car.png"
    myCursor.setMyCursor(mouse_id, image_source,75,75)
    
    • 1
    • 2
    • 3

    还不够,这样的话,跨窗口,鼠标id仍然是蒙层的id,这样一样会被遮挡。最后发现,当我们按下的时候,可以手动设置蒙层的mouseArea的大小区域,使得不被干扰,当释放的时候,又可以设置他的区域,甚至乎设置为0.

    以上,喜欢来个赞!!

  • 相关阅读:
    Photoshop 2024正式发布!内置最新PS AI,创意填充等功能无限制使用!
    html visibilitychange 事件
    【安装windows10 RTX3060 tensorflow的开发环境】
    vue3别名配置(vite)
    git常用命令(github中的token)(.gitignore)
    CRM系统销售自动化功能如何提高销售效率
    自学黑客(网络安全)
    私有化轻量级持续集成部署方案--01-环境配置(上)
    dompdf,这么做就可以支持中文了
    技术的新浪潮:从SOCKS5代理到跨界电商的未来
  • 原文地址:https://blog.csdn.net/qq_41286751/article/details/125892194