I have created a simple tree which I wish to animate by having branches height expand/collapse visually. My code below works by switching only the visible attribute, so that if I click on the branch it can appear/disappear.
However I want to animate the height of the column from normal to 0 and back. The problem is, the height of the column never changes when the state changes. Why?
import QtQuick 2.0
import QtQuick.Layouts 1.12
Column {
id: menuGroup
property string path: "/"
property bool _onCurrentPath: (treeMenu._currentPath.indexOf(path) === 0)
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
// visible: _onCurrentPath <-- this works
states: [
State {
name: "HIDDEN"
when: !_onCurrentPath
PropertyChanges { target: menuGroup; height: 0}
},
State {
name: "VISIBLE"
when: _onCurrentPath
PropertyChanges { target: menuGroup; height: 600 } //menuGroup.implicitHeight}
}
]
transitions: Transition {
PropertyAnimation { property: "height"; duration: 500; easing.type: Easing.InOutQuad }
}
onStateChanged: {
console.log("Group "+path+" state "+state)
}
}
Is it not possible to force the height of a column? (Do I have to wrap it in a Rectangle or some other solution)?
The first thing I noticed when running your code was that onStateChanged was not executing. It seems that your when: _onCurrentPath is not evaluating due to a scoping problem that does not trigger an error or warning. I changed this to when: menuGroup._onCurrentPath in both cases and I was able to start seeing the state changes.
Secondarily, I had to enable clip: true to make sure that the contents of the column were actually trimmed, otherwise it visually didn't make a difference that the column was collapsing, as the contents stayed visible.
Here is a full working example (modified your _onCurrentPath bool to simplify testing):
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
Window {
width: 640
height: 480
visible: true
Column {
id: menuGroup
property string path: "/"
property bool _onCurrentPath: button.checked
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
clip: true // Must enable to actually clip contents of column when collapsing
states: [
State {
name: "HIDDEN"
when: !menuGroup._onCurrentPath // Must explicitly state menuGroup here
PropertyChanges { target: menuGroup; height: 0}
},
State {
name: "VISIBLE"
when: menuGroup._onCurrentPath
PropertyChanges { target: menuGroup; height: 600 }
}
]
transitions: Transition {
PropertyAnimation { property: "height"; duration: 500; easing.type: Easing.InOutQuad }
}
onStateChanged: {
console.log("Group "+path+" state "+state)
}
Rectangle { // Some column content for demonstration
color: "red"
implicitHeight: 600
implicitWidth: 200
}
}
Button {
id: button
anchors.bottom: parent.bottom
checkable: true
text: "Change States"
}
}
Related
I have following QML GridView:
GridView
{
id: ueProductGridView
antialiasing: true
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
cellWidth: 200
cellHeight: 200
delegate: Rectangle
{
id: test
width: 192
height: 192
color: "red"
Text
{
anchors.fill: parent
text: index
}
transform:
[
Rotation
{
id: plateRotation
angle: -90
axis { x: 0; y: 1; z: 0 }
origin.x: -200
origin.y: 0
}
] // transform
SequentialAnimation
{
id: addAnimation
PauseAnimation
{
duration: Math.random()*2000
}
NumberAnimation
{
target: plateRotation
property: "angle"
to: 0
duration: 1000
}
}
SequentialAnimation
{
id: removeAnimation
PropertyAction
{
target: test
property: "GridView.delayRemove"
value: true
}
NumberAnimation
{
target: test
property: "scale"
to: 0
duration: 1000
}
PropertyAction
{
target: test
property: "GridView.delayRemove"
value: false
}
}
GridView.onAdd:
{
addAnimation.start();
} // onAdd
GridView.onRemove:
{
removeAnimation.start();
} // onRemove
} // delegate
Component.onCompleted:
{
model=10;
} // onCompleted:
} // GridView
Now, why delegate animations do not work, i.e., why GridView is empty? The code regarding animations was taken from tutorial and in there it works. However, if I comment/remove all code regarding animations, delegates are visible in GridView and it is all ok:
GridView
{
id: ueProductGridView
antialiasing: true
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
cellWidth: 200
cellHeight: 200
delegate: Rectangle
{
id: test
width: 192
height: 192
color: "red"
Text
{
anchors.fill: parent
text: index
}
} // delegate
Component.onCompleted:
{
model=10;
} // onCompleted:
} // GridView
The animations associated to insertion/removal of elements from the views are Transitions (e.g. see add), between different States and are indeed called ViewTransitions. You should really take a deep look at the documentation page of this type: it is full of nice examples and describes in great details how addition/removal animations should be implemented.
When you define a ViewTransition, any property referenced inside it, if not differently targeted, refer a delegate property. Hence, if you write inside your GridView:
add: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 500 }
NumberAnimation { property: "scale"; easing.type: Easing.OutBounce; from: 0; to: 1.0; duration: 750 }
}
you are saying that each time a new delegate is added to the grid, its opacity as well as scale properties should be animated. Top level animations, like in this case, run in parallel so the delegate is scaled and made visible at the same time.
Now, if you want to animate nested properties, like in the case of the Rotation angle, the easiest way is to alias it inside the delegate. That way it can be handled exactly the same way as any other delegate property, resulting in a clearer and simpler code.
It should be noted that the animations in your example does not work also because they are associated to add Transition. Such Transition is not used during model initialisation when instead the populate Transition is used. From the documentation:
This property holds the transition to apply to the items that are initially created for a view.
It is applied to all items that are created when:
The view is first created
The view's model changes
The view's model is reset, if the model is a QAbstractItemModel subclass
Finally, an advice. If you animate addition and removal of elements, especially if the animation is slow, it is also important to animation the adjustment made by the view to the other elements. Animating them in a graceful way can improve visual feeling A LOT. Hence, when you provide a add and a remove transition, take in account also the addition of a addDisplaced and a removeDisplaced Transition.
Here follows a modified version of your code that shows all the points discussed above:
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
id: window
width: 600
height: 400
visible: true
ListModel {
id: model
ListElement { code: "0" }
ListElement { code: "1" }
ListElement { code: "2" }
ListElement { code: "3" }
ListElement { code: "4" }
ListElement { code: "5" }
}
GridView {
id: ueProductGridView
anchors.fill: parent
antialiasing: true
clip: true
cellWidth: 200
cellHeight: 200
delegate: Rectangle {
width: 192
height: 192
color: "red"
property alias angle: rot.angle // alias!
Text {
anchors.centerIn: parent
text: code
font.pixelSize: 30
}
transform: Rotation {
id: rot
angle: -90
axis { x: 0; y: 1; z: 0 }
origin.x: -200
origin.y: 0
}
MouseArea {
anchors.fill: parent
onClicked: ueProductGridView.model.remove(index)
}
} // delegate
add: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 500 } //since is already at 1.0 we should enforce the start from 0
NumberAnimation { property: "scale"; easing.type: Easing.OutBounce; from: 0; to: 1.0; duration: 750 }
}
addDisplaced: Transition {
NumberAnimation { properties: "x,y"; duration: 200; easing.type: Easing.InBack }
}
remove: Transition {
NumberAnimation { property: "scale"; from: 1.0; to: 0; duration: 200 }
}
removeDisplaced: Transition {
NumberAnimation { properties: "x,y"; duration: 200; easing.type: Easing.OutBack }
}
populate: Transition {
NumberAnimation { properties: "angle"; to: 0; duration: 500 }
}
}
Component.onCompleted:{ ueProductGridView.model= model }
}
I'm new to blackberry cascades, I have looked into some of animations from the blackberry cascades samples from github but I'm not sure how to implement a page flip in and out animation instead of the default push and pop animation. Below is code of the page that performs the default push transition to next page. I need to replace this transition with flip. How should I go about it?
NavigationPane {
id: nav
peekEnabled: false
Page {
id: mainPage
Button:
{
onClicked:{
nav.push(homePageDefinition.createObject());
}
}
attachedObjects: [
ComponentDefinition {
id: homePageDefinition
source: "homepage.qml"
}
]
}
}
Try Flipable item. For example:
Flipable {
id: flipable
anchors.fill: parent
property bool flipped: false
front: Rectangle {anchors.fill: parent; color: "green"}
back: Rectangle {anchors.fill: parent; color: "yellow" }
transform: Rotation {
id: rotation
origin.x: flipable.width/2
origin.y: flipable.height/2
axis.x: 0; axis.y: 1; axis.z: 0
angle: 0
}
states: State {
name: "back"
PropertyChanges { target: rotation; angle: 180 }
when: flipable.flipped
}
transitions: Transition {
NumberAnimation { target: rotation; property: "angle"; duration: 500 }
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
I'm trying to use PySide with QML on Nokia N9, and for some reason, my test app looks unlike the native N9 apps. For example, here I get a double status bar (they both react to tapping).
Here's the code for this:
main.py
import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtDeclarative import *
app = QApplication(sys.argv)
view = QDeclarativeView()
view.setResizeMode(QDeclarativeView.SizeViewToRootObject)
view.setSource('main.qml')
view.window().show()
app.exec_()
And the QML files:
main.qml
import QtQuick 1.1
import com.nokia.meego 1.1
PageStackWindow {
Component.onCompleted: {
var pageComponent = Qt.createComponent("PageX.qml")
pageStack.push(pageComponent)
}
}
PageX.qml
import QtQuick 1.1
import com.nokia.meego 1.1
Page {
id: pageOne
Text {
text: "Hello, this is page one"
}
}
The file main.qml creates a PageStackWindow, and I suspect it's the ...Window part that makes the phone render the status bar again, like it tries to add a status bar to each window created (and here, maybe we have a window inside a window?). Also, there's a space between the window and the toolbar. Can someone point to the right way of doing this? I just want to use normal Pages inside a PageStack.
You can try "showStatusBar : false".
I tryed your example on Qt Simulator, and it works without statusbar. I had a Meego - QML - PySide application, QML app worked without statusbar on Qt Simulator. But When I tryed it on android with Necessitas, I got same problem. After I use "showStatusBar : false" problem solved. Thank you, It is first time I ran my QML-Meego application on android after I saw your question :)
PageStackWindow {
showStatusBar : false
Component.onCompleted: {
var pageComponent = Qt.createComponent("PageX.qml")
pageStack.push(pageComponent)
}
}
Sorry I am new to QML. You can try a different QML Harmattan example and try again with python using this view.window().showFullScreen() My application includes ListView and is based on an example application by Nokia Harmattan developer documentation site. First this is main.py for android:
#!/usr/bin/env python
# A simple PySide example
import sys
import os
import traceback
# log to file on Android
LOG_FOLDER = '/sdcard/'
fSock = open(os.path.join(LOG_FOLDER, 'pyside_example_log.txt'), 'w', 1)
rfSock = open(os.path.join(LOG_FOLDER, 'pyside_example_error_log.txt'), 'w', 1)
sys.stdout = fSock
sys.stderr = rfSock
print("** stdout diverted to file **")
# for some reason, the PySide bindings can't find the libshiboken.so and libshiboken,
# even though they are in a directory in LD_LIBRARY_PATH, resulting in errors like this:
#
# ImportError: Cannot load library: link_image[1965]: 157 could not load needed library
# 'libshiboken.so' for 'QtCore.so' (load_library[1120]: Library 'libshiboken.so' not found)
#
# if both are loaded to memory manually with ctypes, everything works fine
print('manual libshiboken.so and libpyside.so loading')
from ctypes import *
#PROJECT_FOLDER = '/data/data/org.modrana.PySideExample'
# PYSIDE_APPLICATION_FOLDER is set in main.h in the Example project
PROJECT_FOLDER = os.environ['PYSIDE_APPLICATION_FOLDER']
LIB_DIR = os.path.join(PROJECT_FOLDER, 'files/python/lib')
SHIBOKEN_SO = os.path.join(LIB_DIR, 'libshiboken.so')
PYSIDE_SO = os.path.join(LIB_DIR, 'libpyside.so')
print("path to libshiboken and libpyside:")
print(SHIBOKEN_SO)
print(PYSIDE_SO)
shibok = CDLL(SHIBOKEN_SO)
psde = CDLL(PYSIDE_SO)
print("manual loading done")
print("importing PySide")
from PySide import QtCore, QtGui
from PySide.QtCore import QObject
from PySide.QtGui import *
from PySide.QtDeclarative import *
print("PySide import done")
#print(os.environ)
# enable running this program from absolute path
os.chdir(os.path.dirname(os.path.abspath(__file__)))
print("dir changed")
class PropertyExample(QObject):
"""
Python property provider
"""
def __init__(self):
QObject.__init__(self)
self.rootObject = None
#NOTE: the root object is needed only by Python properties
# that call QML code directly
"""#QtCore.Slot(result=str)
def getDate(self):
return str(datetime.datetime.now())"""
"""#QtCore.Slot(str)
def notify(self, text):
#NOTE: QML uses <br> instead of \n for linebreaks
self.rootObject.notify(text)
"""
class ImagesFromPython(QDeclarativeImageProvider):
"""
Image provider example
"""
def __init__(self):
# this image provider supports QImage,
# as specified by the ImageType
QDeclarativeImageProvider.__init__(self, QDeclarativeImageProvider.ImageType.Image)
def main():
app = QApplication(sys.argv) # create the application
view = QDeclarativeView() # create the declarative view
# add Python properties to the
# QML root context
rc = view.rootContext()
# add the example property
property = PropertyExample()
rc.setContextProperty("example", property)
# register image providers
# NOTE: the image provider name in the Image.source URL is automatically lower-cased !!
# NOTE2: view.engine().addImageProvider("from_python", ImagesFromPython())
# doesn't work for some reason
view.setResizeMode(QDeclarativeView.SizeRootObjectToView)
view.setSource("main.qml")
rootObject = view.rootObject()
property.rootObject = rootObject
#view.setWindowTitle(WINDOW_TITLE)
# view.setResizeMode(QDeclarativeView.SizeRootObjectToView)
#view.setResizeMode(QDeclarativeView.SizeViewToRootObject)
view.window().showFullScreen()
# view.resize(480,854)
#view.resize(854,480)
view.show()
app.exec_()
if __name__ == '__main__':
print("__main__")
fSock.flush()
try:
main()
except Exception:
fp = open(os.path.join(LOG_FOLDER, 'pyside_example_exception_log.txt'), 'w', 0)
traceback.print_exc(file=fp)
fp.flush()
fp.close()
traceback.print_exc(file=fSock)
fSock.flush()
rfSock.flush()
rfSock.close()
fSock.flush()
fSock.close()
exit(0)
And QML codes:
//main.qml
import QtQuick 1.1
import com.nokia.meego 1.1
PageStackWindow {
id: rootWindow
property int pageMargin: 16
// ListPage is shown when the application starts, it links to
// the component specific pages
initialPage: MainPage { }
// These tools are shared by most sub-pages by assigning the
// id to a tools property of a page
ToolBarLayout {
id: commonTools
visible: false
ToolIcon {
iconId: "toolbar-back";
onClicked: { myMenu.close(); pageStack.pop(); }
}
ToolIcon {
iconId: "toolbar-view-menu";
onClicked: (myMenu.status == DialogStatus.Closed) ? myMenu.open() : myMenu.close()
}
}
}
//MainPage.qml
import QtQuick 1.1
import com.nokia.meego 1.1
Page {
id: listPage
anchors.margins: rootWindow.pageMargin
function openFile(file) {
var component = Qt.createComponent(file)
if (component.status == Component.Ready)
pageStack.push(component);
else
console.log("Error loading component:", component.errorString());
}
ListModel {
id: pagesModel
ListElement {
page: "SimpleExamplesPage.qml"
title: "Simple examples"
subtitle: "Buttons, TextField, ToolBar and ViewMenu"
}
ListElement {
page: "DialogsPage.qml"
title: "Dialogs"
subtitle: "How to use different dialogs"
}
}
ListView {
id: listView
anchors.fill: parent
model: pagesModel
delegate: Item {
id: listItem
height: 88
width: parent.width
BorderImage {
id: background
anchors.fill: parent
// Fill page borders
anchors.leftMargin: -listPage.anchors.leftMargin
anchors.rightMargin: -listPage.anchors.rightMargin
visible: mouseArea.pressed
source: "image://theme/meegotouch-list-background-pressed-center"
}
Row {
anchors.fill: parent
Column {
anchors.verticalCenter: parent.verticalCenter
Label {
id: mainText
text: model.title
font.weight: Font.Bold
font.pixelSize: 26
}
Label {
id: subText
text: model.subtitle
font.weight: Font.Light
font.pixelSize: 22
color: "#cc6633"
visible: text != ""
}
}
}
Image {
source: "image://theme/icon-m-common-drilldown-arrow" + (theme.inverted ? "-inverse" : "")
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter
}
MouseArea {
id: mouseArea
anchors.fill: background
onClicked: {
listPage.openFile(page)
}
}
}
}
ScrollDecorator {
flickableItem: listView
}
}
//DialogsPage.qml
import QtQuick 1.1
import com.nokia.meego 1.1
Page {
id: root
tools: tabTools
anchors.margins: rootWindow.pageMargin
QueryDialog {
id: query
icon: "image://theme/icon-l-contacts"
titleText: "Query Dialog Example"
message: "Press accept or reject button"
acceptButtonText: "Accept"
rejectButtonText: "Reject"
onAccepted: labelQueryResult.text = "Result: Accepted";
onRejected: labelQueryResult.text = "Result: Rejected";
}
SelectionDialog {
id: singleSelectionDialog
titleText: "Single Selection Dialog Header"
selectedIndex: 1
model: ListModel {
ListElement { name: "ListElement #1" }
ListElement { name: "ListElement #2" }
ListElement { name: "ListElement #3" }
ListElement { name: "ListElement #4" }
ListElement { name: "ListElement #5" }
ListElement { name: "ListElement #6" }
ListElement { name: "ListElement #7" }
ListElement { name: "ListElement #8" }
ListElement { name: "ListElement #9" }
ListElement { name: "ListElement #10" }
}
}
// Create page and buttons
ScrollDecorator {
flickableItem: container
}
Flickable {
id: container
x: 0 // we need to set the width and height
y: 0
width: root.width
height: root.height
contentWidth: dialogs.width
contentHeight: dialogs.height
flickableDirection: Flickable.VerticalFlick
pressDelay: 100
Column {
id: dialogs
spacing: 24
Row {
spacing: 32
Button {
text: "Query"
width: 200
onClicked: {
query.open();
}
}
Label {
id: labelQueryResult
text: "Result: N/A"
}
}
Row {
spacing: 32
Button {
text: "SingleSelection"
width: 200
onClicked: {
singleSelectionDialog.open();
}
}
Grid {
rows: screen.orientation == Screen.Landscape || screen.orientation == Screen.LandscapeInverted ? 1 : 2
Rectangle {
width: 200
height: 30
color: "white"
Text {
y: 10
anchors.centerIn: parent
text: "Selected:"
font.pixelSize: 15
font.bold: true
}
}
Rectangle {
width: 200
height: 30
color: "lightgray"
Text {
anchors.centerIn: parent
text: singleSelectionDialog.model.get(singleSelectionDialog.selectedIndex).name
font.pixelSize: 15
font.bold: true
}
}
}
}
Row {
spacing: 32
Button {
text: "Color menu"
width: 200
onClicked: {
colorMenu.open();
}
}
Rectangle {
id : colorRect
width: 50; height: 50;
color : "black"
MouseArea {
anchors.fill: parent
onClicked: { colorMenu.open(); }
}
}
}
}
}
ToolBarLayout {
id: tabTools
ToolIcon { iconId: "toolbar-back"; onClicked: { colorMenu.close(); pageStack.pop(); } }
ToolIcon { iconId: "toolbar-view-menu" ; onClicked: colorMenu.open(); }
}
Menu {
id: colorMenu
visualParent: pageStack
MenuLayout {
MenuItem {text: "Red"; onClicked: { colorRect.color = "darkred" } }
MenuItem {text: "Green"; onClicked: { colorRect.color = "darkgreen" }}
MenuItem {text: "Blue"; onClicked: { colorRect.color = "darkblue" }}
MenuItem {text: "Yellow"; onClicked: { colorRect.color = "yellow" }}
}
}
}
I'm developing a progress bar in QML.
But the working is not working for all parameter values.
Its more bit of logical issue in UI
Requesting everyone to review it.
below is the code snippet
Rectangle
{
width: 500; height: 480
.....
color: "lightyellow"
Rectangle {
id: container
....
Row {
id:repeaterid
Repeater {
id: repeater
model: 100
Item { id: smallrect2; opacity:0.5; width:_width; height:_height
Rectangle { id: smallrect; color: _newColor }
......
}
}
}
Timer {
id: progressTimer
interval: 100
onTriggered: {
if ((slider.x + slider.width) < repeaterid.width)
{
slider.x += _width
var item = repeater.itemAt(indexCurrent)
item._newColor = "red"
indexCurrent++
}
else
{
progressTimer.repeat = false
}
}
}
Rectangle {
id: slider
// Adjust the dimensions of slider
x: repeaterid.x
y: repeaterid.y
width: _width
height: _height
}
}
}
The desired behaviour is achieved when value of _width is 30 and the model value in the repeater is 18, but as the value is decreased the slider in progress bar doesn't complete its path ( width = 5, model =100 ) or if increased it moves out of the path.
Solved : Adjusted model value according to container size & rectangle size
Is it possible to have a fade in/out animation when the source of an Image element is being changed ?
Do I need two Image elements ? Changing the opacity of one of the from 0 to 1 and another from 1 to 0 ?
To do this without much hassle. Run animations in this way:
Image {
id: toBeCreated
NumberAnimation on opacity {
id: createAnimation
from: 0
to: 1
duration: 2000
}
Component.onCompleted: createAnimation.start()
}
Image {
id: toBeDeleted
NumberAnimation on opacity {
id: destroyAnimation // start this animation from the function where you want to create new Images
to: 0
duration: 2000
onRunningChanged: {
if (!running) {
console.log("Destroying...")
toBeDeleted.destroy();
}
}
}
}
I Know its a little late but felt like sharing
Inspired by RajaRaviVarma ans i tried something like
A Qml for FadeInOut ImageView
import QtQuick 2.0
Item {
property string imageSource : ""
property string imageSourceTemp : ""
property real parentWidth: 0
property real parentHeight: 0
onImageSourceChanged:
{
destroyAnimation.start()
createAnimation.start()
}
Image {
id: toBeDeleted
source: imageSourceTemp
width: parentWidth
height: parentHeight
NumberAnimation on opacity {
id: destroyAnimation
to: 0.5
duration: 400
onRunningChanged: {
if (!running) {
}
}
}
}
Image {
id: toBeCreated
source: imageSource
width: parentWidth
height: parentHeight
NumberAnimation on opacity {
id: createAnimation
from: 0
to: 1
duration: 800
onRunningChanged: {
if (!running) {
imageSourceTemp = imageSource
}
}
}
}
}
And to use it Like
FadeinFadeOutImage {
id: song_image
imageSource: songImage
parentWidth: width
parentHeight: height
width: 406*scaleFactor
height: 406*scaleFactor
}