[英]How to create a Progress Bar in Qml with slant lines?
不确定的Fusion 样式 ProgressBar做了类似的事情,它有移动的斜线。
以下是调整后的代码以显示确定条的行,仅使用公共 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
}
}
}
}
}
}
如果您不想要 animation,只需删除 NumberAnimation。
请注意,由于此示例使用来自 Fusion 样式的图像(为方便起见),因此它必须以 Fusion 样式运行。
如果您想以其他样式运行它,请将图像的 URL 替换为其他样式,并确保您使用的是非原生样式:
注意: macOS 和 Windows styles 不适合定制。 相反,建议始终将自定义控件基于所有平台上可用的单一样式,例如基本样式、融合样式、想象样式、材质样式、通用样式。 通过这样做,您可以保证无论应用程序使用哪种样式运行,它看起来总是一样的。 例如:
我将分阶段解决问题。 这反映了我 go 如何自己解决 QML 问题。 第一个目标是创建一个 200x32 的 ProgressBar 并将其设置为 25% 的进度。 首先,我从自定义 ProgressBar 的空白矩形开始:
ProgressBar {
implicitWidth: 200
implicitHeight: 32
contentItem: Rectangle {
border.color: "black"
}
}
接下来,我决定制作一条 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>`
}
}
}
}
我观察到上述两个问题:
为了解决这些问题,(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>`
}
}
}
}
接下来,我想代表 25%。 我通过绘制一个覆盖右侧斜线的 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"
}
}
}
最后,我们尝试概括我们的解决方案:
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"
}
}
}
我们做了一些假设:
为了进一步概括,我们可以考虑(1)用更有效的等效物替换 SVG 图像,(2)允许颜色可配置。
又一个解决方案。
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
}
}
}
我建议使用ShaderEffect来创建更有效的结果。
所以,当其他人都实现了自己的代码时,我也做了。
结果如下所示。
// 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;
}"
}
它是如何工作的一个例子。
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
也可以嵌入到进度条中。
此模板可从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.