Add the timer and actions

So far, we've created the visual appearance of our app and created a custom timer to keep track of the traffic light countdown. To finish the app, we'll use the custom timer in QML and add the application logic that's needed to respond appropriately when the app's button is clicked.

Use the custom timer in QML

The whole purpose of creating our Timer class was so that we could access it in QML, so let's do that now. To be able to use Timer like any other Cascades control, we first need to register it as a type in QML.

In the src folder of your project, there should be an applicationui.hpp file and applicationui.cpp file. Open the applicationui.cpp file. We need to include our timer.hpp header file here, and then register the Timer class in the constructor for the App class by using the qmlRegisterType() template function:

 #include "timer.hpp" 

// ... 

ApplicationUI::ApplicationUI(bb::cascades::Application *app)
: QObject(app)
{   
    // Register the Timer class in QML as part of version 1.0 of the
    // CustomTimer library    
    qmlRegisterType<Timer>("CustomTimer", 1, 0, "Timer");         
    // ...
} 

The qmlRegisterType() function above registers our custom Timer class and makes it available in QML by the name "Timer". The Timer component is placed in version 1.0 of the CustomTimer library. For more information about registering custom types for use in QML, see Extending QML Functionalities using C++ on the Qt website.

If you're running your app on a device with a physical keyboard, make sure that you make the following changes in both main.qml files in your project (the one in the assets folder and the one in the assets/720x720 folder).

To access Timer in QML, we need to import the CustomTimer library. Open the main.qml file, and add the following line after the existing import bb.cascades 1.0 statement:

import CustomTimer 1.0

When you import this custom library, you'll notice that the QML preview no longer shows the preview of your UI. Don't worry, you haven't done anything wrong. In a future version of the tools, you'll be able to import custom libraries and still use the QML preview.

Now we're ready to create the timers that our app needs. The first timer keeps track of the time remaining until the traffic light turns yellow. Because we want the countdown text that's displayed on the screen to change every second, we specify an interval of 1000 for this timer. We also need to know the current numerical value of the countdown, so we use a custom property for this purpose. Most Cascades controls include predefined properties that you can set, but you can also create any additional properties that you might need by using the property keyword.

We add both the custom property and our first timer to the top of the root container of our app (right after the layout: DockLayout {} line):

property int currentCount: 0
         
Timer {
    id: lightTimer
     
    // Specify a timeout interval of 1 second
    interval: 1000

A signal handler called onTimeout is automatically available for us to use, and the JavaScript code that we place inside this signal handler is executed whenever the timer's timeout() signal is emitted. When the timer emits this signal, we want to decrement the currentCount property and update the countdown text that's displayed. Recall that we gave our TextArea an id of timerDisplay, so we can access the text property of the TextArea and update it accordingly. When the timer reaches 0, we change the state of the traffic light (from green to yellow), stop the countdown timer, and start the timer that pauses at the yellow state of the traffic light:

    onTimeout: {
        // Decrement the counter and update the countdown text
        root.currentCount -= 1;
        timerDisplay.text = "" + root.currentCount;
         
        // When the counter reaches 0, change the traffic light
        // state, stop the countdown timer, and start the pause
        // timer
        if (root.currentCount == 0) {
            trafficLight.changeLight();
            lightTimer.stop();
            pauseTimer.start();
        }
    } // end of onTimeout signal handler
} // end of Timer

Next, let's create the second timer that we need. This timer is used to pause the state of the traffic light in the yellow state, before it changes back to the red state. When this timer emits its timeout() signal, we change the state of the traffic light (from yellow to red). In our finished app, the "Change!" button becomes disabled when it's clicked and the light changes to the green state, and becomes enabled again when the light returns to the red state. In this timer's onTimeout signal handler, we enable the button again and stop the timer:

Timer {
    id: pauseTimer
     
    // Specify a timeout interval of 2 seconds 
    interval: 2000
 
    // When the timeout interval elapses, change the traffic
    // light state, enable the "Change!" button, and stop
    // the pause timer
    onTimeout: {
        trafficLight.changeLight();
        changeButton.enabled = true;
        pauseTimer.stop();
    }
}

Finally, we need to start the sequence of events that occurs when the button is clicked. We implement the button's onClicked signal handler, which does the following:

  • Changes the state of the traffic light (from red to green)
  • Sets currentCount to 9, representing the start of the countdown
  • Starts the countdown timer
  • Updates the countdown TextArea with the initial countdown text
  • Disables the button so that it can't be clicked while the countdown is in progress

Find the button that we created earlier in the tutorial (we gave it an id of changeButton), and add the onClicked signal handler to it:

onClicked: {
    // Change the traffic light state
    trafficLight.changeLight();
     
    // Set the initial countdown time
    root.currentCount = 9;
     
    // Start the countdown timer
    lightTimer.start();
     
    // Update the countdown text
    timerDisplay.text = "" + root.currentCount;
     
    // Disable the "Change!" button
    changeButton.enabled = false;
}

We're done! Build and run the app one last time to see the finished product:


Screen showing the finished app.


Screen showing the finished layout for keyboard devices.

Now that you're done

Here are a couple of ideas for how to extend the features of our traffic light app:

  • Use an attachedObjects list in the root container to use a  QTimer directly, instead of creating a custom Timer class.
  • Create a visual representation for the Timer class that displays the countdown, instead of using a separate  TextArea  control in QML.

Last modified: 2013-12-21

comments powered by Disqus