You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt6 QML线性仪表盘控件实现方案咨询

Qt6 QML 仪表盘控件实现方案

由于Qt6官方未提供仪表盘组件,以下是三种可行的实现方案:

方案一:使用Canvas自定义绘制

通过Canvas组件直接绘制仪表盘的背景、刻度、进度弧和指针,完全自定义样式,适配性强。

import QtQuick

Item {
    width: 300
    height: 300

    // 可配置属性
    property real value: 50          // 当前值(0-100)
    property real minValue: 0        // 最小值
    property real maxValue: 100      // 最大值
    property real startAngle: 135    // 仪表盘起始角度(顺时针)
    property real endAngle: 405      // 仪表盘结束角度(135+270,覆盖270度范围)

    Canvas {
        anchors.fill: parent
        onPaint: {
            const ctx = getContext("2d")
            ctx.clearRect(0, 0, width, height)
            ctx.translate(width/2, height/2) // 原点移至中心

            // 绘制背景圆弧
            ctx.lineWidth = 10
            ctx.strokeStyle = "#e0e0e0"
            ctx.beginPath()
            ctx.arc(0, 0, width/2 - 20, Math.PI * startAngle/180, Math.PI * endAngle/180)
            ctx.stroke()

            // 绘制进度圆弧
            const progressAngle = startAngle + (endAngle - startAngle) * (value - minValue)/(maxValue - minValue)
            ctx.strokeStyle = "#2196F3"
            ctx.beginPath()
            ctx.arc(0, 0, width/2 - 20, Math.PI * startAngle/180, Math.PI * progressAngle/180)
            ctx.stroke()

            // 绘制刻度线与刻度值
            ctx.lineWidth = 2
            ctx.fillStyle = "#333"
            ctx.textAlign = "center"
            ctx.textBaseline = "middle"
            const tickCount = 10
            for(let i = 0; i <= tickCount; i++){
                const angle = startAngle + (endAngle - startAngle) * i/tickCount
                const rad = Math.PI * angle/180
                const tickLength = i%2 === 0 ? 15 : 8

                // 绘制刻度线
                ctx.beginPath()
                ctx.moveTo(Math.cos(rad)*(width/2 - 20), Math.sin(rad)*(width/2 - 20))
                ctx.lineTo(Math.cos(rad)*(width/2 - 20 - tickLength), Math.sin(rad)*(width/2 - 20 - tickLength))
                ctx.stroke()

                // 绘制偶数刻度值
                if(i%2 === 0){
                    const text = Math.round(minValue + (maxValue - minValue)*i/tickCount)
                    const textX = Math.cos(rad)*(width/2 - 50)
                    const textY = Math.sin(rad)*(width/2 - 50)
                    ctx.fillText(text, textX, textY)
                }
            }
        }
    }

    // 指针
    Rectangle {
        anchors.centerIn: parent
        width: 6
        height: width/2 - 30
        color: "#F44336"
        rotation: startAngle + (endAngle - startAngle)*(value - minValue)/(maxValue - minValue)
        transformOrigin: Item.BottomCenter
        radius: 3
    }

    // 中心圆点
    Rectangle {
        anchors.centerIn: parent
        width: 20
        height: 20
        color: "#F44336"
        radius: 10
    }

    // 当前数值显示
    Text {
        anchors.centerIn: parent
        text: value.toFixed(1)
        font.pixelSize: 30
        color: "#333"
    }
}

方案二:使用QtQuick.Shapes声明式绘制

利用QtQuick.Shapes的路径绘制能力,以声明式语法实现仪表盘,更贴合QML的开发风格。

import QtQuick
import QtQuick.Shapes

Item {
    width: 300
    height: 300

    // 可配置属性
    property real value: 50
    property real minValue: 0
    property real maxValue: 100
    property real startAngle: 135
    property real sweepAngle: 270 // 仪表盘总覆盖角度

    Shape {
        anchors.fill: parent

        // 背景圆弧
        ShapePath {
            strokeWidth: 10
            strokeColor: "#e0e0e0"
            fillColor: "transparent"
            PathStart {
                x: width/2 + (width/2-20)*Math.cos(Math.PI*startAngle/180)
                y: height/2 + (width/2-20)*Math.sin(Math.PI*startAngle/180)
            }
            PathArc {
                x: width/2 + (width/2-20)*Math.cos(Math.PI*(startAngle+sweepAngle)/180)
                y: height/2 + (width/2-20)*Math.sin(Math.PI*(startAngle+sweepAngle)/180)
                radiusX: width/2-20
                radiusY: width/2-20
                largeArc: sweepAngle > 180
                sweepDirection: PathArc.Clockwise
            }
        }

        // 进度圆弧
        ShapePath {
            strokeWidth: 10
            strokeColor: "#2196F3"
            fillColor: "transparent"
            PathStart {
                x: width/2 + (width/2-20)*Math.cos(Math.PI*startAngle/180)
                y: height/2 + (width/2-20)*Math.sin(Math.PI*startAngle/180)
            }
            PathArc {
                x: width/2 + (width/2-20)*Math.cos(Math.PI*(startAngle + sweepAngle*(value-minValue)/(maxValue-minValue))/180)
                y: height/2 + (width/2-20)*Math.sin(Math.PI*(startAngle + sweepAngle*(value-minValue)/(maxValue-minValue))/180)
                radiusX: width/2-20
                radiusY: width/2-20
                largeArc: (sweepAngle*(value-minValue)/(maxValue-minValue)) > 180
                sweepDirection: PathArc.Clockwise
            }
        }
    }

    // 刻度线
    Repeater {
        model: 11
        delegate: Rectangle {
            anchors.centerIn: parent
            width: index%2 ===0 ? 3 : 2
            height: index%2 ===0 ? 15 : 8
            color: "#333"
            rotation: startAngle + sweepAngle*(index/10)
            transformOrigin: Item.BottomCenter
            y: -(width/2 - 20)
        }
    }

    // 刻度值
    Repeater {
        model: 6
        delegate: Text {
            anchors.centerIn: parent
            text: minValue + (maxValue-minValue)*(index*2/10)
            color: "#333"
            rotation: startAngle + sweepAngle*(index*2/10)
            transformOrigin: Item.Center
            x: -(width/2 - 50)*Math.sin(Math.PI*(startAngle + sweepAngle*(index*2/10))/180)
            y: (width/2 - 50)*Math.cos(Math.PI*(startAngle + sweepAngle*(index*2/10))/180)
        }
    }

    // 指针
    Rectangle {
        anchors.centerIn: parent
        width: 6
        height: width/2 - 30
        color: "#F44336"
        rotation: startAngle + sweepAngle*(value-minValue)/(maxValue-minValue)
        transformOrigin: Item.BottomCenter
        radius: 3
    }

    // 中心圆点
    Rectangle {
        anchors.centerIn: parent
        width: 20
        height: 20
        color: "#F44336"
        radius: 10
    }

    // 当前数值显示
    Text {
        anchors.centerIn: parent
        text: value.toFixed(1)
        font.pixelSize: 30
        color: "#333"
    }
}

方案三:使用第三方QML组件库

若不想从零开发,可采用社区维护的第三方组件库,例如Qml.Controls.Extra(部分版本兼容Qt6),这类库提供了现成的仪表盘组件,只需引入后直接调用即可,能节省开发时间。

内容的提问来源于stack exchange,提问作者praveen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:07:04