How to trigger An Action Once After NumberAnimation Is Completed In QML - qt

I am trying to call a reset function once after the animation is completed. But in the below given code , the animation starts after resetting the values
On Long press of the button I need to start an animation which basically acts as a progress bar, and once the animation is completed I need to call the reset() function.
I have tried the below given code , But here the animation starts once after the resetting of values are done.
Button {
id: button1
onPressAndHold: {
rectangle.visible = true
timer.restart()
}
}
Item {
id: rectangle
Behavior on width {
NumberAnimation {
duration: 1000
easing.type: Easing.InOutCubic
}
}
Image {
id: img
source: "somesource"
fillMode: Image.PreserveAspectCrop
}
}
Timer {
id: timer
repeat: true
interval: 50
onTriggered: {
rectangle.width = img.sourceSize.width * img.progress
if (rectangle.width <= img.sourceSize.width) {
timer.stop()
reset(values)
}
}
}
can you please let me know on how modify it such that the animation completes first and then the reset is done. Thank you in advance!

Ok, that really works for standalone Animation only that sounds a bit strange for me since the common usecase is using animations inside Behavior or State.
So you can use NumberAnimation.onRunningChanged or ScriptAction as #iam_peter said or use Transition.onRunningChanged as well:
Window {
height: 200
width: 600
visible: true
title: qsTr("Animation test")
RowLayout {
width: parent.width
height: 100
anchors.centerIn: parent
Button {
text: "start"
onClicked: {
testRect.state = "state2"
}
}
Rectangle {
id: testContainer
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
id: testRect
height: 100
width: 0
color: "orange"
states: [
State {
name: "state1"
PropertyChanges {
target: testRect
width: 0
}
},
State {
name: "state2"
PropertyChanges {
target: testRect
width: testContainer.width
}
}
]
transitions: Transition {
NumberAnimation {
property: "width"
duration: 2000
easing.type: Easing.InQuad
}
onRunningChanged: {
if(running == false)
{
finishRect.color = "red";
}
}
}
}
}
Rectangle {
id: finishRect
Layout.preferredWidth: 100
color: "yellow"
width: 100
height: width
radius: width / 2
}
}
}

According to this post you have two options.
You can us the onRunningChanged handler and check if the animation is still running. If not call anything you want at that point.
Rectangle {
id: rect
width: 100; height: 100
color: "red"
Behavior on width {
NumberAnimation {
duration: 1000
onRunningChanged: {
if (!running)
console.log("Animation finished")
}
}
}
MouseArea {
anchors.fill: parent
onClicked: rect.width = 50
}
}
The other option would be to create a SequentialAnimation and add a SrcriptAction that runs after the NumberAnimation is completed.
Rectangle {
id: rect
width: 100; height: 100
color: "red"
Behavior on width {
SequentialAnimation {
NumberAnimation { duration: 1000 }
ScriptAction {
script: console.log("Animation finished")
}
}
}
MouseArea {
anchors.fill: parent
onClicked: rect.width = 50
}
}

Related

QML How to position a child outside it's parent

I am currently working on a Video/Stream Management program for my university. The body of my application is a GridView. I implemented a playbar for each video so I can use my playback functions specifically for each member of the GridView. The Problem now is FullScreen. I couldn't find a showFullScreen() function like the one from Window. Now I came across this question and tried the first solution (States/Transitions) and it works the way it is intended except that I would need it to be able to go out of the parents scope.
Code:
GridView {
id: mainGrid
width: parent.width - (parent.width % cellWidth)
height: parent.height
anchors.centerIn: parent
Layout.fillHeight: true
Layout.fillWidth: true
cellWidth: 300
cellHeight: 300
focus: true
property bool newPlayStatus: true
model: VideoModel {
list: videoList
}
delegate: Component {
id: videoDelegate
Frame {
id: videoContainer
width: mainGrid.cellWidth
height: mainGrid.cellHeight
background: Rectangle {
anchors.centerIn: parent
width: parent.width
height: parent.height
color: "black"
}
VideoDummy {
id: video
list: model.video
videoUrl: model.url
anchors.centerIn: parent
focus: true
playStatus: mainGrid.newPlayStatus
}
onChildrenChanged: {
console.log("url: " + model.url)
}
Playbar {
id: localPlaybar
x: -12
y: mainGrid.cellHeight - 42
width: mainGrid.cellWidth
height: 30
}
Connections{
target:localPlaybar
onToggleFullscreen: {
videoContainer.state = videoContainer.state === "EXPANDED" ? "" : "EXPANDED"
}
}
states: [
State {
name: "EXPANDED"
PropertyChanges {
target: videoContainer
x: application.x
}
PropertyChanges {
target: videoContainer
y: application.y
}
PropertyChanges {
target: videoContainer
width: application.width
}
PropertyChanges {
target: videoContainer
height: application.height
}
}
]
transitions: [
Transition {
ParallelAnimation {
NumberAnimation {
target: videoContainer
property: "x"
duration: 350
}
NumberAnimation {
target: videoContainer
property: "y"
duration: 350
}
NumberAnimation {
target: videoContainer
property: "width"
duration: 350
}
NumberAnimation {
target: videoContainer
property: "height"
duration: 350
}
}
}
]
}
}
}
I excluded some parts of my code because it would only made it harder to see the important parts. application is the id for the ApplicationWindow.
Currently this code isn't scaling everything to the size/position it needs to be but that is something I would do if the general idea would work.
The problem is that the videoContainer isn't able to go out of it's parents space. Is there any way to do it? I could open up a new Window with the needed qml parts and make it showFullScreen() but I do not believe that this is a nice solution is it?
Thanks in advance!
I found a solution:
onToggleFullscreen: {
var i = videoContainer.mapFromGlobal(0,0)
videoContainer.x = i.x
videoContainer.y = i.y
videoContainer.width = application.width
videoContainer.height = application.height
}
With this I can resize and position it outside of its parent.

