Introduction to QML ------------------- QML is a high level, scripted language. Its commands leverage the power and efficiency of the Qt libraries to make easy to use commands that perform intuitive functions. Draw a rectangle, display an image at a position and so on. Behind these commands are complex C++ libraries that efficiently perform the action. Always remember that this ability to easily build applications has a cost, it is not difficult to build a graphically complex QML application that over taxes the hardware and slows down the device. So, QML is fun but make sure the device can handle the graphical processing load in your application. The language also allows more flexibility of these commands by using Javascript rather than C++ to add new layers of logic to your application. Javascript is easier to learn than C++ and can be embedded into the QML scripts or imported from a separate file. The basic syntax of the commands is typically like Someobject { id: myObj ... some other things here ... } The 'id' is a unique identifier for the object, it must start with a lower case letter and only contain letters, numbers and underscores. It is this particular object's name. If this Someobject type was a Rectangle instead, it may be one of many, but with each one having a unique 'id' then they can all be specified individually however the id is optional. Each object is based on, or inherits from, a type of object called Item. Item has certain properties that may be useful in a particular context, and also has a set of functions that can be used. The properties have default values so you need only specify the ones you will need. Take a simple object such as a Rectangle. It has an 'id', we will call it 'myRectangle', it has a width and a height. Imagine that we want a rectangle that is 500 pixels by 400 pixels in the x and y directions (horizontal by vertical). We can implement this Rectangle with these properties this way import Qt 4.6 Rectangle { id: myRectangle width: 500 height: 400 } This is a valid QML script. To run it, save it to a file, say myexample.qml, and on the command line run the command qml myexample.qml It will create a very boring rectangle in its own window. Want to add some color or some text? Item has the property 'color' to produce a background color, so Rectangle will have it as well. Text is handled by a different type called Text. We need to create a Text object inside the Rectangle and set its text property to "Hello world". So to set the text to 'Hello world' and the background colour to light green Rectangle { id: myRectangle width: 500 height: 400 Text { text: "Hello world" } color: "lightgreen" } From now on we will not show the import statement but it should still be there when you create your QML scripts. To make our Hello World example a little nicer set the position of the text to be at pixel position x = 100, y = 100 within the displayed window. This position belongs to the Text object so we set the position inside its definition. Note that we separate different QML statements on the same line with a semi-colon, or we could have simply put each statement on a new line Text { text: "

Hello World

"; color: "darkgreen"; x: 100; y:100 } Not only did we reposition the text, but the text was altered by adding HTML tags to change the font size. The text color was also changed from the default black to dark green by using a standard string for the color's SVG name. We could also have used a hexadecimal string for the RGB (red-green-blue) values of the color similar to the method used in HTML. For example, mostly blue with a green tint, Text { text: "

Hello world again

"; color: "#002288"; x: 100; y: 100 } All of these changes occurred within the Text object which is the scope of these property changes. Other objects may use the information but it belongs to the object where the property has been defined. To add an image to our little application we use the Image type. An Image object uses a path to an image file, and has properties to control the aspect ratio, the image size, to tile the area amongst others. The source of the image, the path to the file, is a URL. Therefore the file can be local: mydir/myimage1.png. Or it can be remote: "http://www.example.com/images/myimage1.png". Image { source: "images/qt-logo.png" } This displays the image, as we would expect, at the top left of the window. The position of the default x = 0, y = 0 coordinate. Let us reposition the image and enlarge it. Place it at the same 'x' offset as the "Hello world again" text, but put it another 50 pixels below the text, also make it 150 by 150 pixels, Image { source: "images/qt-logo.png" x: 100; y: 150 width: 150; height: 150 } Anchors: Using absolute positioning like (x = 100 and y = 150) works well until the user or developer stretches or increases the size of the window. Then the positions need to be recalculated. What would be nice would be a relative means of positioning of objects in a window or rectangle. For example, we want to place an image at the bottom of a rectangle, we would like to specify the image's location as the 'bottom of the window', not a specific coordinate. We can do this with the anchors property, which objects inherit from Item. The anchors property is really a property group. It is a collection of related properties. It has properties within it which can be used by means of the dot notation. The dot notation uses object 'id's and property names to use a particular object or property. Say I have a rectangle r1, which contains a rectangle r2, which contains an Item item1, which has an 'x' property I want to change. I just use the dot notation to identify it: r1.r2.item1.x If we want to position an image at the bottom of the rectangle it is inside. I have to specify that the bottom of the image is also at the bottom of the rectangle import Qt 4.6 Rectangle { id: myWin width: 500 height: 400 Image { id: image1 source: "images/qt-logo.png" width: 150; height: 150 anchors.bottom: myWin.bottom } } This places the logo at the bottom left of the window. We would like it centered and not touching the bottom of the window, for aesthetic reasons. For the centering we use the horizontalCenter property, and to prevent the touching of the image to the bottom of the rectangle the bottomMarging is used. import Qt 4.6 Rectangle { id: myWin width: 500 height: 400 Image { id: image1 source: "images/qt-logo.png" width: 150; height: 150 anchors.bottom: myWin.bottom anchors.horizontalCenter: myWin.horizontalCenter anchors.bottomMargin: 10 } } Run this and resize the window. You will see that now the position of the image adjusts during the resize. You can also add another object say a block of descriptive text and place it above or below the image or to the side. This code places some text just above the image Text { text: "

The Qt Logo

" anchors.bottom: image1.top anchors.horizontalCenter: myWin.horizontalCenter anchors.bottomMargin: 15 } WARNING: 'anchors' is a property group, to be used within the object. When referencing these properties from another object we use the property directly, instead of saying: myRect.anchors.top (Wrong) we use myRect.top (Correct) Transformation: We can transform a graphical object to get additional effects. Rotate print by 180 degrees to display upside-down text. Rotate an image by 90 degrees to lay it on its side. These transformations require additonal information. For rotation, the additional information includes: the origin relative to the object being rotated, the axis of rotation, and the angle in degrees to rotate the image through in a clockwise direction. The axis does not have to be the z-axis, the line between your eyes and the image, it could be along the vertical y-axis or the horizontal x-axis. We have three dimensions to play with. For simplicity in this example we will rotate about the z-axis by 90 degrees in a negative direction, anti-clockwise. Grabbing the example above, import Qt 4.6 Rectangle { id: myWin width: 500 height: 400 Image { id: image1 source: "images/qt-logo.png" width: 150; height: 150 anchors.bottom: myWin.bottom anchors.horizontalCenter: myWin.horizontalCenter anchors.bottomMargin: 10 transform: Rotation { origin.x: 75; origin.y: 75; axis{ x: 0; y: 0; z:1 } angle: -90 } } Text { text: "

The Qt Logo -- taking it easy

