Using JavaScript

We’ve already used JavaScript a lot in our code, but we have only scratched the surface. JavaScript can be used in many more sophisticated and powerful ways in a Qt Quick application. In fact, Qt Quick is implemented as a JavaScript extension. JavaScript can be used almost anywhere as long as the code returns the value of the expected type. Moreover, using JavaScript is the standard way of writing parts of the application code which deal with application logic and calculations.

There are two important topics we need to talk about before we continue developing our application.

JavaScript is not JavaScript

JavaScript has its origins in the web development. In the course of time, JavaScript has rapidly grown in its popularity and inspired development of many extensions and add-ons. In order to support a broad use, JavaScript was formalized as a programming language in ECMAScript-262 standard. It is important to underline that ECMAScript-262 standard only covers the language aspects and leaves out any API providing additional functionality such as objects and libraries to access the web page content. Despite the standardization efforts, many JavaScript details in web development are still browser-specific - even though the situation has improved in the last few years. See the Wikipedia article about JavaScript [http://en.wikipedia.org/wiki/Javascript] for more details.

JavaScript is also used outside of the web where its functionality is tailored to support a use case. Still, the use of JavaScript for client-side programming in web development is dominating. Due to this, all books and most web resources about JavaScript are actually dedicated to web development. Qt Quick belongs to one of the platforms which use JavaScript outside of the web. If you read books or other materials about JavaScript to understand and use it better with Qt Quick, be aware of this difference.

Qt development teams are doing their best to provide all the details the use of JavaScript in Qt Quick, and this guide is part of that effort.

More About JavaScript

This guide contains an annex about basics of JavaScript tailored for Qt Quick. We strongly recommend reading it if you are not familiar with JavaScript, but have some general background in programming.

In addition to the references in the annex, consider reading the following articles on the Mozilla Developer Network:

The following three articles in Qt Documentation explain essential details of JavaScript in Qt Quick:

  • Integrating JavaScript [http://qt-project.org/doc/qt-4.8/qdeclarativejavascript.html] - key aspects to be aware of when using JavaScript in Qt Quick
  • ECMAScript Reference [http://qt-project.org/doc/qt-4.8/ecmascript.html] - a list of built-in objects, functions and properties supported by QtScript and so in Qt Quick
  • QML Scope [http://qt-project.org/doc/qt-4.8/qdeclarativescope.html] - explains the visibility of JavaScript objects and Qt Quick items

Note that significant changes and large updates to Qt Documentation may come with the future Qt releases to provide a full coverage of the use of JavaScript in Qt Quick. Stay tuned and check Qt Documentation again and again.

Adding Logic to Make the Clock Tick

We’ve already used a bit of JavaScript in previous sections. Most of it was for catching error conditions while loading a custom font and an image. In this section, we use JavaScript to show the actual time and date.

We are going to use the Date from the global object to get the current time and date. The returned data has to be formatted so that we only keep parts of the date and time information that we need. We use Qt.formatDateTime [http://qt-project.org/doc/qt-4.8/qml-qt.html#formatDateTime-method] for this:

function getFormattedDateTime(format) {
    var date = new Date
    return Qt.formatDateTime(date, format)
}

The :Qt.formatDateTime [http://qt-project.org/doc/qt-4.8/qml-qt.html#formatDateTime-method] function is part of QML Global Object [http://qt-project.org/doc/qt-4.8/qdeclarativeglobalobject.html], which provides many other useful utilities in addition to the standard set defined by the ECMAScript Reference [http://qt-project.org/doc/qt-4.8/ecmascript.html]. It is worth taking a closer look at its documentation.

The getFormattedDateTime() function is used in another function, which creates actual values for the Text [http://qt-project.org/doc/qt-4.8/qml-text.html] elements in our clock:

function updateTime() {
    root.currentTime = "<big>" +
            getFormattedDateTime(Style.timeFormat) +
            "</big>" +
            (showSeconds ? "<sup><small> " + getFormattedDateTime("ss") +
                "</small></sup>" : "");
    root.currentDate = getFormattedDateTime(Style.dateFormat);
}

Note

We use rich-text formatting in the text value of the time as discussed in the previous section.

We also use the conditional operator (also called the “ternary operator”) on the value of showSeconds. showSeconds is a custom property that defines whether the seconds must be shown on the clock. Using the conditional operator in Qt Quick is a very convenient way to bind a property (or any other variable) to a value depending on a condition.

The updateTime() function needs a trigger so that the currentTime and currentDate properties are constantly updated. We use the Timer [http://qt-project.org/doc/qt-4.8/qml-timer.html] item for this:

Timer {
    id: updateTimer
    running: Qt.application.active && visible == true
    repeat: true
    triggeredOnStart: true
    onTriggered: {
        updateTime()
        // refresh the interval to update time each second or minute.
        // consider the delta in order to update on a full minute
        interval = 1000    (showSeconds? 1 : (60 - getFormattedDateTime("ss")))
    }
}

Our timer implements some interesting aspects.

In order to optimize battery consumption, we bind the timer’s running property to two other properties, which stops the timer, thereby reducing CPU activity. It stops if our clock element becomes invisible (when used as a component in another application) or if our application becomes inactive (running in the background or iconified).

We also assign a value to the interval property while not loading, but when the timer is triggered. This is needed to catch the delta time to the full minute when seconds are not used. This ensures that we update the clock exactly on the minute.

The full code of our application including all enhancements discussed above looks like this:

(NightClock/NightClock.qml in qt_quick_app_dev_intro_src.zip, see Downloads section)

import QtQuick 1.1

Rectangle {
    id: root

    property bool showDate: true
    property bool showSeconds: true
    property string currentTime
    property string currentDate
    // the sizes are in proportion to the hight of the clock.
    // There are three borders, text and date.
    // 3*borderProportion+timeTextProportion+dateTextProportion has to be 1.0
    property real borderProportion: 0.1
    property real timeTextProportion: 0.5
    property real dateTextProportion: 0.2
    property string textColor: "red"
    property string timeFormat: "hh :mm"
    property string dateFormat: "dd/MM/yy"

    height:120
    width:250

    color: "transparent"

    // returns formated time and date
    function getFormattedDateTime(format) {
        var date = new Date
        return Qt.formatDateTime(date, format)
    }

    function updateTime() {
        root.currentTime = "<big>" +
                getFormattedDateTime(timeFormat) +
                "</big>" +
                (showSeconds ? "<sup><small> " + getFormattedDateTime("ss") +
                               "</small></sup>" : "");
        root.currentDate = getFormattedDateTime(dateFormat);
    }

    Image {
        id: background
        source: "../content/resources/background.png"
        fillMode: "Tile"
        anchors.fill: parent
        onStatusChanged: if (background.status == Image.Error)
                             console.log (qsTr("Background image \"") +
                                          source +
                                          qsTr("\" cannot be loaded"))
    }

    FontLoader {
        id: ledFont
        // unfortunately, the font will not load on a Symbian device,
        // and the default font will be used:
        // http://bugreports.qt-project.org/browse/QTBUG-6611
        // The bug should be fixed in 4.8
        source: "../content/resources/font/LED_REAL.TTF"
        onStatusChanged: if (ledFont.status == FontLoader.Error)
                             console.log("Font \"" + source + "\" cannot be loaded")
    }

    Timer {
        id: updateTimer
        running: Qt.application.active && visible == true
        repeat: true
        triggeredOnStart: true
        onTriggered: {
            updateTime()
            // refresh the interval to update the time each second or minute.
            // consider the delta in order to update on a full minute
            interval = 1000*(showSeconds? 1 : (60 - getFormattedDateTime("ss")))
        }
    }

    // trigger an update if the showSeconds setting has changed
    onShowSecondsChanged: {
        updateTime()
    }

    Column {
        id: clockText
        anchors.centerIn: parent
        spacing: root.height*root.borderProportion

        Text {
            id: timeText
            textFormat: Text.RichText
            text: root.currentTime
            font.pixelSize: root.height*root.timeTextProportion
            font.family: ledFont.name // use "Series 60 ZDigi" on Symbian instead
            font.bold: true
            color: root.textColor
            style: Text.Raised
            styleColor: "black"
        }

        Text {
            id: dateText
            text: root.currentDate
            color: root.textColor
            anchors.horizontalCenter: parent.horizontalCenter
            font.family: ledFont.name // use "Series 60 ZDigi" on Symbian instead
            font.pixelSize: root.height*root.dateTextProportion
            visible: root.showDate
            style: Text.Raised
            styleColor: "black"
        }
    }
}

The appearance of the application has remained the same:

../_images/night_clock.png

Importing JavaScript Files

If your application has a lot of JavaScript code, consider moving it to a separate file. You can import those files just like we imported the Qt Quick module. Due to a special role that JavaScript plays in Qt Quick, you must define the namespace for the content of the that file, for example, Logic in this example. Your source code would then use Logic.foo() instead of just foo(). The import statement looks like this:

import QtQuick 1.1
import "logic.js" as Logic

Note

If the application logic is complex, consider implementing it in C++ and importing it into Qt Quick. See the “Extending QML Functionalities using C++” [http://qt-project.org/doc/qt-4.8/qml-extending.html] article for more details.

When you import a JavaScript file, it is used like a library and has the scope of the QML file importing it. In some cases you might need a stateless library or a set of global variables shared by multiple QML files. You can use the .pragma library declaration for this. See the “Integrating JavaScript” [http://qt-project.org/doc/qt-4.8/qdeclarativejavascript.html] article in Qt Documentation for more details.

We move the JavaScript functions of our clock into the logic.js file imported as Logic. We also move all style properties into the style.js file imported as Style. This considerably simplifies the code and allows sharing the style with other components that we’re going to develop later.

The complete code of our application importing JavaScript files as discussed above looks like this:

(components/NightClock.qml in qt_quick_app_dev_intro_src.zip, see Downloads section)

import QtQuick 1.1
import "../js/style.js" as Style
import "../js/logic.js" as Logic

Rectangle {
    id: root

    property bool showDate: true
    property bool showSeconds: true
    property string currentTime
    property string currentDate
    property string textColor: "green"

    height:120
    width:300
    color: "transparent"

    function updateTime() {
        root.currentTime = "<big>" + Logic.getFormattedDateTime(Style.timeFormat) + "</big>" +
                (showSeconds ? "<sup><small> " + Logic.getFormattedDateTime("ss") + "</small></sup>" : "");
        root.currentDate = Logic.getFormattedDateTime(Style.dateFormat);
    }

    FontLoader {
        id: ledFont
        // unfortunately, the font will not load on a Symbian device,
        // and the default font will be used:
        // http://bugreports.qt-project.org/browse/QTBUG-6611
        // The bug should be fixed in 4.8
        source: "../content/resources/font/LED_REAL.TTF"
        onStatusChanged: if (ledFont.status == FontLoader.Error)
                             console.log("Font \"" + source + "\" cannot be loaded")
    }

    Timer {
        id: updateTimer
        running: Qt.application.active && visible == true
        repeat: true
        triggeredOnStart: true
        onTriggered: {
            updateTime()
            // refresh the interval to update the time each second or minute.
            // consider the delta in order to update on a full minute
            interval = 1000*(showSeconds? 1 : (60 - Logic.getFormattedDateTime("ss")))
        }
    }

    // trigger an update if the showSeconds setting has changed
    onShowSecondsChanged: {
        updateTime()
    }

    Column {
        id: clockText
        anchors.centerIn: parent
        spacing: root.height*Style.borderProportion

        Text {
            id: timeText
            textFormat: Text.RichText
            text: root.currentTime
            font.pixelSize: root.height*Style.timeTextProportion
            font.family: ledFont.name // use "Series 60 ZDigi" on Symbian instead
            font.bold: true
            color: root.textColor
            style: Text.Raised
            styleColor: "black"
        }

        Text {
            id: dateText
            text: root.currentDate
            color: root.textColor
            anchors.horizontalCenter: parent.horizontalCenter
            font.family: ledFont.name // use "Series 60 ZDigi" on Symbian instead
            font.pixelSize: root.height*Style.dateTextProportion
            visible: root.showDate
            style: Text.Raised
            styleColor: "black"
        }
    }
}

More Advanced Use of JavaScript

If you are interested in a more advanced use of JavaScript with Qt Quick, consider reading “Qt Quick Application Developer Guide for Desktop” available under this link [http://qt-project.org/wiki/Developer-Guides/].

What’s Next?

In the next chapter, we start developing the weather forecast application and learn how to retrieve and represent data in Qt Quick.