Skip to content

Instantly share code, notes, and snippets.

@0smr
Created August 29, 2022 21:18
Show Gist options
  • Save 0smr/839bd3a176ed934b68378550153e4a82 to your computer and use it in GitHub Desktop.
Save 0smr/839bd3a176ed934b68378550153e4a82 to your computer and use it in GitHub Desktop.
Custom QML Dial

Custom QML Dial

QML Dial created with QML Shapes. related to: issue 73482552 on stackoverflow.

Notes

This is just an example of a customized qml dial that I made out of curiosity.

  1. Shapes displayed in QML won't be particularly well-defined, hence ShaderEffect may be preferred.
  2. Since inline components were first included in Qt5.15, Gauge and ShadedGauge must be split into distinct QML files in order to be used in earlier versions.

Preview Image

import QtQuick 2.15
import QtQuick.Templates 2.15 as T
import QtQuick.Shapes 1.15
import QtGraphicalEffects 1.15
T.Dial {
id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
component Gauge: Item {
id: gauge
width: 220
height: 220
property real sweepAngle: 0
Shape {
anchors.fill: parent
layer.enabled: true
layer.samples: 8
ShapePath {
strokeWidth: gauge.width * 0.20
strokeColor: '#fff'
fillColor: 'transparent'
capStyle: ShapePath.FlatCap
strokeStyle: ShapePath.DashLine
dashPattern: [10/gauge.width,10/gauge.width]
PathAngleArc {
id: arc
centerX: gauge.width/2; centerY: centerX
radiusX: centerX * 0.76; radiusY: radiusX
startAngle: -90 - 149
sweepAngle: Math.max(0,gauge.sweepAngle)
}
}
}
}
component ShadedGauge: RadialGradient {
id: shadedGauge
property real sweepAngle: 0
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Gauge {
width: shadedGauge.width
height: shadedGauge.height
sweepAngle: shadedGauge.sweepAngle
}
}
}
background: Item {
implicitWidth: 184
implicitHeight: 184
ShadedGauge {
sweepAngle: 298
width: parent.width
height: parent.width
gradient: Gradient {
GradientStop { position: 0.300; color: "#234" }
GradientStop { position: 0.350; color: "#000" }
GradientStop { position: 0.355; color: "#234" }
}
}
ShadedGauge {
sweepAngle: 149 + control.angle * 1.07
width: parent.width
height: parent.width
gradient: Gradient {
GradientStop { position: 0.300; color: "#fff" }
GradientStop { position: 0.350; color: "#777" }
GradientStop { position: 0.355; color: "#fff" }
GradientStop { position: 0.460; color: "#fff" }
GradientStop { position: 0.465; color: "#234" }
GradientStop { position: 0.470; color: "#234" }
GradientStop { position: 0.480; color: "#b83" }
}
}
}
handle: Item {
width: control.width
height: control.height
rotation: control.angle * 1.07
Rectangle {
y: 1; x: (parent.width - width)/2
width: 4; height: 4; radius: 1
smooth: true
}
Rectangle {
y: 1; x: (parent.width - width)/2
width: 2; height: parent.height * 0.23
radius: width
smooth: true
}
}
}
@SRI-789
Copy link

SRI-789 commented Oct 26, 2022

hii I just need to know what if i have assets of these two, filling part and background how can i write the code then???
image

@0smr
Copy link
Author

0smr commented Oct 26, 2022

@SRI-789 Hi, This code was written in the event that no assets were used.
However, if you want to use assets, by all means go ahead; it would be even simpler to make anything along these lines.

See the example below; the key to keep in mind is to use ConicalGradient as an opacity mask to only display areas that are less than the control's angle.
Any image can be used in place of the example's; you just need two variations of the same image.
I hope this meets your requirements.

Dial.qml

T.Dial {
    id: control
    implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
                            implicitContentWidth + leftPadding + rightPadding)
    implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
                                implicitContentHeight + topPadding + bottomPadding)
    
    function b64image(color = "#0ae") {
        return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='${ color }'
                d='M4.15 20.37c-1.77-1.6-2.46-3.98-3.59-6.03C-.39 12.1-.02 9.5.8 7.23c.55-1.2 1.54-1.99 2.38-2.76C4.5 3.33 5.57 1.73 7.2.9
                c1.63-.91 3.75-.4 5.37-.47 1.33-.05 3.06-.69 4.54-.12 4.27 1.52 7.58 6.18 6.77 10.73-.29 1.71.25 3.54-.25 5.27-.65 1.65-1.4
                3.3-2.7 4.53 1.15-1.3 1.8-3 2.32-4.63.36-1.72-.24-3.46-.02-5.23.5-4.34-2.48-8.33-6.45-9.77-1.35-.44-2.6.16-4.15.24
                C10.26 1.43 8.1.82 6.3 2.68c-1.42 1.6-3.65 3.08-4.78 4.88-.82 2-1.27 4.5-.49 6.6 1 2.08 1.52 4.48 3.13 6.21z'/%3E%3C/svg%3E`;
    }
    
    component Shape: Image {
        id: shape
        property real sweepAngle: 0
        property color color: "#2fa7e3"

        source: control.b64image(color)
        sourceSize.width: width * 2

        layer.enabled: true
        layer.effect: OpacityMask {
            maskSource: ConicalGradient {
                angle: -135
                width: shape.width; height: shape.height
                gradient: Gradient {
                    GradientStop { position: shape.sweepAngle/360; color: "white" }
                    GradientStop { position: shape.sweepAngle/360 + 0.005; color: "transparent" }
                }
            }
        }
    }

    background: Item {
        implicitWidth: 184
        implicitHeight: 184
        
        Shape {
            anchors.fill: parent
            color: "gray"
            sweepAngle: 360
        }
        
        Shape {
            anchors.fill: parent
            sweepAngle: control.angle * 1.07 + 135
        }
    }

    handle: Item {
        width: control.width
        height: control.height
        rotation: control.angle * 1.07

        Rectangle {
            y: -10; x: (parent.width - width)/2
            width: 2; height: 35
            radius: width
            smooth: true
            color: "#2fa7e3"
        }
    }
}

Preview

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment