仓库源文-进度条定制.md),站点原文-进度条定制)


title: 玩转Qml(6)-进度条定制 photos: /images/Qml6/ProgressBar.gif tags:


简介

本文是《玩转Qml》系列文章的第六篇,涛哥将教大家,进度条组件的定制。

源码

《玩转Qml》系列文章,配套了一个优秀的开源项目:TaoQuick

github https://github.com/jaredtao/TaoQuick

访问不了或者速度太慢,可以用国内的镜像网站gitee

https://gitee.com/jaredtao/TaoQuick

先看预览图

预览

新的渐变效果

Qt 5.12 加入了新的渐变效果,一共180种,效果来自这个网站https://webgradients.com

按照帮助文档的介绍,可以通过下面这两种方式使用

  Rectangle {
      y: 0; width: 80; height: 80
      gradient: Gradient.NightFade
  }

  Rectangle {
      y: 0; width: 80; height: 80
      gradient: "NightFade"
  }

涛哥立即想到了,枚举不就是数字嘛

  Rectangle {
      y: 0; width: 80; height: 80
      gradient: 1
  }
  Rectangle {
      y: 0; width: 80; height: 80
      gradient: 2
  }
  Rectangle {
      y: 0; width: 80; height: 80
      gradient: 3
  }

试了一下,这样也是可以啊,哈哈。

于是涛哥就把180种渐变效果都拉出来看看。

预览

Qt只支持水平和垂直的渐变,其中有小部分是不能用的,所以只有165个能用。

看一下展示全部渐变的Qml代码:

import QtQuick 2.9
import QtQuick.Controls 2.5

Item {
    anchors.fill: parent

    GridView {
        id: g
        anchors.fill: parent
        anchors.margins: 20
        cellWidth: 160
        cellHeight: 160
        model: 180          //这里的数据Model直接给个数字180
        clip: true
        property var invalidList: [27, 39, 40, 45, 71, 74, 105, 111, 119, 130, 135, 141]    //这几个是不能用的,看过运行报错后手动列出来的。
        delegate: Item{
            width: 160
            height: 160
            Rectangle{
                width: 150
                height: 150
                anchors.centerIn: parent
                color: "white"
                radius: 10
                Text {
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
                    anchors.topMargin: 2
                    text: index + 1
                }
                Rectangle {
                    width: 100
                    height: width
                    radius:  width / 2
                    //编号在列表里的,直接渐变赋值为null,就不会在Qml运行时报警告了
                    gradient: g.invalidList.indexOf(modelData + 1) < 0 ? modelData + 1 : null
                    anchors.centerIn: parent
                    anchors.verticalCenterOffset: 10
                }
            }
        }
    }
}

条形进度条

普通进度条的原理,就是有一个比较长的矩形做背景,在上面放一个颜色不同的矩形,其宽度跟着百分比变化,

100%时宽度与背景一致。

可以写一个很简要的进度条。

Rectangle {
    id: back
    width: 300
    height: 50
    radius: height / 2
    color: "white"  
    property int percent: 0
    Rectangle {
        id: front
        //宽度是 背景宽度 * 百分比
        width: percent / 100 * parent.width  
        height: parent.height
        radius: parent.radius
        color: "red"
    }
}

再添加一点元素,在右侧放一个文本,表示百分比,或者放图片。甚至给进度条加个闪光特效。

经过一系列的加工,封装成一个综合的组件,最终结果如下:

//NormalProgressBar.qml

import QtQuick 2.12
import QtQuick.Controls 2.12
Item {
    id: r
    property int percent: 0
    implicitWidth: 200
    implicitHeight: 16
    //枚举, 表示右侧Bar的类型
    enum BarType {  
        Text,               //右侧放文本
        SucceedOrFailed,    //右侧放图片表示成功和失败,没有100%就是失败
        NoBar               //右侧不放东西
    }
    //只读属性,内置一些颜色
    readonly property color __backColor: "#f5f5f5"
    readonly property color __blueColor: "#1890ff"
    readonly property color __succeedColor: "#52c41a"
    readonly property color __failedColor: "#f5222d"

    //背景色,默认值
    property color backgroundColor: __backColor
    //前景色
    property color frontColor: {
        switch (barType) {
        case TNormalProgress.BarType.SucceedOrFailed:
            return percent === 100 ? __succeedColor : __failedColor
        default:
            return __blueColor
        }
    }
    //文字
    property string text: String("%1%").arg(percent)
    //渐变 0-180 除掉不能用的,165种渐变任你选
    property int gradientIndex: -1
    //闪烁特效
    property bool flicker: false
    //右侧Bar类型
    property var barType: TNormalProgress.BarType.Text
    Text {
        id: t
        enabled: barType === TNormalProgress.BarType.Text
        visible: enabled
        text: r.text
        anchors.verticalCenter: parent.verticalCenter
        anchors.right: parent.right
    }
    Image {
        id: image
        source: percent === 100 ? "qrc:/Core/Image/ProgressBar/ok_circle.png" : "qrc:/Core/Image/ProgressBar/fail_circle.png"
        height: parent.height
        width: height
        enabled: barType === TNormalProgress.BarType.SucceedOrFailed
        visible: enabled
        anchors.right: parent.right
    }

    property var __right: {
        switch (barType) {
        case TNormalProgress.BarType.Text:
            return t.left
        case TNormalProgress.BarType.SucceedOrFailed:
            return image.left
        default:
            return r.right
        }
    }
    Rectangle {                             //背景
        id: back
        anchors.left: parent.left
        anchors.right: __right
        anchors.rightMargin: 4
        height: parent.height
        radius: height / 2
        color: backgroundColor
        Rectangle {                         //前景
            id: front
            width: percent / 100 * parent.width
            height: parent.height
            radius: parent.radius
            color: frontColor
            gradient: gradientIndex === -1 ? null : gradientIndex
            Rectangle {                     //前景上的闪光特效
                id: flick
                height: parent.height
                width: 0
                radius: parent.radius
                color: Qt.lighter(parent.color, 1.2)
                enabled: flicker
                visible: enabled
                NumberAnimation on width {
                    running: visible
                    from: 0
                    to: front.width
                    duration: 1000
                    loops: Animation.Infinite;
                }
            }
        }
    }
}

圆形进度条

将一个Rectangle做成圆形: 宽高相等,半径为宽度一半。

再把 颜色设置为透明,边框不透明,边框加粗一点,就是一个圆环了。

Rectangle {
    id: back
    width: 120
    height: width
    radius: width / 2
    color: "transparent"
    border.width: 10
    border.color: "white"
}

接下来给圆环贴上一个圆形渐变色,渐变按照百分比来做。

import QtGraphicalEffects 1.12
Rectangle {
    id: back
    width: 120
    height: width
    radius: width / 2
    color: "transparent"
    border.width: 10
    border.color: "white"
    property int percent: 0
    ConicalGradient {
        anchors.fill: back
        source: back
        gradient: Gradient {
            GradientStop { position: 0.0; color: "white" }
            GradientStop { position: percent / 100 ; color: "red" }
            GradientStop { position: percent / 100 + 0.001; color: "white" }
            GradientStop { position: 1.0; color: "white" }
        }
    }
}

渐变从0 到 percent处都是有渐变颜色的, 再从percent + 0.001 到1.0处,都是背景色,这样就是一个简易的圆形进度条了。

不过这里percent为100的情况,圆形渐变处理不了,我们可以特殊处理,直接让背景圆环变成前景色就行了。(既然都100%了,背景肯定是全部被遮住了,那就让背景做前景,藏掉真正的前景)

