Qt in the Multi-Screen Age

Being a developer today means developing for multiple device types. When supporting different form factors, operating systems, and browsers, it’s not easy to write an app that targets a wide audience. By using cross-platform frameworks like Qt and providing a POSIX compliant operating system in BlackBerry 10, BlackBerry helps developers target multiple platforms. Additionally, this approach allows developers to learn only one API and technology instead of many.

Even with a framework like Qt, careful planning is needed to write an app that can be easily ported to different platforms. Let’s discuss some strategies for writing cross-platform Qt apps.

Separate Application Logic and UI

When designing your app, start by determining what logic can be shared across all platforms.  For existing apps that you want to bring to BlackBerry 10, review your code and identify the components that are consistent across all targeted platforms.

Store the Qt/C++ code that targets multiple platforms in one project and use the “.pro” file used by qmake to control compilation for each platform. To facilitate the portability of your code, the UI design can be implemented in QML and separated from the core business logic. Each platform can have its own QML file, while reusing the same business logic across platforms. The C++ code can be connected to your QML UI through plugins or export properties.

The chart below illustrates how your C++ and QML code can be integrated for both Cascades and pure Qt projects:

qt in the multi-screen age

Minimize Platform Specific Code and Encapsulate It

Not all of the business logic will be able to be shared across all platforms. Some things, like accessing sensors and handling in-app payments tend to be platform-specific. Encapsulate this code in separate modules, which will be built only for the platforms that need it. For example, you won’t need NFC code for a device that doesn’t support NFC. Try to minimize the code in these modules to the distinct calls needed by each supported platform.

An Example of Sharing Data Between Qt Quick and Cascades Apps

In many applications the C++/Qt layer supplies data to the QML layer through models. When writing cross-platform Qt apps, you should be aware that Cascades ListView elements expect different data models than Qt Quick. Data in Qt that is organized as lists of values or objects can be used to instantiate a model in a Cascades UI, and adapters can be created to expose more complex data models. We’ve created a sample for a straightforward case that shows you how to structure your app and share a data model across two different platforms:

  • Cascades running on BlackBerry 10
  • Qt Quick which will run on BlackBerry 10 as well as desktop platforms

The sample, available here, contains a data model that is a QList of our custom QObject class. The data model and data object classes are shared between the two projects. Each project has its own QML and its own way of binding the data to its list. You can see below in this snapshot how the two projects bind the data:

C++ application code of the Qt Quick based UI:

QDeclarativeView view; // create a new model in the C++ backend MyDataModel *myDataModel = new MyDataModel(view.rootContext()); // The dataModel needs to be added before loading the QML file view.rootContext()->setContextProperty("dataModel", QVariant::fromValue(myDataModel->data()) );

C++ code for the Cascades based UI:

// create a new model in the C++ backend MyDataModel *myDataModel = new MyDataModel(app);  // create a data model for Cascades UI QListDataModel<QObject*> *cascadesModel = new QListDataModel<QObject*>(myDataModel->data());  // load QML QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(this);  // make the data model available via a property in the context of the loaded QML file qml->documentContext()->setContextProperty("myCascadesDataModel", cascadesModel);

Notice that the only real difference is that the Cascades code creates a QListDataModel object with the QList defined in the MyDataModel class. Then the QListDataModel is bound to the QML. You can also see how the QML in the two apps differ:

QML code for the Qt Quick based UI:

ListView {         model: dataModel         anchors.fill: parent         delegate: Rectangle {             height: 40; width: root.width;             color: model.modelData.color             Text {                 text: name; anchors.centerIn: parent             }         }     }

QML code for the Cascades based UI:

ListView {             dataModel: myCascadesDataModel             layout: FlowListLayout {}             listItemComponents: [                 ListItemComponent {                     Container {                         background: Color.create(ListItemData.color)                         layoutProperties: FlowListLayoutProperties {                             aspectRatio: 15/1                             fillRatio: 1                         }                         Label {                             text:                             horizontalAlignment: HorizontalAlignment.Center                         }                     }                 }             ]         }

Further Reading

As mentioned above, complex data organized in trees or tables might require more sophisticated models than shown in our example. More advanced models are also needed for large amounts of data.  This is available in Qt as a set of Item Model classes, and adapters can be written in Cascades to support these models. Tobias Koenig from KDAB has a great article and code sample on using the QAbstractItemModel with a Cascades ListView to implement these models.

About erahnen