QML Dial控件代码优化及Canvas绘制角度与游标角度同步问题咨询
自定义Dial控件优化及角度同步修复方案
需求说明
- 对现有QML Dial自定义控件做性能与可维护性优化
- 修复endAngle计算异常问题,实现Canvas进度绘制角度与游标旋转角度完全同步
该控件属于HMI类Dopsoft程序的预制控件体系,目标是成为可在项目设计阶段直接调用的通用自定义控件。
问题根因
原有endAngle计算逻辑存在两处核心错误:
- 角度与弧度的映射逻辑混乱,额外增加了
dial.angle / 0.87的错误换算,导致绘制角度与游标角度偏差 - 阈值判断逻辑错误,将endAngle的上限设为0.87弧度(约50度),远低于Dial默认280度的最大行程,直接导致进度绘制异常
修复后完整代码
Dial.qml
import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Shapes 1.12 import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 import QtQml 2.12 import QtQuick.Layouts 1.12 Item { id: dial_item x: dial_x y: dial_y width: dial_width height: dial_height property string dial_fontStyle: "Calibri" property real dial_x: 400 property real dial_y: 200 property real dial_width: 200 property real dial_height: dial_width property real dial_value: 10 property real dial_textSize: 55 property string dial_textColor: "white" property string dial_backColor: "orange" property bool dial_textBold: false property string dial_borderColorPress: "gray" property string dial_borderColorNotPress: "yellow" property real dial_borderWidth: 2 property string dial_strokeColor: "#29b6f6" property real dial_strokeWidth: 10 property real dial_widthCursor: 16 property real dial_heightCursor: dial_widthCursor property string dial_cursorColorPress: "blue" property string dial_cursorColorNotPress: "#21be2b" property real dial_cursorRadius: 8 property real dial_shadowGlowRadius: 2 property real dial_shadowSpread: 0.8 property string dial_shadowColor: "black" property real dial_shadowRadius: dial_width / 2 // 新增:统一管理Dial总行程角度,默认280度 property real dial_maxAngle: 280 // 控件阴影效果 RectangularGlow { id: effect anchors.fill: parent glowRadius: dial_shadowGlowRadius spread: dial_shadowSpread color: dial_shadowColor cornerRadius: dial_shadowRadius } FontLoader { id: fontStyle; name: dial_fontStyle } // @disable-check M129 Dial { id: dial anchors.fill: parent // 新增:值双向绑定,保证中心数值同步更新 value: dial_value onValueChanged: dial_value = value // 统一设置最大角度 maximumAngle: dial_maxAngle Text { id: txt text: qsTr(dial_value + "%") anchors.centerIn: parent font.family: fontStyle.name font.pixelSize: dial_textSize font.bold: dial_textBold color: dial_textColor } background: Rectangle { id: rect x: dial.width / 2 - width / 2 y: dial.height / 2 - height / 2 width: Math.max(64, Math.min(dial.width, dial.height)) height: width color: dial_backColor radius: width / 2 border.color: dial.pressed ? dial_borderColorPress : dial_borderColorNotPress border.width: dial_borderWidth opacity: dial.enabled ? 1 : 0.3 Canvas { id: canvas anchors.fill: parent Connections { target: dial onAngleChanged: canvas.requestPaint() } onPaint: { var ctx = getContext("2d") ctx.clearRect(0, 0, width, height) ctx.strokeStyle = dial_strokeColor ctx.lineWidth = dial_strokeWidth ctx.beginPath() // 起始角固定为2.2619弧度(129.6度) var startAngle = Math.PI / 5 * 3.6 // 修复:直接将游标角度转弧度累加,完全同步 var endAngle = startAngle + dial.angle * Math.PI / 180 // 修正阈值判断,避免超出最大行程 if (endAngle > startAngle + dial_maxAngle * Math.PI / 180) { endAngle = startAngle + dial_maxAngle * Math.PI / 180 } ctx.arc(width / 2, height / 2, width / 2 - ctx.lineWidth / 2 - 2, startAngle, endAngle) ctx.stroke() } } } handle: Rectangle { id: handleItem x: dial.background.x + dial.background.width / 2 - width / 2 y: dial.background.y + dial.background.height / 2 - height / 2 width: dial_widthCursor height: dial_heightCursor color: dial.pressed ? dial_cursorColorPress : dial_cursorColorNotPress radius: dial_cursorRadius antialiasing: true opacity: dial.enabled ? 1 : 0.3 transform: [ Translate { y: -Math.min(dial.background.width, dial.background.height) * 0.4 + handleItem.height / 2 }, Rotation { angle: dial.angle origin.x: handleItem.width / 2 origin.y: handleItem.height / 2 } ] } } }
优化点说明
- 布局优化:根Item直接承接坐标定位属性,子控件通过anchors.fill自适应布局,减少冗余坐标设置
- 可维护性优化:新增
dial_maxAngle属性统一管控行程角度,删除所有硬编码魔数 - 功能修复:修正endAngle计算逻辑,完全匹配游标的旋转角度,同步修复阈值判断逻辑
- 交互优化:新增
dial_value与Dial控件value的双向绑定,中心百分比显示随操作实时更新 - 注释优化:修正拼写错误,补充核心逻辑说明
内容的提问来源于stack exchange,提问作者MReza Bagherzadeh
相关产品推荐
相关产品推荐