```qml
import QtGraphicalEffects 1.12
Rectangle {
    id: back
    width: 120
    height: width
    radius: width / 2
    color: "transparent"
    border.width: 10
    border.color: percent === 100 ? "red" : "white"     //百分比为100时显示为前景,否则显示为背景
    property int percent: 0
    ConicalGradient {
        anchors.fill: back
        source: back
        enabled: percent != 100     //百分比不为100时有效
        visible: enabled            //百分比不为100时有效
        gradient: Gradient {
            GradientStop { position: 0.0; color: "white" }
            GradientStop { position: percent / 100 ; color: "red" }
            GradientStop { position: percent / 100 + 0.001; color: "white" }
            GradientStop { position: 1.0; color: "white" }
        }
    }
}

再加点料,封装成组件

//CircleProgressBar.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.12
Item {
    id: r
    property int percent: 0

    enum BarType {
        Text,
        SucceedOrFailed,
        NoBar
    }
    readonly property color __backColor: "#f5f5f5"
    readonly property color __blueColor: "#1890ff"
    readonly property color __succeedColor: "#52c41a"
    readonly property color __failedColor: "#f5222d"
    property color backgroundColor: __backColor
    property color frontColor: {
        switch (barType) {
        case TNormalProgress.BarType.SucceedOrFailed:
            return percent === 100 ? __succeedColor : __failedColor
        default:
            return __blueColor
        }
    }
    property string text: String("%1%").arg(percent)
    property var barType: TNormalProgress.BarType.Text
    Rectangle {
        id: back
        color: "transparent"
        anchors.fill: parent
        border.color: percent === 100 ? frontColor : backgroundColor
        border.width: 10
        radius: width / 2
    }
    Text {
        id: t
        enabled: barType === TNormalProgress.BarType.Text
        visible: enabled
        text: r.text
        anchors.centerIn: parent
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }
    Image {
        id: image
        source: percent === 100 ? "qrc:/Core/Image/ProgressBar/ok.png" : "qrc:/Core/Image/ProgressBar/fail.png"
        enabled: barType === TNormalProgress.BarType.SucceedOrFailed
        visible: enabled
        scale: 2
        anchors.centerIn: parent
    }
    ConicalGradient {
        anchors.fill: back
        source: back
        enabled: percent != 100
        visible: enabled
        smooth: true
        antialiasing: true
        gradient: Gradient {
            GradientStop { position: 0.0; color: frontColor }
            GradientStop { position: percent / 100 ; color: frontColor }
            GradientStop { position: percent / 100 + 0.001; color: backgroundColor }
            GradientStop { position: 1.0; color: backgroundColor }
        }
    }
}

最后,来个合影

Item {
    id: r
    anchors.fill: parent
    Grid {
        id: g
        anchors.fill: parent
        anchors.margins: 10
        columns: 2
        spacing: 10
        Column {
            width: g.width / 2 - 10
            height: g.height /2 - 10
            spacing: 10
            TNormalProgress {
                width: parent.width
                backgroundColor: gConfig.reserverColor
                NumberAnimation on percent { from: 0; to: 100; duration: 5000; running: true; loops: Animation.Infinite}
            }
            TNormalProgress {
                width: parent.width
                backgroundColor: gConfig.reserverColor
                flicker: true
                percent: 50
            }
            TNormalProgress {
                width: parent.width
                backgroundColor: gConfig.reserverColor
                barType: TNormalProgress.BarType.SucceedOrFailed
                percent: 70
            }
            TNormalProgress {
                width: parent.width
                backgroundColor: gConfig.reserverColor
                barType: TNormalProgress.BarType.SucceedOrFailed
                percent: 100
            }
            TNormalProgress {
                width: parent.width
                backgroundColor: gConfig.reserverColor
                barType: TNormalProgress.BarType.NoBar
                percent: 50
                gradientIndex: 12
            }
        }
        Row {
            width: g.width / 2 - 10
            height: g.height /2 - 10
            spacing: 10

            TCircleProgress {
                width: 120
                height: 120
                backgroundColor: gConfig.reserverColor
                NumberAnimation on percent { from: 0; to: 100; duration: 5000; running: true; loops: Animation.Infinite}
            }
            TCircleProgress {
                width: 120
                height: 120
                backgroundColor: gConfig.reserverColor
                barType: TNormalProgress.BarType.SucceedOrFailed
                percent: 75
            }
            TCircleProgress {
                width: 120
                height: 120
                backgroundColor: gConfig.reserverColor
                barType: TNormalProgress.BarType.SucceedOrFailed
                percent: 100
            }
        }
        Row {
            width: g.width / 2 - 10
            height: g.height /2 - 10
            spacing: 10

            TCircleProgress {
                width: 120
                height: 120
                backgroundColor: gConfig.reserverColor
                text: String("%1天").arg(percent)
                NumberAnimation on percent { from: 0; to: 100; duration: 5000; running: true; loops: Animation.Infinite}
            }
            TCircleProgress {
                id: ppppp
                width: 120
                height: 120
                backgroundColor: gConfig.reserverColor
                barType: TNormalProgress.BarType.SucceedOrFailed
                SequentialAnimation {
                    running: true
                    loops: Animation.Infinite
                    NumberAnimation {
                        target: ppppp
                        property: "percent"
                        from: 0
                        to: 100
                        duration: 3000
                    }
                    PauseAnimation {
                        duration: 500
                    }
                }
            }
            TCircleProgress {
                width: 120
                height: 120
                backgroundColor: gConfig.reserverColor
                percent: 100
            }
        }
    }
}

效果如下:

预览