通过移动坐标点上的滑块实现修改折线的坐标点的值。具体效果如下:
大体分为两点:1、实现可移动的滑块,并获取实时x,y坐标。
2、根据滑块的x,y坐标转换为折线图上的坐标点并实时更新折线。
一、可移动滑块的实现
完整代码:
import QtQuick 2.0Item {property real mouseXTMP: 0 //鼠标坐标临时值property real mouseYTMP: 0property string backgroundDefaultImage: imageSource("heat_curve_point_unselected") //滑块选中和未选状态图片property string backgroundActiveImage: imageSource("heat_curve_point_selected")property bool clickedFlag: false //滑块的状态标志property bool editEnable: false //可编辑标志onEditEnableChanged: {if(!editEnable)clickedFlag = false}property string movingDirection: "Arbitrary" //Horizontal Vertical Arbitraryproperty int y_Max: 371property int y_Min: 33property int x_Max: 800property int x_Min: 33property int curVal: 35signal mousePosition(int x_pos,int y_pos)signal btnClicked()id: rectwidth: 70height: 80Rectangle{id:btn_temp_rectwidth: 52height: 28color: "#161821"radius:4visible: !clickedFlaganchors.horizontalCenter: parent.horizontalCenterText{id:btn_temp_textanchors.fill: parenttext: curVal + "\u00B0C"color: "#FFFFFF"font.pixelSize: 16opacity: 0.6horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignTop}}Item{width: 70height: 70anchors.bottom: parent.bottomImage {id:activeImagewidth: 70height: 70anchors.fill:parentvisible: {if(backgroundActiveImage === backgroundDefaultImage)return falseelsereturn clickedFlag}source:backgroundActiveImage}Image {id:defaultImagewidth: 30height: 30anchors.centerIn: parentvisible:{if(backgroundActiveImage === backgroundDefaultImage)return trueelsereturn !clickedFlag}source:backgroundDefaultImage}}MouseArea {anchors.fill: parentonPressed: {if(editEnable){
// if(!clickedFlag)
// clickedFlag = !clickedFlag //切换状态标志,取消选中由外面操作,当其他滑块被选中才取消选中状态mouseXTMP = mouseXmouseYTMP = mouseYrect.btnClicked()}}onPositionChanged: {if(editEnable){var tmpX = mouseX + rect.x - mouseXTMP //计算移动后的坐标var tmpY = mouseY + rect.y - mouseYTMPif(tmpX<=x_Min - rect.width/2)tmpX = x_Min - rect.width/2if(tmpX>=x_Max - rect.width/2)tmpX = x_Max - rect.width/2if(tmpY<=y_Min - rect.width/2 -10)tmpY = y_Min - rect.width/2 -10if(tmpY>=y_Max - rect.width/2 -10)tmpY = y_Max - rect.width/2 -10if(movingDirection == "Arbitrary"){rect.x = tmpXrect.y = tmpY}else if(movingDirection == "Horizontal")rect.x = tmpXelse if(movingDirection == "Vertical")rect.y = tmpYmousePosition(rect.x + rect.width/2,rect.y + rect.width/2 + 10)console.log(rect.x + rect.width/2,rect.y + rect.width/2 + 10) //圆形中心位置}}}
}
这部分的核心在鼠标事件上,点击的时候记录鼠标的x、y坐标mouseXTMP,mouseYTMP,鼠标移动后新的坐标x,y减去开始时的mouseXTMP,mouseYTMP就等于移动距离,将这个移动距离加到滑块的x,y即可实现移动,最后通过信号发送坐标。
二、折线
部分代码:
//曲线
import QtQuick 2.12
import QtCharts 2.3
import "../../../Common"
import "../../"
import "../OverviewOfSystemParameters"
import "../../SettingPopup"Rectangle{property string titleText1: "Please drag the control point to adjust" //曲线图标题文本property string titleText2: "the ambient temp Te and corresponding water temp Tw"property string legendText1: "Tw-Low-temp water temp"property string legendText2: "Te-Ambient temp"property bool editEnable: falseproperty string curBtn: "" //当前被点击的btnproperty int twMax: 40 //制热设定温度 环温最值property int twMin: 20property int teMax: 25property int teMin: -25property var defaultValue: [[-25,40],[-15,37],[-5,33],[5,20],[10,20]]onDefaultValueChanged: {init(1)}property var curValue: [[-25,40],[-15,37],[-5,33],[5,29],[10,25]]signal curveValue(var curValue)signal backBtnClicked()id:rootwidth: 906height: 600color: "#05060A" ChartView { //绘制曲线图id: chartViewwidth: 833height: 404z:0
// plotArea: Qt.rect(33, 33, 800, 371)anchors.bottom: parent.bottomanchors.bottomMargin: 36anchors.left: parent.leftanchors.leftMargin: 46antialiasing: true //抗锯齿backgroundColor: "#05060A"
// backgroundColor: "red"legend.visible: falseValueAxis { //横轴id: xAxismin: teMinmax: teMaxtickCount: 7visible: falsegridVisible: false
// labelFormat: "%.0f h"
// labelsColor: "#9B9B9D"}ValueAxis { //纵轴id: yAxismin: twMinmax: twMaxtickCount: 6visible: falsegridVisible: false
// labelFormat: "%.0f°C"
// labelsColor: "#9B9B9D"}LineSeries {id: lineSeriesname: "series1"axisX: xAxisaxisY: yAxiscolor: "#E98017"width: 3XYPoint { x: defaultValue[0][0]; y: defaultValue[0][1] }XYPoint { x: defaultValue[1][0]; y: defaultValue[1][1] }XYPoint { x: defaultValue[2][0]; y: defaultValue[2][1] }XYPoint { x: defaultValue[3][0]; y: defaultValue[3][1] }XYPoint { x: defaultValue[4][0]; y: defaultValue[4][1] }function updatePoint(index, x, y) {var chartPos = chartView.mapToValue(Qt.point(x, y), lineSeries);this.replace(index, chartPos.x, chartPos.y);curValue[index][0] = Math.round(chartPos.x)curValue[index][1] = Math.round(chartPos.y)curTw.text = Math.round(chartPos.y) + "\u00B0C"switch (index){case 0:curTe1.text = Math.round(chartPos.x) + "\u00B0C"btn1.curVal= Math.round(chartPos.y)breakcase 1:curTe2.text = Math.round(chartPos.x) + "\u00B0C"btn2.curVal= Math.round(chartPos.y)breakcase 2:curTe3.text = Math.round(chartPos.x) + "\u00B0C"btn3.curVal= Math.round(chartPos.y)breakcase 3:curTe4.text = Math.round(chartPos.x) + "\u00B0C"btn4.curVal= Math.round(chartPos.y)breakcase 4:curTe5.text = Math.round(chartPos.x) + "\u00B0C"btn5.curVal= Math.round(chartPos.y)break}console.log( "val:",chartPos.x, chartPos.y)}}CustomRemoveableBtn{id:btn1x:100y:100z:1x_Max: btn2.x + btn2.width/2editEnable: root.editEnableonMousePosition: {console.log("x_pos:",x_pos,"y_pos:",y_pos)console.log("x:",btn1.x,"y:",btn1.y)lineSeries.updatePoint(0, x_pos, y_pos);}onBtnClicked: {clickedFlag = truebtn2.clickedFlag = falsebtn3.clickedFlag = falsebtn4.clickedFlag = falsebtn5.clickedFlag = falsecurBtn = "btn1"curTw.text = curValue[0][1] + "\u00B0C"}}CustomRemoveableBtn{id:btn2x:100y:100z:1x_Min:btn1.x + btn1.width/2x_Max:btn3.x + btn3.width/2editEnable: root.editEnableonMousePosition: {console.log("x:",x,"y:",y)lineSeries.updatePoint(1, x_pos, y_pos);}onBtnClicked: {clickedFlag = truebtn1.clickedFlag = falsebtn3.clickedFlag = falsebtn4.clickedFlag = falsebtn5.clickedFlag = falsecurBtn = "btn2"curTw.text = curValue[1][1] + "\u00B0C"}}CustomRemoveableBtn{id:btn3x:100y:100z:1x_Min:btn2.x + btn2.width/2x_Max:btn4.x + btn4.width/2editEnable: root.editEnableonMousePosition: {console.log("x:",x,"y:",y)lineSeries.updatePoint(2, x_pos, y_pos);}onBtnClicked: {clickedFlag = truebtn1.clickedFlag = falsebtn2.clickedFlag = falsebtn4.clickedFlag = falsebtn5.clickedFlag = falsecurBtn = "btn3"curTw.text = curValue[2][1] + "\u00B0C"}}CustomRemoveableBtn{id:btn4x:100y:100z:1x_Min:btn3.x + btn4.width/2x_Max:btn5.x + btn5.width/2editEnable: root.editEnableonMousePosition: {console.log("x:",x,"y:",y)lineSeries.updatePoint(3, x_pos, y_pos);}onBtnClicked: {clickedFlag = truebtn1.clickedFlag = falsebtn2.clickedFlag = falsebtn3.clickedFlag = falsebtn5.clickedFlag = falsecurBtn = "btn4"curTw.text = curValue[3][1] + "\u00B0C"}}CustomRemoveableBtn{id:btn5x:100y:100z:1x_Min:btn4.x + btn4.width/2editEnable: root.editEnableonMousePosition: {console.log("x:",x,"y:",y)lineSeries.updatePoint(4, x_pos, y_pos);}onBtnClicked: {clickedFlag = truebtn1.clickedFlag = falsebtn2.clickedFlag = falsebtn3.clickedFlag = falsebtn4.clickedFlag = false
// dottedLine6.visible = true
// dottedLine6.y = btn5.y + btn5.width/2 + 10
// dottedLine6.width = btn5.xcurBtn = "btn5"curTw.text = curValue[4][1] + "\u00B0C"}}Component.onCompleted: {init()}DottedLine{ //虚线id:dottedLine1x: btn1.x+btn1.width/2y: btn1.y+btn1.height/2+10height: 371-ylineColor: btn1.clickedFlag ? "#E98017" : "#FFFFFF"opacity: btn1.clickedFlag ? 1 : 0.2}DottedLine{ //虚线id:dottedLine2x: btn2.x+btn2.width/2y: btn2.y+btn2.height/2+10height: 371-ylineColor: btn2.clickedFlag ? "#E98017" : "#FFFFFF"opacity: btn2.clickedFlag ? 1 : 0.2}DottedLine{ //虚线id:dottedLine3x: btn3.x+btn3.width/2y: btn3.y+btn3.height/2+10height: 371-ylineColor: btn3.clickedFlag ? "#E98017" : "#FFFFFF"opacity: btn3.clickedFlag ? 1 : 0.2}DottedLine{ //虚线id:dottedLine4x: btn4.x+btn4.width/2y: btn4.y+btn4.height/2+10height: 371-ylineColor: btn4.clickedFlag ? "#E98017" : "#FFFFFF"opacity: btn4.clickedFlag ? 1 : 0.2}DottedLine{ //虚线id:dottedLine5x: btn5.x+btn5.width/2y: btn5.y+btn5.height/2+10height: 371-ylineColor: btn5.clickedFlag ? "#E98017" : "#FFFFFF"opacity: btn5.clickedFlag ? 1 : 0.2}DottedLine{ //横轴虚线id:dottedLine6x: 33y: {switch (curBtn){case "btn1":return btn1.y + btn1.width/2 + 10case "btn2":return btn2.y + btn2.width/2 + 10case "btn3":return btn3.y + btn3.width/2 + 10case "btn4":return btn4.y + btn4.width/2 + 10case "btn5":return btn5.y + btn5.width/2 + 10default:break}}width: {switch (curBtn){case "btn1":return btn1.xcase "btn2":return btn2.xcase "btn3":return btn3.xcase "btn4":return btn4.xcase "btn5":return btn5.xdefault:break}}height: 2visible: falselineDirection: "horizontal"lineColor: "#E98017"}Text {text: "Te/℃"color: "#FFFFFF"font.pixelSize: 16opacity: 0.6anchors.right: parent.rightanchors.rightMargin: 8anchors.bottom: parent.bottomanchors.bottomMargin: 40}}function init(val=0){for(var i=0;i<5;i++){
// var screenPos = chartView.mapToPosition(Qt.point(defaultValue[i][0], defaultValue[i][1]), splineSeries);var X_tmp = ((defaultValue[i][0] - teMin)/(teMax-teMin)) * 757var Y_tmp = (1-((defaultValue[i][1] - twMin)/(twMax-twMin))) * 338 -10if(val)lineSeries.replace(lineSeries.at(i).x,lineSeries.at(i).y,defaultValue[i][0],defaultValue[i][1])curValue[i][0] = defaultValue[i][0]curValue[i][1] = defaultValue[i][1]switch(i){case 0:btn1.x = X_tmpbtn1.y = Y_tmpcurTe1.text = defaultValue[i][0] + "\u00B0C"btn1.curVal= defaultValue[i][1]console.log("-----btn1,x,y-----")console.log(btn1.x,btn1.y)break;case 1:btn2.x = X_tmpbtn2.y = Y_tmpcurTe2.text = defaultValue[i][0] + "\u00B0C"btn2.curVal= defaultValue[i][1]console.log("-----btn2,x,y-----")console.log(btn2.x,btn2.y)break;case 2:btn3.x = X_tmpbtn3.y = Y_tmpcurTe3.text = defaultValue[i][0] + "\u00B0C"btn3.curVal= defaultValue[i][1]console.log("-----btn3,x,y-----")console.log(btn3.x,btn3.y)break;case 3:btn4.x = X_tmpbtn4.y = Y_tmpcurTe4.text = defaultValue[i][0] + "\u00B0C"btn4.curVal= defaultValue[i][1]console.log("-----btn4,x,y-----")console.log(btn4.x,btn4.y)break;case 4:btn5.x = X_tmpbtn5.y = Y_tmpcurTe5.text = defaultValue[i][0] + "\u00B0C"btn5.curVal= defaultValue[i][1]console.log("-----btn5,x,y-----")console.log(btn5.x,btn5.y)break;default:break;}
// console.log("----------")
// console.log(((defaultValue[i][0] - teMin)/(teMax-teMin)))}}
}
1、qml中图形的绘制都是用ChartView,然后根据需求选择LineSeries(折线)、SplineSeries(曲线)。ChartView犹如一张画布,可以在上面绘制不同的图形。
2.1 ChartView中的两种坐标
ChartView上有两种坐标,第一种是原点固定在ChartView左上方的位置坐标,与我们平时给控件设置的x,y位置属性相同。第二种是因为我们图形中设置横轴纵轴产生的新的坐标系,在最开始的效果图中,横坐标从左向右-25到25,纵轴从下到上是20到40,这就是我们绘制图形中新的坐标系。
2.2 坐标转换
在第一部分移动滑块中信号发送出来的坐标是第一种,想要将这个坐标用于曲线或者折线的坐标点,需要转换为第二种坐标。
ChartView里面有mapToPosition()、mapToValue()两个方法,第一个函数用于将第二种坐标转换为第一种坐标,第二个函数用于将第一种坐标转换为第二种坐标。我们可以在折线下创建一个更新函数updatePoint()。该函数将坐标进行转换,并且使用replace()函数更换对应的坐标点以达到实时移动折线的效果。