Component View with transaction in QML - qt

I tries to implement View, which rendering some Component and on change source Component plays fadeout/fadein animation.
My code:
import QtQuick 2.1
Item {
id: componentViewRoot
onOpacityChanged: console.log(opacity)
property Component source: null
PropertyAnimation {
id: fadeout
target: componentViewRoot;
alwaysRunToEnd: true
property: "opacity";
to: 0;
duration: 1000
}
PropertyAnimation {
id: fadein
target: componentViewRoot;
alwaysRunToEnd: true
property: "opacity";
to: 1;
duration: 1000
}
onSourceChanged: {
fadeout.start()
loader.sourceComponent = source;
}
Loader{
id:loader
anchors.fill: parent
onLoaded: fadein.start()
}
}
But it not working (don't plays animation) properly.
Please help me.
UDP:
I want use this View like StackView from QtQuick.Controls without depth.
Some code:
Rectangle {
id: mainObjectRoot
implicitHeight: 440
implicitWidth: 720
color: "#f8f8f8"
ComponentView{
id: compView
height: 265
width: 680
onXChanged: console.log("compView"+x)
onYChanged: console.log("compView"+y)
anchors.right: parent.right
anchors.rightMargin: 20
anchors.bottom: parent.bottom
anchors.bottomMargin: 45
anchors.top: parent.top
anchors.topMargin: 130
anchors.left: parent.left
anchors.leftMargin: 20
source: passwordView
}
Connections{
target: sessionManager
onRequiredPasswordChanged :{
if(sessionManager.requiredPassword){
compView.source = passwordView
}
else{
compView.source = mainview
}
}
}
Component {
id: passwordView
PasswordView{
}
}
Component {
id: helpview
HelpView{
}
}
Component{
id: mainview
MainView{
}
}
Component{
id: settingsview
SettingsView{
}
}
In my example code I want to play transition animation on compView.source changed

For now I was
#ComponentView.qml:
import QtQuick 2.1
Item {
id: componentViewRoot
//#source for setting
property Component source: null
//#item for acces rendering item
property alias item: loader.item
function __changeItem(component){
anim._component = component
anim.start()
}
SequentialAnimation {
id:anim
property Component _component: null
NumberAnimation {
id: fadeout
target: componentViewRoot
alwaysRunToEnd: true
property: "opacity"
from : 1
to: 0
}
PropertyAction{
target: loader
property: "sourceComponent"
value: anim._component
}
NumberAnimation {
id: fadein
target: componentViewRoot
alwaysRunToEnd: true
property: "opacity"
from : 0
to: 1
}
}
onSourceChanged: {
__changeItem(source)
}
Loader{
id:loader
anchors.fill: parent
}
}

Related

Qt Quick - What is the best way to animate an image transition while component state changes?

With Qt Quick, I created a custom button which looks like that:
This button also contains several states (default, hover, pressed), and the transition between each states is animated. I found a way to animate each properties easily with the Qt Quick engine, except for the image one. Indeed, I want to animate the image transition, by performing a crossfade between states.
However I couldn't found a such animator for images. The only way I found was to add 3 images to my button, one for each states, and to animate their respective opacity.
Below is the code for my buttons:
Button
{
property bool hoveredBtn: false
property bool pressedBtn: false
id: btAnimStateDemo
height: 40
anchors.right: parent.right
anchors.rightMargin: 5
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top
anchors.topMargin: 290
state: "DEFAULT"
// the button background
background: Rectangle
{
id: btAnimStateDemoBg
anchors.fill: parent
}
// the button text
Text
{
id: btAnimStateDemoText
text: qsTr("A button showing animated states (default, hovered, pressed)")
renderType: Text.NativeRendering
font.bold: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
Image
{
id: btAnimStateDemoDefaultImage
width: 30
height: 30
anchors.left: parent.left
anchors.leftMargin: 5
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
opacity: 1.0
source: "Resources/Palette.svg"
}
Image
{
id: btAnimStateDemoHoverImage
width: 30
height: 30
anchors.left: parent.left
anchors.leftMargin: 5
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
opacity: 0.0
source: "Resources/Smile.svg"
}
Image
{
id: btAnimStateDemoPressedImage
width: 30
height: 30
anchors.left: parent.left
anchors.leftMargin: 5
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
opacity: 0.0
source: "Resources/Woman.svg"
}
// the component state array
states:
[
State
{
name: "DEFAULT"
PropertyChanges { target: btAnimStateDemoBg; color: "green"}
PropertyChanges { target: btAnimStateDemoBg; radius: 4}
PropertyChanges { target: btAnimStateDemoDefaultImage; opacity: 1.0}
PropertyChanges { target: btAnimStateDemoHoverImage; opacity: 0.0}
PropertyChanges { target: btAnimStateDemoPressedImage; opacity: 0.0}
},
State
{
name: "HOVERED"
PropertyChanges { target: btAnimStateDemoBg; color: "red"}
PropertyChanges { target: btAnimStateDemoBg; radius: 10}
PropertyChanges { target: btAnimStateDemoDefaultImage; opacity: 0.0}
PropertyChanges { target: btAnimStateDemoHoverImage; opacity: 1.0}
PropertyChanges { target: btAnimStateDemoPressedImage; opacity: 0.0}
},
State
{
name: "PRESSED"
PropertyChanges { target: btAnimStateDemoBg; color: "blue"}
PropertyChanges { target: btAnimStateDemoBg; radius: 15}
PropertyChanges { target: btAnimStateDemoDefaultImage; opacity: 0.0}
PropertyChanges { target: btAnimStateDemoHoverImage; opacity: 0.0}
PropertyChanges { target: btAnimStateDemoPressedImage; opacity: 1.0}
}
]
// the matching transitions between states
transitions:
[
Transition
{
from: "*"; to: "DEFAULT"
ColorAnimation { property: "color"; easing.type: Easing.Linear; duration: 1000 }
NumberAnimation { properties: "radius, opacity"; easing.type: Easing.Linear; duration: 1000 }
},
Transition
{
from: "*"; to: "HOVERED"
ColorAnimation { property: "color"; easing.type: Easing.Linear; duration: 1000 }
NumberAnimation { properties: "radius, opacity"; easing.type: Easing.Linear; duration: 1000 }
},
Transition
{
from: "*"; to: "PRESSED"
ColorAnimation { property: "color"; easing.type: Easing.Linear; duration: 1000 }
NumberAnimation { properties: "radius, opacity"; easing.type: Easing.Linear; duration: 1000 }
}
]
// the mouse area which will apply the correct state in relation to the current mouse status
MouseArea
{
anchors.fill: parent
hoverEnabled: true
onEntered: {btAnimStateDemo.state = "HOVERED"; btAnimStateDemo.hoveredBtn = true;}
onExited: {btAnimStateDemo.state = btAnimStateDemo.pressedBtn ? "PRESSED" : "DEFAULT"; btAnimStateDemo.hoveredBtn = false;}
onPressed: {btAnimStateDemo.state = "PRESSED"; btAnimStateDemo.pressedBtn = true;}
onReleased: {btAnimStateDemo.state = btAnimStateDemo.hoveredBtn ? "HOVERED" : "DEFAULT"; btAnimStateDemo.pressedBtn = false;}
}
}
The above code works well, and reaches the purpose I planned, but is a little complicated from my point of view. It would be wonderful if an animator like NumberAnimation or ColorAnimation would exist for the images, but I found none.
So my question is: Is there a simpler way to animate an image transition between component states than my above proposed solution? Do Qt Quick propose an animator to reach a such purpose?
Create a SpriteSheet that assigns states to each of the three frames then set the interpolate property to true to have it transition between the images.
Sprite Animations with SpriteSequence
SpriteSequence demonstrates using a sprite sequence to draw an animated and interactive bear. The SpriteSequence object defines five different sprites. The bear is initially in a still state:
Sprite{
name: "still"
source: "content/BearSheet.png"
frameCount: 1
frameWidth: 256
frameHeight: 256
frameDuration: 100
to: {"still":1, "blink":0.1, "floating":0}
}
When the scene is clicked, an animation sets the sprite sequence to the falling states and animates the bear's y property.
SequentialAnimation {
id: anim
ScriptAction { script: image.goalSprite = "falling"; }
NumberAnimation { target: image; property: "y"; to: 480; duration: 12000; }
ScriptAction { script: {image.goalSprite = ""; image.jumpTo("still");} }
PropertyAction { target: image; property: "y"; value: 0 }
}
At the end of the animation the bear is set back to its initial state.

QML DelegateModel: Access DelegateModel from delegate

I'm trying to create a ListView with different delegates and a drag'n'drop functionality. The delegates shall be loaded with a Loader.
The QML Documentation provides a working example for a ListView without a Loader:
http://doc.qt.io/qt-5/qtquick-tutorials-dynamicview-dynamicview3-example.html
However, using the Loader I get the error: Cannot read property 'DelegateModel' of undefined
I do not understand how I can access the DelegateModel from the Loader.
A hint to the solution is highly appreciated!
main.qml:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import QtQml.Models 2.3
Window {
id: mainroot
visible: true
width: 640
height: 480
Rectangle{
id:viewContainer
anchors.fill: parent
DelegateModel {
id: visualModel
model: ListModel{
id:m_model
ListElement{
type:1
m_text :"Text1"
}
ListElement{
type:1
m_text :"Text2"
}
}
delegate: Loader{
id:idLoader
width: view.width
height: childrenRect.height
Component.onCompleted: {
switch(type){
case 1:
idLoader.setSource("TestDelegate.qml", {"m_text": m_text})
break;
}
}
}
}
ListView{
id: view
anchors.fill: parent
spacing: 5
model: visualModel
}
}
}
TestDelegate.qml:
import QtQuick 2.7
MouseArea {
id: dragArea
property bool held: false
property string m_text
anchors { left: parent.left; right: parent.right }
height: 50
width: view.width
drag.target: held ? content : undefined
drag.axis: Drag.YAxis
onPressAndHold: held = true
onReleased: held = false
Rectangle {
id: content
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea.width
height: textfield.implicitHeight
Drag.active: dragArea.held
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
border.width: 1
border.color: "lightsteelblue"
color: dragArea.held ? "lightsteelblue" : "white"
Behavior on color { ColorAnimation { duration: 100 } }
radius: 2
states: State {
when: dragArea.held
ParentChange { target: content; parent: viewContainer }
AnchorChanges {
target: content
anchors { horizontalCenter: undefined; verticalCenter: undefined }
}
}
Text{
id: textfield
anchors.centerIn: parent
text: m_text
}
}
DropArea {
anchors { fill: parent; margins: 10 }
onEntered: {
visualModel.items.move(
idLoader.item.drag.source.DelegateModel.itemsIndex,
idLoader.item.dragArea.DelegateModel.itemsIndex)
}
}
}
The items defined in the file loaded with Loader or in general with any other .qml file that is imported should not depend directly on the main file since the ids have a scope, it is better to expose properties, in your case:
╭------------------------------------------╮
| bool held ------┿--->
| TestDelegate string m_text ------┿--->
| ============ DelegateModel md ------┿--->
| int index ------┿--->
╰------------------------------------------╯
Considering the above, the solution is the following:
main.qml
import QtQuick 2.7
import QtQuick.Window 2.0
import QtQml.Models 2.3
Window {
id: mainroot
visible: true
width: 640
height: 480
Rectangle{
id:viewContainer
anchors.fill: parent
DelegateModel {
id: visualModel
model: ListModel{
id:m_model
Component.onCompleted: {
for(var i=0; i< 40; i++){
m_model.append({"type": 1, "m_text": "Text" + i})
}
}
}
delegate:
Loader{
id: idLoader
width: view.width
height: childrenRect.height
property int index: DelegateModel.itemsIndex
onIndexChanged: if(status == Loader.Ready) idLoader.item.index = index
Component.onCompleted: {
switch(type){
case 1:
idLoader.setSource("TestDelegate.qml", {
"m_text": m_text,
"index": index,
"md": visualModel
})
break;
}
}
}
}
ListView{
id: view
anchors.fill: parent
spacing: 5
model: visualModel
}
}
}
TestDelegate.qml
import QtQuick 2.7
import QtQml.Models 2.3
MouseArea {
id: dragArea
property bool held: false
property string m_text
property DelegateModel md: null
property int index : -1;
anchors { left: parent.left; right: parent.right }
height: 50
width: view.width
drag.target: held ? content : undefined
drag.axis: Drag.YAxis
onPressAndHold: held = true
onReleased: held = false
Rectangle {
id: content
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
width: dragArea.width
height: textfield.implicitHeight
Drag.active: dragArea.held
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
border.width: 1
border.color: "lightsteelblue"
color: dragArea.held ? "lightsteelblue" : "white"
Behavior on color { ColorAnimation { duration: 100 } }
radius: 2
states: State {
when: dragArea.held
ParentChange { target: content; parent: viewContainer }
AnchorChanges {
target: content
anchors { horizontalCenter: undefined; verticalCenter: undefined }
}
}
Text{
id: textfield
anchors.centerIn: parent
text: m_text
}
}
DropArea {
anchors { fill: parent; margins: 10 }
onEntered: {
if(md !== null)
md.items.move(drag.source.index, dragArea.index)
}
}
}

QtLocation: how to make drag-and-drop on Map?

I have a QML Map widget. I want to drop some objects on it to get the latitude and longitude of the dropping point. How can I do it?
You can do that exactly as all other drag-n-drop applications. There are tons of examples as in Qt documentation as over the Internet.
One of them:
import QtQuick 2.9
import QtQuick.Window 2.0
import QtLocation 5.3
Window {
id: window
width: 600
height: 400
visible: true
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: item
propagateComposedEvents: true
onReleased: item.Drag.drop()
}
Plugin {
id: mapPlugin
preferred: ["osm", "esri"]
}
Map {
id: map
anchors.fill: parent
anchors.topMargin: 40
anchors.bottomMargin: 30
plugin: mapPlugin
center {
latitude: 40.785091
longitude: -73.968285
}
zoomLevel: 14
DropArea {
anchors.fill: parent
onDropped: {
var coord = map.toCoordinate(Qt.point(drop.x, drop.y));
output.text = "latitude:" + coord.latitude + ", longitude:" + coord.longitude;
anim.running = true;
}
}
}
Rectangle {
id: item
x: parent.width/2 - 20
y: 0
width: 40
height: 40
radius: 20
color: "orange"
opacity: 1
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: 20
Drag.hotSpot.y: 20
Text {
anchors.centerIn: parent
text: "Drop me"
font.pixelSize: 8
}
SequentialAnimation {
id: anim
running: false
NumberAnimation { target: item; property: "opacity"; to: 0; duration: 500 }
PropertyAction { target: item; property: "x"; value: window.width/2 - 20 }
PropertyAction { target: item; property: "y"; value: 0 }
NumberAnimation { target: item; property: "opacity"; to: 1; duration: 500 }
}
}
Text {
id: output
anchors.bottom: parent.bottom
height: 30
width: window.width
text: ""
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

Height animation does not work

I'm trying to create TableView with rows that can expand and show some content on click. It works, but does not show height property animation. I tried to add ColorAnimation and change expanded row color to black and it works well, but for some reason it does not work for height.
Here is the code of my table view:
TableView {
id: myTableView
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
anchors.leftMargin: 10
anchors.top: parent.top
anchors.topMargin: 12
anchors.rightMargin: 10
model: myModel
ListModel {
//...
}
rowDelegate: Item {
id: myRowDelegate
Rectangle {
id: rect
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: styleData.alternate ? "#d9e5ea" : "white"
MouseArea {
anchors.fill: parent
onClicked: {
myRowDelegate.state = (myRowDelegate.state == "COLLAPSED") ? "EXPANDED" : "COLLAPSED";
console.log("click");
}
}
}
state: "COLLAPSED"
states: [
State {
name: "COLLAPSED"
PropertyChanges { target: myRowDelegate; height: 22; }
},
State {
name: "EXPANDED"
PropertyChanges { target: myRowDelegate; height: 400; }
// PropertyChanges { target: rect; color: "black"; }
}
]
transitions: [
Transition {
from: "EXPANDED"
to: "COLLAPSED"
PropertyAnimation { property: height; duration: 400; }
// ColorAnimation { duration: 400; }
},
Transition {
from: "COLLAPSED"
to: "EXPANDED"
PropertyAnimation { property: height; duration: 400; }
//ColorAnimation { duration: 400; }
}
]
}
}
}
You have to use property name as a string to make it work:
PropertyAnimation { property:"height"; duration: 400; }
You could also achieve that via Behavior (remove transitions for that):
Behavior on height {
NumberAnimation { duration: 400 }
}

QML: How to make nice transition effect between image sources (one fades into the other)?

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

Resources