Chapter 6. Introducing Qt Quick

Today, there are a lot of tools available for user interface development. In the past two chapters, you've seen a pretty typical approach taken by a platform vendor: provide robust APIs in a commonly known programming language (Qt with C++) to enable developers to create their products. This approach is not without its drawbacks. The cost of learning an entire new API set can be high for some, and even with an API as all-encompassing as Qt, there's still a lot of rote programming (think new and delete) you must do as you develop your application. Surely there's a better way.

To further streamline your development efforts—especially for new applications—Nokia provides Qt Quick, a declarative programming environment consisting of Qt Meta-object Language (QML), common components, and bindings to JavaScript and C++. In this chapter we show you what Qt Quick is, how to use QML, and how to connect QML applications to existing or new C++ and JavaScript. To give you hands-on examples along the way, we take the Shake applications in two directions: first an entirely QML-based implementation to show you how easy it is to write user interfaces using QML, and one that uses a QML interface with the C++ worker thread, XML parsing, and model to show you how to connect C++ code to QML. When you're through with this chapter, you'll be in position to create your own Qt Quick prototypes and full-fledged applications.

Declaring Your User Interface

Qt Quick takes a radically different approach to user interface development than you've seen previously in C++ with Qt. More like HTML than C++, Qt Quick uses QML, a JavaScript-like language to define your user interface. QML is a declarative language—instead of writing imperative statements that do things, you write declarations of your user interface objects. While at the top level both environments are inherently object-oriented, how you work at the level of individual statements is very different. In C++ with Qt, we might draw a new rectangle using pseudocode like this:

QRect rect(0, 0, 32, 32);
QPainter painter;
painter.setBrush(QBrush(Qt::red));
painter.drawRect(rect);

In QML, we'd simply write:

import QtQuick 1.0
Rectangle {
    height:200
    width: 200
    color: "red"
}

The QML example consists of a single object, of type Rectangle. This specific rectangle overrides three of Rectangle's default properties: height, width, and color. The height and width properties are each set to the integer value 32, and color is set to the string "red." Under the hood, the Qt Declarative module includes both a parser for QML and a renderer that renders QML to the screen.

QML can contain scripts, too—here's a button that changes its label to "Hello World" when it's clicked:

import QtQuick 1.0
Item {
    width: 200
    height: 100

    Text {
        id: label
        text: "Click Me"
        color: "black"
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        font { family: "Helvetica"; pixelSize: 12; bold: true }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            label.text = "Hello World"
        }
    }
}

Here, we have a QML Item, the base element for all visible items in QML. It contains a Text object and a MouseArea that spans the entire size of the Item. We control the layout of the Text object and MouseArea using the anchors property, which indicates first that the text should be centered horizontally and vertically, and second that the MouseArea should fill its parent. The Text object is simply a label, with the initial text "Click Me" in black Helvetica bold font. The MouseArea contains a single bit of script that sets the object whose id is label—the Text item—to the string "Hello World"

You've already seen two examples of QML; now let's examine the nuts-and-bolts of the language.

Introducing QML

As you've seen, QML is declarative. Instead of saying how, you simply say what. With syntax based on JavaScript, QML gives you a concise syntax to specify a tree of objects with properties. Properties may be references to other objects, strings, or numbers, making it easy to edit QML using your favorite text editor—or by using Qt Creator's excellent support for the language. Let's look at the first Rectangle example again:

import QtQuick 1.0
Rectangle {
    height:200
    width: 200
    color: "red"
}

The first line simply instructs the QML interpreter to include the definitions provided by QtQuick 1.0; you can provide your own QML files to import as well, or import JavaScript to provide better separation between your user interface and business logic.

This QML defines a single object, a Rectangle. All QML objects are specified first by their type and then the properties of the object as name-value pairs separated by a single colon. Type names are capitalized, just like class names in C++. Here, we've written the properties one at a time, but we can put them on the same line and separate them with a semicolon, like this:

import QtQuick 1.0
Rectangle { height:200; width: 200; color: "red" }

Which you use is mostly a matter of personal preference for readability; we find that closely related properties requiring little explanation—say, the dimensions of an object—can be snuggled together on a single line. More important definitions, or those that require additional thought, should probably be placed on their own line and include a comment, like this:

import QtQuick 1.0
Rectangle {
    height:200; width: 200
    // Required by Sandy's UX documentation as of 5 November.
    color: "red"
}

Comments are written with standard C++ and JavaScript syntax, using either /* and */ for a block comment, or // to indicate that everything that follows until the beginning of the next line is a comment.

Values assigned to properties can be computed, too, using JavaScript syntax. For example, to create a rectangle whose width is twice its height, I might write:

import QtQuick 1.0
Rectangle {
    id: myRectangle
    height: 200
    width: myRectangle.height * 2
    color: "red"
}

Here, the expression includes a reference to the rectangle itself, now named using its id property. You can refer to other objects by their ID, too. The namespace is a tree identical to the objects you define, and a path to a specific object is simply the IDs of the objects along the path separated by periods. Of course, when referencing another property of the same object, you could just write width: height * 2.

A powerful feature of QML is that when you refer to another object in an expression like this, QML creates a binding: if the value of the referent changes, the QML runtime automatically recomputes the expression, updating the visual appearance if necessary. If later in our QML expression we change the value of myRectangle.height to 64, the Rectangle object will automatically change its width to 128 and re-draw, changing the appearance of the UI.

Properties are strongly typed; a property of one type may not be assigned a value of a different type. The QML type system includes the following basic types:

  • The action type, which has the properties of a QAction (see Chapter 5) instance.

  • The bool type, which may be true or false.

  • The color type, which is a standard color name in quotes.

  • A date, in the format YYYY-MM-DD.

  • An enumeration, which can be any one of a set of named values.

  • A font, which encapsulates the properties of a QFont.

  • An int, representing an integer.

  • A list, consisting of a list of objects.

  • A point, with attributes for its x and y coordinates.

  • A real, representing a real number.

  • A rect, bearing attributes for its x, y, width, and height attributes.

  • A size, with attributes for width and height.

  • A string, which is a free-form collection of characters between quotes.

  • A time, specified as HH:MM:SS.

  • A url, which is a string that corresponds to the standard Uniform Resource Locator syntax.

  • A vector3d, consisting of x, y, and z attributes.

You can introduce properties to your own object using the property declaration with a type, like this:

import QtQuick 1.0
Rectangle {
    id: window
    property bool loading: feedModel.status == XmlListModel.Loading
   ...
}

Here, we define the new property loading, whose value is dynamically computed based on the status property of another object.

Finally, it's worth noting that QML supports lists, collections of objects indicated between square brackets, like this:

Item {
    transitions: [
        Transition {...},
        Transition {...}
    ]
}

Here, the transitions property consists of two Transition objects, each with their own (here elided) properties.

Handling Signals in QML

Many Qt objects emit signals, and QML objects are no exception. You've already encountered one, MouseArea's pressed signal:

MouseArea {
    onPressed: {
        label.text = "Hello World"
    }
}

All signal handlers begin with on, and the remainder of a signal handler's name is the name of the signal; hence, onPressed is the signal handler for the MouseArea's pressed signal.

Some signals include an optional parameter, which is given a name and accessed as a variable in the script for the handler. For example, the onPressed signal handler has a mouse parameter, which you're free to use to determine characteristics of the mouse press, like this:

MouseArea {
     acceptedButtons: Qt.LeftButton | Qt.RightButton
     onPressed: if (mouse.button == Qt.RightButton)
             console.log("Right mouse button pressed")
        else if (mouse.button == Qt.LeftButton)
            console.log("Left mouse button pressed");
 }

In this script you also see the console object used; like the JavaScript console object, you can use its log method to log strings to the console for print-style debugging.

Tip

Nokia is working on a mixed-mode debugger for QML and C++ that will let you place breakpoints and inspect properties in both QML and C++. Until it's available, console logging is your best bet for debugging. Console log output appears on the application's standard output, so you can view it in the Application Output pane of Qt Creator or on the command line where you commenced execution of your application.

Speaking of JavaScript, you can import JavaScript into your QML, too. For example, in writing a game, we might want to encapsulate all of our game logic in a single file gamelogic.js. To include this file in our QML, we'd simply use an import directive at the top of the file:

import "gamelogic.js" as Gamelogic

This creates a top-level object named Gamelogic that has properties and methods for each of the fields and functions defined in the file. For example, if our gamelogic.js file defines a method startGame, we might create a start button in QML that begins the game with a declaration such as:

import QtQuick 1.0
import "gamelogic.js" as Gamelogic
Item {
    Id: start
    width: 60
    height: 32

    Text {
        id: startLabel
        text: "Start"
        color: "black"
        font { family: "Helvetica"; pixelSize: 12; bold: true }
    }

    MouseArea {
        onClicked: {
            Gamelogic.startGame()
        }
    }
}

You can also do the reverse. You can access properties of any QML object in JavaScript by referencing it as an object by its ID. For example, localization code written in JavaScript to set the text of the start button in gamelogic.js might read:

function localizeToEo() {
    ...
    startLabel.text = "Starti " // "Start" in Esperanto
    ...
}

Performing Animations in QML

With everything being declarations, you might wonder how dynamic behavior like animation gets represented in QML. While you can implement dynamic behavior in scripts, you can also provide animations across properties using the animation-on-property syntax, like this:

import QtQuick 1.0
Rectangle {
     width: 64; height: 64
     color: "blue"

     PropertyAnimation on x { from: 0; to: 64; duration: 1000;
         loops: Animation.Infinite }
     PropertyAnimation on y { from: 0; to: 64; duration: 1000;
         loops: Animation.Infinite }
 }

This creates a blue rectangle that moves from the origin of the canvas to the position (64, 64), over a second.

There are other animation types that follow the same idea, transitioning from one value to another. For example, ColorAnimation animates changes in color values using QML's color type over Qt RGBA values, while RotationAnimation animates on the rotation of an object around its origin in degrees.

Sometimes you want to link a default animation to when a property changes; for example, you may want the rectangle to follow the mouse and animate to where the mouse is clicked. You can do this by adding Behavior elements and adding a MouseArea, like this:

import QtQuick 1.0
Item {
    width: 400; height: 400

    Rectangle {
        id: rect
        width: 64; height: 64
        color: "blue"

        Behavior on x { PropertyAnimation { duration: 500 } }
        Behavior on y { PropertyAnimation { duration: 500 } }
    }
    MouseArea {
        anchors.fill: parent
        onClicked: { rect.x = mouse.x; rect.y = mouse.y }
    }
}

Here, the Behavior declarations indicate that when x or y changes, the value should be animated over 500 milliseconds. We'll have more to say about anchors later, in the section "Creating the User Interface," later in this chapter.

Animations can be eased, that is, varied over time, according to one of various mathematical curves specified by the easing's type. For example, an animation may accelerate from its start, reach a maximum speed, then slow down to finally stop at its destination. The Easing property of animations has a number of attributes that control how the value should be varied. Its attributes include:

  • type, indicating the mathematical function that the values follow as the animation is computed.

  • amplitude, indicating a relative scale for the easing.

  • overshoot, indicating how far past the final bound the animation should occur before returning to the final bound.

  • period, indicating the degree of repetition between the overshoot value and the final value for some easing curves.

Qt defines a large number of easing curves, including linear, quadratic, cubic, and sinusoidal curves. We might want to add a bit of bounce to our rectangle animation by changing the PropertyAnimations like this:

Behavior on x {
    PropertyAnimation {
        duration: 500
        easing.type: Easing.InOutElastic
        easing.amplitude: 2.0
        easing.period: 1.5
    }
}
Behavior on y {
    PropertyAnimation {
        duration: 500
        easing.type: Easing.InOutElastic
        easing.amplitude: 2.0
        easing.period: 1.5
    }
}

Reviewing the Available Qt Quick Elements

Qt Quick elements can be broadly divided into two classes: things that are visible and things that aren't. Visible elements inherit from Item, and include the following:

  • BorderImage, an image broken into nine tiles and can be used, for example, to create a resizable button that selectively scales only the middle area to retain an undistorted border.

  • Image, an element that displays an image from a specific source.

  • ListView, which provides a list of items provided by a model.

  • Loader, a region that loads its QML from its source attribute (specified as a URL).

  • Repeater, which lets you repeat an item-based component using content from a model.

  • Text, a region that displays formatted text.

  • TextEdit and TextInput, regions that permit the entry of multiple or single lines of text, respectively.

  • WebView, which allows you to add web content to a Qt Quick view.

Each of these items can be created just as you saw us create Rectangle objects in the previous sections. Several items can coexist in a single layout; the QML for a user interface for a web browser in QML with a URL bar might look something like this:

import QtQuick 1.0

Rectangle {
    id: window
    width: 800
    height: 480
    TextInput {
        id: url
        anchors.left: window.left
        anchors.right: go.right
        anchors.top: window.top
        text: "http://qt.nokia.com/"
    }
    Rectangle {
        id: go
        anchors.left: url.right
        anchors.right: window.right
        anchors.top: window.top
        anchors.bottom: url.bottom
       Image {
            source: "go.svg"
        }
    }
    WebView {
        id: content
        anchors.left: window.left
        anchors.right: window.right
        anchors.top: url.bottom
        anchors.bottom: window.bottom
        source: "http://qt.nokia.com/"
    }
}

Here we've placed several items, using their anchor properties. Other visible items control the layout of their children and can be used to provide other means of item positioning, including:

  • Column, a region that positions its child items so they are vertically aligned.

  • Flow, a region that arranges its children side by side, wrapping as necessary,

  • Grid, a region that positions its child objects in a grid.

  • PathView is a cousin to Repeater, and lays out its model-provided items along a path.

  • Row, a region that arranges its children horizontally,

We might modify the layout in the previous QML to better encapsulate the URL navigation line and "Go" button by placing them in a row, like this:

...
Row {
    id: navigation
    anchors.left: window.left
    anchors.right: window.right
    anchors.top: window.top
    TextInput {
        id: url
        text: "http://qt.nokia.com/"
    }
    Rectangle {
        id: go
        anchors.right: navigation.right
        width: 32
        Image {
            source: "go.svg"
        }
    }
}

Finally, some visible items don't actually draw anything, but instead accept user events for processing:

  • Flickable, an item that appears to rotate around an axis as if it's being flipped over.

  • GestureArea, used to enable simple gesture handling, such as panning, pinching, swiping, tapping, and so forth.

  • MouseArea, a region that enables simple mouse event handling.

Each of these has signal handlers; for example, MouseArea has them for common mouse events including press, release, entry, and exit, while GestureArea has signal handlers for tap, tap-and-hold, pan, pinch, and swipe gestures.

Because changing the position, orientation, and scale of items is something you often want to do in user interfaces, Qt Quick defines the Translate, Rotation and Scale elements (subclasses of the Transform element), which you can assign to the transform property of a visible item. For example, the following specifies a rectangle rotated around its center by 45 degrees:

Rectangle {
     width: 100; height: 100
     color: "blue"
     transform: Rotation { origin.x: 50; origin.y: 50; angle: 45}
}

Note that when specifying a transform, the origin is relative to the object's position, not the center. In the previous example, the point (50, 50) is at the object's center, not offset to the lower–right-hand corner of the object.

Some visible elements, like the ListView, need a model of one or more items from which to draw their content. Models include the ListModel, a list of ListItem items, as well as the more flexible XmlListModel element, which draws its list items from an XML document using XPath expressions. (We use the XmlListModel element in the next section to represent the list of earthquakes from the USGS.)

A full list of the supported Qt Quick elements is available at http://doc.qt.nokia.com/qdeclarativeelements.html.

Note

Qt Quick is undergoing heavy development and extension as we write this (Qt 4.7 has just been released), and this quick survey of the elements available to Qt Quick is probably already out of date. To keep with the latest information about Qt Quick, see http://doc.qt.nokia.com/qtquick.html.

Programming for the Web with QML

It's time to build a larger example: our Shake demonstration application, this time entirely in QML. In this section we'll build on the basics you've already learned, and introduce the powerful XmlListModel Qt Quick element that lets you fetch RSS feeds and parse out data from them using only XPath queries. Figure 6-1 shows our sample UI.

The Shake application, this time in QML

Figure 6.1. The Shake application, this time in QML

Before we begin, it's worth noting that the UI is completely different than that of a standard Qt application—here, pictured in the Qt Simulator with an N900 skin. If you're looking to create an application that closely resembles native applications with a look and feel identical to the native experience, QML may not be your first choice, because its presentation is a trifle more basic. As we write this, it doesn't have the necessary UI primitives or styles to match the native MeeGo or Symbian UI (this will soon be introduced by the Qt Quick Components). On the other hand, if you want to establish your own look and feel, or if you're writing a game or other application where it's okay to deviate from the native device UI, QML is an excellent choice.

Our application returns to the split-screen UI you first saw in the prototype in Chapter 4, with a few refinements. First, the event list on the left has a shaded background, and doesn't occupy precisely half the screen. Moreover, list items are formatted neatly, with an event's magnitude and region on separate lines. The basic functionality still remains, although for the brevity of this example, we don't include geolocation integration as we demonstrated in Chapter 6. It's easy to add through the Qt Mobility QML plug-ins (available since Qt Mobility 1.1), though, or you can do it through C++, which we will describe in the section "Mixing C++ with QML" later in the chapter.

