QML:拖动曲线坐标点修改曲线

news/2024/9/23 2:24:39/

        通过移动坐标点上的滑块实现修改折线的坐标点的值。具体效果如下:

大体分为两点: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&deg;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()函数更换对应的坐标点以达到实时移动折线的效果。


http://www.ppmy.cn/news/1124530.html

相关文章

Flask配合Echarts写一个动态可视化大屏

ch 技术 后端&#xff1a;flask 可视化&#xff1a;echarts 前端&#xff1a;HTMLJavaScriptcss 大屏布局 大屏拆分 案例项目中大屏可按版块进行拆解&#xff0c;会发现这里大屏主要由标题、折线图、柱状图、地图、滚动图和词云等组成&#xff0c;整体可切分为8个版块&…

Spring Boot 发送邮件

Spring Boot 发送邮件 准备工作引入依赖邮箱开启SMTP三方登录授权配置邮件服务器 发送邮件普通文本邮件复杂邮件 准备工作 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId&…

从中序遍历和后序遍历构建二叉树

题目描述 106. 从中序与后序遍历序列构造二叉树 中等 1.1K 相关企业 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1…

AI时代的中国困境: ChatGPT为什么难以复制

如今&#xff0c;几乎所有中国互联网大厂都公布了自己的“类ChatGPT”解决方案&#xff0c;有些还公布了背后的关于AI技术模型的详情。 其中最高调的是百度&#xff0c;其“文心一言”解决方案号称即将接入数十家内容平台和数以百计的媒体、自媒体。腾讯公布的微信 AI 模型“W…

redhat 6.1 测试环境安装 yum

redhat 6.1 测试环境安装 yum 记录 1. 新建虚拟机 1.1 自定义建立虚拟机 自定义创建新的虚拟机 选择硬件兼容性 创建空白硬盘&#xff0c;稍后选择 iso 文件创建系统。 选择操作系统类型 为虚拟机命名 选择处理器配置 选择虚拟机内存 选择虚拟机网络类型 选择…

python循环判断

运算符短路逻辑python优先级 思维导图for循环 运算符 运算符含义and左边和右边同时为True,结果为Trueor左右两边其中一个为True,结果为Truenot如果操作数为True(False)&#xff0c;结果为False(True) 短路逻辑 从左到右&#xff0c;只有当第一操作数的值无法确定逻辑运算的结…

Python项目实战:基于2D或3D的区域增长算法

文章目录 一、简介二、项目实战2.1、2D图像&#xff08;10x10&#xff09;2.2、2D图像&#xff08;100x100&#xff09;2.3、3D图像&#xff08;10x10x10&#xff09; 一、简介 区域增长算法是一种用于图像分割方法&#xff0c;将相邻像素按照一定的相似性合并成一个区域。 步…

Android13 动态切换默认laucnher

需求&#xff1a;Android13 动态切换默认laucnher 废话不多说&#xff1a;直接上代码 package com.taide.launcher.util;import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; imp…