qt QML collapsible nested ListView with PropertyAnimation - qt

I am new to QT QML, and I am planning to make a ListView with collapse with smooth animation. I saw this https://gist.github.com/elpuri/3753756 code. I tried adding PropertyAnimation during collapse and expand to the code. But failed, how should i make it work?
I was planning to add state and translation, but it is not working for two different components,
nestedModel.setProperty(index, "collapsed", !collapsed)
nestedModel.state = (collapsed.state === "COLLAPSED") ? "COLLAPSED" : "EXPANDED";
then for states and transitions,
delegate: Rectangle {
id: rect_change
color: "blue"
//height: 200
width: 300
border.color: "black"
border.width: 2
state: "COLLAPSED"
states: [
State {
name: "COLLAPSED"
PropertyChanges { target: rect_change; height: 0; }
},
State {
name: "EXPANDED"
PropertyChanges { target: rect_change; height: 200; }
}
]
transitions: [
Transition {
from: "EXPANDED"
to: "COLLAPSED"
PropertyAnimation { property: "height"; duration: 400; }
},
Transition {
from: "COLLAPSED"
to: "EXPANDED"
PropertyAnimation { property: "height"; duration: 400; }
}
]
}

For a simpler implementation, get rid of your states and transitions and just use a Behavior on height. You can change the Loader in the example that you linked to so it looks like this:
Loader {
id: subItemLoader
sourceComponent: subItemColumnDelegate
onStatusChanged: if (status == Loader.Ready) item.model = subItems
clip: true
height: collapsed ? 0 : subItems.count * 40
Behavior on height {
NumberAnimation {
duration: 400
}
}
}

Related

How to animate adding and removing element to Layout in QML?

I want to animate adding and removing of TextField in ColumnLayout. Basically I want to animate like this:- https://doc.qt.io/qt-5/videos/viewtransitions-basic.mp4
As a ColumnLayout uses the implicitHeight to position the items, you can add an animation to that to create the sliding effect. I used a wrapping Item to preserve the implicitHeight of the TextField itself. It may not look perfect, but I don't think you cannot get that much further
Item {
visible: your_property_here
implicitHeight: visible ? text.implicitHeight : 0
Behavior on implicitHeight { NumberAnimation { duration: 500 } }
TextField {
id: text
anchors.fill: parent
scale: parent.visible ? 1 : 0.2
Behavior on scale { NumberAnimation { duration: 500 } }
}
}
I didn't found effective answer. Until then I'm using column as workaround
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
Page {
id: page_root
component ETextField : TextField {
width: root.width/2
}
state: "LOGIN"
Column {
anchors.centerIn: parent
ETextField {
id:host
placeholderText: "Host e.g:- exam.server.com:8080"
}
ETextField {
id:uane
placeholderText: "Username"
}
ETextField {
id: passwd
placeholderText: "Password"
}
ETextField {
id: confirm_passwd
placeholderText: "Confirm Password"
}
RowLayout {
Button {
id: submit
text: "Login"
}
Button {
id: change_state
text: "Register Instead"
onClicked: page_root.state = page_root.state === "LOGIN" ? "REGISTER" : "LOGIN"
}
}
}
states: [
State {
name: "LOGIN"
PropertyChanges {
target: confirm_passwd
height: passwd.height
}
PropertyChanges {
target: change_state
text : "Register Instead"
}
},
State {
name: "REGISTER"
PropertyChanges {
target: confirm_passwd
height: 0
}
PropertyChanges {
target: change_state
text : "Login Instead"
}
}
]
transitions: [
Transition {
from: "LOGIN"
to: "REGISTER"
NumberAnimation {
target: confirm_passwd
property : "height"
duration: 100
}
},
Transition {
from: "REGISTER"
to: "LOGIN"
NumberAnimation {
target: confirm_passwd
property : "height"
duration: 100
}
}
]
}

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.

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.

QML: animation only when mouse enters image

I would like to make an animation when mouse comes over the image, but NOT when mouse leaves the image.
Item{
width: 800
height:800
Rectangle{
id: blueRec
width: 100; height: 100; color: "blue"
MouseArea{
anchors.fill: parent
onClicked: {
im1.visible = true
im1.source = "1.png"
}
}
}
Image {
id: im1
scale: im1MouseArea.containsMouse ? 0.8 : 1.0
Behavior on scale {
NumberAnimation{
id: anim
from: 0.95
to: 1
duration: 400
easing.type: Easing.OutBounce
}
}
MouseArea{
id: im1MouseArea
hoverEnabled: true
anchors.fill: parent
}
}
}
The code above makes also animation, when mouse is leaving image.
Can someone help?
Setting the scale and then triggering an animation that alters the scale seems like an odd approach. If I were you, I'd break this out into states and set the animation to trigger on the appropriate transition.
Here's an example of how this could be done:
Image {
id: im1
states: [ "mouseIn", "mouseOut" ]
state: "mouseOut"
transitions: [
Transition {
from: "*"
to: "mouseIn"
NumberAnimation {
target: im1
properties: "scale"
from: 0.95
to: 1
duration: 400
easing.type: Easing.OutBounce
}
}
]
MouseArea{
id: im1MouseArea
hoverEnabled: true
anchors.fill: parent
onContainsMouseChanged: {
im1.state = containsMouse ? "mouseIn" : "mouseOut"
}
}
}

Changing state after a transition's animations have finished

I'd like to change state after a transition's animations have completed. I have the following code that achieves this, although it seems kind of hackish:
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
Rectangle {
id: root
width: 400
height: 400
Rectangle {
id: rect
color: "blue"
width: 50
height: 50
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked: rect.state = "animating"
}
states: [
State {
name: "animating"
PropertyChanges {
target: rect
rotation: 360
}
},
State {
name: "shrinking"
PropertyChanges {
target: rect
scale: 0
}
}
]
transitions: [
Transition {
from: ""
to: "animating"
SequentialAnimation {
RotationAnimation {
duration: 500
}
ScriptAction {
script: rect.state = "shrinking"
}
}
},
Transition {
from: "animating"
to: "shrinking"
NumberAnimation {
property: "scale"
duration: 500
}
}
]
}
}
Is there a nicer way to do this without using ScriptAction? Note that I need the second state, and I don't want to just consolidate the scale animation into the SequentialAnimation of the animating transition.
The proper way is to change the state in the runningChanged handler of the transition, when running pass to false than the animation finished.
to do that you have two solutions:
Sol 1. use connections ( you will get a warning about a none notifiable property, ignore it)
Connections{
target:rect.transitions[0]
onRunningChanged:{
if( rect.transitions[0].running === false)
{
rect.state = "shrinking"
}
}
}
the code will be :
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
Rectangle {
id: root
width: 400
height: 400
Rectangle {
id: rect
color: "blue"
width: 50
height: 50
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked: rect.state = "animating"
}
states: [
State {
name: "animating"
PropertyChanges {
target: rect
rotation: 360
}
},
State {
name: "shrinking"
PropertyChanges {
target: rect
scale: 0
}
}
]
Connections{
target:rect.transitions[0]
onRunningChanged:{
if( rect.transitions[0].running === false)
{
rect.state = "shrinking"
}
}
}
transitions: [
Transition {
from: ""
to: "animating"
RotationAnimation {
duration: 500
}
},
Transition {
from: "animating"
to: "shrinking"
NumberAnimation {
property: "scale"
duration: 500
}
}
]
}
}
solution 2:
change state in runningchanged handler in the transition directly:
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
Rectangle {
id: root
width: 400
height: 400
Rectangle {
id: rect
color: "blue"
width: 50
height: 50
anchors.centerIn: parent
MouseArea {
anchors.fill: parent
onClicked: rect.state = "animating"
}
states: [
State {
name: "animating"
PropertyChanges {
target: rect
rotation: 360
}
},
State {
name: "shrinking"
PropertyChanges {
target: rect
scale: 0
}
}
]
transitions: [
Transition {
from: ""
to: "animating"
RotationAnimation {
duration: 500
}
onRunningChanged:{
if( running === false)
{
rect.state = "shrinking"
}
}
},
Transition {
from: "animating"
to: "shrinking"
NumberAnimation {
property: "scale"
duration: 500
}
}
]
}
}
I prefer the first solution (Connections) cause it's more generic
A slightly different approach is to set the shrinking state in the animating state, and use a PropertyAction to force the state change to happen at the end of the transition:
State {
name: "animating"
PropertyChanges {
target: rect
rotation: 360
}
PropertyChanges {
target: rect
state: "shrinking"
}
and
Transition {
SequentialAnimation {
RotationAnimation {
duration: 500
}
PropertyAction {
target: rect
property: "state"
}
}
}
Note that I agree with jturcotte on his assessment of using these states here.

Resources