开源 C++ QT QML 开发(八)自定义控件--圆环

文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C++ QT QML 开发(一)基本介绍

开源 C++ QT QML 开发(二)工程结构

开源 C++ QT QML 开发(三)常用控件

开源 C++ QT QML 开发(四)复杂控件--Listview

开源 C++ QT QML 开发(五)复杂控件--Gridview

开源 C++ QT QML 开发(六)自定义控件--波形图

开源 C++ QT QML 开发(七)自定义控件--仪表盘

开源 C++ QT QML 开发(八)自定义控件--圆环

开源 C++ QT QML 开发(九)文件--文本和二进制

推荐链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

本章节主要内容是:圆环进度控件,参数可设置,数据可实时更新。

1.代码分析

2.所有源码

3.效果演示

一、代码分析1. 主程序文件 (main.qml) 分析

ApplicationWindow 主窗口

复制代码
ApplicationWindow {
    id: mainWindow
    width: 400
    height: 400
    title: "圆环进度控件示例"
    visible: true

    property real currentValue: 0      // 定义当前数值属性,初始为0
    property real maxValue: 100        // 定义最大值属性,初始为100

功能: 创建应用程序主窗口,定义了两个关键属性用于数据绑定。

columnlayout

复制代码
ColumnLayout {
    anchors.centerIn: parent  // 布局居中于父元素
    spacing: 20              // 子元素间距20像素

函数作用: 创建垂直布局,自动管理子元素的位置和大小

戒指

文本

复制代码
RingProgress {
    id: ringProgress
    Layout.preferredWidth: 200    // 设置首选宽度
    Layout.preferredHeight: 200   // 设置首选高度
    currentValue: mainWindow.currentValue    // 绑定到主窗口的当前值
    maxValue: mainWindow.maxValue            // 绑定到主窗口的最大值
    backgroundColor: "#f0f0f0"               // 设置背景色
    ringBackgroundColor: "#e0e0e0"           // 设置圆环底色
    ringColor: "#4CAF50"                     // 设置进度圆环颜色
    ringWidth: 15                            // 设置圆环宽度
}

属性绑定机制: 这里使用了QML的数据绑定,当`mainWindow.currentValue改变时,会自动更新圆环控件。

按钮控制区域

复制代码
Button {
    text: "加10"
    onClicked: {  // 点击事件处理函数
        if (mainWindow.currentValue + 10 <= mainWindow.maxValue) {
            mainWindow.currentValue += 10    // 增加值,不超过最大值
        } else {
            mainWindow.currentValue = mainWindow.maxValue  // 达到最大值
        }
    }
}

函数逻辑:

onClicked: 按钮点击信号的处理函数

条件判断确保数值在有效范围内

直接修改绑定的属性值,触发界面更新

  1. 自定义圆环控件 (RingProgress.qml) 详细分析

属性定义部分

复制代码
property real currentValue: 0        // 当前进度值
property real maxValue: 100          // 最大值
property color backgroundColor: "#f0f0f0"      // 控件背景色
property color ringBackgroundColor: "#e0e0e0"  // 圆环背景色
property color ringColor: "#4CAF50"            // 进度圆环颜色
property real ringWidth: 15                    // 圆环线条宽度

这些属性提供了控件的可配置接口。

计算属性

复制代码
property real progress: currentValue / maxValue  // 计算进度比例(0-1)
property real startAngle: -90      // 起始角度(从顶部开始)
property real endAngle: startAngle + (progress * 360)  // 结束角度

计算原理:

复制代码
progress: 通过数据绑定自动计算当前进度百分比

endAngle: 根据进度比例计算对应的圆弧结束角度

背景矩形

复制代码
Rectangle {
    id: backgroundRect
    anchors.fill: parent        // 填充整个父元素
    radius: width / 2          // 圆角半径设为宽度一半,形成圆形
    color: ringProgress.backgroundColor  // 使用配置的背景色
}

函数作用: 创建圆形背景,anchors.fill确保大小自适应。

圆环底色Canvas

复制代码
Canvas {
    id: backgroundCanvas
    anchors.fill: parent
    antialiasing: true    // 开启抗锯齿
    
    onPaint: {  // 绘制函数,在需要重绘时自动调用
        var ctx = getContext("2d")  // 获取2D绘图上下文
        ctx.reset()                 // 重置画布状态
        
        var centerX = width / 2     // 计算中心点X坐标
        var centerY = height / 2    // 计算中心点Y坐标
        var radius = Math.min(centerX, centerY) - ringWidth / 2  // 计算圆环半径
        
        ctx.beginPath()                         // 开始绘制路径
        ctx.lineWidth = ringWidth              // 设置线条宽度
        ctx.lineCap = "round"                  // 设置线条端点样式为圆形
        ctx.strokeStyle = ringBackgroundColor  // 设置线条颜色
        ctx.arc(centerX, centerY, radius, 0, Math.PI * 2)  // 绘制完整圆形
        ctx.stroke()                           // 描边绘制
    }
}

绘制函数详细分析:

getContext("2d"): 获取HTML5 Canvas风格的2D绘图上下文

ctx.arc(x, y, radius, startAngle, endAngle): 绘制圆弧

参数: 中心坐标(x,y), 半径, 起始角度, 结束角度(弧度制)

Math.PI * 2表示360度,绘制完整圆环

进度圆环Canvas

复制代码
Canvas {
    id: progressCanvas
    // ... 类似的属性设置
    
    OnPaint : { 
        var ctx =  getContext (" 2d" ) 
        ctx 。重置()
        
        var Centerx =宽度/  2 
        var Centery =高度/  2 
        var radius =数学。Min ( Centerx , Centery ) -环宽/  2
        
        // 角度转换:QML使用度,Canvas使用弧度
        var startAngleRad = (startAngle - 90) * Math.PI / 180
        var endAngleRad = (endAngle - 90) * Math.PI / 180
        
        CTX 。BeginPath () 
        CTX 。线宽=环宽
        CTX 。 linecap =  "圆形" 
        CTX 。 strokestyle = ringcolor
        ctx.arc(centerX, centerY, radius, startAngleRad, endAngleRad)  // 绘制进度圆弧
        ctx.stroke()
    }
}

关键算法:

角度转换: (角度 - 90) * π / 180

减90度是因为默认

乘以π/180将角度转换为弧度

只绘制从startAngleRad到的圆弧,显示进度endAngleRad

中心文本

复制代码
Text {
    id: centerText
    anchors.centerIn: parent  // 在父元素中居中
    text: Math.round((currentValue / maxValue) * 100) + "%"  // 计算百分比文本
    font.pixelSize: Math.min(parent.width, parent.height) * 0.25  // 自适应字体大小
    font.bold: true
    color: "#333333"
}

文本计算:

使用Math.round()四舍五入百分比值

字体大小根据控件尺寸自适应属性变化响应

复制代码
onCurrentValueChanged: {
    progressCanvas.requestPaint()  // 请求重绘进度圆环
}

onMaxValueChanged: {
    backgroundCanvas.requestPaint()  // 重绘底色圆环
    progressCanvas.requestPaint()    // 重绘进度圆环
}

数据绑定机制:

当属性值改变时,自动调用对应的信号处理函数

requestPaint()通知Canvas需要重新绘制

初始化函数组件。 ​

作用: 组件完成创建后立即执行初始绘制。

  1. 核心算法总结

圆环绘制算法

半径计算: Math.min(centerX, centerY) - ringWidth / 2

取宽高的最小值确保圆形

减去圆环宽度的一半避免超出边界

角度计算:

起始角度: -

结束角度: -90 + (progress * 360)度

弧度转换: 角度 * π / 180

进度显示:

当progress=0.5(50%)时,绘制从-90度到90度的圆弧,正好是180度

二、所有源码

ringProgress.qml文件源码

复制代码
import QtQuick 2.12

Item {
    id: ringProgress

    // 可配置属性
    property real currentValue: 0
    property real maxValue: 100
    property color backgroundColor: "#f0f0f0"
    property color ringBackgroundColor: "#e0e0e0"
    property color ringColor: "#4CAF50"
    property real ringWidth: 15

    // 计算属性
    property real progress: currentValue / maxValue
    property real startAngle: -90  // 从顶部开始
    property real endAngle: startAngle + (progress * 360)

    width: 200
    height: 200

    // 背景
    Rectangle {
        id: backgroundRect
        anchors.fill: parent
        radius: width / 2
        color: ringProgress.backgroundColor
    }

    // 圆环底色(完整的圆环)
    Canvas {
        id: backgroundCanvas
        anchors.fill: parent
        antialiasing: true

        onPaint: {
            var ctx = getContext("2d")
            ctx.reset()

            var centerX = width / 2
            var centerY = height / 2
            var radius = Math.min(centerX, centerY) - ringWidth / 2

            ctx.beginPath()
            ctx.lineWidth = ringWidth
            ctx.lineCap = "round"
            ctx.strokeStyle = ringBackgroundColor
            ctx.arc(centerX, centerY, radius, 0, Math.PI * 2)
            ctx.stroke()
        }
    }

    // 进度圆环
    Canvas {
        id: progressCanvas
        anchors.fill: parent
        antialiasing: true

        onPaint: {
            var ctx = getContext("2d")
            ctx.reset()

            var centerX = width / 2
            var centerY = height / 2
            var radius = Math.min(centerX, centerY) - ringWidth / 2

            // 转换为弧度
            var startAngleRad = (startAngle - 90) * Math.PI / 180
            var endAngleRad = (endAngle - 90) * Math.PI / 180

            ctx.beginPath()
            ctx.lineWidth = ringWidth
            ctx.lineCap = "round"
            ctx.strokeStyle = ringColor
            ctx.arc(centerX, centerY, radius, startAngleRad, endAngleRad)
            ctx.stroke()
        }
    }

    // 中心文本
    Text {
        id: centerText
        anchors.centerIn: parent
        text: Math.round((currentValue / maxValue) * 100) + "%"
        font.pixelSize: Math.min(parent.width, parent.height) * 0.25
        font.bold: true
        color: "#333333"
    }

    // 当属性改变时重绘Canvas
    onCurrentValueChanged: {
        progressCanvas.requestPaint()
    }

    onMaxValueChanged: {
        backgroundCanvas.requestPaint()
        progressCanvas.requestPaint()
    }

    onRingWidthChanged: {
        backgroundCanvas.requestPaint()
        progressCanvas.requestPaint()
    }

    onRingBackgroundColorChanged: {
        backgroundCanvas.requestPaint()
    }

    onRingColorChanged: {
        progressCanvas.requestPaint()
    }

    Component.onCompleted: {
        backgroundCanvas.requestPaint()
        progressCanvas.requestPaint()
    }
}

main.qml文件源码

复制代码
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12

ApplicationWindow {
    id: mainWindow
    width: 400
    height: 400
    title: "圆环进度控件示例"
    visible: true

    property real currentValue: 0
    property real maxValue: 100

    ColumnLayout {
        anchors.centerIn: parent
        spacing: 20

        // 圆环进度控件
        RingProgress {
            id: ringProgress
            Layout.preferredWidth: 200
            Layout.preferredHeight: 200
            currentValue: mainWindow.currentValue
            maxValue: mainWindow.maxValue
            backgroundColor: "#f0f0f0"
            ringBackgroundColor: "#E6F7FF"    // 非常浅的天蓝色
            ringColor: "#00BFFF"              // 深天蓝色
            ringWidth: 15
        }

        // 按钮控制区域
        RowLayout {
            Layout.alignment: Qt.AlignHCenter
            spacing: 10

            Button {
                text: "加10"
                onClicked: {
                    if (mainWindow.currentValue + 10 <= mainWindow.maxValue) {
                        mainWindow.currentValue += 10
                    } else {
                        mainWindow.currentValue = mainWindow.maxValue
                    }
                }
            }

            Button {
                text: "减10"
                onClicked: {
                    if (mainWindow.currentValue - 10 >= 0) {
                        mainWindow.currentValue -= 10
                    } else {
                        mainWindow.currentValue = 0
                    }
                }
            }
        }

        // 显示当前数值
        Text {
            Layout.alignment: Qt.AlignHCenter
            text: "当前值: " + mainWindow.currentValue
            font.pixelSize: 16
        }
    }
}

三、效果演示

运行后点击按钮,实时更新圆环值,打印当前圆环值。

相关推荐
深思慎考3 小时前
Linux二进制查看工具——hexdump
linux·c++·二进制·文件查看·hexdump
十重幻想3 小时前
PTA6-4 使用函数统计指定数字的个数(C)
c语言·c++·算法
铁手飞鹰3 小时前
VS2022创建项目工程笔记
c++·windows·笔记·visualstudio
ningbinzhang3 小时前
MFC 获取域名TXT记录内容
c++·mfc
佛系彭哥3 小时前
Win11下VSCode与MSYS2配置全攻略
c++·vscode·wxwidgets·msys2
Yupureki4 小时前
从零开始的C++学习生活 5:内存管理和模板初阶
c语言·c++·学习·visual studio
程序员Aries4 小时前
从零开始实现一个高并发内存池_DayThree:内存池整体框架与ThreadCache、TLS无锁访问
c++·学习·单例模式
charlie1145141914 小时前
Windows 10 系统编程——线程专题1
c++·windows·学习·线程
拾光Ծ5 小时前
【C++】STL有序关联容器的双生花:set/multiset 和 map/multimap 使用指南
数据结构·c++·算法