Progress indicators

A ProgressIndicator is a control used to display the progress of a task or process. A ProgressIndicator is a non-interactive control, and it has a fixed height and a variable width.


A ProgressIndicator in the Progress state.

The appearance of a ProgressIndicator is controlled by the fromValue, toValue, value, and state properties.

The fromValue and toValue properties are used to specify the range of a ProgressIndicator. The value you set for the fromValue property doesn't have to be less than the toValue (you could set a range from 80 to 20), and negative numbers are supported. Because you can set the fromValue property to be higher than the toValue property, you can create a ProgressIndicator that displays a full progress bar that decreases from right to left.

The value property controls the progress of the bar. Typically, the progress of a ProgressIndicator is controlled by a task or process that updates the value property.

Sizing

The state property controls the color that's displayed in the ProgressIndicator as follows:

Indeterminate: No color displayed

A ProgressIndicator in the Indeterminate state.

Progress: Light blue progress bar

A ProgressIndicator in the Progress state.

Pause: Yellow progress bar

A ProgressIndicator in the Pause state.

Error: Red progress bar

A ProgressIndicator in the Error state.

Complete: Dark blue progress bar

A ProgressIndicator in the Complete state.

Controlling the indicator

The following sample creates a ProgressIndicator and sets the fromValue and toValue properties in QML. A button is used to start and stop a task in your app. The task in this sample is not defined, but it could be loading a process, playing an animation, or playing audio or video. The onValueChanged signal handler is used to change the state of the indicator to ProgressIndicatorState.Complete when the task is finished. The value of the indicator is also displayed in a label.

// Create the progress indicator
ProgressIndicator {
    id: myProgressIndicator
    fromValue: 0
    toValue: 50
    value: taskValue
    onValueChanged: {
        // Check the current value of the progress
        // indicator. If the current value is 50, 
        // the state of the progress indicator is 
        // set to Complete
        if (value == 50) {
            ProgressIndicatorState.Complete
        }
    }
}
Button {
    text: "Start"
    onClicked: {
        // Use the onClicked signal handler
        // to control your task
    }
}
Label {
    id: myLabel
    text: myProgressIndicator.value
}

Here's how to do the same thing in C++:

#include <bb/cascades/ProgressIndicator>
#include <bb/cascades/Button>
#include <bb/cascades/Label>
//...

// Create a Label. The myLabel object is declared
// in the header as follows:
// Label *myLabel;
myLabel = new Label();

// Create the progress indicator. The myIndicator
// object is declared in the header as follows:
// ProgressIndicator *myIndicator;
myIndicator = ProgressIndicator::create
        .fromValue(0)
        .toValue(50)
        .value(taskValue);
        
        
// If any Q_ASSERT statement(s) indicate that the slot failed  
// to connect to the signal, make sure you know exactly why this 
// has happened. This is not normal, and will cause your
// app to stop working
bool connectResult;

// Since the variable is not used in the app, this is added to avoid a 
// compiler warning
Q_UNUSED(connectResult);

// Connect the ProgressIndicator to the handleValueChanged() slot
connectResult = QObject::connect(myIndicator, SIGNAL 
                                (valueChanged(float)), 
                                 this,
                                 SLOT(handleValueChanged(float)));

// This is only available in Debug builds
Q_ASSERT(connectResult);    

// Create the handleValueChanged() slot to capture the
// valueChanged() signal emitted by the ProgressIndicator
void ProgressIndicatorRecipe::handleValueChanged(float value)
{
    // Use an if statement to check the current value of
    // the progress indicator. If the current value is 50, 
    // the state of the progress indicator is set to Complete
    if (value == 50) {
        myIndicator->setState(ProgressIndicatorState::Complete);
        myLabel->setText(value);
    } else {
        myLabel->setText(value);
    }
}

//...

// Create a button and set the button text
Button *myButton = Button::create()
        .text((const QString) "Start");

// Connect the button to the handleClicked() slot
connectResult = QObject::connect(myButton, SIGNAL(clicked()), 
                                 this,
                                 SLOT(handleClicked()));

// This is only available in Debug builds
Q_ASSERT(connectResult);

// Create the handleClicked() slot to capture the
// clicked() signal emitted by the button
void ButtonRecipe::handleClicked()
{
    // Use the handleClicked() slot to
    // control your task
}

For more information on responding to signals, see Signals and slots.

Progress indicator example

The following example uses a ProgressIndicator to display the progress of a cooking pot.

When the ToggleButton is set to On, the cooking process begins. The pot lid is animated to tilt back and forth, and the ProgressIndicator displays the progress.

If the ToggleButton is set to Off before cooking is completed, the cooking process is paused. When the toggle switch is set to On again, cooking resumes. When cooking is finished, the pot lid animation stops, and the ToggleButton is set to Off.

You can download the images for the sample here: images.zip

Animation showing a ProgressIndicator displaying the progress of an animation.
import bb.cascades 1.0

Page {
    titleBar: TitleBar {
        title: "ProgressIndicator"
    }
    Container {
        layout: DockLayout{}
        Container {
            horizontalAlignment: HorizontalAlignment.Center
            verticalAlignment: VerticalAlignment.Center
            // Create a Container to hold the animated pot and lid
            Container {
                bottomPadding: 50
                horizontalAlignment: HorizontalAlignment.Center
                // Create an ImageView to hold the pot lid image.
                // The cookingProgress and cookingTime 
                // variables are used to determine the total
                // length of the animation
                ImageView {
                    id: potLid
                    property int cookingProgress: 0
                    property int cookingTime: 10
                    
                    translationY: 30
                    horizontalAlignment: HorizontalAlignment.Center
                    imageSource: "asset:///images/lid.png"
                    
                    // Animate the pot lid by rotating the 
                    // image along the Z-axis
                    animations: SequentialAnimation {
                        id: cooking
                        animations: [
                            RotateTransition {
                                toAngleZ: 2
                                duration: 100
                            },
                            RotateTransition {
                                toAngleZ: -2
                                duration: 100
                            }
                        ]
                        // The onEnded handler updates the value
                        // of cookingProgress, and checks the 
                        // value against the cookingTime variable. 
                        // If the two values are equal the animation
                        // stops, otherwise the "cooking" process 
                        // continues
                        onEnded: {
                            // Update the cooking progress
                            potLid.cookingProgress =
                            potLid.cookingProgress + 1;
                            
                            if (potLid.cookingProgress == 
                                			potLid.cookingTime) {
                                // If the "cooking" process has 
                                // finished, reset the rotation 
                                // of the pot lid image and the
                                // checked state of the ToggleButton
                                pausebutton.checked = false
                                potLid.rotationZ = 0;
                            } else {
                                // If cookingProgress does not match
                                // cookingTime, continue the 
                                // animation
                                cooking.play();
                            }
                        }
                    }
                }
                // Specify the pot image and center it horizontally
                ImageView {
                    horizontalAlignment: HorizontalAlignment.Center
                    imageSource: "asset:///images/pot.png"
                }
            }
            
            // Create a container to hold the ProgressIndicator and
            // the ToggleButton. We use a StackLayout with 
            // a left-to-right orientation to place the controls
            // beside each other
            Container {
                rightPadding: 20
                leftPadding: 20
                bottomPadding: 20
                layout: StackLayout {
                    orientation: LayoutOrientation.LeftToRight
                }
                // Create the ProgressIndicator and define
                // the range. The cookingProgress variable
                // updates the value. Because cookingProgress 
                // increases by one in each iteration, the
                // ProgressIndicator has a total of 10 updates
                // before reaching the Complete state
                ProgressIndicator {
                    id: progressbar
                    fromValue: 0
                    toValue: 10
                    value: potLid.cookingProgress
                    verticalAlignment: VerticalAlignment.Center
                    
                    onValueChanged: {
                        if (value == 0) {
                            // Set the state of the indicator to
                            // Indeterminate if the value is 0
                            progressbar.state =
                            ProgressIndicatorState.Indeterminate
                        }
                    }
                }
                
                ToggleButton {
                    id: pausebutton
                    
                    // Attach an onCheckedChanged signal handler
                    onCheckedChanged: {
                        // Check to see if cookingProgress is equal
                        // to cookingTime. If it is, we reset the
                        // value to 0, and begin the animation. If 
                        // the values are not equal, the 
                        // cookingProgress variable remains at its 
                        // current value and the animation resumes
                        if (checked) {
                            if (potLid.cookingProgress == 
                                			potLid.cookingTime) {
                                potLid.cookingProgress = 0;
                            }
                            cooking.play();
                            
                            // Set the state of the
                            // ProgressIndicator to Progress
                            // when the animation is playing
                            progressbar.state = 
                            		ProgressIndicatorState.Progress
                        } else {
                            if (potLid.cookingProgress == 
                                			potLid.cookingTime) {
                                // If the cooking time has
                                // reached its limit, set the
                                // state to Complete
                                progressbar.state =
                                ProgressIndicatorState.Complete
                            } else {
                                // Otherwise, pause the progress
                                progressbar.state =
                                ProgressIndicatorState.Pause
                                cooking.stop();
                            }
                        }
                    }
                } // ToggleButton
            }
        } // Container
    } // Container
}// Page

progressindicatorrecipe.h

#ifndef _PROGRESSINDICATORRECIPE_H_
#define _PROGRESSINDICATORRECIPE_H_

#include <bb/cascades/CustomControl>
#include <bb/cascades/ImageView>
#include <bb/cascades/ProgressIndicator>
#include <bb/cascades/ToggleButton>
#include <bb/cascades/SequentialAnimation>
#include <bb/cascades/Page>
#include <bb/cascades/Container>

using namespace bb::cascades;

namespace bb { namespace cascades { class Application; }}

class ProgressIndicatorRecipe: public QObject
{
    Q_OBJECT

public:

    ProgressIndicatorRecipe(bb::cascades::Application *app);
    virtual ~ProgressIndicatorRecipe() {}

public slots:

    void onValueChanged(float value);
    void onToggleStove(bool on);
    void onCookingAnimEnded();

private:

    int mCookingProgress;
    static int const mCookingTime;
    ProgressIndicator *mProgressIndicator;
    ToggleButton *mButton;
    SequentialAnimation *mCooking;
    ImageView *mLid;
    Page *root;
    Container *recipeContainer;
};

#endif // ifndef _PROGRESSINDICATORRECIPE_H_

progressindicatorrecipe.cpp

#include "progressindicatorrecipe.h"
 
#include <bb/cascades/Application>
#include <bb/cascades/Container>
#include <bb/cascades/ImageView>
#include <bb/cascades/ProgressIndicator>
#include <bb/cascades/SequentialAnimation>
#include <bb/cascades/RotateTransition>
#include <bb/cascades/ScaleTransition>
#include <bb/cascades/StackLayout>
#include <bb/cascades/DockLayout>
#include <bb/cascades/SystemDefaults>
#include <bb/cascades/ToggleButton>
#include <bb/cascades/TitleBar>
 
using namespace bb::cascades;
 
int const ProgressIndicatorRecipe::mCookingTime = 10;
 
