I am trying to create a custom QML Slider style as follows:
SliderStyle {
groove: Item {
anchors.verticalCenter: parent.verticalCenter
implicitWidth: 500
implicitHeight: 10
Rectangle {
radius: height/2
anchors.fill: parent
border.width: 1
border.color: "#888"
gradient: Gradient {
GradientStop { color: "#0A406E" ; position: 0 }
GradientStop { color: "#FFA800" ; position: 1 }
}
}
}
}
However, the gradient on the groove here is going from top to bottom rather than left to right. I tried swapping the width and height values and rotating the Rectangle by -90 degrees but then the slider was unresponsive. Also, I could never get the rotation to be done along the centre of the slider control, which made placement a bit problematic.
I was wondering if there is a way to achieve this left->right gradient flow.
Rectangle's property gradient allows for the construction of simple vertical gradients. For more complex gradients there are LinearGradient, RadialGradient and ConicalGradient types.
For example, horizontal gradient:
import QtGraphicalEffects 1.0
SliderStyle {
groove: Item {
anchors.verticalCenter: parent.verticalCenter
implicitWidth: 500
implicitHeight: 10
Rectangle {
radius: height/2
anchors.fill: parent
border.width: 1
border.color: "#888"
layer.enabled: true
layer.effect: LinearGradient {
start: Qt.point(0, 0)
end: Qt.point(500, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#0A406E" }
GradientStop { position: 1.0; color: "#FFA800" }
}
}
}
}
}
Related
I am using LinearGradient as background for rectangle, but the left and right borders of the rectangle are a little bit white and blurred. How can I avoid this situation?
I have tried to set below properties on the Rectangle but it didn't work.
clip: true
smooth: true
antialiasing: true
Here is my code:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtGraphicalEffects 1.15
Window {
width: 640
height: 480
visible: true
Rectangle {
anchors.fill: parent
color: "#4f4444"
}
Rectangle {
id: root
anchors.centerIn: parent
width: 355
height: 90
radius: 50
LinearGradient {
anchors.fill: parent
source: ShaderEffectSource {
sourceItem: root
recursive: true
}
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#2a3254" }
GradientStop { position: 1.0; color: "#0e1c57" }
}
}
Text {
anchors.centerIn: parent
text: "Click me!"
color: "white"
}
}
}
The problem is that it is smoothing the edges of the shape to blend with the Rectangle that contains the gradient (root). If you change that Rectangle's color to match what is drawn behind it, you won't see those edges anymore.
Rectangle {
id: bground
anchors.fill: parent
color: "#4f4444"
}
Rectangle {
id: root
color: bground.color // Match the background's color
LinearGradient { ... }
}
Is there a way to have a Rectangle with one side rounded edges and also a border in Qt without using the Canvas.
Something like below.
I did try below code and I am able to create the rounded corner on one side.
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
width: 200
height: 200
visible: true
Item {
width: 100
height: 50
opacity: 0.5
layer.enabled: true
anchors.centerIn: parent
Rectangle {
color: "blue"
radius: 10
anchors.fill: parent
}
Rectangle {
color: "blue"
anchors.fill: parent
anchors.leftMargin: 10
}
}
}
With above code I am able to get the one side rounded corners but when I add border then I see overlapping borders.
Is there a clean way of doing this in Qml?
I can think of two ways to do that.
Not the "cleanest" way, but probably the simplest performance-wise. You can keep using the code you have above, but just draw another non-bordered rectangle that covers up the extra border line that you are seeing.
property int borderWidth: 4
Item {
width: 100
height: 50
opacity: 0.5
layer.enabled: true
anchors.centerIn: parent
Rectangle {
id: roundCorners
color: "blue"
radius: 10
border.width: borderWidth
anchors.fill: parent
}
Rectangle {
id: squareCorners
color: "blue"
border.width: borderWidth
anchors.fill: parent
anchors.leftMargin: 10
}
Rectangle {
anchors.left: squareCorners.left
anchors.verticalCenter: squareCorners.verticalCenter
width: borderWidth
height: squareCorners.height - borderWidth * 2
color: "blue"
}
}
You can use QML's Shape object and use a ShapePath to define it. The docs can be found here.
Shape {
ShapePath {
strokeWidth: 4
strokeColor: "black"
fillColor: "blue"
PathLine { ... }
PathLine { ... }
PathLine { ... }
PathArc { ... }
}
}
I'm using a PieSeries in QML as a way to create a makeshift mask.
I have a circular animated gif and on top of it, I've added a PieSeries.
The PieSeries has 3 PieSlices. 2 of them are "black" essentially blocking out there corresponding portions of the gif. The remaining PieSlice is transparent.
This works fine, but I would like the 2 black PieSlices to have a gradient to them so that their edge is not so obvious (gradient transparency).
I've tried using a LinearGradient with an OpacityMask as described here, but this did not have any effect when used on a PieSlice.
Any suggestions would be appreciated :)
Here's my code:
import QtQuick 2.0
import QtCharts 2.2
import QtGraphicalEffects 1.0
Item {
anchors.fill: parent //Fill the gif area
ChartView {
id: chart
anchors.centerIn: parent
height: parent.height+200
width: parent.width
legend.visible: false
antialiasing: false
backgroundColor: "transparent"
PieSeries {
id: pieOuter
size: 1.5
holeSize: 0.2
PieSlice { id: black1; value: 1; color: "black"; borderColor: "transparent" }
PieSlice { id: valSlice; value: d.val; color: "transparent"; borderColor: "transparent" }
PieSlice { id: black2; value: 1 - d.val; color: "black"; borderColor: "transparent" }
}
}
OpacityMask{
source: mask
maskSource: black1
}
LinearGradient {
id: mask
anchors.fill: black1
gradient: Gradient {
GradientStop { position: 0.2; color: "transparent"}
GradientStop { position: 0.5; color: "white" }
}
}
}
Note: d.val here holds a value between 0 and 1
Edit
Some images to help visualise what I'm trying to achieve:
The GIF that I'm starting off with (source - original file was too large to attach):
Snapshot of original Gif
This is how it is currently animated to look:
Low d.val
Higher d.val
Max d.val
This is what I want it to actually look like (depicted here for d.val):
Higher d.val with gradient
I'm trying to achieve a simple Sliding Bar in QML. Here's a prerequisite I have to follow: no SVG images ( for some BR I cannot discuss here ).
Rectangle {
id: backgroundPower
anchors.fill: parent
color: "#a2aaae"
radius: 50
MouseArea {
id: progressArea
anchors.fill: parent
onPositionChanged: {
updateValue(progressArea.mouseX)
}
onReleased: {
updateValue(progressArea.mouseX)
}
onClicked: updateValue(progressArea.mouseX)
}
Rectangle {
id: powerImage
width: calculateWidth()
radius: 100
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
LinearGradient {
id: defaultGradient
anchors.fill: parent
source: powerImage
start: Qt.point(0, 0)
end: Qt.point(0, powerImage.height)
gradient: Gradient {
GradientStop { position: 0.0; color: "#46c0e4" }
GradientStop { position: 0.50; color: "#0583ca" }
GradientStop { position: 0.95; color: "#fbffff" }
GradientStop { position: 1.0; color: "#fbffff" }
}
}
Here's what I obtain with this code when the bar is at 100%:
When I update the value ( mouse area events ) I change the width of the
powerImage with the function:
var maxWidth = powerImage.parent.width
var currentWidth = (maxWidth * barPercentage) / 100
if (currentWidth >= maxWidth){
currentWidth = maxWidth
}
PROBLEM
When the percentage of the bar reaches a low value ( like 10% ) I cannot fit the bar into the background, like this:
you can see how the bar doesn't fin anymore.
I know the problem is linked to the rectangle ( powerImage ) and its radius. But I don't know how to fix this.
Clearly with a SVG it's really simple but in this case, I cannot use this.
Is there a way to show the bar when it's only inside the background rectangle?
Unfortunately it is not possible to use clip: true on non-rectangular regions.
But following this answer, you can use an OpacityMask:
Rectangle {
id: backgroundPower
anchors.fill: parent
color: "#a2aaae"
radius: 50
MouseArea {
// ...
}
}
Item {
anchors.fill: backgroundPower
layer.enabled: true
layer.effect: OpacityMask {
maskSource: backgroundPower
}
Rectangle {
id: powerImage
// ...
}
}
Result:
But as mentionned in the answer:
But using it will be GPU consuming task as the inner item and mask have to be drawn on buffer first and then redrawn on window, so not very good for old mobile or weak embedded devices.
So I wanted to create a slider style, similar to the default one, and also more matching to my applications style, but I found it hard to assign a different color before and after the slider's handle.
Here's a simplified version of my code, without gradients, anchor points and other properties:
Slider{
id: widthSlider
style: SliderStyle {
groove: Rectangle {
height: widthSlider.height*0.17
width: widthSlider.width
color: "black"
radius: 8
}
handle: Rectangle {
color: "grey"
}
}
}
I tried a rough workaround, to put two rectangles in the slider, anchored to the handle position like this:
Slider{
id: widthSlider
Rectangle {
anchors.left: widthSlider.left
anchors.right: widthSlider.__handlePos
color: "black"
}
Rectangle {
anchors.left: widthSlider.__handlePos
anchors.right: widthSlider.right
color: "white"
}
...
}
but I cannot anchor to the handle's position since it's just a double(I get the error: Unable to assign double to QQuickAnchorLine).
Does anyone have an idea of how I could do this in Qml?
Something like this?
Slider {
anchors.centerIn: parent
value: 0.5
width: 400
style: SliderStyle {
groove: Item {
implicitHeight: 10
LinearGradient {
anchors.fill: parent
start: Qt.point(0, control.height / 2)
end: Qt.point(control.width, control.height / 2)
gradient: Gradient {
GradientStop { position: 0.0; color: "orange" }
GradientStop { position: control.value; color: "brown" }
GradientStop { position: 1.0; color: "orange" }
}
}
}
}
}
Or:
Slider {
anchors.centerIn: parent
value: 0.5
width: 400
style: SliderStyle {
groove: Rectangle {
implicitHeight: 10
color: "lightgrey"
border {
color: "#999"
width: 1
}
Rectangle {
implicitHeight: 10
color: "orange"
implicitWidth: control.value * parent.width
border {
color: "#999"
width: 1
}
}
}
}
}
I realize this is an old question. However for future reference the Rectangle that represents the colour before the handle should have a width of styleData.handlePosition. The below code is from folibis second solution with this change.
Slider {
anchors.centerIn: parent
value: 0.5
width: 400
style: SliderStyle {
groove: Rectangle {
implicitHeight: 10
color: "lightgrey"
border {
color: "#999"
width: 1
}
Rectangle {
implicitHeight: 10
color: "orange"
implicitWidth: styleData.handlePosition
border {
color: "#999"
width: 1
}
}
}
}
}