In my QML application I'm trying to create a grid of items that can be flipped at the press of a button. The backside of such an item should then fill a major part of the screen until it is flipped back.
Let's say I start off with the following view of my application
When I press the question mark button of the item in the center then the item is flipped and moved slightly. What I would expect to see after this is the following
The blue box is the backside of my item and it covers most of the screen. Pressing the 'X'-Button on the top right would again flip the item back.
However what I actually see after flipping the first time is the following
You can see that parts of the items in my grid are covered by my flipped item and parts are not.
The code I'm using is as follows
import QtQuick 2.9
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
Window {
id: main
width: 640
height: 480
visible: true
title: qsTr("Hello World")
function absolutePos(item) {
var my_x = item.x
var my_y = item.y
if (item.parent !== null) {
var parent_pos = absolutePos(item.parent)
my_x += parent_pos.x
my_y += parent_pos.y
}
return {x: my_x, y: my_y}
}
GridLayout {
columns: 5; rows: 3
Repeater {
model: 15
delegate: Item {
width: main.width / 5 - 10
height: main.height / 3 - 10
Flipable {
id: flipable
anchors.fill: parent
property bool flipped: false
front: Rectangle {
anchors.fill: parent
border.color: "black"
border.width: 2
}
back: Rectangle {
id: backSide
width: 580; height: 400
property var absolute_pos: absolutePos(this)
border.color: "blue"
border.width: 2
Button {
anchors.top: parent.top
anchors.right: parent.right
text: "X"
width: 30; height: 30
onClicked: {
flipable.flipped = !flipable.flipped
}
}
}
transform: [
Rotation {
id: rotation
origin.x: flipable.width / 2
origin.y: flipable.height / 2
axis.x: 0; axis.y: 1; axis.z: 0
angle: 0
},
Translate {
id: translation
x: 0; y: 0
}
]
states: State {
name: "back"
PropertyChanges {
target: rotation
angle: 180
}
PropertyChanges {
target: translation
x: 490 - backSide.absolute_pos.x
}
PropertyChanges {
target: translation
y: 40 - backSide.absolute_pos.y
}
when: flipable.flipped
}
transitions: Transition {
ParallelAnimation {
NumberAnimation {
target: rotation
property: "angle"; duration: 300
}
NumberAnimation {
target: translation
property: "x"; duration: 300
}
NumberAnimation {
target: translation
property: "y"; duration: 300
}
}
}
}
Button {
anchors.top: parent.top
anchors.right: parent.right
text: "?"
width: 30; height: 30
onClicked: {
flipable.flipped = !flipable.flipped
}
}
}
}
}
}
I was already trying to achieve the effect by manually setting the parent of my Flipable to Window.contentItem so that it will always be above any other items. However this also doesn't fix the problem since the item will still cover the siblings following the current item.
Also I'm still hoping, there is a solution which doesn't require manipulating the z-order of my items in some arcane way.
I am not sure what you mean by "some arcane way", but changing the z property of your delegate is perfectly fine:
delegate: Item {
z: flipable.flipped ? 1 : 0
// ...
}
You will also probably want to hide the "?" button when flipped:
visible: !flipable.flipped
Related
lets just say theoreticaly, that I have
Rectangle {
id: testRect
width: 100
}
and once i start the timer with interval tick 50ms, it should just extend the width of Rect:
Timer {
id: testTimer
interval: 50
onTriggered: testRect.width += 50
}
which works fine, but even when its onlz 50ms, its still seems to be quite not smooth transition.
Any idea how to smoothen the width change?
Please note this is only for learning purposes, what I will learn here will use in different situations, therefore please dont ask what is the puspose of the code...
Thank you!
You should rely on the animation features available in QtQuick to animate property changes.
In your case, you can define different states, with transitions between states where you define how an item should behave when going from one state to another. (See relevant documentation about states)
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Rectangle {
id: rect
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 100
height: 200
color: "red"
state: "default"
states: [
State {
name: "default"
PropertyChanges {
target: rect
width: 200
}
},
State {
name: "bigger"
PropertyChanges {
target: rect
width: 250
}
}
]
transitions: Transition {
NumberAnimation {
duration: 500 //ms
target: rect
properties: "width"
}
}
// Just there to trigger the state change by clicking on the Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.state === "default")
rect.state = "bigger"
else
rect.state = "default"
}
}
}
}
Or you can define a behavior, which is more simple to define when you only act on a single property:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Rectangle {
id: rect
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 100
height: 200
width: 200
color: "red"
Behavior on width {
NumberAnimation {
duration: 500 //ms
}
}
// Just there to trigger the width change by clicking on the Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.width === 200)
rect.width = 250
else
rect.width = 200
}
}
}
}
Finally, if you really want a smooth animation, you can use SmoothedAnimation instead of NumberAnimation (which is a linear animation by default)
I want to move a qml Item out of the left side of the app window.
While this task works perfectly for the right side of the window by defining a state like this
states: State {
name: "hidden"
when: is_hidden == true
AnchorChanges {
target: right_item_to_move
anchors.right: undefined
}
PropertyChanges {
target: right_item_to_move
x: main_window.width
}
}
and defining the appropriate Transition, I can't get it to work on the left side of the main window because negative x coordinates are not allowed.
I.e. this does not work:
states: State {
name: "hidden"
when: is_hidden == true
AnchorChanges {
target: left_item_to_move
anchors.left: undefined
}
PropertyChanges {
target: left_item_to_move
x: -left_item_to_move.width
}
}
How can I achieve this task? I'm using Qt 5.8 and QtQuick 2.0.
In my opinion, one should strive to stay true to one way of positioning, so you should either use anchors or x/y-coordinates.
Here you can find an overview how to make the right choice.
In short: When in doubt, use anchors. When the positioning is only relative to the parent (static) use x and y and if not possible otherwise do so even when not relative to the parent.
As you have chosen anchors, in my opinion you should stick to that - meaning: change the anchoring, so that instead of the left anchor line of the object, the right anchor line will be anchored to the window's left.
This would look like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Rectangle {
anchors.centerIn: parent
width: 300
height: 600
color: 'green'
Button {
id: but
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
state = (state === 'left' ? '' : 'left')
}
states: [
State {
name: 'left'
AnchorChanges {
target: but
anchors.left: undefined
anchors.right: parent.left
}
}
]
transitions: [
Transition {
AnchorAnimation {
duration: 200
}
}
]
}
}
}
An example, how it might look, if you choose to modify the x value, it might look like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Rectangle {
anchors.centerIn: parent
width: 300
height: 600
color: 'green'
Button {
id: but
property bool shown: true
anchors {
verticalCenter: parent.verticalCenter
}
onClicked: {
shown = !shown
}
x: (shown ? 0 : -width)
Behavior on x {
XAnimator {
duration: 200
}
}
}
}
}
Is there anything for animating text changes? there is already animations for property changes, for example this code do the animation for properties opacity, width and scale and whenever they changed by states, they will get animations.
NumberAnimation {
properties: "opacity, width, scale, visible"
easing.type: Easing.OutBack; duration:500
}
However i didn't find anything for text change, for example counting from N to N+1 became animating (eg. fading out old value and fading new one). how i can animating text changes?
For this usecase I use Behavior with a custom Animation :
//FadeAnimation.qml
import QtQuick 2.0
SequentialAnimation {
id: root
property QtObject target
property string fadeProperty: "scale"
property int fadeDuration: 150
property alias outValue: outAnimation.to
property alias inValue: inAnimation.to
property alias outEasingType: outAnimation.easing.type
property alias inEasingType: inAnimation.easing.type
property string easingType: "Quad"
NumberAnimation { // in the default case, fade scale to 0
id: outAnimation
target: root.target
property: root.fadeProperty
duration: root.fadeDuration
to: 0
easing.type: Easing["In"+root.easingType]
}
PropertyAction { } // actually change the property targeted by the Behavior between the 2 other animations
NumberAnimation { // in the default case, fade scale back to 1
id: inAnimation
target: root.target
property: root.fadeProperty
duration: root.fadeDuration
to: 1
easing.type: Easing["Out"+root.easingType]
}
}
Please note that it can be done without all the added properties, but I have them to enable easy customization.
An example usage could be :
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
anchors.centerIn: parent
color: "red"
width: 100
height: width
radius: width/2
Text {
id: textItem
anchors.centerIn: parent
font.pixelSize: 30
color: "white"
property int foo: 0
// ### Important part ###
text: foo
Behavior on foo {
FadeAnimation {
target: textItem
}
}
// ######################
}
MouseArea {
anchors.fill: parent
onClicked: textItem.foo++
}
}
}
Output : https://zippy.gfycat.com/SilentImpressiveChameleon.webm
The fadeProperty is scale by default but it also works great with opacity.
EDIT :
I've implemented this as ready-to-use components in https://github.com/okcerg/quickbehaviors
I would suggest something like the following AnimatedText.qml:
import QtQuick 2.5
Item{
property real progress: 0.0
property string text0
property string text1
Text{
text: text0
opacity: 1.0 - progress
}
Text{
text: text1
opacity: progress
}
}
Can be used as follows:
AnimatedText{
text0: "First text"
text1: "Second text"
NumberAnimation on progress {
from: 0.0
to: 1.0
duration: 5000
}
}
I created a custom Item for this purpose with fade animation. You can edit it for any animation:
AnimatedText.qml
import QtQuick 2.7
Item {
id: root
width: Math.max(txt1.width, txt2.width);
height: Math.max(txt1.height, txt2.height);
property string text: ""
property int currentActiveTxt: 1
property real pointSize: 20
Text {
id: txt1
font { pointSize: root.pointSize }
}
Text {
id: txt2
font { pointSize: root.pointSize }
}
onTextChanged: {
if(currentActiveTxt == 1) {
txt2.text = root.text;
currentActiveTxt = 2;
root.state = "txt2 is active";
} else {
txt1.text = root.text;
currentActiveTxt = 1;
root.state = "txt1 is active";
}
}
states: [
State {
name: "txt1 is active"
PropertyChanges {
target: txt1
opacity: 1.0
}
PropertyChanges {
target: txt2
opacity: 0.0
}
},
State {
name: "txt2 is active"
PropertyChanges {
target: txt1
opacity: 0.0
}
PropertyChanges {
target: txt2
opacity: 1.0
}
}
]
state: "txt1 is active"
transitions: [
Transition {
from: "txt1 is active"
to: "txt2 is active"
NumberAnimation {
property: "opacity"
duration: 200
}
},
Transition {
from: "txt2 is active"
to: "txt1 is active"
NumberAnimation {
property: "opacity"
duration: 200
}
}
]
}
Sample usage:
Window {
id:root
visible: true
width: 340
height: 480
title: qsTr("Hello World")
AnimatedText {
id: txt
pointSize: 30
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 10
text: ":)"
}
Timer {
interval: 1000
running: true
repeat: true
property int i: 0
onTriggered: txt.text = i++;
}
}
http://doc.qt.io/qt-5/qml-qtquick-controls-styles-sliderstyle.html
Slider {
anchors.centerIn: parent
style: SliderStyle {
groove: Rectangle {
implicitWidth: 200
implicitHeight: 8
color: "gray"
radius: 8
}
handle: Rectangle {
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
border.width: 2
implicitWidth: 34
implicitHeight: 34
radius: 12
}
}
How to access the onReleased and onPressed of the slider in order to start and stop some animation?
Here is what I tried:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 1.4
Window {
visible: true
Slider
{
id: head
property Rectangle thumb: thumb
anchors.centerIn: parent
style: SliderStyle {
groove: Rectangle {
implicitWidth: 200
implicitHeight: 8
color: "gray"
radius: 8
}
handle: Rectangle {
id: thumb
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
border.width: 2
implicitWidth: 34
implicitHeight: 34
radius: 12
}
}
onPressedChanged:
{
if(pressed)
{
console.log("pressed")
returnAnimation.stop()
}
else
{
console.log("released")
returnAnimation.start()
}
}
ParallelAnimation {
id: returnAnimation
NumberAnimation { target: thumb.anchors; property: "horizontalCenterOffset";
to: 0; duration: 200; easing.type: Easing.OutSine }
NumberAnimation { target: thumb.anchors; property: "verticalCenterOffset";
to: 0; duration: 200; easing.type: Easing.OutSine }
}
}
}
Error:
ReferenceError: thumb is not defined
Here is a fully working example. You will have to create your own images referenced here since I can't attach them.
I have found scoping is tricky in QML with component objects. The ":style:handle" component in Slider can "see out" to the higher levels but the higher levels cannot "see in" to the ":style:handle" component.
General Strategy
Create a property in the Top Level Slider scope
Use the property inside the ":style:handle" component since it can "see out"
Use the higher level onPressedChanged handler and the pressed property to adjust the high level property which will be "seen" by the low level component.
Slider {
id: portVoltageSlider
width: 100; height: 27
maximumValue: 150; minimumValue: -150
value: 0.00
stepSize: 10
anchors { centerIn: parent }
// style:handle component will be able to see/access this property
// opacity value of style: SliderStyle:handle.sliderHover
property real hoverOpacity: 0
// adjust property on slider pressed
onPressedChanged: {
// show slider Hover when pressed, hide otherwise
if( pressed ) {
console.log("slider pressed. show hover.")
hoverShowAnimation.start()
}
else {
console.log("slider released. hide hover.")
hoverHideAnimation.start()
}
}
// gratuitous animation using opacity
PropertyAnimation {
id: hoverShowAnimation
target: portVoltageSlider; properties: "hoverOpacity"; from: portVoltageSlider.hoverOpacity; to: 1; duration: 500
}
PropertyAnimation {
id: hoverHideAnimation
target: portVoltageSlider; properties: "hoverOpacity"; from: portVoltageSlider.hoverOpacity; to: 0; duration: 500
}
style: SliderStyle {
id: sliderStyle
property bool hoverVisible: false
groove: Rectangle {
// x: slider1.leftPadding
y: portVoltageSlider.topPadding + portVoltageSlider.availableHeight / 2 - height / 2
implicitWidth: 200; implicitHeight: 4
width: portVoltageSlider.availableWidth; height: implicitHeight
radius: 2
color: "#bdbebf"
Rectangle {
width: portVoltageSlider.visualPosition * parent.width; height: parent.height
color: "yellow"
radius: 2
}
}
handle: Image {
id: sliderHandle
width: 22; height: 24
source: "sliderThumb.svg"
anchors { centerIn: parent }
Image {
id: sliderHover
width: 22; height: 24
source: "sliderValue.svg"
anchors { bottom: sliderHandle.top }
opacity: portVoltageSlider.hoverOpacity
Label {
id: check
anchors {centerIn: parent; verticalCenterOffset: -4 }
text: portVoltageSlider.value
font.pointSize: 6
font.bold: true
}
}
}
}
}
That what I meant in the comment above:
Slider {
...
onPressedChanged: {
if(pressed)
console.log("pressed")
else
console.log("released")
}
}
Would this work?
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Controls 1.4
Window {
visible: true
Slider
{
id: head
property Rectangle thumb: thumb
//Added these signals:
signal startAnim
signal stopAnim
anchors.centerIn: parent
style: SliderStyle {
groove: Rectangle {
implicitWidth: 200
implicitHeight: 8
color: "gray"
radius: 8
}
handle: Rectangle {
id: thumb
anchors.centerIn: parent
color: control.pressed ? "white" : "lightgray"
border.color: "gray"
border.width: 2
implicitWidth: 34
implicitHeight: 34
radius: 12
//Moved animation within the confines of the object that it actually pertains to
ParallelAnimation {
id: returnAnimation
NumberAnimation { target: thumb.anchors; property: "horizontalCenterOffset";
to: 0; duration: 200; easing.type: Easing.OutSine }
NumberAnimation { target: thumb.anchors; property: "verticalCenterOffset";
to: 0; duration: 200; easing.type: Easing.OutSine }
}
//Signal connections done here:
Component.onCompleted: {
head.startAnim.connect(returnAnimation.start)
head.stopAnim.connect(returnAnimation.stop)
}
}
}
onPressedChanged:
{
if(pressed)
{
console.log("pressed")
startAnim()
}
else
{
console.log("released")
stopAnim()
}
}
}
}
I wonder how to make smooth transitions betwen image sources in QML, I try
import QtQuick 1.1
Image {
id: rect
source: "quit.png"
smooth: true
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.margins: -10
hoverEnabled: true //this line will enable mouseArea.containsMouse
onClicked: Qt.quit()
}
states: State {
name: "mouse-over"; when: mouseArea.containsMouse
PropertyChanges { target: rect; scale: 0.8; source :"quit2.png" }
}
transitions: Transition {
NumberAnimation { properties: "scale, source"; easing.type: Easing.InOutQuad; duration: 1000 }
}
}
But It does not work on source as a transition just as final state change.. so I wonder how to make one image source fade into andothe and back?
You want the first image to fade out into the other? How about if you place two Image objects on top of each other, then animate the opacity property?
EDIT: This worked for me (I'm using QtQuick 1.0 because my Qt Creator installation is a bit outdated):
import QtQuick 1.0
Rectangle {
Image {
id: rect
source: "quit.png"
smooth: true
opacity: 1
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.margins: -10
hoverEnabled: true //this line will enable mouseArea.containsMouse
onClicked: Qt.quit()
}
states: State {
name: "mouse-over"; when: mouseArea.containsMouse
PropertyChanges { target: rect; scale: 0.8; opacity: 0}
PropertyChanges { target: rect2; scale: 0.8; opacity: 1}
}
transitions: Transition {
NumberAnimation { properties: "scale, opacity"; easing.type: Easing.InOutQuad; duration: 1000 }
}
}
Image {
id: rect2
source: "quit2.png"
smooth: true
opacity: 0
anchors.fill: rect
}
}
To the question in your comment: you can place the image exactly on top of the other by copying the anchors thru anchors.fill: rect
Here is also a simple scroll transition between images:
import QtQuick 2.6
import QtQuick.Controls 1.4
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: imageRect
anchors.centerIn: parent
width: 240
height: 320
clip: true
property int currentIndex: 0
property var imageSources: [ "imageLeft.jpg", "imageCenter.jpg" ]
Repeater {
model: imageRect.imageSources
Image {
id: image
width: parent.width
height: parent.height
x: index * parent.width - imageRect.currentIndex * parent.width
fillMode: Image.PreserveAspectFit
source: imageRect.imageSources[index]
Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } }
}
}
}
Button {
id: leftButton
anchors.bottom: parent.bottom
text: "left"
onClicked: if(imageRect.currentIndex > 0) imageRect.currentIndex--
}
Button {
id: rightButton
anchors.bottom: parent.bottom
anchors.left: leftButton.right
text: "right"
onClicked: if(imageRect.currentIndex < imageRect.imageSources.length - 1) imageRect.currentIndex++
}
Button {
id: addButton
anchors.bottom: parent.bottom
anchors.left: rightButton.right
text: "+"
onClicked: imageRect.imageSources = [ "imageLeft.jpg", "imageCenter.jpg" , "imageRight.jpg" ]
}
}