我的应用程序使用QML+JS制作,我想创建一个圆形进度条小部件。我可以使用QML矩形来创建圆形,并将其半径设置为其宽度的1/2以使其成为圆形。如何基于此创建进度条?
我计划实现以下模型。
我的应用程序使用QML+JS制作,我想创建一个圆形进度条小部件。我可以使用QML矩形来创建圆形,并将其半径设置为其宽度的1/2以使其成为圆形。如何基于此创建进度条?
我计划实现以下模型。
我使用Canvas实现了一个基本的圆形进度条。
import QtQml 2.2
import QtQuick 2.0
// draws two arcs (portion of a circle)
// fills the circle with a lighter secondary color
// when pressed
Canvas {
id: canvas
width: 240
height: 240
antialiasing: true
property color primaryColor: "orange"
property color secondaryColor: "lightblue"
property real centerWidth: width / 2
property real centerHeight: height / 2
property real radius: Math.min(canvas.width, canvas.height) / 2
property real minimumValue: 0
property real maximumValue: 100
property real currentValue: 33
// this is the angle that splits the circle in two arcs
// first arc is drawn from 0 radians to angle radians
// second arc is angle radians to 2*PI radians
property real angle: (currentValue - minimumValue) / (maximumValue - minimumValue) * 2 * Math.PI
// we want both circle to start / end at 12 o'clock
// without this offset we would start / end at 9 o'clock
property real angleOffset: -Math.PI / 2
property string text: "Text"
signal clicked()
onPrimaryColorChanged: requestPaint()
onSecondaryColorChanged: requestPaint()
onMinimumValueChanged: requestPaint()
onMaximumValueChanged: requestPaint()
onCurrentValueChanged: requestPaint()
onPaint: {
var ctx = getContext("2d");
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
// fills the mouse area when pressed
// the fill color is a lighter version of the
// secondary color
if (mouseArea.pressed) {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = Qt.lighter(canvas.secondaryColor, 1.25);
ctx.arc(canvas.centerWidth,
canvas.centerHeight,
canvas.radius,
0,
2*Math.PI);
ctx.fill();
}
// First, thinner arc
// From angle to 2*PI
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = primaryColor;
ctx.arc(canvas.centerWidth,
canvas.centerHeight,
canvas.radius,
angleOffset + canvas.angle,
angleOffset + 2*Math.PI);
ctx.stroke();
// Second, thicker arc
// From 0 to angle
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeStyle = canvas.secondaryColor;
ctx.arc(canvas.centerWidth,
canvas.centerHeight,
canvas.radius,
canvas.angleOffset,
canvas.angleOffset + canvas.angle);
ctx.stroke();
ctx.restore();
}
Text {
anchors.centerIn: parent
text: canvas.text
color: canvas.primaryColor
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: canvas.clicked()
onPressedChanged: canvas.requestPaint()
}
}
Math.min(canvas.width, canvas.height)/4
为例,它会完美地适配(但比填充整个父组件的大小要小)。只需根据其父级的大小进行调整并使其变小(还要考虑线条的宽度)。 - rbaleksandar我在纯QML中找到了一种相当优雅的解决方案,也可以用于样式化常规的QtQuick ProgressBar组件。这背后的想法是在仅具边框的Rectangle
上使用ConicalGradient
。
以下是代码:
import QtQuick 2.3
import QtQuick.Controls.Styles 1.2
import QtGraphicalEffects 1.0
ProgressBarStyle
{
panel : Rectangle
{
color: "transparent"
implicitWidth: 80
implicitHeight: implicitWidth
Rectangle
{
id: outerRing
z: 0
anchors.fill: parent
radius: Math.max(width, height) / 2
color: "transparent"
border.color: "gray"
order.width: 8
}
Rectangle
{
id: innerRing
z: 1
anchors.fill: parent
anchors.margins: (outerRing.border.width - border.width) / 2
radius: outerRing.radius
color: "transparent"
border.color: "darkgray"
border.width: 4
ConicalGradient
{
source: innerRing
anchors.fill: parent
gradient: Gradient
{
GradientStop { position: 0.00; color: "white" }
GradientStop { position: control.value; color: "white" }
GradientStop { position: control.value + 0.01; color: "transparent" }
GradientStop { position: 1.00; color: "transparent" }
}
}
}
Text
{
id: progressLabel
anchors.centerIn: parent
color: "black"
text: (control.value * 100).toFixed() + "%"
}
}
}
outerRing
中应该使用 border.width
而不是 order.width
。 - applesoupmain.qml:
import QtQuick 2.0
import Ubuntu.Components 0.1
Rectangle {
width: units.gu(50)
height: units.gu(50)
property int seconds : 0
LoadCircle {
id: circle
anchors.centerIn: parent
loadtimer: 10*1000 // 10 seconds
Component.onCompleted: start();
onFinishedChanged: {
timer.stop();
borderColor = "green"
}
}
Rectangle {
id : theTimer
anchors.centerIn: parent
width : units.gu(10) ; height: units.gu(10)
Label {
text: seconds
font.bold: true
fontSize: "x-large"
anchors.centerIn: parent
}
}
Timer {
id: timer
interval: 1000; running: true; repeat: true;
onTriggered: seconds++;
}
}
LoadCircle.qml:
import QtQuick 2.0
import Ubuntu.Components 0.1
Row{
id: circle
property int loadtimer: 4000
property color circleColor: "transparent"
property color borderColor: "red"
property int borderWidth: 10
property alias running: initCircle.running
property bool finished: false;
width: units.gu(30)
height: width
function start(){
part1.rotation = 180
part2.rotation = 180
initCircle.start()
}
function stop(){
initCircle.stop()
}
Item{
width: parent.width/2
height: parent.height
clip: true
Item{
id: part1
width: parent.width
height: parent.height
clip: true
rotation: 180
transformOrigin: Item.Right
Rectangle{
width: circle.width-(borderWidth*2)
height: circle.height-(borderWidth*2)
radius: width/2
x:borderWidth
y:borderWidth
color: circleColor
border.color: borderColor
border.width: borderWidth
smooth: true
}
}
}
Item{
width: parent.width/2
height: parent.height
clip: true
Item{
id: part2
width: parent.width
height: parent.height
clip: true
rotation: 180
transformOrigin: Item.Left
Rectangle{
width: circle.width-(borderWidth*2)
height: circle.height-(borderWidth*2)
radius: width/2
x: -width/2
y: borderWidth
color: circleColor
border.color: borderColor
border.width: borderWidth
smooth: true
}
}
}
SequentialAnimation{
id: initCircle
PropertyAnimation{ target: part2; property: "rotation"; to:360; duration:loadtimer/2 }
PropertyAnimation{ target: part1; property: "rotation"; to:360; duration:loadtimer/2 }
ScriptAction { script: finished = true; }
}
}
我尝试了Canvas,就像被接受的答案建议的那样,但我发现它很慢。在我的情况下,我需要指示器显示用户需要按住鼠标多长时间才能发生状态转换,如果指示器落后于实际时间,这会引起问题,因为用户认为他们有更多的时间,但实际上并没有。
我发现一个更快的解决方案是使用Shape。
import QtQuick 2.15
import QtQuick.Shapes 1.15
Shape {
id: root
property real radius: 18
property alias strokeWidth: path.strokeWidth
// value between 0 and 1
property real progress: .75
// don't set these externally. Set radius instead
width: radius * 2
height: width
// antialiasing
layer.enabled: true
layer.samples: 8
ShapePath {
id: path
fillColor: "transparent"
strokeColor: "#77999999"
strokeWidth: 3
startX: radius
startY: strokeWidth/2
PathArc {
x: radiusX * Math.sin(Math.PI * 2 * progress) + radius
y: -radiusY * Math.cos(Math.PI * 2 * progress) + radius
radiusX: radius - strokeWidth/2
radiusY: radius - strokeWidth/2
useLargeArc: x < radius
}
}
}
我知道使用 rotation
属性的解决方案。请参考示例
https://gitorious.org/apps-4-me/staq-me/source/fd20fe5b6fec053f364219842905e2afc5cfdc9d:ui.qml#L172
只需使用EEIoT(https://github.com/IndeemaSoftware/EEIoT)旋钮组件。将参数从角度:0和toAngle:Math.PI * 2更改。如果需要反向进度,请将reverse:true也设置为true。
Knob {
id: knob
x: 0
y: 83
width: 100
height: 100
from:0
to: 100
fromAngle: 0
toAngle: Math.PI*2
reverse: false
}
最好的方法是使用PNG文件图像。因为它比纯QML运行得更快,特别是如果您使用渐变。如果您只想使用纯QML,则我没有找到任何方法,除非您向项目添加自定义C++模块。请参见http://qt-project.org/doc/qt-4.8/qml-extending.html