For QML, why LayoutMirroring doesn't work in Slider? - qt

Today I tried the Slider in QtQuick.Controls, my slider is left to right, I want to set my slider as from right to left by using LayoutMirroring.enabled, at last I found I cann't inverted the slider.
Here is my little demo code, so how can we invert a slider?
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Slider{
id:test
value: 0.2
width:400
LayoutMirroring.enabled: true
}
}

If you use the Slider from QtQuick.Controls 2.x - at least for me - it works like a charm. If you use the Slider from QtQuick.Controls 1.x it does not.
From the documentation:
Keep in mind, however, that mirroring does not affect any positioning that is defined by the Item x coordinate value, so even with mirroring enabled, it will often be necessary to apply some layout fixes to support the desired layout direction.
The QtQuick.Controls 1.x-Slider however uses a largely coordinate based implementation and has no further precautions to support the LayoutMirroring.
However the Sliders layout is usually symetrically, so all you need to do is to map the values like from (0,1) to (1,0). This should be a easy task to a developer.
import QtQuick.Controls 1.3
import QtQuick.Controls.Layouts 1.3
import QtQuick.Controls.Private 1.3 // Needed for a mysterious value from the original, now mirrored style.
Slider {
y: 40
id: sli
width: parent.width
minimumValue: 50
maximumValue: 100
property real mirroredValue: maximumValue - value + minimumValue
// Invert style
style: SliderStyle {
groove: Item {
property color fillColor: "#49d"
anchors.verticalCenter: parent.verticalCenter
// Whatever TextSingleton is. You need to import QtQuick.Controls.Private 1.x for it.
implicitWidth: Math.round(TextSingleton.implicitHeight * 4.5)
implicitHeight: Math.max(6, Math.round(TextSingleton.implicitHeight * 0.3))
Rectangle {
radius: height/2
anchors.fill: parent
border.width: 1
border.color: "#888"
gradient: Gradient {
GradientStop { color: "#bbb" ; position: 0 }
GradientStop { color: "#ccc" ; position: 0.6 }
GradientStop { color: "#ccc" ; position: 1 }
}
}
Item {
clip: true
x: styleData.handlePosition // let the fill-stuff start at the handle position...
width: parent.width - styleData.handlePosition // and end at the end of the groove.
height: parent.height
Rectangle {
anchors.fill: parent
border.color: Qt.darker(fillColor, 1.2)
radius: height/2
gradient: Gradient {
GradientStop {color: Qt.lighter(fillColor, 1.3) ; position: 0}
GradientStop {color: fillColor ; position: 1.4}
}
}
}
}
}
}
If you wan't to set the value of your slider, you need to install a bidirectional binding between mirroredValue and value.

I had a similar problem. My slider was vertical with values increasing from bottom to the top. I wanted them to increase from top to bottom. I accomplished it using rotation. I think that you could have solved your problem like this:
Slider {
id: test
value: 0.2
width: 400
rotation: 180 // This should make the slider right-to-left
}

Related

How to do an opacity gradient in an image?

I have implemented a cover flow similar to Itunes. But there is still one thing that I can't solve, even after researching and reading the documentation...
I have the following result, but I would like to display only half of the reflection.
I have found the following StackOverflow post : How do I make a gradient opacity in an image - QML which is very similar to what I would like to achieve. Except that as my background is a gradient, I can't select the white color (or any plain color for the last GradientStop element in the solution code offered as solution).
Here is my code so far:
Image {
id: rectDelegate
anchors.fill: parent
source: images[index % images.length]
}
ShaderEffectSource {
id: reflection
sourceItem: rectDelegate
y: sourceItem.height
width: sourceItem.width
height: sourceItem.height
transform: [
Rotation {
origin.x: reflection.width / 2
origin.y: reflection.height / 2
axis.x: 1
axis.y: 0
axis.z: 0
angle: 180
}
]
visible: reflection_visible
}
Rectangle {
anchors.fill: reflection
gradient: Gradient {
GradientStop {
position: 0.0
color: "#55ffffff"
}
GradientStop {
// This determines the point at which the reflection fades out.
position: 1
color: "#ffffff"
}
}
visible: reflection_visible
}
I tried to embed the ShaderEffectSource element inside a transparent rectangle whose height would be half of the reflection height and to clip the ShaderEffectSource inside, but the reflection wouldn't appear :/
Here is the code :
Rectangle {
anchors.top: rectDelegate.bottom
color: "transparent"
width: rectDelegate.width
height: rectDelegate.height - 300
visible: reflection_visible
clip: true
border.color: "yellow"
ShaderEffectSource {
id: reflection
sourceItem: rectDelegate
y: sourceItem.height
width: sourceItem.width
height: sourceItem.height
transform: [
Rotation {
origin.x: reflection.width / 2
origin.y: reflection.height / 2
axis.x: 1
axis.y: 0
axis.z: 0
angle: 180
}
]
visible: reflection_visible
}
}
Any idea would be welcome :) I tried to make my post as clear as possible but if there is something missing for understanding, please, ask :)
read this post:
How do I make a gradient opacity in an image? - QML
or you can use Canvas type to draw your image with transparent gradient , but the proper way is the first one.

QML PieSlice Gradient colour

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

How to implement curved scrollview in qml?

How to get the look of curved Scroll bar/scroll view as shown below in QML with Label or TextArea?
Basically this application is not a touch application.
Environment, Qt 5.7.0 in Linux.
You can use PathInterpolator from Controls.2. The example below is some Slider modification, you can adopt it for your needs:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
id: mainWindow
visible: true
width: 400
height: 400
Path {
id: myPath
startX: 0; startY: 20
PathCurve { x: 100; y: 40 }
PathCurve { x: 200; y: 10 }
PathCurve { x: 300; y: 40 }
}
Slider {
id: control
width: 300
height: 50
anchors.centerIn: parent
background: Rectangle {
anchors.fill: parent
color: "orange"
Canvas {
anchors.fill: parent
contextType: "2d"
onPaint: {
context.strokeStyle = "MediumPurple";
context.path = myPath;
context.stroke();
}
}
PathInterpolator {
id: motionPath
path: myPath
progress: control.visualPosition
}
}
handle: Rectangle {
width: 30
height: 30
radius: 15
color: "DodgerBlue"
x: motionPath.x - 15
y: motionPath.y - 15
}
}
}
You can use a Flickable to have your view. To this Flickable you attatch a ScrollBar which you can style.
To style this ScrollBar is a bit tricky, for some of its properties are bullshit.
The position-property, which is documented as
This property holds the position of the scroll bar, scaled to 0.0 - 1.0.
will never reach 1.0 unless, the handles size is 0. You don't really have the ability to set the size of the handle, though. It will be automatically resized. So if you don't want to have a handle that fills the width of the ScrollBar entirely, you need to use a Item as a base and add a the visual inside this, so you have the sovereignity again.
All together, it might look like this:
Flickable {
anchors.fill: parent
contentWidth: width
contentHeight: mainWindow.height * 10
Rectangle {
width: 640
height: mainWindow.height * 10
gradient: Gradient {
GradientStop { color: 'orchid'; position: 0 }
GradientStop { color: 'orange'; position: 1 }
}
}
ScrollBar.vertical: ScrollBar {
id: scrollBar
width: 50
contentItem: Item {
// This will deal with the bullshit of the position. Imperfect, as I do not consider any margins/paddings
property real normalizedPosition: scrollBar.position * (scrollBar.height / (scrollBar.height - height))
Rectangle {
// Draw the curve by defining a function for x in dependance of the position.
x: Math.sin(Math.PI * parent.normalizedPosition) * 40
width: 10
height: parent.height // I use the default height, so it
// really goes from top to bottom.
// A smaller height means, you should
// also alter the y value to have a
// more natural behavior.
radius: 5
color: 'green'
Text {
text: parent.parent.normalizedPosition
}
}
}
}
}

QT QML How to add LinearGradient to a Rectangle with a border?

I would like to have a more specific gradient than the default vertical one for a Rectangle. I try adding a LinearGradient for a diagonal effect, but it overwrites the border.
Consider this example. Top Rectangle ok with vertical gradient and border. Bottom Rectangle gradient overwrites border and radius. I tried clip and gradient: LinearGradient but they didn't work either.
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
ApplicationWindow
{
visible: true
width: 640
height: 480
Column
{
spacing: 20
width: parent.width
Rectangle
{
width: 200
height: 200
border.width: 4
border.color: "#888"
radius: 10
// adds a vertical gradient to button
gradient: Gradient
{
GradientStop
{
position: 0
color: "#eee"
}
GradientStop
{
position: 1
color: "#ccc"
}
}
}
Rectangle
{
width: 200
height: 200
border.width: 4
border.color: "#888"
radius: 10
// try to add diagonal gradient, but overwrites button border
// also can't do, gradient: LinearGradient ?
LinearGradient
{
anchors.fill: parent
start: Qt.point(0,0)
end: Qt.point(parent.width,parent.height)
gradient: Gradient
{
GradientStop
{
position: 0
color: "#eee"
}
GradientStop
{
position: 1
color: "#ccc"
}
}
}
}
}
}
Results in this:
I can see why this might have this result, but how to clip the gradient to a Rectangle with a radius?
clip always clips at the bounding rectangle of the Item that is clipping, and does not care for alpha-values.
However the LinearGradient has another tool, to achive what you want:
- the source-property.
See this example:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
Window {
width: 1024
height: 800
visible: true
Rectangle {
id: rect1
width: 100
height: 100
radius: 20
}
LinearGradient {
anchors.fill: rect1
source: rect1 <-- Here is where you specify its shape.
start: Qt.point(0, 0)
end: Qt.point(300, 300)
gradient: Gradient {
GradientStop { position: 0.0; color: "white" }
GradientStop { position: 1.0; color: "black" }
}
}
}
QML only supports vertical gradients. You can fake horizontal gradients by flipping the item's width and height and rotating it.
For a diagonal that won't work, as the rectangle would also be rotated.
As for the plan to use clipping, that won't work either, because the QML screnegraph only clips to the item's rectangle, not its actual visible pixels.
There are two approaches you ca take:
1 - try to accomplish the desired result via the Canvas element.
2 - use a ShaderEffect to which you pass a ShaderEffectSource texture with the gradient and a rounded rectangle, then in the actual shader use the rgb from the first source (the gradient) and the alpha from the second (the rounded rectangle) to manually clip.
3 - if you are going to be using ShaderEffect you can easily skip using a gradient as a source, and look up how to implement a gradient at an arbitrary angle in GLSL, and only use the rounded rectangle source for the alpha, even though the "rounded" part can just as well be implemented in the shader.

