QML 实现可以用鼠标拖动曲线上的采样点来编辑曲线

效果

可以用鼠标拖动曲线上的采样点来编辑曲线。
默认情况下只能在Y方向上编译。可以通过修改dragableDirection: DragableChart.DragableDirect.Veritcal 让点可以全向拖动

代码文件

  • 仓库
    https://gitee.com/deng-gang/QMLTestProjects
    这是一些测试项目的集合
  • 文件目录
    QT512/10-6/10-6-1/mycharts/DragableChart.qml

代码

import QtQuick 2.0
import QtCharts 2.3

ChartView{
    property var points_X:[1.51,1.57,2.0,2.55]
    property var points_Y: [2.51,3.57,4.0,4.55]
    id: myChart
    anchors.fill: parent

    legend.visible: false
    antialiasing: true

    // 为了使用枚举,必须把这个ChartView放到一个独立的qml文件里
    enum DragableDirect{
        Both, //0
        Veritcal, // 1
        Horizontal // 2
    }
    // line curve
    LineSeries{id: line}
    property point selectedPointInChart
    ScatterSeries{
        id: points

        onHovered:{
            var global_position
            //global_position = myChart.mapToGlobal(point.x,point.y)
            global_position = myChart.mapToPosition(point,points)
            selectedHandler.x = global_position.x - selectedHandler.width / 2
            selectedHandler.y = global_position.y - selectedHandler.height / 2
            selectedPointInChart = point
            //console.log(global_position)
            // 在鼠标移入的时候显示selectedPointer
            // 当selectedPointer 被显示后,焦点转换到 selectedPointer 上,这时会收到一个 hovered事件,且 state 是 false
            // 因此鼠标移出,消除selectedPointer的动作要由 selectedPointer onHovered 事件去处理,如果在points 里删除 selectedPointer,会导致 qmlscence crash

            if (state === true){
                //console.log("hovered in : x:" + point.x + " y:" + point.y)
                selectedHandler.state = "HOVERED"
                //selectedPointer.clear()
                //selectedPointer.append(point.x,point.y)
            }else{
                //console.log("hovered out : x:" + point.x + " y:" + point.y)

                // importent!!!! Can not clear selectedPointer here!
                //selectedPointer.clear()
            }

        }

        Connections{
            target: selectedHandler
            onDataModified:{

                var newPoint
                newPoint = myChart.mapToValue(Qt.point(newX, newY),points)
                //console.log("myChart.onDataModified: x:" + newX + " y:" + newY + " new value:" + newPoint)

                points.replace(selectedPointInChart.x,selectedPointInChart.y,newPoint.x,newPoint.y)
                line.replace(selectedPointInChart.x,selectedPointInChart.y,newPoint.x,newPoint.y)
            }
        }
    }

    Item{
        id: selectedHandler;
        width: 20; height:20
        visible: false
        property real xCenter: x+width/2
        property real yCenter: y+height/2
        property int dragableDirection: DragableChart.DragableDirect.Veritcal

        signal posChange(int xOffset, int yOffset)
        onPosChange: {
            if(dragableDirection === DragableChart.DragableDirect.Both){
                x += xOffset;y += yOffset
            }else if(dragableDirection === DragableChart.DragableDirect.Veritcal){
                y += yOffset
            }else if(dragableDirection === DragableChart.DragableDirect.Horizontal){
                x += xOffset
            }
        }

        signal dataModified(real newX, real newY);
        onDataModified: {
            //console.log("newX:"+newX + " newY:" + newY)
        }

        state: "HIDE"
        states: [
            State{
                name:"HIDE"
                PropertyChanges{target: selectedHandler; opacity:0.8  }
                PropertyChanges{target: selectedHandler; visible:false}
            },
            State {
                name: "HOVERED"
                PropertyChanges {target: selectedHandler; opacity:0.5 }
                PropertyChanges {target: selectedHandler; visible:true}
            },

            State{
                name: "SELECTED"
                PropertyChanges{target: selectedHandler; opacity:0.8 }
                PropertyChanges{target: selectedHandler; visible:true}
            },
            State{
                name: "MODIFING"
                PropertyChanges{target: selectedHandler; opacity:0.9 }
                PropertyChanges{target: selectedHandler; visible:true}
            },
            State{
                name: "MODIFIED"
                PropertyChanges{target: selectedHandler; opacity:1.0 }
                PropertyChanges{target: selectedHandler; visible:true}

            }

        ]
        onStateChanged: {
            //console.log("onStateChanged : ", state)

            // 当数据修改完成,转入"MODIFIED"状态时,发送signal,
            // 外面的chart需要根据这个signal更新曲线
            if(state === "MODIFIED"){
                dataModified(xCenter,yCenter);
            }
        }
        Rectangle{
            anchors.fill: parent
            color: "red"
            radius: width/2
        }
        MouseArea{
            anchors.fill: parent
            hoverEnabled: true // 必须设置为true 才能接受hover事件

            property int lastX: 0   // mouseX 是当前光标与root中心点的偏移量,在拖动的过程中要保持光标和root中心的偏移量不变
            property int lastY: 0

            onExited: {
                if(selectedHandler.state != "MODIFING"){ // sometimes move too fast
                    selectedHandler.state = "HIDE"
                    //console.log("onExited")
                }
            }

            onPressed: {
                selectPoint(mouseX,mouseY)
            }

            onReleased: {
                if(selectedHandler.state === "MODIFING"){
                    selectedHandler.state = "MODIFIED"
                }
            }

            onPositionChanged: {
                if (selectedHandler.state === "SELECTED" || selectedHandler.state === "MODIFING") {
                    selectedHandler.posChange(mouseX - lastX, mouseY - lastY)
                    if(selectedHandler.state === "SELECTED" ){
                        selectedHandler.state = "MODIFING"
                    }

                }
            }

            function selectPoint(mouseX,mouseY){
                if (selectedHandler.state === "HOVERED" ||  selectedHandler.state === "MODIFIED"){
                    selectedHandler.state = "SELECTED"

                    lastX = mouseX;
                    lastY = mouseY;
                }
            }
        }
    }

    Component.onCompleted:  {
        var index
        //console.log("points_X:"+ points_X.length)
        for(index = 0;index < points_X.length;index++){
            line.append(points_X[index],points_Y[index])
            points.append(points_X[index],points_Y[index])
            //console.log("append : "+ points_X[index],points_Y[index])

        }

        myChart.axisX().max = points_X[points_X.length-1] * 1.2
        myChart.axisY().max = points_Y[points_Y.length-1] * 1.2
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值