[英]How to create a Progress Bar in Qml with slant lines?
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>`
}
}
}
}
I observe two problems with the above:我观察到上述两个问题:
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"
}
}
}
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:我们做了一些假设:
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
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;
}"
}
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
}
}
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.