Home · All Classes · Main Classes · Deprecated

Showing a responsive window quickly in a MeeGo Touch application

Sometimes when heavy applications use the Writing launchable applications, the "application launcher" does not start up the application fast enough. In these cases, consider implementing your application so that loading the GUI content of the application occurs in several phases. This approach enables the application to show the window with minimal content as fast as possible when the user starts the application, and, after that, update the application page with the "real" content. This makes the start-up experience more pleasant for the users, since they do not have to wait until the complete GUI content appears on the screen.

This section describes how to implement this feature in your application using means provided by Qt and how to avoid general problems with this approach.

Implementation basics

To implement this feature:

In practice, you need to create the initial GUI content inside the constructor of the application's main page and define a new public slot createFinalContent() which loads the actual content of the application's GUI:

MainPage::MainPage() 
{
    // Create the initial content here

}

void MainPage::createFinalContent()
{

    // Create the actual content here

}

In application's main() you can now create the main page (with the initial content inside it) and display it. After this, connect MApplicationWindow::displayEntered() to MainPage::createFinalContent(), which creates the actual GUI of the application. This connection ensures that the loading of the actual GUI content starts immediately after the initial content has become visible on the display:

M_EXPORT int main(int argc, char **argv)
{
    MApplication *app = MComponentCache::mApplication(argc, argv);
    MApplicationWindow *window = MComponentCache::mApplicationWindow();
    window->show();

    MainPage *mainPage = new MainPage();
    mainPage->appear();

    app->connect(mainPage, SIGNAL(displayEntered()), mainPage, SLOT(createFinalContent()));

    return app->exec();
}

Finally, disconnect the signal as a first step inside function MainPage::createFinalContent(). This ensures that the slot is called only once when starting the application:

void MainPage::createFinalContent()
{
    app->disconnect(this, SIGNAL(displayEntered()), this, SLOT(createFinalContent()));
    
    // Create the actual content here
}

Possible problems

Minimising loading time for shared libraries

Shared libraries are loaded during the application start-up, which can take a lot of time. Unfortunately, the loading time also causes delay before any initial content can be shown.

To minimise the loading time:

Here are some tips for optimising loading time for your own shared libraries:

Keeping the GUI responsive

When performing heavy operations in MeeGo Touch applications, it is important that the GUI of the application still remains responsive. This is a common issue in Qt applications and there are different ways to maintain the responsiveness, depending on the situation.

Loading the actual application content after showing the initial GUI can be a time-consuming operation. Usually the initial content itself does not contain interactive items which should be responsive. However, take into account that the user may, for example, want to close the application while the real content is being built up.

Since this a known issue in Qt application development in general, there is very good documentation available which presents a range of possible solutions to avoid freezing the GUI during long operations. This section presents two possible ways to avoid this problem when implementing the GUI content loading in phases.

Processing events manually

The simplest solution to avoid freezing is handling pending events manually from time to time while creating the content of the actual application page. In practice, this means calling MApplication::processEvents() periodically whenever possible.

For example, if you need to load and scale all the images in the /root directory and show them in the actual application page, keep the GUI responsive as follows:

QDir dir("/root");
Q_FOREACH(QString file, dir.entryList(QStringList("*.jpg"))) {
    QImage scaledImg = QImage(file).scaledToWidth(864);
    policy->addItem(new MImageWidget(&scaledImg));
    QApplication::instance()->processEvents();
}

There are still some problems in this approach. Events are handled only after loading and scaling each individual image. In practice, this means that GUI stays quite responsive as long as loading and scaling one image does not take too much time. When trying this example with very large image files, the operations can take even several seconds per image. This means that if the user tries to close the application during that time, the GUI responds to the close event only after a delay of several seconds. Therefore, in general the "processEvents()" approach should be used only in cases when several short operations are executed.

Building the actual GUI in a separate thread

Another way to keep responsiveness is to build the actual content inside a separate thread. The main advantage of this approach is that the event loop stays running virtually without any interrupts and, thus, the GUI also stays responsive for user actions. The main disadvantage is that this approach is a bit more complex to implement than simply calling processEvents() here and there.

Here is the same image loader example with a worker class inherited from QThread which takes care of the heavy stuff:

Worker::Worker(MApplicationWindow * window) :
    m_window(window)
{
    // Set parent so that the thread will be automatically deleted
    setParent(window);

    // Connect signal to start the thread
    connect(m_window, SIGNAL(displayEntered()), this, SLOT(start()));
}

QList<QImage> Worker::imageList() const
{
    return m_imageList;
}

void Worker::run() 
{
    // Disconnect from the triggering signal 
    QObject::disconnect(m_window, SIGNAL(displayEntered()), this, SLOT(start()));

    QDir dir("/root");  
    Q_FOREACH(QString file, dir.entryList(QStringList("*.jpg"))) {
        QImage scaledImg = QImage(file).scaledToWidth(864);
        m_imageList.append(scaledImg);
    }
}

This signal connection is now needed in main():

    app->connect(worker, SIGNAL(finished()), mainPage, SLOT(showContent()));

Now, in main page's showContent() just use the already loaded and scaled images from Worker class:

void MainPage::showContent()
{
    QObject::disconnect(m_worker, SIGNAL(finished()), this, SLOT(appear()));

    MLinearLayoutPolicy *policy = new MLinearLayoutPolicy(m_layout, Qt::Vertical);

    Q_FOREACH(QImage image, m_worker->imageList()) {
        policy->addItem(new MImageWidget(&image, this));
    }

    m_layout->setLandscapePolicy(policy);
    m_layout->setPortraitPolicy(policy);
}

Note: MImageWidget's cannot be created in the worker thread due to thread safety issues. If you try this, Qt throws some warnings.

Screen rotation

As with the normal application GUI, it is important to notice possible screen orientation changes when the window is visible. In practice, this means that you have to define both landscape and portrait policies for the initial GUI layout.


Copyright © 2010 Nokia Corporation
MeeGo Touch