" anchors.bottom: image1.top anchors.horizontalCenter: myWin.horizontalCenter anchors.bottomMargin: 15 } } The single line starting with 'transform' specifies that the transform property will be a Rotation through -90 degrees, which is anti-clockwise, about the z-axis running through the center of the image at (75,75). Remember the image is 150 x 150 pixels. Animations: Animation in QML is done by animating properties of objects. Properties that are numbers, colors, Rectangles, points and directions, or as they are described in the QML documentation: real, int, color, rect, point, size, and vector3d. To do this there are various ways of performing the animation. The problem for us is that there are so many ways to do animation, each with its own advantages. Here we will look at a few of them To illustrate some of the animation types we will develop a small animation. Firstly, we will need two images. The first image will be placed at the center of a window (Rectangle) and the second image will be at the upper left of the window. The animation will move the second image from the top left of the window to the bottom right. In doing so we will be animating the position and the size of the image. First create two images import Qt 4.6 Rectangle { id: mainRec width: 600 height: 400 z: 0 Image { id: image1 source: "images/qt-logo.png" x: 20; y: 20 ; z: 1 width: 100; height: 100 } Image { id: image2 source: "images/qt-logo.png" width: 100; height: 100 x: (mainRec.width - 100)/2; y: (mainRec.height - 100)/2 z: 2 } } We will add to 'image1' a SequentialAnimation from x = 20 to the target of x = 450. The 'from' values will be used because we will be repeating the animation, so the object needs to know where the original position is, both x and y. The SequentialAnimation of x will set it to repeat, and have a NumberAnimation to vary the numeric property between the x values and over a given duration. Then after the NumberAnimation there will be a PauseAnimation that will pause the animation for 500 milliseconds (half a second) simply for visual effect. SequentialAnimation on x { repeat: true NumberAnimation { from: 20; to: 450; easing.type: "InOutQuad"; duration: 2000 } PauseAnimation { duration: 500 } } A similar block of code is written for the animation of the 'y' value of the position. We will also animate the scale of the object, so as it goes from top left to bottom right of the window it will become smaller about midway, and then become larger. To complete the animation we will set the 'z' values of the images. 'z' is the stacking order, the z-axis effectively points out from the screen to your eyes with the default being 0. So if we set the Rectangle to zero, just to be sure, and image1 to 1 and image2 to 2 then image2 will be in the foreground and image1 in the background. When image1 passes image2 it will pass behind it. The completed code looks like Rectangle { id: mainRec width: 600 height: 400 z: 0 Image { id: image2 source: "images/qt-logo.png" width: 100; height: 100 x: (mainRec.width - 100)/2; y: (mainRec.height - 100)/2 z: 2 } Image { id: image1 source: "images/qt-logo.png" x: 20; y: 20 ; z: 1 width: 100; height: 100 SequentialAnimation on x { repeat: true NumberAnimation { from: 20; to: 450; easing.type: "InOutQuad"; duration: 2000 } PauseAnimation { duration: 500 } } SequentialAnimation on y { repeat: true NumberAnimation { from: 20; to: 250; easing.type: "InOutQuad"; duration: 2000 } PauseAnimation { duration: 500 } } SequentialAnimation on scale { repeat: true NumberAnimation { from: 1; to: 0.5; duration: 1000 } NumberAnimation { from: 0.5; to: 1; duration: 1000 } PauseAnimation { duration: 500 } } } } The 'easing.type' has many options, expressed as a string. It specifies the kind of equation that describes the acceleration of the property value, not necessarily position, over time. InOutQuad, means that at the start and the end of the animation the 'velocity' is low but the acceleration is high. Much like a car accelerating from stop, and decelerating to stop at the end of a journey, with the maximum speed being in the middle. Examine the easing documentation and the various graphs that show the effect. The horizontal axis, 'progress', can be thought of as time. The vertical axis is the value of the particular property. In discussing animation we need to describe three objects: State, MouseArea and Signals. Although independent of the animation types, animation delivers some of the best examples that illustrate these new objects. State A state is a change in the configuration of an object. When a state changes a transition occurs, this is an opportunity to make changes or take actions that depend on the movement to the new state. For example, if we had a scene in the country where the state variable has two states "daylight" and "night". Then when the state changes "night" at this transition the sky would be made dark, stars would be shown, the countryside would be darkened. And when the state changes to "daylight" the opposite changes would be made. Here is a simple QML program that shows the change use of state in the above example. We have two rectangles, the top one is the 'sky' and the bottom one is the 'ground'. We will animate the change from daylight to night. There will be two states, but we only need to define one since we will just go to night by clicking and holding the left mouse button down, releasing the mouse button will reverse the process Rectangle { id: mainRec width: 600 height: 400 color: "black" Rectangle { id: sky width: 600 height: 200 y: 0 color: "lightblue" } Rectangle { id: ground width: 600; height: 200 y: 200 color: "green" } MouseArea { id: mousearea anchors.fill: mainRec } states: State { name: "night" when: mousearea.pressed == true PropertyChanges { target: sky; color: "darkblue" } PropertyChanges { target: ground; color: "black" } } transitions: Transition { from: ""; to: "night" reversible: true ColorAnimation { duration: 700 } } } MouseArea defines a region that will respond to mouse clicks. In this case we are only concerned with when the mouse is pressed, not the particular key or other details. The area of the MouseArea is the entire main window, mainRec, so that clicking anywhere in this region will start the animation. Since we are using the 'pressed' mouse state, then the animation will only continue while the mouse button remains pressed. Only one state is defined in our example: "night". This will be the target state when the condition for this state, that the mouse is pressed, becomes true. Once the mouse is released then the 'reversible' property will come into play and the animation will reverse back to the initial values. When the state changes from the default daylight to "night", the transition associated with this is triggered. The transition sets the reversible flag to unwind the change after it completes, and specifies the duration in milliseconds of the ColorAnimation. The PropertyChanges command is the way that we nominate which properties will change in a change of state, and what new value the property will take. Since we want the 'sky' region to turn to dark blue and the 'ground' regioni to turn to black then the rectangles for those regions are the 'target' and the property in the target is 'color'. Animation Summary ----------------- PropertyAnimation, a property value on a target object is varied to a specified value over a given time. NumberAnimation, animate a numeric property from one value to another over a given time. PauseAnimation results in the task waiting for the specified duration, in milliseconds. SequentialAnimation allows us to list in order the animation events we want to occur, first A then B then C and so on. ParallelAnimation, enables us to run different animations at the same time instead of sequential.