Reputation: 13
I started learning the Qt framework, and I wanted to create a MultiColored Gradient (like below image) background for the Window:
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
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
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();
}
}
}
}
}
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