QML Circular Gauge Styling - Needle trailing colour/glow



  • G'day,

    I'm playing with circular gauge styling in QML and am wondering how I could implement a glowing colour trailing the needle like in this gauge:

    0_1522221562391_Screen Shot 2018-03-28 at 6.17.44 pm.png

    In essence, the colour starts off with a high opacity, which gradually diminishes with distance away from the needle.

    Thanks!



  • ShaderEffect is made for this sort of bling

    I got this:

    QML gauge

    from this:

    import QtQuick 2.2
    
    Rectangle {
      id: main
      width: 512
      height: 512
      color: 'black'
    
      // Put some text in the background just to check opacity
      Text {
        x: main.width/6.0-0.5*contentWidth
        y: main.height/2.0-0.5*contentHeight
        text: '30'
        color: 'white'
      }
      Text {
        x: main.width/2.0-0.5*contentWidth
        y: main.height/6.0-0.5*contentHeight
        text: '60'
        color: 'white'
      }
      Text {
        x: 5.0*main.width/6.0-0.5*contentWidth
        y: main.height/2.0-0.5*contentHeight
        text: '90'
        color: 'white'
      }
    
      // Shader effect to provide gradient-based gauge
      ShaderEffect {
        id: gauge
        anchors.fill: parent
    
        opacity: 0.75  // Making it totally opaque on leading edge obscures the number!
    
        // Angles measured clockwise from up, in range -pi to pi
        property real angleBase: -pi*0.75
        property real angle
    
        readonly property real pi: 3.1415926535897932384626433832795
    
        vertexShader: "
          uniform highp mat4 qt_Matrix;
          attribute highp vec4 qt_Vertex;
          attribute highp vec2 qt_MultiTexCoord0;
          varying highp vec2 coord;
          void main() {
            coord = qt_MultiTexCoord0;
            gl_Position = qt_Matrix * qt_Vertex;
          }"
    
        fragmentShader: "
          uniform lowp float qt_Opacity;
          uniform highp float angleBase;
          uniform highp float angle;
          varying highp vec2 coord;
          void main() {
            gl_FragColor = vec4(0.0,0.0,0.0,0.0);
            highp vec2 d=2.0*coord-vec2(1.0,1.0);
            highp float r=length(d);
            if (0.25<=r && r<=0.75) {
              highp float a=atan2(d.x,-d.y);
              if (angleBase<=a && a<=angle) {
                highp float p=(a-angleBase)/(angle-angleBase);
                gl_FragColor = vec4(0.0,0.0,p,p) * qt_Opacity;
              }
            }
          }"
      }
    
      // Animate the gauge position
      SequentialAnimation {
        running: true
        loops: Animation.Infinite
        NumberAnimation {
          from: gauge.angleBase
          to: gauge.angleBase+1.5*gauge.pi
          duration: 1000
          target: gauge
          property: 'angle'
          easing.type: Easing.InOutSine
        }
        NumberAnimation {
          from: gauge.angleBase+1.5*gauge.pi
          to: gauge.angleBase
          duration: 1000
          target: gauge
          property: 'angle'
          easing.type: Easing.InOutSine
        }
      }
    }
    

    Runs in 5.10.1's qmlscene but no reason it shouldn't work in older versions.



  • That looks like exactly what I'm after! Thanks for bringing this functionality to my attention!

    I'll give it a go this evening and report back with any issues.



  • I've taken the exact code presented above and am receiving the following compilation errors:

    QOpenGLShader::compile(Fragment): ERROR: 0:12: Invalid call of undeclared identifier 'atan2'
    ERROR: 0:13: Use of undeclared identifier 'a'
    ERROR: 0:13: Use of undeclared identifier 'a'
    ERROR: 0:14: Use of undeclared identifier 'a'
    ERROR: 0:15: Use of undeclared identifier 'p'
    ERROR: 0:15: Use of undeclared identifier 'p'
    
    *** Problematic Fragment shader source code ***
    #define lowp
    #define mediump
    #define highp
    #line 1
     
                              uniform lowp float qt_Opacity; 
                              uniform highp float angleBase; 
                              uniform highp float angle; 
                              varying highp vec2 coord; 
                              void main() { 
                                gl_FragColor = vec4(0.0,0.0,0.0,0.0); 
                                highp vec2 d=2.0*coord-vec2(1.0,1.0); 
                                highp float r=length(d); 
                                if (0.25<=r && r<=0.75) { 
                                  highp float a=atan2(d.x,-d.y); 
                                  if (angleBase<=a && a<=angle) { 
                                    highp float p=(a-angleBase)/(angle-angleBase); 
                                    gl_FragColor = vec4(0.0,0.0,p,p) * qt_Opacity; 
                                  } 
                                } 
                              }
    
    ***
    QQuickCustomMaterialShader: Shader compilation failed:
    "ERROR: 0:12: Invalid call of undeclared identifier 'atan2'\nERROR: 0:13: Use of undeclared identifier 'a'\nERROR: 0:13: Use of undeclared identifier 'a'\nERROR: 0:14: Use of undeclared identifier 'a'\nERROR: 0:15: Use of undeclared identifier 'p'\nERROR: 0:15: Use of undeclared identifier 'p'\n"
    

    I've never worked with OpenGL before so am struggling somewhat to make sense of the above.


  • Moderators

    @jars121
    why not simply use a conical gradient? (In conjunction with a opacity mask)



  • @raven-worx that certainly appears to be simpler! I'll have a play with that approach now and report back, thanks!



  • @raven-worx Thanks again, I've got it working almost 100% now!

    Quick question, is there a way to limit/stop the ConicalMask? At the moment the OpacityMask is working nicely, but when the needle is at very low values, the ConicalMask actually appears on the other side of the gauge! I can combat this by shortening the Gradient Stop, but I'd like to have a longer GradientStop, and simply 'turn off' the ConicalGradient at a certain value/angle.


  • Moderators

    @jars121
    i don't quite understand your remaining issue.
    But i think everything should be achievable by setting the gradient stops properly and/or the opacity mask.



  • I got this working by changing the atan2 calls in the fragment shader to atan.
    This was on an ubunut 16.04 system in a VM.



  • @mranger90 Oops sorry for causing confusion... I was on a system with an Nvidia card (and running Debian) when I threw the ShaderEffect version together... and it seems Nvidia do provide an atan2 in their GLSL compiler (perhaps because their old "Cg" precursor language used to have it). Didn't try it anywhere else. GLSL's regular atan with two arguments does indeed seem to do the same thing.

    Have to admit I'd completely forgotten about ConicalGradient! I do like ShaderEffect though... it's QML's magic wand... you can do some really nifty stuff with it e,g https://youtu.be/FM0CbKvYpYI or https://youtu.be/BuTYBBScfMo or https://forum.qt.io/topic/84532/qml-animation-trail-on-object/2 ...


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.