QML - Why do animations conflict?

Example of Qml - Flipable:
import QtQuick 2.0
Flipable {
id: flipable
width: 240
height: 240
property bool flipped: false
front: Rectangle { width: 200; height: 200; color: 'red'; anchors.centerIn: parent }
back: Rectangle { width: 200; height: 200; color: 'blue'; anchors.centerIn: parent }
transform: Rotation {
id: rotation
origin.x: flipable.width/2
origin.y: flipable.height/2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges { target: rotation; angle: 180 }
when: flipable.flipped
}
transitions: Transition {
NumberAnimation { target: rotation; property: "angle"; duration: 4000 }
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
This example is running good but, if I use this code, Flipable doesn't run:
MouseArea {
anchors.fill: parent
onClicked: {
flipable.flipped = true;
flipable.flipped = false;
}
}
I think the animation conflicts when I first make the flipped property is true then false. Whereas I want flipable open and then close.
The problem is that you set the property flipped back to false before the flip animation even started.
If you want the full open/close animation, you have to wait for the "open" animation to finish before starting the "close" animation:
transitions: Transition {
id: transition
onRunningChanged: {
if (!running && flipable.flipped) {
flipable.flipped = false;
}
}
NumberAnimation { target: rotation; property: "angle"; duration: 4000 }
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!transition.running) {
flipable.flipped = true;
}
}
}

How to trigger Animation on one Item in a ListView

I try to trigger a SequentialAnimation on a given Item of a ListView.
For example:
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id: modelList
ListElement {}
ListElement {}
ListElement {}
}
ListView {
width: window.width
height: window.height
model: modelList
delegate: Rectangle {
width: window.width
height: window.height/10
color: "red"
radius : 10
SequentialAnimation on color { //Should be only triggered when button clicked
ColorAnimation { to: "yellow"; duration: 1000 }
ColorAnimation { to: "red"; duration: 1000 }
}
Button {
text: "trigger animation"
anchors{
right: parent.right
top: parent.top
bottom: parent.bottom
margins: 10
}
onClicked: {
//Trigger SequentialAnimation
}
}
}
}
}
I try to trigger the Animation when you click on the button but I don't know how to use a condition on an Animation.
How could I proceed ?
Use animation on property only if you want changes to be automatically animated.
In your case you need to remove the on color part, then give the animation an id: yourAnimation, and on the button click yourAnimation.start()
Actually, it seems that on color is also possible, skipping setting the target:
SequentialAnimation on color {
id: yourAnimation
ColorAnimation { to: "yellow"; duration: 1000 }
ColorAnimation { to: "red"; duration: 1000 }
running: false
}

How to show an image for few seconds in QML?

I have an image that i need to show for few seconds. How to do it in QML ?
Does anyone has an an example ?
i have this one with state but its doest not appear . I need also to make ir repetitive
Item {
id: myimageanimation
x:0
y:0
z:2000
Image {
id: rect
x: 293
y: 242
source: "assets/ic_sound_popup_off.png"
}
states: [
State {
name: "actif"
PropertyChanges {
target: rect
visible:true
}
},
State {
name:"inactif"
PropertyChanges {
target:rect
visible : false
}
}
]
transitions: [
Transition {
NumberAnimation {
property: "visible"
duration:2000
easing.type: Easing.InOutQuad
}
}
]
You can use an PropertyAnimation on visible
Window {
id: root
visible: true
width: 1200
height: 1000
Image {
id: rect
anchors.fill: parent
source: 'qrc:/Pics/mypic.png'
}
PropertyAnimation {
running: true
target: rect
property: 'visible'
to: false
duration: 5000 // turns to false after 5000 ms
}
}
Or, as #ddriver mentioned, use the Timer-Object, e.g. like this:
Window {
id: root
visible: true
width: 1200
height: 1000
Image {
id: rect
anchors.fill: parent
source: 'qrc:/Pics/mypic.png'
}
Timer {
interval: 5000 // triggers every 5000 ms
onTriggered: rect.visible = false
running: true
}
}
You can also use the Animation to fade it away:
Window {
id: root
visible: true
width: 1200
height: 1000
Rectangle {
id: rect
anchors.fill: parent
color: 'red'
visible: opacity > 0
}
SequentialAnimation {
running: true
PauseAnimation {
duration: 4000 // Wait for 4000ms
}
NumberAnimation {
target: rect
property: 'opacity'
to: 0
duration: 1000 // Then fade away for 1000ms
}
}
}
With the Timer, you can use a Behavior on opacity to achive the same.

Dynamically reparenting objects in QML fails on initial load

I am trying to dynamically reparent QML objects generated in a repeater according to data they inherit from their model.
This works like a charm - with one catch. When the object is generated for the first time, it is automatically reparented to the Repeater's parent after the state's ParentChange object makes its changes. Run the following QML file in a QML viewer, paying attention to the order of the console messages to see what I'm describing.
After you've clicked on each of the objects, they behave as expected.
import QtQuick 1.1
Rectangle {
id: container
height: 300
width: 300
signal completed
ListModel {
id: fooModel
ListElement { data: "red" }
ListElement { data: "red" }
ListElement { data: "blue" }
}
Component.onCompleted: {
console.log("Rect Completed!")
container.completed()
}
// The object I want to dynamically move
Component {
id: delg
Rectangle {
id: moveable
height: 40; width: 100
border.width: 1; border.color: "black"
state: model.data
color: state
// The following code makes it work, but feels very hackish
/*Connections {
target: container
onCompleted: {
moveable.parent = moveable.state == "red" ? red_col : blue_col
}
}*/
onStateChanged: { console.log("New state: " + state) }
onParentChanged: { console.log("New parent: " + parent) }
Component.onCompleted: { console.log("Delegate Completed!") }
MouseArea {
anchors.fill: parent
onClicked: {
// I know this is bad to do, but in my REAL application,
// the change is triggered through the model, not the qml
// object
moveable.state = (moveable.state == "red" ? "blue" : "red")
}
}
states: [
State {
name: 'red'
ParentChange { target: moveable; parent: red_col; x: 0 }
},
State {
name: 'blue'
ParentChange { target: moveable; parent: blue_col; x: 0 }
}
]
transitions: [ Transition {
ParentAnimation {
NumberAnimation { properties: 'x,y,height,width' }
}
}]
}
}
// Generates the Objects
Repeater {
id: repeat
model: fooModel
delegate: delg
}
// Display
Row {
spacing: 100
Column {
id: red_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
Column {
id: blue_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
}
}
I figured out a way to fix the behavior, but it's not pretty. (See the commented out "Connections" code above for that fix).
Is there a cleaner/less-hacky way to accomplish the same thing I'm trying here?
Easy way of doing it is placing extra Item under your delegate. This will cause Repeater to reparent Item and your own code would set new parent of its child, your Rectangle element. Like this:
import QtQuick 1.1
Rectangle {
id: container
height: 300
width: 300
signal completed
ListModel {
id: fooModel
ListElement { data: "red" }
ListElement { data: "red" }
ListElement { data: "blue" }
}
Component.onCompleted: {
console.log("Rect Completed!")
container.completed()
}
// The object I want to dynamically move
Component {
id: delg
Item {
Rectangle {
id: moveable
height: 40; width: 100
border.width: 1; border.color: "black"
state: model.data
color: state
// The following code makes it work, but feels very hackish
/*Connections {
target: container
onCompleted: {
moveable.parent = moveable.state == "red" ? red_col : blue_col
}
}*/
onStateChanged: { console.log("New state: " + state) }
onParentChanged: { console.log("New parent: " + parent) }
Component.onCompleted: { console.log("Delegate Completed!") }
MouseArea {
anchors.fill: parent
onClicked: {
// I know this is bad to do, but in my REAL application,
// the change is triggered through the model, not the qml
// object
moveable.state = (moveable.state == "red" ? "blue" : "red")
}
}
states: [
State {
name: 'red'
ParentChange { target: moveable; parent: red_col; x: 0 }
},
State {
name: 'blue'
ParentChange { target: moveable; parent: blue_col; x: 0 }
}
]
transitions: [ Transition {
ParentAnimation {
NumberAnimation { properties: 'x,y,height,width' }
}
}]
}
}
}
// Generates the Objects
Repeater {
id: repeat
model: fooModel
delegate: delg
}
// Display
Row {
spacing: 100
Column {
id: red_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
Column {
id: blue_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
}
}

Resources