Friday, September 6, 2013

Accessing C++ model's roles from SelectionDialog

Consider you have a Qt C++ model with several roles (as an example, a model Customer with roles "id", "name" and "address"), and you wanted to have a SelectionDialog to select one of the items in the model with access to the data on its roles.

SelectionDialog only gives access to the selected index, and C++ model's roles are not directly accessible from QML.

Here I present a somewhat hacky way in pure QML around this restriction without modifying the C++ model. Basically we create a new SelectionDialog, called ItemSelectionDialog, that is similar to SelectionDialog. The difference is that instead of emitting selectedIndexChanged signals, it emits itemSelected signals that have as parameter the model's item. Then you can in QML access the selected item's different roles. It works by creating a proxy ListView for the model. Since ListView has functions to access the model's items, it can be used in the selection dialog to access the item.

ItemSelectionDialog:

import QtQuick 1.1
import com.nokia.meego 1.0

Item {
    id: root
    property alias model: dDialog.model
    property alias titleText: dDialog.titleText
    property alias selectedIndex: dDialog.selectedIndex
    property variant selectedItem

    signal itemSelected(variant item)
    signal accepted

    function open() {
        dDialog.open()
    }

    // This is a hack to access from SelectionDialog the roles of the model.
    ListView {
        id: lModel
        model: root.model
        delegate: Item {
            property variant myModel: model
        }
    }

    SelectionDialog {
        id: dDialog
        onSelectedIndexChanged: {
            if (selectedIndex > -1) {
                // This is a hack to access from SelectionDialog the roles of the model.
                // Ugly but works.
                lModel.currentIndex = selectedIndex
                root.selectedItem = lModel.currentItem.myModel
                itemSelected(lModel.currentItem.myModel)
            } else {
                root.selectedItem = null
            }
        }
        onAccepted: {
            root.accepted()
        }
    }
}

Using it is similar to using SelectionDialog, but instead of watching for onSelectedIndexChanged signals, you're watching for onItemSelected signals.

For example using the example given at the beginning:

ItemSelectionDialog {
    model: customers
    onItemSelected: {
        console.log("id: ", item.id)
        console.log("name: ", item.name)
        console.log("address: ", item.address)
    }
}

Saturday, April 28, 2012

Showing numerical virtual keyboard in QML

Getting the virtual keyboard to show in numerical input mode is not hard, but perhaps poorly documented.

Here's an example code how to do it:

TextField {
    inputMethodHints: Qt.ImhDigitsOnly
    validator: RegExpValidator { regExp: /\d*/ }
}

The above code allows unlimited amount of numbers to be entered, yet allows for an empty field.

If you want the field to have at least one to three numbers entered, something like this can be used:

TextField {
    inputMethodHints: Qt.ImhDigitsOnly
    validator: RegExpValidator { regExp: /\d{1,3}/ }
}

If the field is empty, you'll notice its border is red - as a mark that it is invalid input. This won't prevent the user to leave the field empty though. It's application specific how to handle such a case: whether to display a warning, an error, or preventing leaving the page.

The basic operation is nevertheless this:

  • inputMethodHints instructs the virtual keyboard to open in numerical mode only.
  • validator is used to match the key presses to the regular expression pattern, so that unwanted input can not be entered to the field. As a practical example, the numerical keyboard on harmattan allows entering characters such as '+', '-' and '.'. Validator can be used to disallow those, as has been done in above examples.

Saturday, February 4, 2012

Creating application with background daemon with QtCreator

This posting outlines how to create a program to run in the background (daemon) in MeeGo Harmattan with QtCreator. The resulting installation file is a single .deb that can be submitted to Nokia Store. The daemon will be started after installing the application, and will be started upon each reboot of the device.

The tutorial assumes you want to create a QML application that can be used to control the background daemon, and the daemon itself. The name of the app will be exampleapp.

Quick outline

This is a quick outline of what has to be done, with details in later chapters:
  • Create in QtCreator a subdirs project.
  • Inside the subdirs project create a console application project for the daemon and a Qt Quick Application project for the UI application.
  • Create a configuration file that controls launching of the daemon.
  • Create aegis manifest file to set credentials for daemon.
  • Create debian post-installation and before removal scripts to start the daemon after installation, and stopping the daemon before uninstallation.

Creating the basic project structure in QtCreator

The applications submitted to Nokia store should be contained in single .deb file. To have an application that contains two binaries - the daemon and the application providing the graphical user interface - we need two different projects. We can have both a single .deb file and two different projects by creating a subdirs project.

The final project will have a file structure like this:

- exampleapp/
  - exampleapp/
    - exampleapp*.png
    - exampleapp_harmattan.desktop
    - exampleapp.pro
    - main.cpp
    - qml/
  exampleappd/
    - exampleappd.conf
    - exampleappd.pro
    - main.cpp
  qtc_packaging/
    - debian_harmattan/
      - manifest.aegis
      - ... + various other files

Select File - New File or Project - Other Project - Subdirs project. Give it a name, in this example it's named exampleapp. Select the compilation targets, of course at least including Harmattan. Then click Finish & Add Subproject. As you've selected Harmattan as a target, QtCreator then asks if it should include various debian packaging related files to the project. Click Yes.

Now QtCreator offers to create the first of the subprojects. We can start by creating the QML project first, although the order does not matter. Choose from the dialog Qt Quick project - Qt Quick Application. As its name give the same name as the main subdirs project's name, in this example the name will be exampleapp. Select as "Qt Quick Application Type" Qt Quick Components for Meego/Harmattan. In Target Setup dialog make sure at least Harmattan is selected.

After the graphical QML based project is created, it's time to create the daemon project. Right click on Projects view on the subdirs project and select New subproject. Choose as project template Other Project - Qt Console Application. Give it a name, in this example I enter exampleappd (postscript 'd' is a common unix convention for daemon processes). Again on Target Setup make sure Harmattan is selected.

After this you have two projects, one for the graphical user interface, and one for the daemon running in the background. You do what you do in each of them, such as communicating with each other via dbus (which is out of scope for this blog posting).

Creating daemon control file

In order for the daemon to be started automatically on device reboots, a .conf file that is installed under
/etc/init/apps
is needed. The configuration files in there are processed by Upstart (Google it for more information). Here's a bare bones version of it for the example. Create a file exampleapp/exampleappd/exampleappd.conf

description "ExampleApp daemon startup script"
author "harmarto@somewherecom"
stop on core_shutdown
console output
respawn
respawn limit 3 300
normal exit 0
# This tries executing the daemon as 'user', this is
# what's almost always wanted, but you also need
# aegis.manifest file for that to work (shown later).
exec /usr/bin/aegis-exec -s -u user /opt/exampleapp/bin/exampleappd

For more options in the configuration file and some explanations on the values used see these resources:

You need to add the file to to exampleapp/exampleappd/exampleappd.pro so that it will be installed to a place where Upstart finds it (you can add this to the end of the file):
 
daemonconf.path = /etc/init/apps
daemonconf.files = exampleapp.conf
INSTALLS += daemonconf 
As it is preferable that all your binaries are bundled in the same location (/opt/exampleapp/bin/), you need to also add this to exampleappd.pro:
target.path = /opt/exampleapp/bin
INSTALLS += target

Aegis manifest

On Harmattan you will almost always want to run the daemon as user, not as a root. On Harmattan the credentials your application requests and receives from the security platform are far more important than the traditional unix root/user division. As Upstart doesn't run as 'user', you will need to request credentials for your daemon to switch to running as user. QtCreator should already have created an empty file at exampleapp/qtc_packaging/debian_harmattan/manifest.aegis. Put the following into it to get your daemon running as user:
<aegis>
  <request>
    <credential name="UID::user" />
    <credential name="GID::users" />
    <for path="/opt/exampleapp/bin/exampleappd"/>
  </request>
</aegis>

Starting the daemon after installation

If you want the daemon to start after user installs your application, you need to add a script that is executed after successfull installation.

Create following file at exampleapp/qtc_packaging/debian_harmattan/postinst

#!/bin/sh
set -e

case "$1" in
    configure)

    echo "Starting exampleappd ..."
    [[ -e /etc/init/apps/exampleappd.conf ]] && initctl start apps/exampleappd
    ;;

    abort-upgrade|abort-remove|abort-deconfigure)
    ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.

#DEBHELPER#

exit 0
the initstl start command is an Upstart command to execute the commands in your exampleappd.conf file.

Stopping the daemon after uninstalling

Even more important is to ensure that your daemon is stopped if user uninstalls your application. In a similar way to to running script after installing, you can instruct the system to run a script before uninstalling to stop the daemon.

Create following file exampleapp/qtc_packaging/debian_harmattan/prerm

#!/bin/sh

set -e

case "$1" in
    purge|remove|upgrade)
        initctl stop apps/exampleappd || true
        # Just in case Upstart could not stop the daemon, kill it    
        killall exampleappd || true        
    ;;
    
    failed-upgrade|abort-install|abort-upgrade|disappear)
    ;;

    *)
        echo "postrm called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

#DEBHELPER#

exit 0

Running from QtCreator

You can run the resulting project from QtCreator, but it is not guaranteed that your daemon starts before the user interface application. That can be slight annoyance depending on the project. Nevertheless, when you run the application with Harmattan target, the files are installed on your device. You can then, if needed, close the user interface application, start the daemon yourself from command line by running /opt/bin/exampleapp/bin/exampleappd, and then start the UI from the application grid.

Caveats

The daemon application must be pure console application. In other words, no graphics should be displayed by it. Trying to create any graphics context will most likely just result in abrupt termination of your daemon. There are simple ways to have daemons that can display graphics and QML, but that's a different topic if there's interest.

Thanks to

The postinst/prerm scripts are lifted pretty much straight from sandst1's MyMoves sources. Many thanks to his great application, as well as for sharing the sources.

Sunday, January 29, 2012

Closing virtual keyboard when pressing enter/return in QML

The stock apps on N9/N950 close the virtual keyboard on single line text edit when pressing the enter/return key on the virtual keyboard. This allows easily to see the content of the whole page without trying to aim at some area in the page that is not used for input.

Unfortunately, when you create UIs with QML this is not the default behavior. With some help from IRC #harmattan channel and some googling, this is how I got it to work:

TextField {
    Keys.onReturnPressed: {
        platformCloseSoftwareInputPanel()
    }
}

Now the virtual keyboard is closed. But there is still one issue. The keyboard is closed, but the text input still retains focus. If you try clicking on the text field again to start typing, nothing happens. You will have to click somewhere else, so that it loses focus, and then click again the text field. The stock applications make the input field lose focus when the virtual keyboard is closed.

This was a little more hackish to implement. TextField contains a TextInput, which has focus property. But unfortunately TextField does not contain an alias for that property, so you can't just simply set focus = false for the TextField. This kind of rather ugly workaround does the job:

TextField {
    Keys.onReturnPressed: {
        platformCloseSoftwareInputPanel()
        dummy.focus = true
    }
}
// This is just a dummy invisible item that takes the focus when virtual keyboard is closed
Item { id: dummy }

Thursday, November 10, 2011

Simple C++ UnitTests with QtCreator

I wanted to have some simple unit tests for my project to speed up testing and to cover corner cases. I'll outline here what I settled with. This is, at least for me, a suitable solution for a small free-time project. The goal was to have minimum hassle and modifications needed to the stock QtCreator created project, but still allowing me to save manual testing time with unit tests.

Here is the directory structure I used:

project.pro
 - src/main.cpp
 - src/module/implementation.cpp
 - src/module/implementation.h
 - tests/testmain.cpp
 - tests/module/testimplementation.cpp
 - tests/module/testimplementation.h

Optimally when developing and running the application, I'd want the workflow to be:

  • Compile the program. Continue if successful.
  • Compile the unit tests. Continue if successful.
  • Run the unit tests against the program
  • If unit tests fail, abort and display the errors
  • If unit tests succeed, create the application package (but without the unit test code), deploy it and run)
Qt has a pretty nice unit test framework, QTest. Unfortunately creating the workflow described above doesn't seem to be that straightforward when using QtCreator and qmake. Basically, from what I have gathered, you would need to divide your project to sub-projects where the main application code you want to test is built into a library and then different sub-projects for the proper application and the application doing the unit tests, each of which use the library. I'd like to hear if someone has an easier setup with examples for Harmattan applications?

I didn't want to use that much time into setting up the project and working out the kinks, so I settled for something simpler. If I want to run unit tests, I compile the application and the unit test files, but I exclude the application's main.cpp. Instead I include testmain.cpp which creates the unit test cases to run. If I want to run the application properly I just compile the application files without the unit test files. Whether to run the unit tests version is controlled from the project file with one CONFIG line. This removes some of the advantages of unit tests, as I have to remember to run the unit tests myself every once in a while to make sure nothing broke. But for me it's a good compromise for small and simple projects as I can use the project pretty much as it was created by QtCreator.

In my project.pro I have a line to switch between running unit tests or the proper application. This switch actually instructs the build system to include Qt Testing Library, but I use it as a trigger to switch between the unit test version and normal application version.

# Uncomment if just unit tests are to be ran.
# CONFIG += qtestlib
To run the unit tests instead of the main application, add after OTHER_FILES in project.pro rules to remove the main application's main.cpp and instead include the unit test's testmain.cpp. Also, the unit test sources are added here. Here's example from above directory structure:
qtestlib {
    # If qtestlib is in CONFIG, replace application's main.cpp
    # with unit test main. Also add unit test sources.
    SOURCES -= src/main.cpp
    SOURCES += tests/testmain.cpp \
        tests/module/testimplementation.cpp
    HEADERS += tests/logic/testimplementation.h
}
The default QtTest framework is a bit cumbersome in my opinion. It basically assumes that you have one executable for each test. I like to run all tests from the same tester. So that's why I have the testmain.cpp there. It contains the code to run all the tests. In the example it contains only the code to test the class "Implementation", but it's easy to add more tests there. Here's an example of it:

tests/testmain.cpp:

#include 
#include "module/testimplementation.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    int retval(0);

    retval += QTest::qExec(&TestImplementation(), argc, argv);
    // Add more test like above here

    return (retval ? 1 : 0);
}
A skeleton of the TestImplementation class that tests the Implementation class could be like: tests/module/testimplementation.h
#ifndef TESTIMPLEMENTATION_H
#define TESTIMPLEMENTATION_H

#include 

#include "../../src/module/implementation.h"

class TestImplementation : public QObject
{
    Q_OBJECT

private slots:
    void dummyTest();
};

#endif // TESTIMPLEMENTATION_H
tests/module/testimplementation.h
void
TestImplementation::dummyTest() {
    // Always succeeds
    QVERIFY(true);
}

Final notes

As you may read between the lines, I'm not totally satisfied with this solution. So if you have concrete examples of better solutions, please post in the comments. But if you just want something simple for a small project, hopefully this will help get you started.

Further reading

Here's some sources I used, see them for more information and ideas for more advanced methods for unit testing with Qt:

Tuesday, November 1, 2011

QmlApplicationViewer and setContextProperty

Qt SDK 1.1.3 application wizard creates a nice stub for starting the application, QmlApplicationViewer. It takes care of some of the differences in starting the application in different environments like Desktop, Symbian and Harmattan. It also handles Harmattan booster for improving application start-up time and sets-up QML debugging.

So using it, and even transferring an existing application to using it, is worth to do.

Unfortunately, the stub QmlApplicationViewer created by QtCreator does not work correctly in Harmattan if you want to set context properties to access C++ functionality from QML. Here's how I got it working without breaking the application in Desktop and Harmattan environments.

First of all, here's the basic skeleton program created by QtCreator where I want "myContextProperty" to be accessible from QML:

main.cpp:

#include <QtGui/QApplication>
#include <QtDeclarative>

#include "qmlapplicationviewer.h"

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> 
       app(createApplication(argc, argv));
    QScopedPointer<QmlApplicationViewer> 
       viewer(QmlApplicationViewer::create());

    // Here you create "myContextProperty" that 
    // can be assigned as context property for QML.
    // See Qt documents or my earlier postings 
    // for examples.
    // 
    // After that, assign it as context property:

    QDeclarativeContext *ctxt = viewer->rootContext();
    ctxt->setContextProperty("myContextProperty", 
                             &myContextProperty);

    viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer->setMainQmlFile(QLatin1String("qml/main.qml"));
    viewer->showExpanded();

    return app->exec();
}
This is how it should work out of the box. But not with the QmlApplicationViewer created by the current QtQuick application wizard. You can see the problem yourself from the source of QmlApplicationViewer, but the basic gist of the issue is that for Harmattan the QmlApplicationViewer::create() constructs a view from MDeclarativeCache, whose root context can not be accessed from QmlApplicationViewer. So above, you are setting a context properties for something that's not really used in QML.

To fix this, we add a function to QmlApplicationViewer to access it.

qmlapplicationviewer/qmlapplicationviewer.h:

public:
    QDeclarativeContext *rootContext();

qmlapplicationviewer/qmlapplicationviewer.cpp:

QDeclarativeContext *QmlApplicationViewer::rootContext()
{
    return d->view->rootContext();
}
Note that since qmlapplicationviewer is created by the wizard, the comments in it quite correctly warn against modifying. So when updating the SDK or QtCreator, check if some automatic updates happened add/remove above code if necessary.

One more thing... the working of the new qmlapplicationviewer also depends on updating the project file. There's some nice things anyway compared to the previous SDK, so well worth the effort in my opinion.

Tuesday, July 26, 2011

Changing and accessing profiles programmatically in Harmattan

Qt's mobility APIs have currently quite restricted support for any profile based operations. For example, you can not change the current active profile. Luckily you can do so by using D-Bus, as Harmattan uses as a backend MeeGo's profile daemon.

In this posting I'll show the code to do the following:

  • Iterating all available profiles
  • Setting the used active profile
  • Catching a notification when the active profile changes

A little background information first. Harmattan's profiles that you can see (Silent, Beep, Ringing), are not really profiles. Instead they match existing profiles (silent, meeting, general) like this:

Harmattan profile type Real profile name
Silent silent
Beep meeting
Ringing general

The profile backend specifies all profiles, and each profile contains properties as key/value pairs. The Harmattan profile types are defined in each profile type under a string key of "ringing.alert.type". So when selecting "Beep" profile from N9/N950, in the background really the "Silent" profile is selected because that profile has as a property "ringing.alert.type=Beep". What this means in practice is best shown with code.

For easier comprehension, these code samples are minimal and lack any error checking (ie. they assume the D-Bus queries always succeed).

Common defines

Here's some common defines used in the code samples. They are for using the D-Bus invocations
#define PROFILED_SERVICE "com.nokia.profiled"
#define PROFILED_PATH "/com/nokia/profiled"
#define PROFILED_INTERFACE "com.nokia.profiled"
#define PROFILED_GET_PROFILES "get_profiles"
#define PROFILED_SET_PROFILE "set_profile"
#define PROFILED_GET_VALUE "get_value"
#define PROFILED_PROFILE_CHANGED "profile_changed"
// The key for accessing Harmattan's profile type under profile
#define PROFILED_TYPE_VALUE "ringing.alert.type"

Iterating all known profiles

This piece of code can be used to iterate all known profiles. These are not the Harmattan profile types that you want to show if targetting N9/N950, but it is something that is anyway needed when constructing the Harmattan profile types list:
QStringList
ProfileClient::profiles() const
{
    QDBusInterface dbus_iface(PROFILED_SERVICE, PROFILED_PATH,
                              PROFILED_INTERFACE);

    QDBusReply<QStringList> reply = 
        dbus_iface.call(PROFILED_GET_PROFILES);
    return reply.value();
}
Here's the function that should be used with Harmattan if you want to show the same values as in Harmattan UI. This function basically iterates through the profiles and takes from each profile the property that defines the Harmattan profile type (Silent, Beep or Ringing):
QStringList
ProfileClient::profileTypes() const
{
    QDBusInterface dbus_iface(PROFILED_SERVICE, PROFILED_PATH,
                              PROFILED_INTERFACE);

    QStringList profileTypes;
    QString profile;
    foreach (profile, profiles())
    {
        QDBusReply<QString> reply = 
            dbus_iface.call(PROFILED_GET_VALUE, profile, 
                            PROFILED_TYPE_VALUE);
        profileTypes.append(reply.value());
    }

    // In Harmattan at least, profile type Ringing is 
    // attached to  "general" and "outdoors" profiles. 
    // The "general" profile is the one that's used for
    // "ringing", profile "outdoors" should not be used
    // when setting a profile.
    profileTypes.removeDuplicates();
    return profileTypes;
}

Setting the used active profile

Here's the code to set the active profile to the named one. Note that this function requires the real profile (silent, meeting, general). In Harmattan you probably want to set the profile by Harmattan's profile type. Since it's an easy mapping from Harmattan profile type to the real profile name, I'll leave that detail as a fun little exercise for anyone interested.
bool
ProfileClient::setProfile(QString profileName)
{
    QDBusInterface dbus_iface(PROFILED_SERVICE, PROFILED_PATH,
                              PROFILED_INTERFACE);

    // Returns true if success
    QDBusReply<bool> reply = 
        dbus_iface.call(PROFILED_SET_PROFILE, profileName);
    return reply.value();
}

Catching a notification when the active profile changes

If there's a need to do something when active profile changes, you can attach a signal handler to using the following code sample. This catches changes done either by your program or by anything else that uses the profile backend (like changing it from N9/N950 menu)
ProfileClient::ProfileClient(QObject *parent) : QObject(parent)
{
   QDBusConnection::sessionBus().connect("", "",
       PROFILED_INTERFACE, 
       PROFILED_PROFILE_CHANGED,
       this, 
       SLOT(profileChanged(bool, bool, QString))))
}

void
ProfileClient::profileChanged(bool changed, bool active, QString profile)
{
    if (changed && active) {
        qDebug("Profile changed to %s", qPrintable(profile));
    }
}

Further reading

That's the basics for getting started, but if you want more look for hints here:

Profile backend's configuration files

See in the device (or in QEMU emulator) the files stored under /etc/profiled. These are the configuration files, and there you can see under each profile the properties that they define and that you can access like I did above with the Harmattan profile type.

Profile backend's D-Bus API

MeeGo's profile backend that Harmattan uses is open source (luckily, or I would not have been able to figure out these things), and you can see other D-Bus functions that are available for use here: MeeGo profile_dbus.h