I want to move a qml Item out of the left side of the app window.
While this task works perfectly for the right side of the window by defining a state like this
states: State {
name: "hidden"
when: is_hidden == true
AnchorChanges {
target: right_item_to_move
anchors.right: undefined
}
PropertyChanges {
target: right_item_to_move
x: main_window.width
}
}
and defining the appropriate Transition, I can't get it to work on the left side of the main window because negative x coordinates are not allowed.
I.e. this does not work:
states: State {
name: "hidden"
when: is_hidden == true
AnchorChanges {
target: left_item_to_move
anchors.left: undefined
}
PropertyChanges {
target: left_item_to_move
x: -left_item_to_move.width
}
}
How can I achieve this task? I'm using Qt 5.8 and QtQuick 2.0.
In my opinion, one should strive to stay true to one way of positioning, so you should either use anchors or x/y-coordinates.
Here you can find an overview how to make the right choice.
In short: When in doubt, use anchors. When the positioning is only relative to the parent (static) use x and y and if not possible otherwise do so even when not relative to the parent.
As you have chosen anchors, in my opinion you should stick to that - meaning: change the anchoring, so that instead of the left anchor line of the object, the right anchor line will be anchored to the window's left.
This would look like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Rectangle {
anchors.centerIn: parent
width: 300
height: 600
color: 'green'
Button {
id: but
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
state = (state === 'left' ? '' : 'left')
}
states: [
State {
name: 'left'
AnchorChanges {
target: but
anchors.left: undefined
anchors.right: parent.left
}
}
]
transitions: [
Transition {
AnchorAnimation {
duration: 200
}
}
]
}
}
}
An example, how it might look, if you choose to modify the x value, it might look like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Rectangle {
anchors.centerIn: parent
width: 300
height: 600
color: 'green'
Button {
id: but
property bool shown: true
anchors {
verticalCenter: parent.verticalCenter
}
onClicked: {
shown = !shown
}
x: (shown ? 0 : -width)
Behavior on x {
XAnimator {
duration: 200
}
}
}
}
}
Related
I am using a template to create an app using QT Creator and QML and am hoping to create a landing page that allows users to select which "page" they want to navigate to by clicking an icon.
I've figured out how to get a button on the landing page and have it open another page. However, I am using x and y positions of the button and it doesn't scale correctly when the window size changes.
Ultimately, I am trying to put 6 buttons on the landing page in a way that scales correctly.
I have attached an image of my ideal Landing Page design and have also attached code for what I already have.
I hope I was able to explain this well enough. Please let me know if I can clarify anything.
import QtQuick 2.2
import QtQuick 2.6
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.4
import "components" as Components
//BACKGROUND COLOR
Rectangle {
signal signInClicked(string tourId)
color: "#242424"
AnimatedImage {
anchors.fill: parent
source: app.landingpageBackground
fillMode: Image.PreserveAspectCrop
visible: source > ""
}
Rectangle {
anchors.fill: parent
gradient: Gradient {
GradientStop { position: 0.0; color: "#00000000";}
GradientStop { position: 1.0; color: "#00000000";}
}
}
//TITLE TEXT
Text {
id: titleText
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: app.height/10
}
font.family: app.customTitleFont.name
text: app.info.title
font {
pointSize: 60
pointSize: app.titleFontSize * 1.4
}
color: "#00000000"
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
}
Button {
id: signInButton
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 60 * app.scaleFactor
}
opacity: 0.0
style: ButtonStyle {
id: btnStyle
property real width: parent.width
label: Text {
id: lbl
text: signInButton.text
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
width: parent.width
maximumLineCount: 2
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: app.titleColor
font.family: app.customTextFont.name
font.pointSize: app.baseFontSize
}
background: Rectangle {
color: Qt.darker(app.headerBackgroundColor, 1.2)
border.color: app.titleColor
radius: app.scaleFactor * 2
}
}
height: implicitHeight < app.units(56) ? app.units(56) : undefined // set minHeight = 64, otherwise let it scale by content height which is the default behavior
width: Math.min(0.5 * parent.width, app.units(250))
text: qsTr("Let's Play!")
MouseArea{
anchors.fill: parent
onClicked: {
signInClicked("");
}
}
NumberAnimation{
id: signInButtonAnimation
target: signInButton
running: false
properties: "opacity"
from: 0.0
to: 1.0
easing.type: Easing.InQuad
duration: 1000
}
}
AboutPage {
id: aboutPage
}
NewsAndUpdates {
id: newsPage
}
ProgramsPage {
id: programsPage
}
Connections {
target: app
onUrlParametersChanged: {
if (app.urlParameters.hasOwnProperty("appid")) {
signInClicked(app.urlParameters.appid)
}
}
}
Component.onCompleted: {
signInButtonAnimation.start()
}
}
I think this doc should help you. You haven't really defined what you want very well, but I'll show you some examples so you can hopefully take from it what you need.
QML has the concept of "positioners" and "layouts". Positioners help automatically position your objects neatly on the screen. And Layouts try to do that too, but can also stretch your objects to fill available space.
Row:
You can arrange all your buttons in a row and center the row horizontally.
Row {
anchors.horizontalCenter: parent.horizontalCenter
Button { id: btn1 }
Button { id: btn2 }
...
}
Grid:
Similarly, Grid is a positioner that arranges objects into a grid:
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 3
Button { id: btn1 }
Button { id: btn2 }
...
}
GridLayout:
A GridLayout is just like a Grid, but it can also resize the objects to fill up the space. My opinion is layouts can do more, but they're often trickier to use. In this example, the first button should be a fixed size, while the second button should fill up the remaining width.
GridLayout {
anchors.fill: parent
rows: 2
Button { id: btn1; Layout.preferredWidth: 200 }
Button { id: btn2; Layout.fillWidth: true}
...
}
In my QML application I'm trying to create a grid of items that can be flipped at the press of a button. The backside of such an item should then fill a major part of the screen until it is flipped back.
Let's say I start off with the following view of my application
When I press the question mark button of the item in the center then the item is flipped and moved slightly. What I would expect to see after this is the following
The blue box is the backside of my item and it covers most of the screen. Pressing the 'X'-Button on the top right would again flip the item back.
However what I actually see after flipping the first time is the following
You can see that parts of the items in my grid are covered by my flipped item and parts are not.
The code I'm using is as follows
import QtQuick 2.9
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
import QtQuick.Window 2.2
Window {
id: main
width: 640
height: 480
visible: true
title: qsTr("Hello World")
function absolutePos(item) {
var my_x = item.x
var my_y = item.y
if (item.parent !== null) {
var parent_pos = absolutePos(item.parent)
my_x += parent_pos.x
my_y += parent_pos.y
}
return {x: my_x, y: my_y}
}
GridLayout {
columns: 5; rows: 3
Repeater {
model: 15
delegate: Item {
width: main.width / 5 - 10
height: main.height / 3 - 10
Flipable {
id: flipable
anchors.fill: parent
property bool flipped: false
front: Rectangle {
anchors.fill: parent
border.color: "black"
border.width: 2
}
back: Rectangle {
id: backSide
width: 580; height: 400
property var absolute_pos: absolutePos(this)
border.color: "blue"
border.width: 2
Button {
anchors.top: parent.top
anchors.right: parent.right
text: "X"
width: 30; height: 30
onClicked: {
flipable.flipped = !flipable.flipped
}
}
}
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
},
Translate {
id: translation
x: 0; y: 0
}
]
states: State {
name: "back"
PropertyChanges {
target: rotation
angle: 180
}
PropertyChanges {
target: translation
x: 490 - backSide.absolute_pos.x
}
PropertyChanges {
target: translation
y: 40 - backSide.absolute_pos.y
}
when: flipable.flipped
}
transitions: Transition {
ParallelAnimation {
NumberAnimation {
target: rotation
property: "angle"; duration: 300
}
NumberAnimation {
target: translation
property: "x"; duration: 300
}
NumberAnimation {
target: translation
property: "y"; duration: 300
}
}
}
}
Button {
anchors.top: parent.top
anchors.right: parent.right
text: "?"
width: 30; height: 30
onClicked: {
flipable.flipped = !flipable.flipped
}
}
}
}
}
}
I was already trying to achieve the effect by manually setting the parent of my Flipable to Window.contentItem so that it will always be above any other items. However this also doesn't fix the problem since the item will still cover the siblings following the current item.
Also I'm still hoping, there is a solution which doesn't require manipulating the z-order of my items in some arcane way.
I am not sure what you mean by "some arcane way", but changing the z property of your delegate is perfectly fine:
delegate: Item {
z: flipable.flipped ? 1 : 0
// ...
}
You will also probably want to hide the "?" button when flipped:
visible: !flipable.flipped
lets just say theoreticaly, that I have
Rectangle {
id: testRect
width: 100
}
and once i start the timer with interval tick 50ms, it should just extend the width of Rect:
Timer {
id: testTimer
interval: 50
onTriggered: testRect.width += 50
}
which works fine, but even when its onlz 50ms, its still seems to be quite not smooth transition.
Any idea how to smoothen the width change?
Please note this is only for learning purposes, what I will learn here will use in different situations, therefore please dont ask what is the puspose of the code...
Thank you!
You should rely on the animation features available in QtQuick to animate property changes.
In your case, you can define different states, with transitions between states where you define how an item should behave when going from one state to another. (See relevant documentation about states)
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Rectangle {
id: rect
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 100
height: 200
color: "red"
state: "default"
states: [
State {
name: "default"
PropertyChanges {
target: rect
width: 200
}
},
State {
name: "bigger"
PropertyChanges {
target: rect
width: 250
}
}
]
transitions: Transition {
NumberAnimation {
duration: 500 //ms
target: rect
properties: "width"
}
}
// Just there to trigger the state change by clicking on the Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.state === "default")
rect.state = "bigger"
else
rect.state = "default"
}
}
}
}
Or you can define a behavior, which is more simple to define when you only act on a single property:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
Rectangle {
id: rect
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 100
height: 200
width: 200
color: "red"
Behavior on width {
NumberAnimation {
duration: 500 //ms
}
}
// Just there to trigger the width change by clicking on the Rectangle
MouseArea {
anchors.fill: parent
onClicked: {
if (rect.width === 200)
rect.width = 250
else
rect.width = 200
}
}
}
}
Finally, if you really want a smooth animation, you can use SmoothedAnimation instead of NumberAnimation (which is a linear animation by default)
Is there possible to use choreography animations in qml (in a REUSABLE manner)?
for example in StackView transitions from page1 to page2.
I tried following code, but ParentChange does not work as expected. This code just changes red rectangle's position.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
StackView {
id: stack
anchors.fill: parent
initialItem: Page {
id: page1
Label {
text: qsTr("First page")
anchors.centerIn: parent
}
Rectangle {
id: rect
width: 50
height: 50
x: 600
y: 100
color: "red"
states: [
State {
when: stack.depth > 1
ParentChange { target: rect; parent: stack.currentItem; x: 100; y: 100; }
}
]
transitions: Transition {
ParentAnimation {
NumberAnimation { properties: "x,y"; duration: 1000 }
}
}
}
MouseArea {
anchors.fill: parent
onClicked: stack.push(page2)
}
}
Component {
id: page2
Page {
background: Rectangle { color: "yellow"; anchors.fill: parent }
Label {
text: qsTr("Second page")
anchors.centerIn: parent
}
}
}
}
Rectangle {
id: back
color: "blue"
width: 50
height: 50
radius: 25
MouseArea {
anchors.fill: parent
onClicked: stack.pop()
}
}
}
but the usage is not limited to StackView. It can be used in many other situations. (just like above link)
import QtQuick 1.0
Rectangle
{
width: 100; height: 100
color: "red"
MouseArea
{
anchors.fill: parent
onPressed:
{
NumberAnimation
{
target: parent.x
to: 50;
duration: 1000
}
}
}
}
I expect this code to shift the x position of the rectangle on the button press event, but this does nothing.
Where am I going wrong?
You are defining an NumberAnimation in a signal handler, it's not going to work properly. Furthermore, NumberAnimation target should be an item, and here you are targeting a property of an item. Here is your code corrected :
import QtQuick 1.0
Rectangle
{
id: rect
width: 100; height: 100
color: "red"
MouseArea
{
anchors.fill: parent
onPressed:
{
animation.start()
}
NumberAnimation
{
id: animation
target: rect
property: "x"
to: 50;
duration: 1000
}
}
}
If your rectangle animation should revert when mouse is released, you would like to take benefit of a proper state definition, and animate property "x" at each state change (between default and "pressed" states. Here is a self contained example :
import QtQuick 1.0
Rectangle {
id: root
width: 360
height: 200
Rectangle
{
id: rect
width: 100; height: 100
color: "red"
MouseArea
{
id: mouse
anchors.fill: parent
}
states: [
State {
name: "pressed"
when: mouse.pressed
PropertyChanges {
target: rect
x: 50
}
}
]
Behavior on x {
NumberAnimation { duration: 1000 }
}
}
}
If you need more complex animation, define a proper Transition. A simple Behavior, here, is more readable I find.
Try this code:
import QtQuick 1.0
Rectangle
{
width: 100; height: 100
color: "red"
MouseArea {
anchors.fill: parent
onPressed: {
animation.start();
}
}
NumberAnimation on x {
id: animation
running: false
to: 50
duration: 1000
}
}
From docs:
NumberAnimation is a specialized PropertyAnimation that defines an animation to be applied when a numerical value changes.
So, you want not to animate on click, but you want to assign animation to x property of rectangle and start it on click.