ProgressIndicatorRecipe::ProgressIndicatorRecipe(bb::cascades::Application *app)
: QObject(app), mCookingProgress(0)
{
    Page *page = new Page();
    Container *root = Container::create()
                .layout(new DockLayout());
 
    TitleBar *tbar = TitleBar::create().title("ProgressIndicator");
 
    page->setTitleBar(tbar);
 
    // The recipe Container
    Container *recipeContainer = Container::create()
                .vertical(VerticalAlignment::Center);
 
    // Create a Container to hold the images for the
    // pot and lid
    Container *potContainer = Container::create()
                .bottom(50)
                .horizontal(HorizontalAlignment::Center);
 
    // Create an ImageView for the pot lid
    mLid = ImageView::create("asset:///images/progressindicator/lid.png")
        .horizontal(HorizontalAlignment::Center)
        .translate(0, 30);
 
    // Animate the pot lid by rotating the image along the Z-axis
    mCooking = SequentialAnimation::create(mLid)
            .add(RotateTransition::create(mLid).toAngleZ(2).duration(100))
            .add(RotateTransition::create(mLid).toAngleZ(-2).duration(100))
            .parent(this);
     
    // If any Q_ASSERT statement(s) indicate that the slot failed
    // to connect to the signal, make sure you know exactly why this
    // has happened. This is not normal, and will cause your
    // app to stop working
    bool connectResult;
     
    // Since the variable is not used in the app, this is added to avoid a
    // compiler warning.
    Q_UNUSED(connectResult);
     
    // Connect the ended() signal to the onCookingAnimEnded() slot
    connectResult = connect(mCooking, SIGNAL(ended()),
                        this, SLOT(onCookingAnimEnded()));
     
    // This is only available in Debug builds
    Q_ASSERT(connectResult);
     
    // Add the image of the pot
    ImageView *pot = ImageView::create
        ("asset:///images/progressindicator/pot.png")
        .horizontal(HorizontalAlignment::Center);
 
    // Add the pot and lid to the Container
    potContainer->add(mLid);
    potContainer->add(pot);
 
    Container *progressContainer = Container::create()
        .layout(StackLayout::create().orientation
            (LayoutOrientation::LeftToRight))
        .left(20.0f)
        .right(20.0f)
        .bottom(20.0f);
 
 
    // Create a ProgressIndicator, and connect the valueChanged()
    // signal to the onValueChanged slot
    mProgressIndicator = new ProgressIndicator();
    mProgressIndicator->setVerticalAlignment(VerticalAlignment::Center);
    mProgressIndicator->setFromValue(0);
    mProgressIndicator->setToValue(10);
    connectResult = connect(mProgressIndicator, SIGNAL(valueChanged(float)),
                           this, 
                           SLOT(onValueChanged(float)));
         
    // This is only available in Debug builds
    Q_ASSERT(connectResult);
 
    // Create a ToggleButton
    mButton = new ToggleButton();
     
    // Connect the checkedChanged()
    // signal to the onToggleStove() function
    connectResult = connect(mButton,
                            SIGNAL(checkedChanged(bool)),
                            this,
                            SLOT(onToggleStove(bool)));
         
    // This is only available in Debug builds
    Q_ASSERT(connectResult);
     
    // Add the ProgressIndicator and the ToggleButton
    // to the Container
    progressContainer->add(mProgressIndicator);
    progressContainer->add(mButton);
 
    // Add the Containers to the recipe Container, and
    // set it as the root
    recipeContainer->add(potContainer);
    recipeContainer->add(progressContainer);
 
    root->add(recipeContainer);
    page->setContent(root);
    app->setScene(page);
}
 
// Create the onToggleStove() slot to capture the checkedChanged()
// signal emitted by the ToggleButton
void ProgressIndicatorRecipe::onToggleStove(bool on)
{
    if (on) {
        // If the values are equal, the function resets
        // the mCookingProgress variable to 0 before
        // starting the animation
        if (mCookingProgress == mCookingTime) {
            mCookingProgress = 0;
            mProgressIndicator->setValue(mCookingProgress);
        }
        mCooking->play();
 
        // Change the state of the ProgressIndicator to Progress
        mProgressIndicator->setState(ProgressIndicatorState::Progress);
    } else {
        if (mCookingProgress == mCookingTime) {
            // If mCookingProgress equals the total cooking time, set
            // the state of the ProgressIndicator to Complete
            mProgressIndicator->setState(ProgressIndicatorState::Complete);
        } else {
            // Otherwise, pause the progress
            mProgressIndicator->setState(ProgressIndicatorState::Pause);
            mCooking->stop();
        }
    }
}
 
// Create the onValueChanged() slot to capture the valueChanged()
// signal emitted by the ProgressIndicator
void ProgressIndicatorRecipe::onValueChanged(float value)
{
    // Use an if statement to set the state of the ProgressIndicator
    // to Indeterminate if the value is 0
    if (value == 0) {
        mProgressIndicator->setState
            (ProgressIndicatorState::Indeterminate);
    }
}
 
// Create the onCookingAnimEnded() slot to capture the ended()
// signal emitted by the animation
void ProgressIndicatorRecipe::onCookingAnimEnded()
{
    // mCookingProgress accumulator
    mCookingProgress = mCookingProgress + 1;
 
    // Update the value
    mProgressIndicator->setValue(mCookingProgress);
 
    if (mCookingProgress == mCookingTime) {
        // When the cooking process is complete, set
        // the checked state of the ToggleButton to false,
        // and set the rotation of the pot lid image to
        // the default position
        mButton->setChecked(false);
        mLid->setRotationZ(0);
    } else {
        // Otherwise, continue cooking
        mCooking->play();
    }
}

main.cpp

#include "ProgressIndicatorRecipe.h"
  
#include <bb/cascades/Application>
#include <Qt/qdeclarativedebug.h>
  
using namespace bb::cascades;
  
Q_DECL_EXPORT int main(int argc, char **argv)
{
    Application app(argc, argv);
  
    new ProgressIndicatorRecipe(&app);
  
    return Application::exec();
}

Last modified: 2013-12-21



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus