简体   繁体   English

如何在 Qml 中用斜线创建进度条?

[英]How to create a Progress Bar in Qml with slant lines?

在此处输入图像描述

Hello,你好,

I am looking for some solution to create a progress bar in qml having slant lines.我正在寻找一些解决方案来在 qml 中创建具有斜线的进度条。

I dont having any snippet to share here, as I am not aware how to do so.我没有任何片段可以在这里分享,因为我不知道该怎么做。

Can anyone please guide??谁能指导一下??

The indeterminate Fusion style ProgressBar does something similar, where it has moving slanted lines. 不确定Fusion 样式 ProgressBar做了类似的事情,它有移动的斜线。

Here's the code from that adjusted to show the lines for determinate bars as well, using only public APIs:以下是调整后的代码以显示确定条的行,仅使用公共 API:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
    width: 640
    height: 480
    visible: true

    ProgressBar {
        id: control
        value: 0.5

        contentItem: Item {
            implicitWidth: 120
            implicitHeight: 24

            Rectangle {
                height: parent.height
                width: control.position * parent.width

                radius: 2
                border.color: "steelblue"
                gradient: Gradient {
                    GradientStop {
                        position: 0
                        color: Qt.lighter("steelblue", 1.2)
                    }
                    GradientStop {
                        position: 1
                        color: Qt.darker("steelblue", 1.2)
                    }
                }
            }

            Item {
                x: 1
                y: 1
                width: parent.width - 2
                height: parent.height - 2
                clip: true

                Image {
                    width: Math.ceil(parent.width / implicitWidth + 1) * implicitWidth
                    height: parent.height

                    mirror: control.mirrored
                    fillMode: Image.TileHorizontally
                    source: "qrc:/qt-project.org/imports/QtQuick/Controls/Fusion/images/progressmask.png"
                    opacity: 0.25

                    NumberAnimation on x {
                        running: control.visible
                        from: -31 // progressmask.png width
                        to: 0
                        loops: Animation.Infinite
                        duration: 750
                    }
                }
            }
        }
    }
}

If you don't want the animation, just remove the NumberAnimation.如果您不想要 animation,只需删除 NumberAnimation。

Note that since this example uses an image from the Fusion style (for convenience), it must be run with the Fusion style.请注意,由于此示例使用来自 Fusion 样式的图像(为方便起见),因此它必须以 Fusion 样式运行。

If you want to run it with another style, replace the image's URL with another, and make sure you're using a non-native style :如果您想以其他样式运行它,请将图像的 URL 替换为其他样式,并确保您使用的是非原生样式

Note: The macOS and Windows styles are not suitable for customizing.注意: macOS 和 Windows styles 不适合定制。 It is instead recommended to always base a customized control on top of a single style that is available on all platforms, eg Basic Style, Fusion Style, Imagine Style, Material Style, Universal Style.相反,建议始终将自定义控件基于所有平台上可用的单一样式,例如基本样式、融合样式、想象样式、材质样式、通用样式。 By doing so, you are guaranteed that it will always look the same, regardless of which style the application is run with.通过这样做,您可以保证无论应用程序使用哪种样式运行,它看起来总是一样的。 For example:例如:

I am going to solve the problem in stages.我将分阶段解决问题。 This is reflective of how I go about solving QML problems myself.这反映了我 go 如何自己解决 QML 问题。 The first goal is to create a 200x32 ProgressBar and to set it to 25% progress.第一个目标是创建一个 200x32 的 ProgressBar 并将其设置为 25% 的进度。 First, I start with a blank Rectangle for my custom ProgressBar:首先,我从自定义 ProgressBar 的空白矩形开始:

ProgressBar {
  implicitWidth: 200
  implicitHeight: 32
  contentItem: Rectangle {
    border.color: "black"
  }
}

进度条空白矩形

Next, I decide to craft a 32x32 SVG of a diagonal line and I repeat this line at 4 pixel intervals covering the whole width (ie 200 / 4 === 50).接下来,我决定制作一条 32x32 SVG 的对角线,并以 4 个像素间隔重复这条线,覆盖整个宽度(即 200 / 4 === 50)。

ProgressBar {
  implicitWidth: 200
  implicitHeight: 32
  contentItem: Rectangle {
    border.color: "black"
    Repeater {
      model: 200 / 4
      Image {
        x: index * 4
        y: 0
        source: `data:image/svg+xml;utf8,
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
        <path fill="none" stroke="black" d="M 0 0 L 32 32" />
        </svg>`
      }
    }
  }
}

进度条-try-slant-1

I observe two problems with the above:我观察到上述两个问题:

  1. The triangle gap at the left左边的三角形间隙
  2. The overshoot at the right右边的过冲

To fix these problems, (1) shift the lines 32 pixels to the left to cover the gap, (2) I extend the lines to be (200 + 32) / 4 === 58, (3) use clip to crop clean the diagonals that are outside my Rectangle.为了解决这些问题,(1)将线条向左移动 32 像素以覆盖间隙,(2)我将线条扩展为(200 + 32)/ 4 === 58,(3)使用剪辑裁剪干净我的矩形之外的对角线。

ProgressBar {
  implicitWidth: 200
  implicitHeight: 32
  contentItem: Rectangle {
    border.color: "black"
    clip: true
    Repeater {
      model: (200 + 32) / 4
      Image {
        x: index * 4 - 32
        y: 0
        source: `data:image/svg+xml;utf8,
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
        <path fill="none" stroke="black" d="M 0 0 L 32 32" />
        </svg>`
      }
    }
  }
}

进度条清洁倾斜

Next, I want to represent 25%.接下来,我想代表 25%。 I do this by drawing a 75% white Rectangle covering the slant lines on the right.我通过绘制一个覆盖右侧斜线的 75% 白色矩形来做到这一点。

ProgressBar {
  implicitWidth: 200
  implicitHeight: 32
  value: 0.25
  contentItem: Rectangle {
    border.color: "black"
    clip: true
    Repeater {
      model: (200 + 32) / 4
      Image {
        x: index * 4 - 32
        y: 0
        source: `data:image/svg+xml;utf8,
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
        <path fill="none" stroke="black" d="M 0 0 L 32 32" />
        </svg>`
      }
    }
    Rectangle {
      anchors.top: parent.top
      anchors.right: parent.right
      anchors.bottom: parent.bottom
      implicitWidth: parent.width * 0.75
      implicitHeight: parent.height
      border.color: "black"
      color: "white"
    }
  }
}

进度条-25-%

Finally, we try to generalize our solution:最后,我们尝试概括我们的解决方案:

ProgressBar {
  id: progressBar
  implicitWidth: 200
  implicitHeight: 32
  contentItem: Rectangle {
    border.color: "black"
    clip: true
    Repeater {
      model: (parent.width + parent.height) / 4
      Image {
        x: index * 4 - parent.height
        y: 0
        source: `data:image/svg+xml;utf8,
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${parent.height} ${parent.height}">
        <path fill="none" stroke="black" d="M 0 0 L ${parent.height} ${parent.height}" />
        </svg>`
      }
    }
    Rectangle {
      anchors.top: parent.top
      anchors.right: parent.right
      anchors.bottom: parent.bottom
      implicitWidth: parent.width * (1 - parent.parent.value)
      implicitHeight: parent.height
      border.color: "black"
      color: "white"
    }
  }
}

进度条结果

There are assumptions that we made:我们做了一些假设:

  • ProgressBar is 200x32进度条是 200x32
  • Color is white bar with a black outline with black diagonal lines颜色是带有黑色轮廓和黑色对角线的白色条
  • The bar to the right is filled white右边的栏是白色的
  • A SVG image is used使用了 SVG 图像

To generalize further, we can consider (1) replacing the SVG Image with a more efficient equivalent, (2) allow the colours to be configurable.为了进一步概括,我们可以考虑(1)用更有效的等效物替换 SVG 图像,(2)允许颜色可配置。

Yet another solution.又一个解决方案。

在此处输入图像描述

ProgressBar {
    id: control

    property color contentItemColor: "white"
    property color backgroundColor: "yellow"
    property color borderColor: "red"
    property int borderWidth: 1
    property color stripColor: "green"
    property int stripGap: 10
    property int stripWidth: 2
    property color indicatorColor: "red"
    property int indicatorWidth: 2
    property int indicatorRadius: 0
    property int indicatorExtend: 0

    value: 0.5
    width: 600
    height: 80

    background: Rectangle {
        color: control.backgroundColor
        border.width: control.borderWidth
        border.color: control.borderColor

        Canvas {
            x: control.borderWidth
            y: control.borderWidth
            width: control.width - (2 * control.borderWidth)
            height: control.height - (2 * control.borderWidth)

            onPaint: {
                var ctx = getContext("2d");
                ctx.strokeStyle = control.stripColor
                ctx.lineWidth = control.stripWidth
                ctx.lineCap = "square"

                var p1, p2

                let n = Math.ceil((control.width + control.height) / (control.stripGap)) + 1

                for (let i = 0; i != n; ++i) {
                    let p = i * control.stripGap
                    p1 = Qt.vector2d(p - control.height, 0)
                    p2 = Qt.vector2d(p, control.height)

                    ctx.beginPath()
                    ctx.moveTo(p1.x, p1.y)
                    ctx.lineTo(p2.x, p2.y)
                    ctx.closePath()
                    ctx.stroke()
                }
            }
        }
    }

    contentItem: Item {
        Rectangle {
            anchors.fill: parent
            anchors.margins: control.borderWidth
            anchors.leftMargin: Math.max(control.borderWidth,
                                         control.visualPosition * control.width)

            color: control.contentItemColor
        }

        Rectangle {
            x: control.visualPosition * control.width - (control.indicatorWidth / 2)
            y: -control.indicatorExtend
            width: control.indicatorWidth
            height: control.height + (2 * control.indicatorExtend)
            color: control.indicatorColor
            radius: control.indicatorRadius
            visible: control.visualPosition > 0.0 && control.visualPosition < 1.0
        }
    }
}

I recommend using ShaderEffect to create a more efficient result.我建议使用ShaderEffect来创建更有效的结果。
So, as everyone else implemented their own code, I did as well.所以,当其他人都实现了自己的代码时,我也做了。
The results are shown below.结果如下所示。

SlantLines.qml斜线.qml

// SlantLines.qml
import QtQuick 2.12

ShaderEffect {
    id: effect
    property real seed: 0 // To animate the lines' x axis.
    property real num: width / 6 // The number of lines changes with width and may even be fixed.
    property color color: '#000' // Color of lines (background is transparent)
    property vector2d ratio: {
        const max = Math.max(width, height);
        return Qt.vector2d(width/max, height/max);
    }

    fragmentShader: "
        uniform lowp float qt_Opacity;
        uniform lowp float seed;
        uniform lowp float num;
        uniform highp vec2 ratio;
        uniform highp vec4 color;
        varying highp vec2 qt_TexCoord0;
        void main() {
            vec2 uv = qt_TexCoord0*ratio/ ratio.x;
            // Slope can be changed by multiplying 'y' by any number.
            uv.x -= uv.y + seed/num; 
            vec2 grid = fract(uv * vec2(num,1.));
            // You may also change the line width from 0.0 to 1.0. (change 0.5)
            gl_FragColor = color * smoothstep(0.0,ratio.x*1e-1,grid.x - 0.5) * qt_Opacity;
        }"
}

Preview预习

An example of how it works.它是如何工作的一个例子。

板条线预览
component SlantLinesColumn: Column {
    property real xSeed: 0
    spacing: 5
    Repeater {
        model: 12
        SlantLines {
            height: 10
            width: (index + 1) * 20
            color: Qt.hsva(index/13,1,1,1)
            seed: xSeed
        }
    }
}

SlantLinesColumn { }
SlantLinesColumn {
    y: 190
    Timer {
        running: true; repeat: true
        interval: 50 // Almost 30Hz.
        onTriggered: parent.xSeed -= 0.1
    }
}

ProgressBar进度条

SlantLines can also be embedded in a progressbar. SlantLines也可以嵌入到进度条中。
This template is available from the Qt source code .此模板可从Qt 源代码中获得。

import QtQuick 2.15
import QtQuick.Templates 2.15  as T

T.ProgressBar {
    id: control
    implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
    implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
    
    property int orientation: Qt.Vertical
    readonly property bool vertical: orientation == Qt.Vertical
    
    contentItem: Item {
        SlantLines {
            y: control.vertical ? parent.height - height : 0
            width: !control.vertical && !control.indeterminate ? 
                        parent.width * control.position : parent.width 
            height: control.vertical && !control.indeterminate ? 
                        parent.height * control.position : parent.height
            color: control.palette.dark
            
            Timer {
                running: control.visible && control.indeterminate
                repeat: true; interval: 50
                onTriggered: parent.seed -= 0.1
            }
        }
    }

    background: Rectangle {
        implicitWidth: !control.vertical ? 200 : 18
        implicitHeight: control.vertical ? 200 : 18
        color: control.palette.midlight            
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM