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.
Related
I am using a template to create an app using QT Creator and QML and am hoping to create a landing page that allows users to select which "page" they want to navigate to by clicking an icon.
I've figured out how to get a button on the landing page and have it open another page. However, I am using x and y positions of the button and it doesn't scale correctly when the window size changes.
Ultimately, I am trying to put 6 buttons on the landing page in a way that scales correctly.
I have attached an image of my ideal Landing Page design and have also attached code for what I already have.
I hope I was able to explain this well enough. Please let me know if I can clarify anything.
import QtQuick 2.2
import QtQuick 2.6
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.4
import "components" as Components
//BACKGROUND COLOR
Rectangle {
signal signInClicked(string tourId)
color: "#242424"
AnimatedImage {
anchors.fill: parent
source: app.landingpageBackground
fillMode: Image.PreserveAspectCrop
visible: source > ""
}
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#00000000";}
GradientStop { position: 1.0; color: "#00000000";}
}
}
//TITLE TEXT
Text {
id: titleText
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: app.height/10
}
font.family: app.customTitleFont.name
text: app.info.title
font {
pointSize: 60
pointSize: app.titleFontSize * 1.4
}
color: "#00000000"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
}
Button {
id: signInButton
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 60 * app.scaleFactor
}
opacity: 0.0
style: ButtonStyle {
id: btnStyle
property real width: parent.width
label: Text {
id: lbl
text: signInButton.text
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
width: parent.width
maximumLineCount: 2
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: app.titleColor
font.family: app.customTextFont.name
font.pointSize: app.baseFontSize
}
background: Rectangle {
color: Qt.darker(app.headerBackgroundColor, 1.2)
border.color: app.titleColor
radius: app.scaleFactor * 2
}
}
height: implicitHeight < app.units(56) ? app.units(56) : undefined // set minHeight = 64, otherwise let it scale by content height which is the default behavior
width: Math.min(0.5 * parent.width, app.units(250))
text: qsTr("Let's Play!")
MouseArea{
anchors.fill: parent
onClicked: {
signInClicked("");
}
}
NumberAnimation{
id: signInButtonAnimation
target: signInButton
running: false
properties: "opacity"
from: 0.0
to: 1.0
easing.type: Easing.InQuad
duration: 1000
}
}
AboutPage {
id: aboutPage
}
NewsAndUpdates {
id: newsPage
}
ProgramsPage {
id: programsPage
}
Connections {
target: app
onUrlParametersChanged: {
if (app.urlParameters.hasOwnProperty("appid")) {
signInClicked(app.urlParameters.appid)
}
}
}
Component.onCompleted: {
signInButtonAnimation.start()
}
}
I think this doc should help you. You haven't really defined what you want very well, but I'll show you some examples so you can hopefully take from it what you need.
QML has the concept of "positioners" and "layouts". Positioners help automatically position your objects neatly on the screen. And Layouts try to do that too, but can also stretch your objects to fill available space.
Row:
You can arrange all your buttons in a row and center the row horizontally.
Row {
anchors.horizontalCenter: parent.horizontalCenter
Button { id: btn1 }
Button { id: btn2 }
...
}
Grid:
Similarly, Grid is a positioner that arranges objects into a grid:
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 3
Button { id: btn1 }
Button { id: btn2 }
...
}
GridLayout:
A GridLayout is just like a Grid, but it can also resize the objects to fill up the space. My opinion is layouts can do more, but they're often trickier to use. In this example, the first button should be a fixed size, while the second button should fill up the remaining width.
GridLayout {
anchors.fill: parent
rows: 2
Button { id: btn1; Layout.preferredWidth: 200 }
Button { id: btn2; Layout.fillWidth: true}
...
}
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 { ... }
}
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" }
}
}
I have a problem to create a shadow in an item. The item is not completely opaque and the drawn shadow appears behind the item reducing the transparency effect.
I need something as the picture to the right, but what I got with my attempts is shown to the left. I need you to look through the object, because the background is not solid.
I tried to use maskEf but the object becomes completely opaque. The closest solution I've managed to define is to use another element of the same shape but fully transparent and with solid edge. However I don't like the solid edge, any suggestions?
First attempt. This makes opacity equal to 1 in rec1:
Rectangle {
id: rec1; color: "white"; opacity: 0.5
anchors.fill: parent; radius: CalcSize.getW(8)
layer.enabled: true
layer.effect: DropShadow {
id: shadowEf
anchors.fill: rec1
source: rec1
horizontalOffset: 3
verticalOffset: 3
radius: 15
samples: 16
color: "red"
transparentBorder: true
}
}
Second attempt. This maintains opacity of rec1 but show the border of sourceMaskEf
DropShadow {
id: shadowEf
anchors.fill: sourceMaskEf
source: sourceMaskEf
horizontalOffset: 3
verticalOffset: 3
radius: 15
samples: 16
color: "red"
transparentBorder: true
}
Rectangle {
id: sourceMaskEf; color: "transparent"
anchors.fill: rec1; radius: rec1.radius
border { width: offset; color: "white"; }
}
OpacityMask {
id: maskEf
opacity: 1
anchors.fill: rec1
source: ShaderEffectSource {
sourceItem: shadowEf
hideSource: false
}
maskSource: ShaderEffectSource {
sourceItem: sourceMaskEf
hideSource: false // if set true the shadow is hide to
}
cached: true
}
Rectangle {
id: rec1; color: "white"; opacity: 0.5
anchors.fill: parent; radius: CalcSize.getW(8)
}
Edit
Well, after the suggestion of BaCaRoZzo, this is my solution. It is much closer to what I'm looking for:
Component {
id: fondoItemPromo
Item {
id: item1; opacity: 0.5
layer.enabled: true; anchors.fill: parent
anchors.margins: CalcSize.getW(5) //Just for test
Rectangle {
id: rec1; color: "white"
anchors.fill: parent; radius: CalcSize.getW(8)
Item {
id: item2; opacity: 0.5; layer.enabled: true
anchors.fill: parent; clip: true
Rectangle {
id: rec2; color: "white"
anchors.fill: parent; radius: CalcSize.getW(8)
layer.enabled: true
}
DropShadow {
anchors.fill: rec2
source: rec2
transparentBorder: true
horizontalOffset: 3
verticalOffset: 3
radius: 15
samples: 16
color: "black"; clip: true
}
}
}
}
}
However, the shadow does not extend beyond the limits of the element, as can be seen in the corners:
Any suggestions?
Assuming also the shadow should be semi-transparent - the overall effect would be pretty ugly otherwise - you can solve the issue with the following approach:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
import QtQuick.Controls.Styles 1.3
ApplicationWindow {
width: 200
height: 300
visible: true
color: "steelblue"
Item {
id: layered
opacity: 0.2
layer.enabled: true
anchors.fill: parent
Rectangle {
id: rec1
width: 100
height: 100
anchors.centerIn: parent
radius: 8
}
DropShadow {
id: drop
anchors.fill: rec1
source: rec1
horizontalOffset: 5
verticalOffset: 5
radius: 15
samples: 16
color: "red"
transparentBorder: true
}
}
}
Here is the resulting Rectangle barely visible w.r.t. the background color and with the correct shadow applied:
Edit
The effect can be combined at various level as done in this example.
Given your edit, I think you have overcomplicated a bit the stuff here. The example I've given above served as a way to show that opacity should work as expected. Given the error you have shown I decided to provide a general solution which you should (hopefully) apply out of the box.
The white Rectangle acts as a container for the actual content. Hence, it should be defined in a different QML file. This way we can provide a default property, i.e. define where children Items should be positioned when added to the component. By adding aliases we are also able to fine tune the component, changing colors, shadow orientation and other graphical aspects. A possible definition of your component is the following:
// ShadowedComponent.qml
import QtQuick 2.0
import QtGraphicalEffects 1.0
Item {
opacity: 0.5
layer.enabled: true
clip: true
property alias color: rec.color
property alias dropColor: drop.color
property alias voff: drop.verticalOffset
property alias hoff: drop.horizontalOffset
property alias radius: rec.radius
property alias dropRadius: drop.radius
property alias samples: drop.samples
default property alias childrenz: rec.children //(1)
property int margins: 20 //(2)
Rectangle {
id: rec
width: 100
height: 100
anchors.fill: parent
anchors.margins: margins
radius: 8
clip: true
}
DropShadow {
id: drop
anchors.fill: rec
source: rec
horizontalOffset: 5
verticalOffset: 5
radius: 15
samples: 16
color: "darkgray"
transparentBorder: true
}
}
The declaration in (1) is crucial as hinted in the previous text: with that we specify that any child of ShadowedComponent is automagically child of the inner Rectangle positioning it inside the component (with the desired alignement - see below). Also crucial is the property margins in (2): it gives the necessary gap for the shadow to correctly appear. A value equal to zero result in the error you get as the shadow cannot exceed boundaries of an item.
The component can be used like this:
ShadowedComponent {
color: "red"
dropColor: "black"
voff: 3
hoff: 3
radius: 8
dropRadius: 8
samples: 16
// inner content...
}
or, since all the properties have a default value, like this:
ShadowedComponent {
// inner content...
}
Finally a possible usage example can be the following:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ApplicationWindow {
width: 300
height: 600
visible: true
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "cyan" }
GradientStop { position: 0.5; color: "#0099FF" }
GradientStop { position: 1.0; color: "#6699FF" }
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
ShadowedComponent {
Layout.fillHeight: true
Layout.fillWidth: true
voff: -5
hoff: -10
Image {
source: "http://avatarmaker.net/free-avatars/avatars/animals_216/cats_237/kitty_close_up_avatar_100x100_36619.jpg"
anchors.fill: parent
anchors.margins: 3
}
}
ShadowedComponent {
Layout.fillHeight: true
Layout.fillWidth: true
dropColor: "red"
opacity: 0.7
Text {
anchors.centerIn: parent
text: qsTr("QML ROCKS!!!")
}
}
ShadowedComponent {
Layout.fillHeight: true
Layout.fillWidth: true
voff: -5
hoff: -10
dropColor: "red"
BusyIndicator {
anchors.centerIn: parent
running: true
}
}
ShadowedComponent {
Layout.fillHeight: true
Layout.fillWidth: true
opacity: 0.6
Test {
anchors.fill: parent
opacity: 1
margins: 10
Text {
anchors.centerIn: parent
text: qsTr("QML ROCKS!!!")
}
}
}
}
}
}
Using a component defined in a different file you are able to compose it, declaratively, with any other (custom) component thanks to the default property. The overall result of our example is the following. Note how each component is unique in its overall appearence thanks to the numerous aliases we defined, and used. Note also that the component can be composed with itself (by also carefully tuning the margin w.r.t. the given shadow parameters):
I'm working on a QML StackView that starts with a list of items to select from. Once selected I want to transition _.push(...) to a input form which has larger dimensions than the initialItem.
The only way I have trial-and-errored my way into a situation that works is by making the form Item a nested borderless window.
Q1. A nested window can't be the right type of concept to use for this... right ? there must be another way to do it. What is the right way ?
Q2. My goal after this is to have a transition animation that grows or shrinks between stacks of different sizes. Advice that doesn't preclude that would be best.
code
Main.qml :
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
ApplicationWindow {
property int itemHeight: 30
property int cornerRadius : 5
visible: true
color: "transparent"
flags: Qt.FramelessWindowHint
ListModel {
id: searchFacets
ListElement {
title: "Topics"
page: "content/TopicSearch.qml"
}
// ListElement {
// title: "Domains"
// }
}
StackView {
id: stackView
focus: true
initialItem: SearchFacets {
id: facets
}
delegate: StackViewDelegate {
pushTransition: StackViewTransition {
PropertyAnimation {
target: enterItem
property: "opacity"
from: 0
to: 1
}
}
}
}
}
Initial Item:
import QtQuick 2.3
Item {
height : listView.count * itemHeight
ListView {
id: listView
model: searchFacets
anchors.fill: parent
focus: true
highlightFollowsCurrentItem: false
highlight: Rectangle {
width: parent.width
height: itemHeight
radius : cornerRadius
color: "green"
opacity: 0.5
z:2
x: listView.currentItem.x;
y: listView.currentItem.y
Behavior on y {
SpringAnimation {
spring: 60
damping: 1.0
}
}
}
delegate: Component {
Item {
width: parent.width
height : itemHeight
Rectangle {
anchors.fill: parent
color: "#212126"
radius: cornerRadius
z:0
border.width: 2
border.color : "white"
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("clicked index: " + index)
listView.currentIndex = index
// listView.forceActiveFocus()
stackView.push(Qt.resolvedUrl(page))
}
}
Text {
text: title
font.pixelSize: 24
font.bold: true
z:1
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "white"
antialiasing: true
}
}
}
}
}
Input Form:
import QtQuick 2.3
import QtQuick.Window 2.0
Item {
Window {
width: 400
height: 400
visible: true
flags: Qt.FramelessWindowHint
Rectangle {
anchors.fill: parent
visible: true
color: "red"
}
}
}
One possible solution is to update the size of the dimensions of the StackView in the click handler that causes the transition. I do not know if that causes any problems with animating the transition.
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("clicked index: " + index)
listView.currentIndex = index
var component = Qt.createComponent(page)
var res = component.createObject(stackView)
stackView.height = res.height
stackView.width = res.width
stackView.push(res)
}
}