Animating height - qt

I want to show/hide an element by modifying it's height. Here is an example code showing my issue:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
Window {
id: win
width: 300
height: 300
visible: true
ColumnLayout {
width: parent ? parent.width : 200
Switch {
id: someswitch
Layout.alignment: Qt.AlignCenter
}
Label {
id: myText
text: "dummy"
height: 0
wrapMode: Text.WordWrap
clip: true
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
states: State {
name: "visible"
when: someswitch.checked
PropertyChanges { target: myText; height: implicitHeight }
}
Behavior on height {
NumberAnimation { duration: 100 }
}
}
}
}
I didn't add a Transition/Animation yet, but the behavior is already wrong on this stage. someswitch is unchecked by default but the text is shown. On the other hand, after checking the switch, the text hides and never appears back.
How should I handle that? I'd like the text to "slide out". I don't want to change its opacity.

Generally speaking, States consistency should be guaranteed to ensure that Transitions work properly. Consistency can be achieved either:
by defining a consistent default state
by defining all the necessary States, like the other nice answer proposed.
That begin said, it should be noted that the presence of a Layout plays a key role here. The Layout somewhat supersedes the height settings of the Items with its minimumHeight property. In such a scenario States defined over height does not really affect the Label. The obvious solution is to force States consistency but over Layout.preferredHeight, i.e. define the default State as well as the "invisible" with different values for Layout.preferredHeight instead of height. A revisited version of your code could look like this:
Label {
id: myText
text: "dummy"
wrapMode: Text.WordWrap
clip: true
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: implicitHeight //visible --> layout height = text implicit height
states: State {
name: "invisible"
when: !someswitch.checked
PropertyChanges { target: myText; Layout.preferredHeight: 0 } // invisible --> layout item height forced to zero
}
Behavior on Layout.preferredHeight {
NumberAnimation { duration: 100 }
}
}
A fully working example can be found here (whereas a version with a "visible" state can be found here).

You should use States and Transition in your case. For example:
import QtQuick 2.4
import QtQuick.Window 2.0
import QtQuick.Controls 1.2
Window {
id: mainWindow
width: 600
height: 600
visible: true
CheckBox {
text: "hide/show"
id: someswitch
x: 10
y: 10
}
Rectangle {
id: mytext
width: parent.width
anchors.centerIn: parent
color: "orange"
state: "shown"
states: [
State {
name: "shown"
when: !someswitch.checked
PropertyChanges { target: mytext; height: 300 }
},
State {
name: "hidden"
when: someswitch.checked
PropertyChanges { target: mytext; height: 0 }
}
]
transitions: Transition {
PropertyAnimation { property: "height"; duration: 500; easing.type: Easing.InOutQuad }
}
}
}

Related

Animation problems with the property when

why doesn't animation in the left work in the first case?
qmlonline.
if you go to the second case and comment out the code with when, then the animation works. the same problem if you disable sequential animation.
this is the first case without animation
this is the second case with animation
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
id: root
property int type: 0
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MouseArea {
anchors.horizontalCenter: parent.horizontalCenter
width: 50
height: 50
Rectangle { anchors.fill: parent; color: "blue" }
onClicked: {
root.type = !root.type
// stG.state = root.type ? "right" : "left" // uncomment to the second case
}
}
Rectangle {
id: switcher1
width: 50
height: 50
color: "red"
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: switcher2
width: 50
height: 50
color: "green"
anchors.top: switcher1.bottom
anchors.topMargin: 10
}
StateGroup {
id: stG
states: [
State {
name: "left"
when: type === 0 // comment to the second case
PropertyChanges {
target: switcher1
x: 0
}
PropertyChanges {
target: switcher2
x: 0
}
},
State {
name: "right"
when: type === 1 // comment to the second case
PropertyChanges {
target: switcher1
x: root.width - switcher1.width
}
PropertyChanges {
target: switcher2
x: root.width - switcher2.width
}
}
]
transitions: [
Transition {
to: "left"
SequentialAnimation {
NumberAnimation {
target: switcher2
property: "x"
duration: 500
}
NumberAnimation {
target: switcher1
property: "x"
duration: 500
}
}
},
Transition {
to: "right"
SequentialAnimation {
NumberAnimation {
target: switcher1
property: "x"
duration: 500
}
NumberAnimation {
target: switcher2
property: "x"
duration: 500
}
}
}
]
}
}
that's enough
I can give you a way to fix the problem, but it actually feels like there might be a bug with Qt here.
I tried adding a printout to show me what state Qt thinks it's in:
onStateChanged:
{
console.log("state: " + stG.state);
}
The output I got was this:
// First click
> state: right
// Second click
> state:
> state: left
So, what seems to be happening is that for a split second, the state enters some non-existent state and resets your x-values to 0 without using the transitions. Then the correct state gets applied, but we don't see the transition because everything is already at 0. Maybe someone smarter than me can help figure out why we switch to the bad state first.
Thankfully, there is a simple enough fix. If you make the "right" transition reversible, then it works as expected.
Transition {
to: "right"
reversible: true
...
}

ListView with section, Remove Animation not working for the top item

I'm using a QML ListView with section, click on item to remove with animation. Here the code:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
visible: true
width: 400
height: 400
ListView {
id: list
anchors.fill: parent
clip: true
spacing: 0
onContentYChanged: console.log("onContentYChanged: " + contentY)
onContentHeightChanged: console.log("onContentHeightChanged: " + contentHeight)
model: ListModel {
id: myModel
ListElement {name: "Item 1";type: "A"}
ListElement {name: "Item 2";type: "A"}
ListElement {name: "Item 3";type: "B"}
}
delegate: Rectangle {
width: parent.width
height: 50
color: (index % 2 == 1) ? "#5678a2" : "#88a345"
Text {
anchors.verticalCenter: parent.verticalCenter
text: name
}
MouseArea {
anchors.fill: parent
onClicked: {
console.log("remove: " + index + ", contentY:" + list.contentY)
myModel.remove(index)
}
}
}
section.property: "type"
section.delegate: Rectangle {
height: 30
Text {
anchors.verticalCenter: parent.verticalCenter
text: section
}
}
displaced: Transition {
NumberAnimation { properties: "x,y"; duration: 500; easing.type: Easing.OutCubic }
}
remove: Transition {
NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 500 }
NumberAnimation { property: "scale"; from: 1.0; to: 0; duration: 500 }
}
}
}
When I clicked on the first item(Item 1), it got deleted, but the Item 2 was flying up to outside the window. The ListView displayed the remaining items in wrong positions. ContentY changed to 80 (which was the y position of Item 2 before) instead of remaining at 0.
qml: onContentHeightChanged: 300
qml: onContentHeightChanged: 240
qml: onContentHeightChanged: 210
qml: remove: 0, contentY:0
qml: onContentYChanged: 80
qml: onContentHeightChanged: 160
It will work correctly if:
Delete other items except the top one.
Disable either the section or animation.
I tried your code with Qt 5.13.1. And currently downloading Qt 5.15. For now it looks like it is a bug with section, because I found a lot of not not closed bug reports on bugtracker. I can suggest 2 ways of solving your problem.
Performing animation while locking removal.
Using model with categories.
1st solution I tested by meself. Here is what you need to change to try it:
Delete ListView's removal animations. Add following code to your delegate
ListView.onRemove: SequentialAnimation {
PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true }
ParallelAnimation {
NumberAnimation { target: wrapper; property: "opacity"; to: 0; duration: 500 }
NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 500 }
}
PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
}
What is this? ListView has a signal remove() which is called BEFORE removing an item from the view. It is described in documentation It is also noted, that
If a remove transition has been specified, it is applied after this signal is handled, providing that delayRemove is false.
So in delegate you simply block removal from view, do you animation and unblock it. I suppose it won't be as clean and beautiful as you want it to be simply because view doesn't andjust it's size in this case.
2nd solution
I didn't try to implement it, but I can imagine having a model like this:
ListModel {
id: myModel
ListElement { type: "category"; name: "cat1" }
ListElement { name: "delegate1"; type: "delegate"; catrgory: "cat1"}
ListElement { name: "delegate2"; type: "delegate"; catrgory: "cat1"}
ListElement { name: "delegate3"; type: "delegate"; catrgory: "cat1"}
ListElement { type: "category"; name: "cat2" }
ListElement { name: "delegate4"; type: "delegate"; catrgory: "cat2"}
To use this as you want, you will need to castomize your delegate accordingly and removal function accordingly, which will lead to much more complex code in comparison to what it would be if section would work properly.
UPD: Same problem in 5.15

Multi item swipedelegate

Edited due to insufficient intial posting.
Hi,
thanks for your help!
You're right, I guess it is better to include the whole file, in spite of the size:
import QtQuick 2.5
import QtQuick.LocalStorage 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Controls 2.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.0
import QtQuick.Controls.Material 2.1
import "./database.js" as Database
ApplicationWindow {
visible: true
width: 640
height: 480
id: appWindow
x: Screen.width / 2 - width / 2
y: Screen.height / 2 - height / 2
title: qsTr("Project Stats")
Material.theme: Material.Dark
ListModel {
id: projectModel
ListElement {
projectID: "123654"
manager: "Schneider"
sponsor: "3466"
}
}
Component {
id: projectDelegate
SwipeDelegate {
id: projectSwipeDelegate
width: parent.width
height: projectDelegateItem.implicitHeight
anchors.horizontalCenter: parent.horizontalCenter
spacing: 10
contentItem: Item {
id: projectDelegateItem
Text {
id: projectID_text
text: "Project ID: " + projectID
font.pointSize: 20
anchors.horizontalCenter: parent.horizontalCenter
font.weight: Font.Black
color: "white"
}
Text {
id: manager_text
text: 'Manager: ' + manager + " Sponsor: " + sponsor
anchors.top: projectID_text.bottom
anchors.horizontalCenter: parent.horizontalCenter
font.weight: Font.Thin
color: "lightgrey"
}
}
onClicked: {
console.log(index, projectModel.get(index).projectID)
if (swipe.complete)
projectModel.remove(index)
else {
//var component= Qt.createComponent("timepointsstackview.qml")
//var loadwin = component.createObject(appWindow)
//loadwin.selected_project = projectModel.get(index).projectID
// stackView.push(Qt.resolvedUrl("timepointsstackview.qml"), {properties: {selected_project: projectModel.get(index).projectID}})
stackView.push(component, {properties: {selected_project: projectModel.get(index).projectID}})
}
}
swipe.right: Label {
id: deleteLabel
text: qsTr("Delete")
color: "white"
verticalAlignment: Label.AlignVCenter
padding: 12
height: parent.height
anchors.right: parent.right
SwipeDelegate.onClicked: projectListView.model.remove(index)
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
}
}
}
}
Item {
Component.onCompleted: {
Database.getDatabase()
Database.getProjects()
}
}
StackView {
id: stackView
anchors.fill: parent
// Implements back key navigation
focus: true
Keys.onReleased: if (event.key === Qt.Key_Back && stackView.depth > 1) {
stackView.pop();
event.accepted = true;
}
initialItem: Item {
width: parent.width
height: parent.height
ListView {
id: projectListView
anchors.fill: parent
clip: true
model: projectModel
delegate: projectDelegate
}
}
}
onClosing: {
if (Qt.platform.os == "android") {
close.accepted = false;
// if (stack.depth > 1) stack.pop();
}
}
}
Meanwhile I already had removed the row/column stuff, which I put in to get it working somehow though I started without it.
I also experimented with implicitheight before intially posting, but sadly to no avail. The above is my current code, though putting in
height: projectDelegateItem.implicitHeight
in that spot (probabaly not the correct one or the wrong reference? Had to change it from your suggestion as I already took out the row) leads to rendering in one spot only.
Thanks for your time so far and also if you still have the patience to give me a clue where to turn the screws...
Ok, first of all:
Take warnings serious. If qml tells you, you should not try to use anchors within rows or columns, don't do it!
QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row. Row will not function.
QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function.
Also don't do it, if you can't see those warnings. It will mess up a lot.
A row automatically anchors all its children side by side to each other. A column does the same, just horizontraly. If you mess with it, everything breaks.
Frankly: I don't even understand why you use this strange Row/Column-Setup.
For your case it seems way better to just resort to anchoring. If you have reasons for that, why not take a grid?
Secondly: You need to specify a height for your delegate. Unfortunately it seems like, it does not calculate an implicit height.
The SwipeDelegate calculates its own implcitHeight based on the implicitHeight of its contentItem.
The problem is, that you don't assign the row (which has a proper implicitHeight) as the contentItem, but add it as a child instead.
Assigning it as contentItem would fix that for you.
Regarding your edit, and removal of the Row: The Item you use now does not calculate a implicitHeight based on its children. So you need to provide your calculation yourself.
This will set a proper height to your delegate, and your delegates won't overlap.
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
Window {
width: 1024
height: 800
visible: true
ListView {
width: 400
height: 800
model: ListModel {
ListElement { projectID: 0; manager: 'I'; sponsor: 'mom' }
ListElement { projectID: 1; manager: 'YOU'; sponsor: 'dad' }
ListElement { projectID: 1; manager: 'HE'; sponsor: 'auntie' }
}
delegate: SwipeDelegate {
id: projectSwipeDelegate
width: parent.width
// height: <--- provide a height, if the contentItem does not provide it.
contentItem: Row{ // <--- Add your content as contentItem.
id: rowProjectDelegate
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
Column {
id: column
// anchors.horizontalCenter: parent.horizontalCenter <--- Don't do that!
width: parent.width
Rectangle{ // If you don't want to have them in a column, they can't be siblings. If you want to, then you should.
height: 10
width: 250
color: "red"
Rectangle {
height: 10
width: 200
color: "blue"
}
}
Label {
id: projectID_text
text: "Project ID: " + projectID
font.pointSize: 20
font.weight: Font.Black
color: "white"
}
Label {
id: manager_text
text: 'Manager: ' + manager + " Sponsor: " + sponsor
// anchors.top: projectID_text.bottom <--- Don't do that!
font.weight: Font.Thin
color: "lightgrey"
}
}
}
}
}
}

