|
Apr 06
2010
|
Using Qt on Symbian Part 0: Introduction and Qt BasicsPosted by: admin on Apr 06, 2010 Tagged in: Untagged
|
|
Introduction
Over the course of three weeks earlier this year, Maurizio Scotti and I attempted to develop a Universal Inbox application using Qt on S60 here at Symsource, as a way of getting to grips with the framework and assessing whether it was ready for use in production quality applications.
Brainstorming with the rest of our team, we decided that a good learning project would be to write a simple aggregator that would gather together all of a users SMS, email, Twitter and Facebook messages in a single application. The main view of the application would be a date ordered summary list of messages, with additional views to support the viewing of message details and the composition of new messages.
This series of blog posts is a summary of what we did, how we used Qt to do it, and what we thought of Qt as a platform. By its very nature, it’s a selective look at Qt, as I can only write about the pieces of the platform we actually used. It’s also deliberately not a guide to the practicalities of getting Qt installed and running, this being admirably dealt with in the Qt Quick Start page on the Symbian wiki.
Qt basics from the perspective of a Symbian programmer
Before we dive into the specifics of what we encountered, it’s worthwhile spending a bit of time going through some of the basic ideas behind Qt.
Qt was originally designed as a cross-platform widget and UI library for use on Linux and Windows. It’s since spread to more platforms and offers a standard API that supports a lot more than just GUI programming. There’s a decent selection of network interfaces, for example, and a fairly comprehensive set of container classes.
The basic components of the the UI will be familiar to anyone who’s done any modern UI programming: widgets, views, dialogs, menus and commands. The novel thing is how they fit together - rather than having a system of callbacks and interfaces or providing a simple loosely typed message passsing mechanism, Qt uses signals and slots.
Together, these make up a generalised publish and subscribe service. Signals correspond to events being published, slots act as sinks for the events, and the connect function is used to subscribe a slot to a signal. It’s also possible to chain signals to other signals, again using the connect function. Both signals and slots are declared as methods on a class, but only slots have definitions. Signals are emitted using the special Qt emit keyword, and received by the slots as though they were conventional function calls.1
All the necessary scaffolding needed for a class to handle signals and slots is wrapped up inside the Qt base class, QObject, and the Q_OBJECT declaration macro. Simply derive a class from QObject and include the Q_OBJECT declaration at the beginning of the class declaration and you can create signals and slots at will.
Doing this doesn’t just get you signal handling. Also included as part of QObject are: child object cleanup; low level event interception for things like key presses; support for Qt’s timer mechanism; and a degree of runtime reflection and dynamism. As a consequence of some of these features, objects inherited from QObject have to be treated as though each one has a unique identity, which means that naive copying of QObjects is a bad idea. To prevent problems with this, the QObject assignment operators and copy constructors are automatically disabled.
This identity/value issue, plus the overhead required for the rest of the functionality means that not all Qt classes inherit from QObject. In particular, the string-like classes and containers are all outside the QObject hierarchy. Instead, these all have copy-on-write value semantics, meaning that they can be passed around and returned as though they were simple, small objects, like TInt or TReal, without fear of incurring huge copying costs.
The standard Qt string class, QString, is a full fledged unicode string class, which should be used whenever you are manipulating text internally. It has all the expected string manipulation, searching and conversion operations, and grows and shrinks dynamically as you add and remove data from it.
To handle arrays of bytes, Qt provides a QByteArray with much of the same functionality. The relationship between the two is much like that between TDes16 and TDes8 - one is for text, the other for binary data (that may just happen to be encoded text).
There are several different templated container classes available:
QLinkedList- a linked listQVector- a resizable arrayQList- a hybrid list/array (somewhat like C++’s std::deque)QMap- a sorted associative array (with binary tree performance)QHash- a hashmap
There are also assorted subtypes, including a stack, queue and multimap. In general, they are easier to use than the Symbian equivalents, particularly the QHash, which has built in hash functions for a much greater variety of types than Symbian’s equivalent. The only obvious thing missing is a set or hashed set class.
The split between two different types of classes, and especially the difference in their copy semantics is reminiscent of Symbian’s division of its classes into C, T and R classes. However, beware of falling into the trap of assuming that the distinctions are equivalent: they are not.
Qt uses error codes (plus the standard C++ bad_alloc exception) rather than leaves, so there is no need for a cleanup stack, or anything approximating to it, and no explicit two stage construction. This in turn means that there isn’t the same need for a clear correlation between the type of an object and whether it’s created on the stack or not. Instead, everything can equally be created on the stack or heap. If created on the stack, it gets cleaned up normally when the stack is unwound (either normally, or when an exception is thrown); if created on the heap, it either has to be managed manually, or the ownership can be assigned to a parent object derived from QObject, with the object then being automatically deleted when its parent is. This ownership pattern tends to lead to a tree of ownership, with the QApplication object at the root.2
One side effect of this lack of type specialisation on storage type is that there is no automatic zeroing of memory for any Qt-derived object. So you have to make sure that you manually zero anything you expect to be zeroed as part of the object’s construction.
Coming up next…
Tomorrow, I dive into the details of how we used Qt in our application, starting right at the beginning with the implementation of the main function, followed by a look at the way Qt uses the MVC pattern.
1. The signals and slots are supposed to be typesafe, with the arguments of the signal checked against those of the slot. But on a couple of occasions I successfully compiled what appeared to be mismatched signals and slots.
2. This has some implications for the stack/heap balance in applications. Symbian code is generally written with the assumption that the available stack is (by non-embedded standards) extremely small, so very little gets created on the stack. This pattern can be followed without too much difficulty if you are writing all your Qt code from scratch, but when porting code, or using third party libraries, this is not necessarily possible. The current documentation is a bit vague on how this should be handled, though it may be interesting to note that the Qt friendly MMP file generated by Carbide automatically contains this line:
EPOCSTACKSIZE 0×14000
Which corresponds to 80K - ten times that used by Series60v3 application by default.

