LinearGradient inside Rectangle is displaying blurred white borders - qt

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 { ... }
}

Related

How to handle press state from inside the button if contentItem is defined from the outside?

I have a custom button that shows a white overlay over the content and background area when pressed. The user of the button can specify how to align the content (label + text) by defining the contentItem.
The current implementation works, but I would like to control the press state inside the button. How can the button display the white overlay on top of the contentItem from within the button?
I prefer not to add a bunch of properties to the button to define the layout of the text/icon. Is there another way to accomplish this?
Working code so far:
ButtonOverlay {
id: btnOverlay
width: 100
height: 50
state: ""
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.state === "pressed"
}
}
onPressed: {
btnOverlay.state = "pressed"
}
onReleased: {
btnOverlay.state = ""
}
}
ButtonOverlay.qml
Button {
id: root
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: root.state === "pressed"
color: "white"
opacity: 0.5
}
}
}
I've removed the overlay in the contentItem and moved the overlay from the background into the Button. Furthermore I've used the z property to change the drawing order.
component ButtonOverlay: Button {
id: btn
background: Rectangle {
radius: 10
color: "gray"
}
Rectangle {
id: overlay
anchors.fill: parent
radius: btn.background.radius
color: "white"
opacity: 0.5
visible: btn.pressed
z: 1
}
}
ButtonOverlay {
width: 100
height: 50
contentItem: Rectangle {
color: "red"
Label { text: "button" }
}
}
Perhaps you could use Frame for your ButtonOverlay. Frame works by automatically sizing to your inner contents but allows you to easily replace the borders via the background property. To use it, you will have to move the entire Button out:
import QtQuick
import QtQuick.Controls
Page {
ButtonOverlay {
id: btnOverlay
pressed: btn.pressed
Button {
id: btn
width: 100
height: 50
implicitWidth: width
implicitHeight: height
contentItem: Rectangle {
color: "red"
Label {
text: "button"
}
Rectangle {
id: rectId
anchors.fill: parent
color: "white"
opacity: 0.5
visible: btnOverlay.pressed
}
}
}
}
}
// ButtonOverlay.qml
import QtQuick
import QtQuick.Controls
Frame {
id: root
property bool pressed: false
background: Rectangle {
radius: 10
color: "gray"
Rectangle {
id: overlay
anchors.fill: parent
radius: parent.radius
visible: pressed
color: "white"
opacity: 0.5
}
}
}
You can Try it Online!

Qml rounded corner of one side with border

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 { ... }
}
}

QML layer element "reveals" element above it when they overlap

Is it possible in QML, maybe using shader effects with layers, to create an item that makes another item (with a higher z index) visible only when the two layers overlap? I've been messing aroud with OpacityMask and ThresholdMask but have been unable to figure it out. The effect I'm looking for in the context of the example below would be if the the black circle was only visible when the two red squares are under it:
current:
desired:
Some key points are that the bottom layer (red squares) must be moveable (OpacityMask doesn't seem to let you drag the maskSource) and the bottom layer needs to also be able to contain other elements within it that the black circle responds to. Any guidance towards the right things to learn in order to achieve this would be appreciated. Here is the QML for the red squares and black circle thing. The red squares are draggable as one element:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
Window {
id: main_window
visible: true
width: 1500
height: 1000
title: qsTr("Hello World")
Item {
width: main_window.width
height: main_window.height
LinearGradient {
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(main_window.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#003cff" }
GradientStop { position: 1.0; color: "#9afff9" }
}
}
}
Rectangle {
id: sfg
width: 175
height: 75
color: 'transparent'
RowLayout {
width: parent.width
height: parent.height
spacing: 25
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
drag {
target: sfg
}
}
}
Rectangle {
id: mask
color: 'black'
x: 400
y: 200
width: 100
height: 100
visible: true
opacity: 1
radius: 50
}
}
Like this?
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Layouts 1.12
import QtGraphicalEffects 1.12
Window {
id: main_window
visible: true
width: 1500
height: 1000
title: qsTr("Hello World")
Item {
width: main_window.width
height: main_window.height
LinearGradient {
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(main_window.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#003cff" }
GradientStop { position: 1.0; color: "#9afff9" }
}
}
}
Rectangle {
id: sgfBox
anchors.fill: parent
color: "transparent"
Rectangle {
id: sfg
width: 175
height: 75
color: 'transparent'
RowLayout {
width: parent.width
height: parent.height
spacing: 25
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
Rectangle {
Layout.preferredWidth: 75
Layout.fillWidth: false
Layout.fillHeight: true
color: 'red'
}
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
drag {
target: sfg
}
}
}
}
Rectangle {
id: mask
anchors.fill: parent
color: "transparent"
Rectangle {
color: 'black'
x: 400
y: 200
width: 100
height: 100
opacity: 1
radius: 50
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: sgfBox
}
}
}

How do I make a gradient opacity in an image?

How do I make an image fade in qml? How do I achieve this effect? here I attach the image of how I want it to look
A possible solution is to use OpacityMask with a LinearGradient as source
import QtQuick 2.9
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
Window {
visible: true
width: 600
height: 600
title: qsTr("Hello World")
Image {
id: input
source: "input.jpg"
anchors.fill: parent
OpacityMask {
source: mask
maskSource: input
}
LinearGradient {
id: mask
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.2; color: "transparent"}
GradientStop { position: 0.5; color: "white" }
}
}
}
}
Input:
Output:
While the accepted solution achieves the desired effect, it's not actually creating an opacity gradient in the original image (which is what I needed). It's just overlaying the LinearGradient on top of the image; the OpacityMask isn't doing anything and can be removed from that example. The white color at the bottom of the image is from the LinearGradient, not from the background.
Here's an example of how to create an opacity gradient in the original image. Note that the visibility of the "source image" (a Rectangle, in this case) and the LinearGradient are both false.
import QtQuick 2.9
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
Window {
visible: true
width: 400
height: 400
title: qsTr("Hello World")
// The background, to ensure it's working
Rectangle {
color: "red"
anchors.fill: parent
}
// The "source image"
Rectangle {
id: blueBox
color: "blue"
anchors.fill: parent
opacity: 0.5
visible: false
}
LinearGradient {
id: mask
anchors.fill: blueBox
gradient: Gradient {
GradientStop {
position: 0.2
color: "transparent"
}
GradientStop {
position: 0.5
color: "white"
}
}
visible: false
}
OpacityMask {
anchors.fill: parent
source: blueBox
maskSource: mask
}
}
With few lines code:
LinearGradient {
source: Image { source: 'qrc:/Login Images/ir.svg' }
anchors.fill: parent
start: Qt.point(0, 0)
end: Qt.point(0, 300)
gradient: Gradient {
GradientStop { position: 0.0; color: "white" }
GradientStop { position: 1.0; color: "teal" }
}
}

Qt Quick adding gradient frame around Image

I am working on application using Qt 5.4.1 and its Qt Quick module. I load some .svg pictures from /images directory and then show them in ListView, which works ok. But, how do I add shadow gradient around every loaded .svg image? Here is MWE:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import Qt.labs.folderlistmodel 2.1
Rectangle
{
id: ueMainWindow
visible: true
width: 800
height: 1280
color: "black"
property string ueRootDirectory:"/images"
property real ueImagesLoadProgress;
property bool ueImageLoading;
Rectangle
{
id: ueContainerThumbnails
antialiasing: true
color: "black"
anchors.bottom: ueMainWindow.bottom
width: ueMainWindow.width
height: 256
gradient: Gradient
{
GradientStop { position: 0.0; color: "black" }
GradientStop { position: 1.0; color: "grey" }
}
Text
{
id: ueTextImageName
antialiasing: true
color: "white"
anchors.horizontalCenter: ueContainerThumbnails.horizontalCenter
text: qsTr("TestApp")
}
ListView
{
id: ueViewThumbnails
antialiasing: true
orientation: ListView.Horizontal
anchors
{
topMargin: parent.height-(parent.height-50)
fill: parent
}
FolderListModel
{
id: ueModelImages
folder: "file://"+ueRootDirectory
nameFilters: ["*.svg"]
}
Component
{
id: ueDelegateImage
Image
{
id: ueImage
source: ueModelImages.folder + "/" + fileName
antialiasing: true
asynchronous: true
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
width: 192
height: 192
fillMode: Image.PreserveAspectFit
}
}
focus: true
spacing: 10
leftMargin: 10
rightMargin: 35
visible: ueModelImages.status==FolderListModel.Ready
model: ueModelImages
delegate: ueDelegateImage
}
}
}
Well, you should put that gradient into your delegate somehow. You can either:
create an empty Item and put the Rectangle and Image inside it
example:
Component {
id: ueDelegateImage
Item { // container
Rectangle {
// gradient rectangle
}
Image {
// image
}
}
}
or put the Image inside the Rectangle
example:
Component {
id: ueDelegateImage
Rectangle {
// gradient rectangle acts as a container
Image {
// image
}
}
}
In both cases stacking order will draw the gradient rectangle behind the image. A delegate should only have one root element, but is not limited to just one element, you can nest as many as you like.

Resources