Before we begin discussing the main user interface, you'll want to create a new QML project. To do this, launch Qt Creator and select "Create Project..." and then choose "Qt Quick UI" from the New Project dialog, as you see in Figure 6-2.

If all you want to do is run the application, you can do so using the qmlviewer command, which takes as its argument the name of a QML file to execute, like this:

qmlviewer main.qml

This works only on your development workstation; to display QML on the device, you'll use the wizard provided in Qt Creator (for versions after Qt Creator 2.1 beta), as we show you in the section "Displaying QML within a C++ Application" later in this chapter.

Creating a new Qt Quick project

Figure 6.2. Creating a new Qt Quick project

Creating the User Interface

The user interface consists of two pieces: the list view, a ListView element, and the item view, a Text element. Listing 6-1 shows main.qml, the QML that defines the entire user interface (and the application's data model, as you'll see as we go along).

Example 6.1. The main UI for the QML version of Shake

import QtQuick 1.0
Rectangle {
    property bool loading: feedModel.status == XmlListModel.Loading
    id: window
    width: 800
    height: 480

    Rectangle {
        id: listView
anchors.left: window.left
        anchors.top: window.top;
        width: window.width/3
        height: window.height
        color: "#efefef"

        ListView {
            id: events
            property string text: window.loading ?
                "Loading data... please wait" :
                "<b><center>" +
                    feedModel.get(0).title.replace(",","
").replace(",","
") +
                    "</center></b><br/>" + feedModel.get(0).summary
            focus: true
            anchors.fill: parent
            model: feedModel
            delegate: QuakeListDelegate {}
            highlight: Rectangle { color: "steelblue" }
            highlightMoveSpeed: 9999999
        }
    }

    Text {
        id: itemView
        anchors.left: listView.right
        anchors.top: window.top;
        width: window.width - listView.width
        height: window.height
        wrapMode: Text.Wrap
        text: events.text
    }

    XmlListModel {
         id: feedModel
         source: "http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml"
         namespaceDeclarations:
           "declare default element namespace 'http://www.w3.org/2005/Atom';"
         query: "/feed/entry"
         XmlRole { name: "title"; query: "title/string()" }
         XmlRole { name: "summary"; query: "summary/string()" }
    }
}

The top level of the UI is a single rectangle, sized to fit the MeeGo device screen at 800×480 characters with the ID window. It has a single property, a Boolean value loading, which is true while the XmlListModel is loading the XML from the USGS.

Inside the main rectangle is a smaller rectangle containing a ListView, and the Text element that shows the details of a single earthquake event. We place the ListView in its own rectangle and position this and the Text element to be adjacent to each other, spanning the entire height of the containing rectangle using their anchor properties. They permit you to anchor item borders by referring to the borders of adjacent items.

The ListView itself has a property, the text to show for the current element. When the application starts, it simply shows a canned string indicating that the application is loading data; because QML maintains bindings between all the properties, as the list model's status changes, so does the window's loading property, and so does the text property of the ListView. The JavaScript expression for the text property creates a bit of HTML to present a rich-text version of the earthquake data that reiterates the quake's magnitude, location, and detail data the USGS provides.

A ListView doesn't draw its own items; instead, it relies on a delegate, a separate item that draws contents once for each item in the ListView's model. Listing 6-2 shows the QuakeListDelegate.qml (put it into the same directory as your main.qml file), our item for displaying a single item of the list.

Example 6.2. The delegate responsible for drawing a single list item

import QtQuick 1.0

Item {
    id: delegate

    width: delegate.ListView.view.width; height: 60

    Text {
        text: title.replace(",","
").replace(",","
")
        color: delegate.ListView.isCurrentItem ? "white" : "black"
        font { family: "Helvetica"; pixelSize: 16; bold: true }
        anchors {
            left: parent.left; leftMargin: 15
            verticalCenter: parent.verticalCenter
        }
    }

    Rectangle {
        width: delegate.width; height: 1; color: "#cccccc"
        anchors.bottom: delegate.bottom
        visible: delegate.ListView.isCurrentItem ? false : true
    }

    Rectangle {
        width: delegate.width; height: 1; color: "white"
        visible: delegate.ListView.isCurrentItem ? false : true
    }

    MouseArea {
        anchors.fill: delegate
        onClicked: {
            delegate.ListView.view.currentIndex = index
            delegate.ListView.view.text = "<b><center>" +
                title.replace(",","
").replace(",","
") +
                "</center></b><br/>" + summary
        }
    }
}

The delegate has a single Text item that displays the title of an earthquake report as a series of three lines. It's in black for all items but the currently focused item, which is white and drawn over the highlight rectangle at the end of the listing. After the Text item is a dividing line one pixel tall. It provides separation between this and subsequent items. A MouseArea in the item filling the entire region handles clicks by setting the ListView's text property to the full text description of the event.

When the XmlListModel finishes loading or you click on an item, the QML runtime updates the ListView's text property. The itemView, a single Text element, displays this by setting its text property to shadow the text property of the event list itself.

Downloading the Data

The XmlListModel is a specific list model that handles both the fetching of an XML feed and parsing the feed into roles defined by XPath queries. The XmlListModel does the work of the WorkerThread in the previous chapters' examples, fetching the RSS feed and parsing it to provide title and summary attributes from the source XML available from the USGS. The fetch begins when the XmlListModel is created, and the status is updated after the load completes.

The ListView draws each item using the delegate you saw in Listing 6-2, obtaining the fields in each list item using the title and summary attributes extracted from a specific feed entry based on the entry's index. You can also fetch a specific XmlListModel's item using the get method and passing an index, as we do when we draw the 0th element after the loading completes.

The XmlListModel highlights a key feature of QML we've only hinted at: content can be fetched not just from the local device, but also over the Internet. Any element with a source property can present data from any URL, letting you freely mix local and remote resources in your Qt Quick applications. In fact, you can do this with whole Qt Quick items. The Loader element has a source property and at runtime replaces itself with the contents at the URL of its source element. That allows a Qt Quick application to load other QML from the Web.

Integrating C++ with QML

While QML is arguably a powerful environment, there are still uses for C++ in Qt development. For example, interfacing with platform enablers like QtDBus on MeeGo still requires some C++ work, even if your UI is entirely written in QML. Fortunately, it's easy to bind QML with QObject subclasses written in C++ using Qt's meta-object features, which we touched on in Chapter 4.

As you'll see in the section "Mingling QObjects with QML" later in this chapter, any QObject can be added to QML's object tree, exposing Qt properties as QML properties and slots as methods.

Other times you may just want to introduce a QML interface as a visible component of your application, either as all or part of your UI. The Qt Declarative library, on which Qt Quick is based, provides a collection of classes that let you do just this. The most obvious example is that when you want to ship a QML application on a mobile device, you'll need to create a QDeclarativeView in which to render your QML application.

Displaying QML within a C++ Application

Displaying QML in a Qt application is easy. Simply create an instance of QDeclarativeView and add it to your widget hierarchy. Then, set its source to the URL of the entry point to your QML application. For example, a player application for the previous section's QML is as simple as what you see in Listing 6-3.

Example 6.3. Rendering QML in an application's main window

#include <QApplication>
#include <QMainWindow>
#include <QDeclarativeView>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QMainWindow window();
    QDeclarativeView* view = new QDeclarativeView();

    window.setCentralWidget(view);

    view->setSource(QUrl::fromLocalFile ("main.qml"));
    window.showMaximized();

    return app.exec();
}

QDeclarativeView acts as a QWidget, so you can just set it as the central widget of the application's main window and give it some QML to render. In fact, if you choose a Qt Quick application from Qt Creator's "New Project," the resulting project includes an entry point (main function) whose body is very similar to what you see in Listing 6-3.

Mingling QObjects with QML

Through the rootContext method, the QDeclarativeView exposes a QDeclarativeContext, which provides an interface to QML's context within the QML engine that the QDeclarativeView uses to render its content. Using the QDeclarativeContent, you inject new QObject values to the context tree, providing the name that the QML content will use to access the QObject. When you do this, the QObject's properties become QML properties of the object in the QML context, and slots become methods that QML can invoke on the object.

As an example, let's imagine we wanted to reuse the model and network code from the previous chapter's example with the QML user interface we presented in Listing 6-1 and 6-2. In practice, this probably isn't a good idea, because the XmlListModel does what we need and requires less code, but this example will show you how you can introduce a model from C++ to QML and use it with QML's ListView.

The only change we need to make to Listing 6-1 is to remove the XmlListModel from the QML entirely; we'll replace it with our QuakeListModel using the code you see in Listing 6-4.

Example 6.4. Introducing a QObject into the QML context

static const char* kUrl =
    "http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml";

int main(int argc, char *argv[])
{
    qRegisterMetaType<QModelIndex>("QModelIndex");

    QApplication app(argc, argv);

    QMainWindow window();
    QuakeListModel* model = new QuakeListModel(&window);
    WorkerThread* worker = new WorkerThread(&window, *model);
    worker->fetch(kUrl);

    QDeclarativeView* view = new QDeclarativeView();

    // The only thing we show is the declarative view.
    window.setCentralWidget(view);
    window.showMaximized();
    view->rootContext()->setContextProperty("feedModel", model);

    view->setSource(QUrl::fromLocalFile("main.qml"));

    return app.exec();
}

Listing 6-4 introduces a QtDeclarativeView to the main window, but only after it creates an instance of the QuakeListModel and WorkerThread to fetch the earthquake feed from the USGS server. While the thread is working, the code inserts the QuakeListModel instance into the declarative view's context using the line of code

view->rootContext()->setContextProperty("feedModel", model);

This assigns the model to the QML entity feedModel.

The QuakeListModel we presented previously doesn't provide status notifications as the worker thread does its work; we need to add a Qt property that indicates the feed status the worker thread will update as it fetches and parses the data. Listing 6-5 shows the modified interface to the QuakeListModel.

Example 6.5. Adding the status property to the QuakeListModel.

class QuakeListModel : public QStandardItemModel
{
    Q_OBJECT

    Q_PROPERTY(int status READ status WRITE setStatus NOTIFY statusChanged)

public:
    QuakeListModel(QObject* parent = 0);

    enum {
        ... // Role enum elided for brevity
    };

    bool setData(int row, const QuakeEvent& value);
int status();
    void setStatus(int status);

signals:
    void statusChanged();

... // remainder of class follows
};

The status property uses the status and setStatus methods as its implementation, and setStatus must also emit statusChanged to provide QML's binding something to hook on to while watching for status changes. These methods (Listing 6-6) are trivial.

Example 6.6. Changes to the QuakeListModel implementation

int QuakeListModel::status() {
    return mStatus;
}

void QuakeListModel::setStatus(int status) {
    if (status != mStatus) {
        mStatus = status;
        emit statusChanged();
    }
}

QuakeListModel::QuakeListModel(QObject* parent)
    : QStandardItemModel(parent) {
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "title";
    roles[QuakeListModel::Description] = "summary";
    setRoleNames(roles);
}

Listing 6-6 also shows a key change to the QuakeListModel's notion of its roles; for each named QML role, such as title, we need to provide the corresponding Qt::Role enumeration value. The QML context uses these when resolving the attributes referenced in specific list items while drawing the delegate for the list view. We do this when we construct the model by creating a QHash that links the QML attribute names to the Qt::Role enumeration values.

Next, the worker thread needs to update the model's status property throughout the HTTP transaction; for example, the beginning of the fetch method needs to look like this:

void WorkerThread::fetch(const QString& url)
{
    // Don't try to re-start if we're running
    if (isRunning()) {
        this->cancel();
    }
    mEventModel.setStatus(2); // XmlListModel.loading

    // Configure the access point, do the fetch, etc.
    // See Chapter 4 for details.
    ...
}

The model's status also needs to be set at the end of run to signal the end of the transaction for success or error conditions, of course.

Wrapping Up

In this chapter, we've shown you how to use Qt Quick, Nokia's declarative environment for creating user interfaces using QML, JavaScript, and C++. By using QML entities like Rectangle, MouseArea, Item, Text, and ListView, you learned how to specify user interfaces by their contents, instead of C++'s imperative declarations in method definitions. You saw how QML uses properties and runtime binding to share data between user interface objects, automatically updating each object in its context tree as necessary. The process uses JavaScript to let you create programmatic linkages between one object's properties and another. We showed how that extended to both the JavaScript and C++ runtimes, letting you add JavaScript and C++ objects to your QML-based application. We also showed how to display QML content in a C++ application.

In the next chapter, we switch gears, and discuss Nokia's support for Web technologies, including HTML5, which lets you deploy existing or new web-based applications on Nokia's products. Take a walk to clear your head, and we'll be ready when you return!

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

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