Why my animations works in a strange way? - qt

I have write a simple animation demo,but the animation work in a strange way.
The Code
import QtQuick 2.5
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
width: 480
height: 680
id: root
Rectangle {
id: rect
width: 200
height: 200
color: "blue"
anchors.centerIn: parent
states: State {
name: "A"
when: mouseArea.pressed
PropertyChanges {target: rect; color:"red"; }
PropertyChanges {target: rect; width: rect.width + 100}
PropertyChanges {target: rect; rotation: 720}
}
transitions: Transition {
ColorAnimation {duration: 1000}
NumberAnimation {duration: 1000}
RotationAnimation {duration: 1000}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
}
}
Q1: When mouse press and hold, I want the rectange width increase 100 every time,but my code seems not work?
Q2: If the width assign a const value(eg 100), the NumberAnimation seems not work, the width change immediately?
Q3:The RotationAnimation not rotate 720, it rotate exceed 720?
Currently, I am not familiar with js&qml, Hope Good Man(Woman) can help me.

Q1: You shouldn't bind rect.width to itself. That causes a binding loop. Either use a constant value or come up with some way outside of rect to keep track of what size you want the rect to be.
Q2: You need to tell the NumberAnimation which property to animate on. In this case it's "width".
Q3: 720 degrees means twice all the way around. That's exactly what I'm seeing when I test it, so I think it's working correctly.
The code below works for me.
Rectangle {
id: rect
width: 200
height: 200
color: "blue"
anchors.centerIn: parent
states: State {
name: "A"
when: mouseArea.pressed
PropertyChanges {target: rect; color:"red"; }
PropertyChanges {target: rect; width: 300} // Fixed value
PropertyChanges {target: rect; rotation: 720}
}
transitions: Transition {
ColorAnimation {duration: 1000}
NumberAnimation {property: "width"; duration: 1000} // Specify property
RotationAnimation {duration: 1000}
}
}

In addition to the answer from JarMan, I think you want to define a onPressed handler in the MouseArea, where you assign a new value to the width of the rect (note the difference between "binding" and "assigning"):
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: rect.width += 100
}
To clarify why that PropertyChange on width didn't work: as long as State "A" is active (thus during the mouse press), the PropertyChange overwrites the binding in the original Rectangle code, and you are defining it as a binding, meaning during the "A" state, the width is bound to itself (the binding loop that JarMan writes about). When state "A" is not active anymore, it will return to width: 300 (which is basically also a binding, albeit being constant).
When you use the above onPressed handler, the width property will loose it's binding and become fixed to the value assigned to it. Note: you can make it bound again by using Qt.binding or temporarily by using another PropertyChanges from a State

Related

QML Transition Animation using Animators and PropertyAnimation Combined

Facing some issue using QML Animatiors in state Transition animations. Here is my working sample Code. In this sample if I decide not to provide "from" Attribute for the OpacityAnimator( reason is i wanted it to consider the current property value) the animator is not animating the property.
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property bool isOpen: false
Button {
anchors.centerIn: parent
text: "Fade Animation Open Layer "
onClicked: {
isOpen = true;
overlay.state = "open";
}
}
Rectangle {
id: overlay
anchors.fill: parent
color: "green"
visible: isOpen
opacity: 0
Button {
anchors.centerIn: parent
text: "Close Overlay"
onClicked: {
overlay.state = "close"
}
}
state: "close"
states: [
State {
name: "hidden"
},
State {
name: "visible"
}
]
transitions: [
Transition {
from: "close"
to: "open"
OpacityAnimator {
target: overlay
from: 0.0
to: 1.0
duration: 600
easing.type: Easing.OutCubic
}
},
Transition {
from: "open"
to: "close"
SequentialAnimation {
OpacityAnimator {
target: overlay
//from: overlay.opacity
to: 0.0
duration: 600
easing.type: Easing.OutCubic
}
ScriptAction {
script: {
isOpen = false;
}
}
}
}
]
}
}
If i use PropertyAnimation instead, the fade effect works absolutely fine. So I just digged in to the Animator Code to and found out the below snippet from the apply function of qquickanimator.cpp and assume it means if "from" not specified it is supposed to fetch it from the target's property value and a comment Claims the magic line works like PropertyAnimation. But it is not working this way
if (isFromDefined)
job->setFrom(from);
else if (action.fromValue.isValid())
job->setFrom(action.fromValue.toReal());
else
job->setFrom(action.property.read().toReal());
// This magic line is in sync with what PropertyAnimation does
// and prevents the animation to end up in the "completeList"
// which forces action.toValue to be written directly to
// the item when a transition is cancelled.
action.fromValue = action.toValue;
So my Questions are:
Is it a Bug in the Qt Animator or am I missing something here?
Is it ok to combine PropertyAnimation and Animators(Opacity,Scale) in a Grouped animation(Sequential or Parallel) for a state Transition provided Animators scope is Scene graph and for PropertyAnimation it's the target object
See this comment in the documentation for the from property of Animator:
If the Animator is defined within a Transition or Behavior, this value defaults to the value defined in the starting state of the Transition, or the current value of the property at the moment the Behavior is triggered.
I think this means it will use the to value of 1.0 from the open state instead of the current value on the target. Seems like the state machinery is tracking and using how the transitions left things instead of current values.
Your from: overlay.opacity should workaround that behavior?

PropertyAnimation vs. NumberAnimation

The QML code below animates two rectangles. One uses PropertyAnimation, while the other uses NumberAnimation. Both rectangles move similarly. I don't see anything different between the two animation types.
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
visible: true
width: 640
height: 480
Rectangle {
id: r1
width: 100; height: 100
color: "red"
Behavior on x { PropertyAnimation {} }
}
Rectangle {
id: r2
y: 150
width: 100; height: 100
color: "blue"
Behavior on x { NumberAnimation {} }
}
// click anywhere to start animation
MouseArea { anchors.fill: parent; onClicked: r1.x = r2.x = 200 }
}
What is the difference between PropertyAnimation and NumberAnimation; and when should I use one over the other?
tl;dr.
NumberAnimation is derived from PropertyAnimation, and thus, it makes logical sense for them to exhibit similar behaviour.
NumberAnimation is a specialized PropertyAnimation that defines an animation to be applied when a numerical value changes.
(Source)
While NumberAnimation specifically animates numeric values (e.g. x, y, width, opacity), PropertyAnimation is generic and can animate non-numeric ones (e.g. color, size).
Lé longer answer:
1. PropertyAnimation can animate non-numeric types. NumberAnimation only animates numbers.
NumericAnimation can animate numeric properties such as x, y, width, height, opacity. But it can't animate color, size, or points.
Here's an example where the animation types differ in animating the color property. The first rectangle transitions from red to green while the second rectangle stays blue. In this case, PropertyAnimation should be used over NumberAnimation.
Rectangle {
id: r1
width: 100; height: 100
color: "red"
Behavior on color { PropertyAnimation {} } // works
}
Rectangle {
id: r2
y: 150
width: 100; height: 100
color: "blue"
Behavior on color { NumberAnimation {} } // fails
}
MouseArea { anchors.fill: parent; onClicked: r1.color = r2.color = "green" }
But then again, you can ColorAnimation instead...
2. PropertyAnimation is generic.
This is a build-off from #1. But this is another advantage on its own.
Since PropertyAnimation is more generic, it can be used if you decide to have a dynamic PropertyAnimation::property.
Here's an example where the animation property is user-provided:
Rectangle {
id: rect
width: 100; height: 100
color: "red"
PropertyAnimation { id: animation; target: rect }
}
MouseArea {
anchors.fill: parent
onClicked: {
animation.property = t1.text;
animation.to = t2.text;
animation.start();
}
}
Row {
width: parent.width; height: 50
anchors.bottom: parent.bottom
TextField { id: t1; width: parent.width/2; height: 50; placeholderText: "property" }
TextField { id: t2; width: parent.width/2; height: 50; placeholderText: "to" }
}
Using NumberAnimation also works, but restricts the viable properties to only numeric ones... users can't simulate supernovas or rainbows. :(
3. NumberAnimation is strict.
Let's compare the from and to properties.
NumberAnimation
from: real
to: real
PropertyAnimation
from: variant
to: variant
This makes NumberAnimation stricter. QML will prevent you from making silly mistakes:
NumberAnimation {
id: animation
to: "green" // Invalid property assignment: number expected
}
Use it when you're strictly limited animating numbers.
This also means that using NumberAnimation can improve readability and communication. It tells the people reading your code that you're only intending to animate numbers — not anchors, colours, unicorns or whatever.
4. NumberAnimation is more efficient at animating numbers.
– says Qt:
Specialized property animation types have more efficient implementations than the PropertyAnimation type.
(Source)
Here, the "specialized types" refers to NumberAnimation, along with other types such as AnchorAnimation and ColorAnimation.
I haven't tried profiling QML to benchmark the differences, but it seems like the rule of thumb for choosing animation types is:
If you're animating numbers, you should default to NumberAnimation.
PropertyAnimation should be a last resort (prefer the other types).

Animating height

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 }
}
}
}

Drag after long press

I want to drag my custom buttons QML after a long press over them. I've implemented that behaviour, however the problem is that after enabling drag, I need to press button once again to actually start dragging. How should I implement this mechanism if I want to move buttons without releasing after long press?
Here is my button code (onReleased and onLongPressed are my own signals):
ButtonWidget.SoftButtonUI
{
id:softButtonDelegate2
x:500
y:300
labelText: "button"
iconImageSource: path
isGrayedOut: false
Drag.active: dragArea2.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
onReleased:
{
console.log("onClicked")
}
onLongPressed:
{
console.log("onLongPressed")
dragArea2.enabled = true
}
MouseArea {
id: dragArea2
enabled: false
anchors.fill: parent
drag.target: parent
onReleased: parent.Drag.drop()
onClicked: {
console.log("MouseArea onClicked")
}
onPressAndHold: {
console.log("MouseArea onPressAndHold")
}
}
}
Any idea?
Generally speaking you can connect different signals and concatenate operations as discussed in this page. You should have a look at it since it is full of nice and useful information.
However, when it comes to mouse events, an interesting approach to events concatenation is given by MouseEvents acceptation. Documentation says about MouseEvent::accepted:
Setting accepted to true prevents the mouse event from being
propagated to items below this item. Generally, if the item acts on
the mouse event then it should be accepted so that items lower in the
stacking order do not also respond to the same event.
In this case we can take the opposite approach by not accepting the event. This way the pressed event can be used to both activate the drag and actually perform it. Then the MouseEvent can be accepted (implicitly) in the release event, occurring at the end of the drag.
Here is a simple example following this approach. As the mouse is pressed and hold the drag.target is set and drag can start, whereas when the mouse is released the drag.target is removed, removing the dragging behaviour. To test it, just press and hold the mouse over the rectangle and when it changes color just drag it.
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
width: 300
height: 300
visible: true
Rectangle {
id: item
border.width: 2
x: 100
y: 100
width: 100
height: 100
state: "BASE"
states: [
State {
name: "BASE"
PropertyChanges { target: mouseArea; drag.target: undefined}
PropertyChanges { target: item; color: "steelblue"}
},
State {
name: "DRAGGABLE"
PropertyChanges { target: mouseArea; drag.target: item}
PropertyChanges { target: item; color: "darkblue"}
}
]
MouseArea {
id: mouseArea
anchors.fill: parent
drag{
// target: NOT SET HERE
minimumX: 0
minimumY: 0
maximumX: parent.parent.width - parent.width
maximumY: parent.parent.height - parent.height
smoothed: true
}
onPressAndHold: {
item.state = "DRAGGABLE"
mouse.accepted = false // mouse event is USED but NOT CONSUMED...
}
onReleased: {
item.state = "BASE" // mouse event acceptation occurs here!
}
}
}
}
This simple approach should work perfectly also with your custom signals.

QML zoom from a clicked rectangle to another UI element

I have 9:9 matrix of Rectangle elements on the main QML form with Repeater. What I want to implement is if user clicks on one of rectangles, it zooms to TextEdit widget which on Esc press zooms back.
Is it possible with QML?
If yes, how am I supposed to turn Rectangle to TextEdit and zoom this TextEdit to fill the parent?
Just starting to work with QML and can't quite get an answer from http://doc.qt.nokia.com/4.7-snapshot/qdeclarativeanimation.html yet.
Thank you.
1) Sure thing! This is more or less what QML is made for.
2) This is an example of how you can do what you want (not the only way to do it):
Rectangle {
id: parentRect
width: 500; height: 500
// Every item in the grid should look like this
Rectangle {
id: singleItem
color: "red"
state: "closed"
// Hidden text input, shown when user clicks
TextInput {
id: textInput
anchors.fill: parent
text: "Input here"
cursorVisible: true
}
// MouseArea that will catch the user click
MouseArea {
anchors.fill: parent
onClicked: singleItem.state = "open"
}
// Item states
states: [
State {
name: "closed"
PropertyChanges {target: singleItem; width: 25; height: 25}
PropertyChanges {target: textInput; opacity: 0}
},
State {
name: "open"
PropertyChanges {target: singleItem; width: parentRect.width; height: parentRect.height}
PropertyChanges {target: textInput; opacity: 1; focus: true}
}
]
// Transitions between states
transitions: Transition {
ParallelAnimation {
NumberAnimation {
target: singleItem
properties: "width,height"
duration: 1000
}
NumberAnimation {
target: textInput
property: "opacity"
duration: 1000
}
}
}
}
}
Even I'm new to qt-quick. I don't think it is possible to zoom unless we write our code to do such. I'm not sure though. :-)
This effect is good and it will b nice to see in coming versions. Try to give a feature request to the community <3

Resources