Qt - change property from a component in a different qml file

Update 1
The idea is to be able to change the front and back of CardForm from main.qml because i want to be able to use multiple CardForm instances. I tried to do what they did here but it doesnt work.
Here is the code:
CardForm.qml
import QtQuick 2.0
Flipable {
id: sCard
width: 75
height: 200
property bool flipped: false
property string front: "Front"
property string back: "Back"
property alias callFront : front
property alias callBack : back
front: Rectangle{
id: front
anchors.fill: sCard
border.width: 2
border.color: "black"
radius: 5
Text{
anchors.centerIn: parent
text: sCard.front
}
}
back: Column{
Rectangle{
id: back
anchors.fill: sCard
radius: 5
border.width: 2
border.color: "black"
Text{
anchors.centerIn: parent
text: sCard.front
}
Text{
anchors.centerIn: parent
text: sCard.front
}
}
}
transform: Rotation{
id: flip
origin.x: sCard.width
origin.y: sCard.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: flip
angle: 180
}
when: sCard.flipped
}
transitions: Transition{
NumberAnimation {
target: flip
property: "angle"
duration: 200
}
}
MouseArea{
anchors.fill: parent
onClicked: sCard.flipped = !sCard.flipped
}
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Neuro Seed")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Column {
CardForm{
id: test
anchors.centerIn: parent
test.callFront: "Hello World!"
test.callBack: "Bonjour le Monde!
}
}
}
}
Here are the error messages:
SHGetSpecialFolderPath() failed for standard location "Shared Configuration", clsid=0x1c. ()
qrc:/main.qml:17:13: QML CardForm: back is a write-once property
qrc:/main.qml:17:13: QML CardForm: front is a write-once property
qrc:/main.qml:16:9: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function.
the c1.getFront() and getBack() were from a C++ class that I made. I changed these to "Hello World!" and "Bonjour le Monde!"
So after many hours of struggling I figured out that to create a property which is accessible by other .qml files you must create a property alias name: id.property. The id must point towards an existing instance of a object in your code and the property of this instance that you wish to be able to change from the outside. So in my case it would be like so:
CardForm.qml
Flipable {
id: sCard
width: 75
height: 200
property bool flipped: false
property alias frontText : front.text
front: Rectangle{
id: front
anchors.fill: sCard
border.width: 2
border.color: "black"
radius: 5
Text{
anchors.centerIn: parent
text: frontText
}
}
}
and in the main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Neuro Seed")
Rectangle {
anchors.fill: parent
CardForm{
id: test
anchors.centerIn: parent
frontText: "Hello World!"
}
}
}
}

Behavior on scale often is not working

I would like to have an application, where always when new image is loaded, it is appeared by scaling from 0 size to default size. This behavior is often not working. In this image I am also using animation for bouncing when mouse enters to image. Is it possible, that this two animations are not loving themselves and that is, why scaling up is often not working?
I am using Linux Mint 13, Qt 5.3
Here is my Image element:
Image {
id: pic1
width: appWindow.height*0.4
height: appWindow.height*0.4
smooth: { enabled = true
pic1MouseArea.containsMouse
}
states: [ "mouseIn", "mouseOut" ]
state: "mouseOut"
transitions: [
Transition {
from: "*"
to: "mouseIn"
NumberAnimation {
target: pic1
properties: "scale"
from: 0.95
to: 1
duration: 400
easing.type: Easing.OutBounce
}
}
]
scale: {
status === Image.Ready ? 1 : 0
}
Behavior on scale {
NumberAnimation{
from: 0
to: 1
duration: 1000
easing.type: Easing.OutBounce
}
}
MouseArea{
id: pic1MouseArea
hoverEnabled: true
anchors.fill: parent
onContainsMouseChanged: {
pic1.state = containsMouse ? "mouseIn" : "mouseOut"
}
onClicked: {
MyScript.getRandomFile()
}
}
}
First of all, read this doc. The states property must be defined as list<State>, not as an array of strings. Also, the State element defines some state when a property or set of properties changes from default configuration. In your example states define nothing. Read more about State type.
Finally, here is a small example to help you getting on:
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Window 2.2
Window {
width: 600
height: 400
visible: true
Image {
id: img
source: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"
anchors.centerIn: parent
opacity: 1
state: "mouseOut"
states: [
State {
name: "mouseIn"
PropertyChanges { target: img; opacity: 0 }
},
State {
name: "mouseOut"
PropertyChanges { target: img; opacity: 1 }
}
]
transitions: Transition {
PropertyAnimation {
target: img
property: "opacity"
easing.type: Easing.InCirc
duration: 1000
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: img.state = "mouseIn"
onExited: img.state = "mouseOut"
}
}
}
Sure, you can replace transitions with Behavior, if you need exactly this functionality, as shown below:
Behavior on opacity {
PropertyAnimation {
duration: 1000
easing.type: Easing.InCirc
}
}

Resources