通过移动坐标点上的滑块实现修改折线的坐标点的值。具体效果如下:
大体分为两点:1、实现可移动的滑块,并获取实时x,y坐标。
2、根据滑块的x,y坐标转换为折线图上的坐标点并实时更新折线。
一、可移动滑块的实现
完整代码:
import QtQuick 2.0
Item {
property real mouseXTMP: 0 //鼠标坐标临时值
property real mouseYTMP: 0
property 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 Arbitrary
property int y_Max: 371
property int y_Min: 33
property int x_Max: 800
property int x_Min: 33
property int curVal: 35
signal mousePosition(int x_pos,int y_pos)
signal btnClicked()
id: rect
width: 70
height: 80
Rectangle{
id:btn_temp_rect
width: 52
height: 28
color: "#161821"
radius:4
visible: !clickedFlag
anchors.horizontalCenter: parent.horizontalCenter
Text{
id:btn_temp_text
anchors.fill: parent
text: curVal + "\u00B0C"
color: "#FFFFFF"
font.pixelSize: 16
opacity: 0.6
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
}
}
Item{
width: 70
height: 70
anchors.bottom: parent.bottom
Image {
id:activeImage
width: 70
height: 70
anchors.fill:parent
visible: {
if(backgroundActiveImage === backgroundDefaultImage)
return false
else
return clickedFlag
}
source:backgroundActiveImage
}
Image {
id:defaultImage
width: 30
height: 30
anchors.centerIn: parent
visible:{
if(backgroundActiveImage === backgroundDefaultImage)
return true
else
return !clickedFlag
}
source:backgroundDefaultImage
}
}
MouseArea {
anchors.fill: parent
onPressed: {
if(editEnable){
// if(!clickedFlag)
// clickedFlag = !clickedFlag //切换状态标志,取消选中由外面操作,当其他滑块被选中才取消选中状态
mouseXTMP = mouseX
mouseYTMP = mouseY
rect.btnClicked()
}
}
onPositionChanged: {
if(editEnable){
var tmpX = mouseX + rect.x - mouseXTMP //计算移动后的坐标
var tmpY = mouseY + rect.y - mouseYTMP
if(tmpX<=x_Min - rect.width/2)
tmpX = x_Min - rect.width/2
if(tmpX>=x_Max - rect.width/2)
tmpX = x_Max - rect.width/2
if(tmpY<=y_Min - rect.width/2 -10)
tmpY = y_Min - rect.width/2 -10
if(tmpY>=y_Max - rect.width/2 -10)
tmpY = y_Max - rect.width/2 -10
if(movingDirection == "Arbitrary"){
rect.x = tmpX
rect.y = tmpY
}
else if(movingDirection == "Horizontal")
rect.x = tmpX
else if(movingDirection == "Vertical")
rect.y = tmpY
mousePosition(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: false
property string curBtn: "" //当前被点击的btn
property int twMax: 40 //制热设定温度 环温最值
property int twMin: 20
property int teMax: 25
property int teMin: -25
property 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:root
width: 906
height: 600
color: "#05060A"
ChartView { //绘制曲线图
id: chartView
width: 833
height: 404
z:0
// plotArea: Qt.rect(33, 33, 800, 371)
anchors.bottom: parent.bottom
anchors.bottomMargin: 36
anchors.left: parent.left
anchors.leftMargin: 46
antialiasing: true //抗锯齿
backgroundColor: "#05060A"
// backgroundColor: "red"
legend.visible: false
ValueAxis { //横轴
id: xAxis
min: teMin
max: teMax
tickCount: 7
visible: false
gridVisible: false
// labelFormat: "%.0f h"
// labelsColor: "#9B9B9D"
}
ValueAxis { //纵轴
id: yAxis
min: twMin
max: twMax
tickCount: 6
visible: false
gridVisible: false
// labelFormat: "%.0f°C"
// labelsColor: "#9B9B9D"
}
LineSeries {
id: lineSeries
name: "series1"
axisX: xAxis
axisY: yAxis
color: "#E98017"
width: 3
XYPoint { 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)
break
case 1:
curTe2.text = Math.round(chartPos.x) + "\u00B0C"
btn2.curVal= Math.round(chartPos.y)
break
case 2:
curTe3.text = Math.round(chartPos.x) + "\u00B0C"
btn3.curVal= Math.round(chartPos.y)
break
case 3:
curTe4.text = Math.round(chartPos.x) + "\u00B0C"
btn4.curVal= Math.round(chartPos.y)
break
case 4:
curTe5.text = Math.round(chartPos.x) + "\u00B0C"
btn5.curVal= Math.round(chartPos.y)
break
}
console.log( "val:",chartPos.x, chartPos.y)
}
}
CustomRemoveableBtn{
id:btn1
x:100
y:100
z:1
x_Max: btn2.x + btn2.width/2
editEnable: root.editEnable
onMousePosition: {
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 = true
btn2.clickedFlag = false
btn3.clickedFlag = false
btn4.clickedFlag = false
btn5.clickedFlag = false
curBtn = "btn1"
curTw.text = curValue[0][1] + "\u00B0C"
}
}
CustomRemoveableBtn{
id:btn2
x:100
y:100
z:1
x_Min:btn1.x + btn1.width/2
x_Max:btn3.x + btn3.width/2
editEnable: root.editEnable
onMousePosition: {
console.log("x:",x,"y:",y)
lineSeries.updatePoint(1, x_pos, y_pos);
}
onBtnClicked: {
clickedFlag = true
btn1.clickedFlag = false
btn3.clickedFlag = false
btn4.clickedFlag = false
btn5.clickedFlag = false
curBtn = "btn2"
curTw.text = curValue[1][1] + "\u00B0C"
}
}
CustomRemoveableBtn{
id:btn3
x:100
y:100
z:1
x_Min:btn2.x + btn2.width/2
x_Max:btn4.x + btn4.width/2
editEnable: root.editEnable
onMousePosition: {
console.log("x:",x,"y:",y)
lineSeries.updatePoint(2, x_pos, y_pos);
}
onBtnClicked: {
clickedFlag = true
btn1.clickedFlag = false
btn2.clickedFlag = false
btn4.clickedFlag = false
btn5.clickedFlag = false
curBtn = "btn3"
curTw.text = curValue[2][1] + "\u00B0C"
}
}
CustomRemoveableBtn{
id:btn4
x:100
y:100
z:1
x_Min:btn3.x + btn4.width/2
x_Max:btn5.x + btn5.width/2
editEnable: root.editEnable
onMousePosition: {
console.log("x:",x,"y:",y)
lineSeries.updatePoint(3, x_pos, y_pos);
}
onBtnClicked: {
clickedFlag = true
btn1.clickedFlag = false
btn2.clickedFlag = false
btn3.clickedFlag = false
btn5.clickedFlag = false
curBtn = "btn4"
curTw.text = curValue[3][1] + "\u00B0C"
}
}
CustomRemoveableBtn{
id:btn5
x:100
y:100
z:1
x_Min:btn4.x + btn4.width/2
editEnable: root.editEnable
onMousePosition: {
console.log("x:",x,"y:",y)
lineSeries.updatePoint(4, x_pos, y_pos);
}
onBtnClicked: {
clickedFlag = true
btn1.clickedFlag = false
btn2.clickedFlag = false
btn3.clickedFlag = false
btn4.clickedFlag = false
// dottedLine6.visible = true
// dottedLine6.y = btn5.y + btn5.width/2 + 10
// dottedLine6.width = btn5.x
curBtn = "btn5"
curTw.text = curValue[4][1] + "\u00B0C"
}
}
Component.onCompleted: {
init()
}
DottedLine{ //虚线
id:dottedLine1
x: btn1.x+btn1.width/2
y: btn1.y+btn1.height/2+10
height: 371-y
lineColor: btn1.clickedFlag ? "#E98017" : "#FFFFFF"
opacity: btn1.clickedFlag ? 1 : 0.2
}
DottedLine{ //虚线
id:dottedLine2
x: btn2.x+btn2.width/2
y: btn2.y+btn2.height/2+10
height: 371-y
lineColor: btn2.clickedFlag ? "#E98017" : "#FFFFFF"
opacity: btn2.clickedFlag ? 1 : 0.2
}
DottedLine{ //虚线
id:dottedLine3
x: btn3.x+btn3.width/2
y: btn3.y+btn3.height/2+10
height: 371-y
lineColor: btn3.clickedFlag ? "#E98017" : "#FFFFFF"
opacity: btn3.clickedFlag ? 1 : 0.2
}
DottedLine{ //虚线
id:dottedLine4
x: btn4.x+btn4.width/2
y: btn4.y+btn4.height/2+10
height: 371-y
lineColor: btn4.clickedFlag ? "#E98017" : "#FFFFFF"
opacity: btn4.clickedFlag ? 1 : 0.2
}
DottedLine{ //虚线
id:dottedLine5
x: btn5.x+btn5.width/2
y: btn5.y+btn5.height/2+10
height: 371-y
lineColor: btn5.clickedFlag ? "#E98017" : "#FFFFFF"
opacity: btn5.clickedFlag ? 1 : 0.2
}
DottedLine{ //横轴虚线
id:dottedLine6
x: 33
y: {
switch (curBtn){
case "btn1":
return btn1.y + btn1.width/2 + 10
case "btn2":
return btn2.y + btn2.width/2 + 10
case "btn3":
return btn3.y + btn3.width/2 + 10
case "btn4":
return btn4.y + btn4.width/2 + 10
case "btn5":
return btn5.y + btn5.width/2 + 10
default:break
}
}
width: {
switch (curBtn){
case "btn1":
return btn1.x
case "btn2":
return btn2.x
case "btn3":
return btn3.x
case "btn4":
return btn4.x
case "btn5":
return btn5.x
default:break
}
}
height: 2
visible: false
lineDirection: "horizontal"
lineColor: "#E98017"
}
Text {
text: "Te/℃"
color: "#FFFFFF"
font.pixelSize: 16
opacity: 0.6
anchors.right: parent.right
anchors.rightMargin: 8
anchors.bottom: parent.bottom
anchors.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)) * 757
var Y_tmp = (1-((defaultValue[i][1] - twMin)/(twMax-twMin))) * 338 -10
if(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_tmp
btn1.y = Y_tmp
curTe1.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_tmp
btn2.y = Y_tmp
curTe2.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_tmp
btn3.y = Y_tmp
curTe3.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_tmp
btn4.y = Y_tmp
curTe4.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_tmp
btn5.y = Y_tmp
curTe5.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()函数更换对应的坐标点以达到实时移动折线的效果。