Streams are used for serializing object data, saving it to files, and transferring it between devices and threads. In most cases there is no need to convert stream data between Symbian and Qt. Geometry conversion involves converting points, sizes, rectangles and x and y coordinates and in most cases is straight-forward.
In this section we briefly discuss mechanisms for
The Symbian C++ stream classes are used to serialize object's internal data into a series of bytes and to initialize them from a series of bytes.
Objects that need an external representation
define ExternaliseL()
and InternaliseL()
methods as shown below (note that classes which define these methods
can use the global >>
and <<
streaming operators to externalise/internalize data to/from a stream).
void ExternalizeL(RWriteStream& aStream) const; void InternalizeL(RReadStream& aStream);
The methods
write/read the object's members in terms of their own ExternalizeL()
/InternalizeL()
methods, and ultimately platform-independent
representation of the basic types (including descriptors) defined
in the RWriteStream
/RReadStream
classes.
RWriteStream
/RReadStream
are abstract classes. When we externalize objects we use a concrete
stream that sends the data to a file, file store, raw memory, or a
fixed or dynamic buffer. Some streams are initialized with other stream
objects - for example to compress or encrypt the data. Native Symbian
applications usually store their data as file based "stores" of streams,
that are associated with the application using its unique identifier.
Symbian
uses the concrete 8 bit variant descriptors as buffers for non-string data: TBufC8
, TBuf8
, TPtrC8
, TPtr8
, RBuf8
, HBufC8
. For example,
they are used as the send and receive buffers to the RSocket
APIs. We can also use a stream interfaced socket to serialize our
objects directly to/from a socket.
Package Buffers (aligned 8 bit descriptors) are used for the purpose of transferring
objects between threads and processes. These buffers allow developers
to package any value type (a T
class) as a descriptor.
Note that this approach is acceptable because we don't need a platform
independent representation to communicate with another thread.
There are three package buffer variants: TPckgBuf
takes a copy of the object data, while TPckgC
and TPckg
simply point to existing const and
non-const objects (respectively).
Qt's QIODevice
is an abstraction
for a "device" capable of reading or writing blocks of data. This
has a number of subclasses (including QTcpSocket
, QUdpSocket
, QBuffer
, QFile
, QLocalSocket
, QNetworkReply
,
and QProcess
) that are used for writing to files,
processes, sockets, buffers etc.
Qt also provides higher level
stream classes QDataStream
and QTextStream
, that can be used to stream binary and text data (respectively)
to any QIODevice
. The stream classes serialize data
in a platform independent (but Qt-version specific) manner. Classes
that can serialize data overload the >> and << operators with
variants that take a QDataStream
argument (or have
an associated method).
In-memory 8-bit text and binary data
are usually stored in a QByteArray
. This is an array
of bytes which has a very similar API to the QString class. Note that
QBuffer class provides a QIODevice interface for a QByteArray.
The Input/Output and binary classes are well documented in the
class documentation. There is also a very good overview of QByteArray
in chapter 11 and Input/Output in chapter 12
of C++ GUI Programming with Qt 4 , Second Edition, Jasmin
Blanchette and Mark Summerfield, Prentice Hall (2006) (the
first edition is available free online here).
Symbian and Qt's classes
and approaches for serializing data are fundamentally the same; data
is externalized to a stream in a platform independent form. In Symbian
this stream might be a file or memory buffer, while in Qt the stream
is associated with a QIODevice
that is a file, buffer
etc. The main difference between the implementations is that Symbian
C++ has a very small set of platform-independent types (that it has
maintained consistently across versions), while Qt has a richer set
of types for which the implementation has varied across versions.
The good news is that there is unlikely to be a reason to convert between the Qt and Symbian serialization mechanisms. If you do need to transfer data that is serialized in one or the other development environments then first import and then convert appropriately (using casts, or some more complicated method).
If you're working
with raw data then converting between a QByteArray
and a descriptor is much the same as converting
between a QString and a descriptor - e.g.:
TPtrC8 myDataDescriptor( reinterpret_cast<const TText8*> (myQtData.constData()),myQtData.size()); //Take a copy of the data HBufC8* buffer = HBufC8::New(myDescriptor.Length()); Q_CHECK_PTR(buffer); buffer->Des().Copy(myDataDescriptor );
Remember that
the data returned by QByteArray::constData()
and data()
belongs to the QByteArray
, so you
may need to take a copy as shown above.
To convert the other
way you can use the QByteArray to create a deep copy of the data in
the descriptor, or QByteArray::fromRawData()
if you
know the lifetime of your QByteArray
will exceed
that of its users in Symbian C++ code:
QByteArray myQtArray(reinterpret_cast<const char*>(theDescriptor.Ptr()),theDescriptor.Length());
Qt and Symbian C++ define similar geometry types.
TPoint
and QPoint
are effectively the same. Both store a two-dimensional point in
Cartesian co-ordinates using x and y co-ordinate values (of type TInt (typedefd to signed int
) and int
respectively).
TSize
and QSize
are also the effectively the same. Both store the
width and height value, again using a TInt
and int
respectively. Converting is straightforward:
QSize myQSize = QSize(myTSize.iWidth, myTSize.iHeight); //To QSize TSize myTSize = TSize(myQSize .width(), myQSize .height()); //to TSize
TRect
and QRect
both define
a rectangular area with a particular position within a co-ordinate
system. Both store the top left co-ordinate of the rectangle. TRect
stores a TSize
for the rectangle,
while QRect
stores separate values for the width
and height. Converting is straightforward:
QRect myQRect = QRect(myTRect.iTl.iX, myTRect.iTl.iY, myTRect.Width(), myTRect.Height()); //to QRect TRect myTRect = TRect(TPoint(myQRect.left(), myQRect.top()), TSize(myQRect.width(), myQRect.height())); //to TRect
qcore_symbian_p.h
defines the following inline
functions which do these conversions:
static inline QSize qt_TSize2QSize(const TSize&
ts)
static inline TSize qt_QSize2TSize(const QSize&
qs)
static inline QRect qt_TRect2QRect(const TRect&
tr)
static inline TRect qt_QRect2TRect(const QRect&
qr)
Since this file is not part of the public API, you may choose to copy the code into your own project.
Note that QRect
also provides mechanisms for getting the bottom and right co-ordinates
of the rectangle. These should not be used for translating to TRect
s, as for historical reasons they deviate from the
"true" bottom right of the rectangle. See the QRect
documentation for more information.
Most of the material in this topic is based with permission on a Symbian Foundation wiki article Apps:Using Qt and Symbian C++ Together . The version used was that available at Symbian Foundation on 3 November 2010. The content in this page is licensed under the Creative Commons Attribution-Share Alike 2.0 UK: England & Wales License (http://creativecommons.org/licenses/by-sa/2.0/uk).