The indeterminate Fusion style ProgressBar does something similar, where it has moving slanted lines.
Here's the code from that adjusted to show the lines for determinate bars as well, using only public APIs:
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.
Note that since this example uses an image from the Fusion style (for convenience), it must be run with the Fusion style.
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 :
Note: The macOS and Windows styles are not suitable for customizing. 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. The first goal is to create a 200x32 ProgressBar and to set it to 25% progress. First, I start with a blank Rectangle for my custom 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).
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.
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%. I do this by drawing a 75% white Rectangle covering the slant lines on the right.
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.
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.
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.
This template is available from the Qt source code .
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
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.