Draw an arc/circle sector in QML?

I know that it is possible to draw a circle in QML using the following code:
Rectangle {
width: 150
height: 150
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
color: "#095e7b"
border.color: "#0a2f4a"
border.width: 2
radius: width*0.5
}
My question is: what if I need to draw a sector of a circle. (Pizza Slices) and make each of these slices clickable? Can I do this using QML only?
Yes, using Canvas (and Context2D):
import QtQuick 2.3
Rectangle {
width: 400
height: 400
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
ctx.reset();
var centreX = width / 2;
var centreY = height / 2;
ctx.beginPath();
ctx.fillStyle = "black";
ctx.moveTo(centreX, centreY);
ctx.arc(centreX, centreY, width / 4, 0, Math.PI * 0.5, false);
ctx.lineTo(centreX, centreY);
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "red";
ctx.moveTo(centreX, centreY);
ctx.arc(centreX, centreY, width / 4, Math.PI * 0.5, Math.PI * 2, false);
ctx.lineTo(centreX, centreY);
ctx.fill();
}
}
}
I actually took the code for this from this answer, as Qt's Canvas implements the HTML5 Canvas API. This makes it really easy to find examples on the web; just search for "draw pie slice blah html5 canvas", for example.
For the mouse detection, you'll have to brush off your maths skills...
... or just steal the code from here. :)
Note that Canvas only repaints when it's resized, or when requestPaint() is called, so if you want to change the colour of a slice depending on the mouse position, you'll need to call that function to see the colour change.
Draw it using qml, you don't need the canvas. As a guideline I usually go through Qt's examples before deciding on an implementation. The code below can be found in "Shapes" example.
import QtQuick 2.11
import QtQuick.Shapes 1.15
Shape {
width: 120
height: 130
anchors.bottom: parent.bottom
anchors.right: parent.right
// multisample, decide based on your scene settings
layer.enabled: true
layer.samples: 4
ShapePath {
fillColor: "black"
strokeColor: "darkBlue"
strokeWidth: 20
capStyle: ShapePath.FlatCap
PathAngleArc {
centerX: 65; centerY: 95
radiusX: 45; radiusY: 45
startAngle: -180
sweepAngle: 180
}
}
}
Use charts http://doc.qt.io/QtCharts/qtcharts-qmlmodule.html
import QtQuick 2.0
import QtCharts 2.0
ChartView {
width: 400
height: 300
theme: ChartView.ChartThemeBrownSand
antialiasing: true
PieSeries {
id: pieSeries
PieSlice { label: "eaten"; value: 94.9 }
PieSlice { label: "not yet eaten"; value: 5.1 }
}
}
Since the question was about drawing "pizza slices" and making each of them clickable, an important detail is mapping clicks to the right segment (aka the right "pizza slice").
None of the prior answers seem to contain any onClicked handling, so I offer yet another possibility. (All the prior answers are also valid, they just don't make it immediately clear where to intercept clicks.)
I had a similar case wherein I needed to slice a rectangle on a 45 degree diagonal, and detect whether clicks fall above or below the diagonal line.
Thankfully, it turns out that QML allows you to:
create a grid
apply a transform: Rotation to that grid
... and then automatically your clicks "just work"
(meaning: clicking on a 45-degree rotated rectangle maps as you would wish)
The demo code renders a rotated grid like the following, and outputs (via console.log) the color that you click on:
The following has been tested on Ubuntu using Qt 5.14
import QtGraphicalEffects 1.12
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.Universal 2.2
import QtQuick.Layouts 1.14
Item {
height: 1000
width: 2000
GridLayout {
id: grid
columnSpacing: 0 // the default is non-zero!
rowSpacing: 0
anchors.centerIn: parent
rows: 2
columns: 2
Rectangle {
height: 200
width: 200
color: 'red'
MouseArea {
anchors.fill: parent
onClicked: {
console.log('red')
}
}
}
Rectangle {
height: 200
width: 200
color: 'yellow'
MouseArea {
anchors.fill: parent
onClicked: {
console.log('yellow')
}
}
}
Rectangle {
height: 200
width: 200
color: 'green'
MouseArea {
anchors.fill: parent
onClicked: {
console.log('green')
}
}
}
Rectangle {
height: 200
width: 200
color: 'blue'
MouseArea {
anchors.fill: parent
onClicked: {
console.log('blue')
}
}
}
transform: Rotation {
origin.x: grid.width * 0.5
origin.y: grid.height * 0.5
axis {
x: 0
y: 0
z: 1
}
angle: 45
}
}
}

Resources