NoOne
NoOne

Reputation: 13

MultiColor Gradient in QML

I started learning the Qt framework, and I wanted to create a MultiColored Gradient (like below image) background for the Window:

Sample MutiColored Gradient Image

I went through the official documentation on Linear/Conical/Radial/Shape Gradient QML type and also did Google. But couldn't see any option to achieve this.

Is there any other way to make it possible? Preferably in QML.

Edit 1:

As stated in Comments, GradientStop can be used for multiple colors. But its can't be used it to produce the result as in the given image.

Upvotes: 0

Views: 1851

Answers (2)

JarMan
JarMan

Reputation: 8277

Ihor's answer is good. I found that Qt actually provides an example that may be of use as well. Check out their Squircle example. I won't reproduce it all here, but it creates a QQuickItem in C++ and does some custom OpenGL rendering on it. The object can then be used in QML.

Upvotes: 1

Ihor Drachuk
Ihor Drachuk

Reputation: 1293

At first familiarize with Qt example quick\scenegraph\fboitem. It demonstrates creating custom QML control with C++/OpenGL backend.

Also you can implement it with ShaderEffect QML control. I recommend this site https://www.shadertoy.com/new for debugging your shaders. Also you can use, for example, Canvas for passing arrays to your shader as texture (ShaderEffect doesn't allow passing arrays normally, https://bugreports.qt.io/browse/QTBUG-50493).

Here is possible implementation of your control with dynamic points count, colors and positions.

import QtQuick 2.12
import QtQuick.Controls 2.5

ApplicationWindow {
    id: window
    visible: true
    width: 640
    height: 480
    title: "Custom gradient"

    Item {
        anchors.fill: parent

        layer.enabled: true
        layer.effect: ShaderEffect {
            readonly property var dataTex: dataCanvas
            readonly property int count: dataCanvas.colors.length

            fragmentShader: "
                  varying highp vec2 qt_TexCoord0;
                  uniform lowp float qt_Opacity;
                  uniform sampler2D dataTex;
                  const uniform lowp int count;
                  void main() {
                      vec3 col = 0.0;

                      for (int i = 0; i < count; i++) {
                          vec4 color = texture2D(dataTex, vec2(i / (float)count + 0.01, 0.0));
                          vec2 point = texture2D(dataTex, vec2(i / (float)count + 0.01, 1.0)).rg;

                          float dist = distance(qt_TexCoord0, point);
                          col += color * (1.0 - dist);
                      }

                      gl_FragColor = vec4(col, 1.0);
                  }
        ";
        }

        // Because ShaderEffect doesn't allow passing arrays to shader, we will
        // convert arrays to graphical representation and pass them as texture.
        Canvas {
            id: dataCanvas
            readonly property var colors: ["cyan", "red", "green", "darkblue", "yellow"]
            readonly property var positions: [Qt.point(0,0),
                                              Qt.point(10, parent.height - 20),
                                              Qt.point(parent.width / 2, parent.height / 4),
                                              Qt.point(parent.width, 0),
                                              Qt.point(parent.width, parent.height)]

            height: 2
            width: colors.length
            antialiasing: false
            visible: false

            onPaint: {
                if (colors.length !== positions.length) {
                    console.error("Array size of 'colors' doesn't equal array size of 'positions'");
                    return;
                }

                var ctx = getContext("2d");
                ctx.reset();
                ctx.lineWidth = 1;

                for (var i = 0; i < colors.length; i++) {
                    ctx.beginPath();
                    ctx.strokeStyle = colors[i];
                    ctx.moveTo(i, 0);
                    ctx.lineTo(i+1, 0);
                    ctx.stroke();
                    ctx.closePath();

                    ctx.beginPath();
                    ctx.strokeStyle = Qt.rgba(positions[i].x / parent.width, positions[i].y / parent.height, 0, 1);
                    ctx.moveTo(i, 1);
                    ctx.lineTo(i+1, 1);
                    ctx.stroke();
                    ctx.closePath();
                }
            }
        }
    }
}

That is how it looks enter image description here

I'm not OpenGL guru, so this code isn't perfect. I'll be glad if someone will edit this answer to make it better.

Upvotes: 1

Related Questions