States and transitions in Qt Quick

Traditional GUI programming often involves easy-to-write but boilerplate state machines for controls to track control and application states. For example, a button might have several states: when the mouse hovers over it, when it's pressed, and then once pressed, a separate state for on or off in the case of a checkbox or a push button. While this code isn't hard to write, it does involve some writing, and more sophisticated interfaces require more of it.

Qt Quick provides an abstraction for this through its State construct, which groups a state's name as well as the condition under which it occurs, and which properties of an object should take on new values. We first saw this in Chapter 3, Designing Your Application with Qt Designer, when we wrote our own button component, reprinted here:

import QtQuick 2.3

Rectangle {
  id: button

  width: 64
  height: 64

  property alias operation: buttonText.text
  signal clicked

  color: "green"

  Rectangle {
    id: shade
    anchors.fill: button;
    color: "black"; opacity: 0
  }

  Text {
    id: buttonText
    anchors.centerIn: parent;
    color: "white"
    font.pointSize: 16
  }

  MouseArea {
    id: mouseArea
    anchors.fill: parent
    onClicked: {
    button.clicked();
    }
  }

  states: State {
    name: "pressed"; when: mouseArea.pressed == true
    PropertyChanges { target: shade; opacity: .4 }
  }
}

Here, the button has two states: the default state in which the shade is fully transparent, and when the user is pressing the button—represented by the pressed state—setting the shade's opacity property to 0.4. The states field of an item can actually be an array; another way to write these states is to declare both explicitly, shown as follows:

  states: [
    State {
      name: "pressed"; when: mouseArea.pressed == true
      PropertyChanges { target: shade; opacity: .4 }
    },
    State {
      name: "released"; when: mouseArea.pressed != true
      PropertyChanges { target: shade; opacity: 0.0 }
    }
  ]

Each State construct can have more than one PropertyChanges element, each with its own target and property, so you can create sophisticated state transitions using simple declarations.

Qt Quick uses transitions to control the animation of a property when a state change occurs. To create a transition, define an animation; you can apply an animation on any property that can be changed, specifying the start and end values of the animation. Consider a simple animation on a mouse press such as the following:

import QtQuick 2.3
import QtQuick.Window 2.2

Window
{
  visible: true
  height: 360
  width: 360

  Rectangle {
    id: rect1
    width: 100; height: 100
    color: "red"
  
    MouseArea {
      id: mouseArea
      anchors.fill: parent
    }

    states: State {
      name: "moved"; when: mouseArea.pressed
      PropertyChanges { target: rect1; x: 100; y: 100 }
    }

    transitions: Transition {
      NumberAnimation { 
        properties: "x,y"
        easing.type: Easing.InOutQuad 
      }
    }
  }
}

Here, the rectangle starts at the origin of the parent coordinate system, with MouseArea in the rectangle. When you press the mouse in the rectangle, the state of the rectangle changes to moved, and the rectangle's top-left corner moves to (100,100) on the parent's canvas. However, we also specify an animation, NumberAnimation, on the x and y properties, so the property change is animated to this value, with the easing curve we specify (a quadratic curve at both the start and finish of the animation).

  • There are different animations for different kinds of properties. You can use NumberAnimation to animate numeric properties, as you see here
  • There's also ColorAnimation, which animates colors, and RotationAnimation, which animates rotations

Here's the QML to rotate a red rectangle in a half-circle when it's clicked on:

import QtQuick 2.3
import QtQuick.Window 2.2

Window
{
  visible: true
  height: 360
  width: 360

  Item {
    width: 300; height: 300

    Rectangle {
      id: rect
      width: 150; height: 100; 
      anchors.centerIn: parent
      color: "red"
      antialiasing: true

      states: State {
        name: "rotated"
        PropertyChanges { target: rect; rotation: 180 }
      }

      transitions: Transition {
        RotationAnimation { 
          duration: 1000
          direction: RotationAnimation.Counterclockwise
        }
      }
    }

    MouseArea { 
      anchors.fill: parent; 
      onClicked: rect.state = "rotated" 
    }
  }
}

Again, the pattern is the same: we define a state with PropertyChanges, indicating the new property value, and then specify a transition over this property using an animation (in this case, RotationAnimation). Here, the transition has a duration specified in milliseconds. Note that the rotation only occurs once, and the rotation angles are bound by the values -360 and 360 degrees.

Note

For more information on the animation and transition framework in Qt Quick, see the Qt Quick documentation at http://qt-project.org/doc/qt-5/qtquick-statesanimations-animations.html and the Qt Quick Animation samples at http://qt-project.org/doc/qt-5/qtquick-animation-example.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset