当前位置: 首页 > news >正文

QML Charts组件之折线图的鼠标交互

目录

    • 前言
    • 相关系列
    • 代码示例详解(LineSeriesDemo3.qml)
      • 功能概览
      • 运行效果
      • 代码说明
    • 工程下载
    • 参考

前言

接上文(QML Charts组件之折线图的基础属性),本文将重点介绍LineSeries的鼠标交互,包括:鼠标拖拽平移、滚轮缩放等操作。


相关系列

  • QML Charts组件之折线图的基础属性
  • QML Charts组件之LineSeries、SplineSeries与ScatterSeries
  • QML Charts组件之坐标轴共有属性

代码示例详解(LineSeriesDemo3.qml)

示例文件:Series/LineSeriesDemo3.qml

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtChartsRectangle {Layout.fillWidth: trueLayout.fillHeight: trueColumnLayout {anchors.fill: parentChartView {id: chartViewtitle: "折线图示例"titleFont.bold: truetitleFont.pointSize: 14Layout.fillWidth: trueLayout.fillHeight: trueantialiasing: trueValueAxis {id: axis_xmin: 0max: 10tickCount: 11}ValueAxis {id: axis_ymin: 0max: 10tickCount: 11}LineSeries {id: seriesname: "line"color: "#1296FF"width: 3bestFitLineColor : "#00FF00"bestFitLineVisible : true// 设置点是否可见pointsVisible : true// 设置点标签是否可见pointLabelsVisible : true// 设置点标签文本颜色pointLabelsColor: "#FF0000"// 设置点标签字体pointLabelsFont.bold: truepointLabelsFont.pointSize: 12pointLabelsFont.family: "Courier"// 设置点标签显示格式// 具体格式可能受Qt版本或平台影响// 如果数据太多,会影响性能// 格式标签限制:不支持更复杂的运算或格式化指令,如果是小数,可能需要对点数据预处理pointLabelsFormat : "(@xPoint,@yPoint)"// 控制当点标签超出绘图区域时是否被裁剪,默认为true// 设为false可允许标签显示在绘图区域之外,但需要注意可能布局重叠。pointLabelsClipping : trueaxisX: axis_xaxisY: axis_yonClicked: function(point){console.log("onClicked: " + Math.round(point.x) + ", " + Math.round(point.y));}}// 交互:拖拽平移与滚轮缩放property real __panStartX: 0property real __panStartY: 0property real __panStartMinX: 0property real __panStartMaxX: 0property real __panStartMinY: 0property real __panStartMaxY: 0MouseArea {anchors.fill: parentacceptedButtons: Qt.LeftButtonhoverEnabled: truepreventStealing: trueonPressed: function(mouse) {var pa = chartView.plotAreaif (!(mouse.x >= pa.x && mouse.x <= pa.x + pa.width &&mouse.y >= pa.y && mouse.y <= pa.y + pa.height)) {return}chartView.__panStartX = mouse.xchartView.__panStartY = mouse.ychartView.__panStartMinX = axis_x.minchartView.__panStartMaxX = axis_x.maxchartView.__panStartMinY = axis_y.minchartView.__panStartMaxY = axis_y.max}onPositionChanged: function(mouse) {if (!pressed) returnvar pa = chartView.plotAreaif (pa.width <= 0 || pa.height <= 0) returnvar dx = mouse.x - chartView.__panStartXvar dy = mouse.y - chartView.__panStartYvar rangeX = chartView.__panStartMaxX - chartView.__panStartMinXvar rangeY = chartView.__panStartMaxY - chartView.__panStartMinY// 像素 -> 数值映射(注意Y轴方向)var valueDeltaX = -dx * rangeX / pa.widthvar valueDeltaY =  dy * rangeY / pa.heightaxis_x.min = chartView.__panStartMinX + valueDeltaXaxis_x.max = chartView.__panStartMaxX + valueDeltaXaxis_y.min = chartView.__panStartMinY + valueDeltaYaxis_y.max = chartView.__panStartMaxY + valueDeltaY}}WheelHandler {// 以光标为中心缩放,支持鼠标与触控板acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPadtarget: chartViewonWheel: function(event) {var pa = chartView.plotArea//var pos = event.pointvar x = event.xvar y = event.yif (!(x >= pa.x && x <= pa.x + pa.width &&y >= pa.y && y <= pa.y + pa.height)) {return}// 计算缩放步数(15度/步),向上滚动放大var degrees = event.angleDelta.y / 8.0var steps = degrees / 15.0var factor = Math.pow(1.2, -steps)var minX = axis_x.minvar maxX = axis_x.maxvar minY = axis_y.minvar maxY = axis_y.maxvar rangeX = maxX - minXvar rangeY = maxY - minYif (rangeX <= 0 || rangeY <= 0) returnvar fx = (x - pa.x) / pa.widthvar fy = (y - pa.y) / pa.heightfx = Math.max(0, Math.min(1, fx))fy = Math.max(0, Math.min(1, fy))var newRangeX = rangeX * factorvar newRangeY = rangeY * factor// 以光标数值位置为锚点缩放var newMinX = minX + fx * (rangeX - newRangeX)var newMinY = minY + fy * (rangeY - newRangeY)axis_x.min = newMinXaxis_x.max = newMinX + newRangeXaxis_y.min = newMinYaxis_y.max = newMinY + newRangeYevent.accepted = true}}// Add data dynamically to the seriesComponent.onCompleted: {for (var i = 0; i <= 5; i++) {var num = Math.floor((Math.random()*10))series.append(i, num);}}}Row {Layout.minimumHeight: 50Button {text: "append"width: 100height: 25onClicked: {var num = Math.floor((Math.random()*10))series.append(series.count, num)axis_x.min++;axis_x.max++;}}Button {text: "remove"width: 100height: 25onClicked: {if (series.count > 0) {series.removePoints(series.count-1, 1)axis_x.min--;axis_x.max--;}}}}}
}

功能概览

这段 QML 代码实现了一个可交互的折线图窗口,能够进行鼠标拖拽平移和滚轮缩放操作。

功能实现方式
显示折线图使用ChartViewLineSeries
动态追加 / 删除数据使用 series.appendseries.removePoints 方法。
鼠标拖拽平移使用MouseArea ,记录按下/移动坐标。
滚轮缩放使用WheelHandler ,以光标为中心缩放,支持鼠标与触控板。
point标签显示使用pointLabelsFormat : "(@xPoint,@yPoint)", 显示坐标。
点击数据点触发onClicked 信号,打印坐标。

运行效果

请添加图片描述


代码说明

折线属性: 详细的属性描述见上文 — QML Charts组件之折线图的基础属性

拖拽平移(鼠标左键按住拖图表):

MouseArea {anchors.fill: parentonPressed:  { /* 记录初始状态 */ }onPositionChanged: { /* 根据位移更新轴范围 */ }
}

1. 按下瞬间干什么?
记住四件事:

  • 鼠标当时在屏幕的 x、y 坐标。
  • 当时 X 轴的最小值、最大值。
  • 当时 Y 轴的最小值、最大值。
    它们一起构成初始快照,后面所有计算都以这个快照为基准,不会累积误差。

2. 拖动过程中干什么?

  • 把鼠标当前位置跟按下时的位置做减法,得到像素位移 dx、dy。
  • 用 dx 除以绘图区宽度,得到横向走了百分之几;同样处理 dy。
  • 把百分比乘以当时的轴范围,就换算成全局坐标该走多少。
  • 右拖 → 画面要向右,于是把轴整体向左平移相同的量;左拖相反。
  • 下拖 → 画面要向上,于是把轴整体向下平移;上拖相反。
  • 每移动一次鼠标,就重新给轴的最小、最大值赋一次新结果,图就实时跟过来了。

简而言之 鼠标拖拽 是把鼠标像素差按宽高比例变成轴坐标差,然后整体平移轴范围。


滚轮缩放(以光标为中心放大/缩小):

WheelHandler {onWheel: function(event) {// 计算缩放步数(15度/步),向上滚动放大...// 以光标数值位置为锚点缩放...}
}

1. 什么时候生效?
只在绘图区内部滚轮才处理,滚到外边就不管,避免整个窗口一起乱动。

2. 滚一次算几步?
系统告诉你这次滚了多少度,15° 算一步。向上滚一步算 +1,向下滚一步算 −1。
把步数代进公式 1.2^(-步数) 得到缩放因子:

  • 向上滚一步 → 因子 ≈ 1.2,表示放大 20%。
  • 向下滚一步 → 因子 ≈ 0.83,表示缩小 17%。

3. 怎样以光标为中心?

  • 先算出光标在绘图区里的横向百分比 fx、纵向百分比 fy。
  • 缩放前后,光标对应的那个数据值必须保持不变。
  • 于是用百分比做插值:
    • 新范围 = 旧范围 × 因子。
    • 新最小值 = 旧最小值 + fx × (旧范围 − 新范围)。
    • 这样光标在旧矩形里占多少比例,在新矩形里还是同样比例,视觉上就是以光标为锚点放大或缩小。

简而言之 滚轮缩放 是把滚了几步变成缩放因子,再用光标位置做插值,重新算轴边界。


工程下载

Git Code 下载链接:QML Charts组件之折线图的基础属性示例

在这里插入图片描述


参考

  • Qt官方文档 - LineSeries
  • Qt官方文档 - XYSeries

文章转载自:

http://LuyU8CND.jkftn.cn
http://NyQTSJEB.jkftn.cn
http://60KrDeNe.jkftn.cn
http://z1xgcOMC.jkftn.cn
http://OUNrHNus.jkftn.cn
http://ZYGNHhJs.jkftn.cn
http://jchorwFl.jkftn.cn
http://vroDMvkq.jkftn.cn
http://tWJguYJn.jkftn.cn
http://EneOe9mx.jkftn.cn
http://Sd7pThnW.jkftn.cn
http://z24iAh6t.jkftn.cn
http://FftJeA1g.jkftn.cn
http://xuW0L0kv.jkftn.cn
http://HglJy4Ny.jkftn.cn
http://MTFLkDCO.jkftn.cn
http://qGVtT9f3.jkftn.cn
http://vGvkoP9E.jkftn.cn
http://vQMtHtfM.jkftn.cn
http://Cie9HBmb.jkftn.cn
http://yrBDfL6B.jkftn.cn
http://c8yscuAt.jkftn.cn
http://iGNgN1t0.jkftn.cn
http://a9dx04GE.jkftn.cn
http://Ip4dtVCl.jkftn.cn
http://2dZfGmRo.jkftn.cn
http://5XZiOYaF.jkftn.cn
http://w42FqB4g.jkftn.cn
http://qjD7C7Qe.jkftn.cn
http://alJgGLz4.jkftn.cn
http://www.dtcms.com/a/382725.html

相关文章:

  • 工程机械健康管理物联网系统:AIoT技术赋能装备全生命周期智能运维​
  • 第5课:上下文管理与状态持久化
  • SpringBootCodeGenerator使用JSqlParser解析DDL CREATE SQL 语句
  • 【WebSocket✨】入门之旅(五):WebSocket 的安全性
  • PHP使用echarts制作一个很漂亮的天气预报网站(曲线图+实况+未来一周预报)
  • 数据库造神计划第九天---增删改查(CRUD)(5)
  • 简单的折叠cell
  • 贪心算法在边缘计算卸载问题中的应用
  • pyAutoGUI 模块主要功能介绍-(2)键盘功能
  • 基于Qt Creator的Serial Port串口调试助手项目(代码开源)
  • Node.js 编码规范
  • Spring Boot 调度任务在分布式环境下的坑:任务重复执行与一致性保证
  • 【数据结构】 ArrayList深入解析
  • 4. 数系
  • 08 函数式编程
  • 安卓 Google Maps 的使用和开发步骤
  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第十三章知识点问答(15题)
  • 深入理解 Spring @Async 注解:原理、实现与实践
  • 【Qt开发】显示类控件(三)-> QProgressBar
  • 《Linux——gflags》
  • leetcode35.搜索插入位置
  • Java调用UniHttp接口请求失败?一次开源的深入实践-百度SN签名认证场景下参数乱序问题的三种解决策略
  • MongoDB 监控
  • 【Linux】system V共享内存
  • --- 统一请求入口 Gateway ---
  • 豆包Seedream 4.0多图融合实力派:田园犬+三花猫多场景创作,AI绘画新时代来了!
  • 贪心算法应用:数据包调度问题详解
  • html基本知识
  • 视觉SLAM第10讲:后端2(滑动窗口与位子图优化)
  • Modbus协议原理与Go语言实现详解