Qt Quick Demo - Calqlatr

A QML app designed for portrait devices that uses custom components, animated with AnimationController, and JavaScript for the application logic.

Calqlatr demonstrates various QML and Qt Quick features, such as displaying custom components and using animation to move the components around in the application view. The application logic is implemented in JavaScript and the appearance is implemented in QML.

Running the Example

To run the example from Qt Creator, open the Welcome mode and select the example from Examples. For more information, visit Building and Running an Example.

Displaying Custom Components

In the Calqlatr application, we use the following custom types that are each defined in a separate .qml file:

  • Button.qml
  • Display.qml
  • NumberPad.qml
  • StyleLabel.qml

To use the custom types, we add an import statement to the main QML file, calqlatr.qml that imports the folder called content where the types are located:

import "content"

We can then display custom components by adding the component types to any QML file. For example, we use the NumberPad type in calqlatr.qml to create the number pad of the calculator. We place the type inside an Item QML type, which is the base type for all visual items in Qt Quick:

    Item {
        id: pad
        width: 180
        NumberPad { y: 10; anchors.horizontalCenter: parent.horizontalCenter }
    }

Further, we use the Button type in the NumberPad type to create the calculator buttons. Button.qml specifies the basic properties for a button that we can modify for each button instance in NumberPad.qml. For the digit and separator buttons, we additionally specify the text property using the property alias text that we define in Button.qml.

For the operator buttons, we also specify another color (green) using the property alias color and set the operator property to true. We use the operator property in functions that perform the calculations.

We place the buttons inside a Grid QML type to position them in a grid:

Grid {
    columns: 3
    columnSpacing: 32
    rowSpacing: 16

    Button { text: "7" }
    Button { text: "8" }
    Button { text: "9" }
    Button { text: "4" }
    Button { text: "5" }
    Button { text: "6" }
    Button { text: "1" }
    Button { text: "2" }
    Button { text: "3" }
    Button { text: "0" }
    Button { text: "." }
    Button { text: " " }
    Button { text: "��"; color: "#6da43d"; operator: true }
    Button { text: "���"; color: "#6da43d"; operator: true }
    Button { text: "+"; color: "#6da43d"; operator: true }
    Button { text: "���"; color: "#6da43d"; operator: true }
    Button { text: "��"; color: "#6da43d"; operator: true }
    Button { text: "��"; color: "#6da43d"; operator: true }
    Button { text: "C"; color: "#6da43d"; operator: true }
    Button { text: " "; color: "#6da43d"; operator: true }
    Button { text: "="; color: "#6da43d"; operator: true }
}

Animating Components

We use the Display type to display calculations. In Display.qml, we use images to make the display component look like a slip of paper that contains a grip. Users can drag the grip to move the display from left to right.

When users release the grip, the AnimationController QML type that we define in the calqlatr.qml file finishes running the controlled animation in either a forwards or a backwards direction. To run the animation, we call either completeToEnd() or completeToBeginning(), depending on the direction. We do this in the MouseArea's onReleased signal handler, where controller is the id of our AnimationController:

            onPressed: startX = mapToItem(window, mouse.x).x
            onReleased: {
                if (rewind)
                    controller.completeToBeginning()
                else
                    controller.completeToEnd()
            }

Unlike other QML animation types, AnimationController is not driven by internal timers but by explicitly setting its progress property to a value between 0.0 and 1.0.

Inside the AnimationController, we run two NumberAnimation instances in parallel to move the number pad and the display components simultaneously to the opposite sides of the view. In addition, we run a SequentialAnimation instance to scale the number pad during the transition, giving the animation some depth.

    AnimationController {
        id: controller
        animation: ParallelAnimation {
            id: anim
            NumberAnimation { target: display; property: "x"; duration: 400; from: -16; to: window.width - display.width; easing.type: Easing.InOutQuad }
            NumberAnimation { target: pad; property: "x"; duration: 400; from: window.width - pad.width; to: 0; easing.type: Easing.InOutQuad }
            SequentialAnimation {
                NumberAnimation { target: pad; property: "scale"; duration: 200; from: 1; to: 0.97; easing.type: Easing.InOutQuad }
                NumberAnimation { target: pad; property: "scale"; duration: 200; from: 0.97; to: 1; easing.type: Easing.InOutQuad }
            }
        }
    }

We use the easing curve of the type Easing.InOutQuad to accelerate the motion until halfway and then decelerate it.

Performing Calculations

The calculator.js file contains definitions for the functions to execute when users press the digit and operator buttons. To use the functions, we import calculator.js in the calqlatr.qml file as CalcEngine:

import "content/calculator.js" as CalcEngine

We can then declare the functions to execute depending on whether the operator property for a button is set to true in NumberPad.qml:

    function operatorPressed(operator) { CalcEngine.operatorPressed(operator) }
    function digitPressed(digit) { CalcEngine.digitPressed(digit) }

When users press a digit or operator, the text from the digit appears on the display. When they press the equals operator (=), the appropriate calculation is performed, and the results appear on the display.

List of Files

Files:

See also QML Applications.