| Home · All Classes · Grouped Classes · Annotated · Functions |
Qtopia 4.2 introduces a new inter-process communication (IPC) API aimed to make it extremely easy to send and receive messages between applications. It is designed to be used in several ways, and features an intelligent data serialization infrastructure based on the Qt meta-type system. This enables the user to easily send messages across applications that can contain custom types. The user only has to implement serialization and deserialization functions and register the custom type with the API.
The Qtopia IPC API is based on the notion of channels and messages. Messages can be used by by an application to notify another application to take some action. Messages can have arguments associated with them. In essence, a message can be thought of as a remote method call. A message consists of a function identifier followed by a list of types in parantheses. There is no whitespace in the message name.
Channels are unique string based identifiers which are used to logically group a set of messages together. Effectively, a set of messages on a particular channel defines an interface. By convention all channels within Qtopia start with "QPE/".
The Qtopia IPC system uses the event-processing loop. If you create a message it will not be sent until the next the event loop is entered.
Qtopia IPC system consists of three major classes. The QtopiaIpcAdaptor is the preferred way to interface with the system. In addition, QtopiaChannel and QtopiaIpcEnvelope classes are provided to ease the transition between the Qtopia QCopChannel based systems and the new API.
Qtopia IPC API is exposed to the user through the QtopiaIpcAdaptor class. QtopiaIpcAdaptor can be used in two ways: direct access and an inheritance based approach.
To use QtopiaIpcAdaptor directly, we first construct a new object by providing a channel as so:
QtopiaIpcAdaptor *adaptor = new QtopiaIpcAdaptor("QPE/CannonExample");
The newly created adaptor object can now be used to send messages to remote applications, or to receive messages. To send messages use the send method calls:
// First we aim the cannon QtopiaIpcSendEnvelope envelope = adaptor->send(MESSAGE(aimCannon(int,int))); envelope << direction << elevation; // Alternatively if there are less than 3 arguments we can use send directly adaptor->send(MESSAGE(shootCannon(int)), cannonPower);
Another application is responsible for actually aiming and shooting the cannon. Let us suppose that the cannon application can reply with two types of messages, a hit or a miss. E.g. the remote application will send a missed() or hit() message. We can listen for such messages in the following manner:
class CannonListener : public QObject
{
Q_OBJECT
public slots:
void missed();
void hit();
};
...
CannonListener *listener = new CannonListener;
QtopiaIpcAdaptor *adaptor = new QtopiaIpcAdaptor("QPE/CannonExample");
QtopiaIpcAdaptor::connect(adaptor, SIGNAL(missed()), listener, SLOT(missed()));
QtopiaIpcAdaptor::connect(adaptor, SIGNAL(hit()), listener, SLOT(hit()));
We can also use QtopiaIpcAdaptor::connect to an object's signals to be sent over a channel automatically. For instance, we can rewrite the above example like this:
class CannonController : public QObject
{
Q_OBJECT
public slots:
void missed();
void hit();
signals:
void aimCannon(int dir, int elev);
void shootCannon(int power);
};
...
CannonController *control = new CannonController;
QtopiaIpcAdaptor *adaptor = new QtopiaIpcAdaptor("QPE/CannonExample");
// Hookup remote messages to our object
QtopiaIpcAdaptor::connect(adaptor, SIGNAL(missed()), control, SLOT(missed()));
QtopiaIpcAdaptor::connect(adaptor, SIGNAL(hit()), control, SLOT(hit()));
// Send our signals to the remote object
QtopiaIpcAdaptor::connect(control, SIGNAL(aimCannon(int,int)), adaptor, SLOT(aimCannon(int,int)));
QtopiaIpcAdaptor::connect(control, SIGNAL(shootCannon(int)), adaptor, SLOT(shootCannon(int)));
We should now recognize that for non-trivial interactions the last example is a quite common use case. It is thus desirable to avoid using QtopiaIpcAdaptor::connect for each connection, which can be quite error prone. QtopiaIpcAdaptor provides an alternate interaction technique based on inheritance. Instead of creating another class and using QtopiaIpcAdaptor::connect, we inherit directly from QtopiaIpcAdaptor and use the publish or publishAll methods to automatically publish all signals and slots on the channel. E.g.:
class CannonController : public QtopiaIpcAdaptor
{
Q_OBJECT
public:
CannonController(QObject *parent = 0);
void shoot();
public slots:
void missed();
void hit();
signals:
void aimCannon(int dir, int elev);
void shootCannon(int power);
};
CannonController::CannonController(QObject *parent)
: QtopiaIpcAdaptor("QPE/CannonExample", parent)
{
publishAll(SignalsAndSlots);
}
void CannonController::missed()
{
qDebug() << "Cannon missed!";
}
void CannonController::hit()
{
qDebug() << "Cannon HIT!";
}
void CannonController::shoot()
{
emit aimCannon(0, 45);
emit shootCannon(100);
}
...
CannonController *control = new CannonController;
control->shoot();
To make the example complete, we show how to extend our example with custom data types. First let us suppose that we'd like to use a custom ammunition type. We define a new enumeration named appropriately AmmunitionType. In order to exchange these enumerations, we must first register it with the meta-type system. This is accomplished by using the Q_DECLARE_USER_METATYPE_ENUM macro. This macro must be used in the header file where the enumeration is defined. Additionally we must use the Q_IMPLEMENT_USER_METATYPE_ENUM macro to implement the necessary serialization and deserialization functions.
// Header class CannonController : public QtopiaIpcAdaptor { Q_OBJECT public: enum AmmunitionType { Explosive = 0, ArmorPiercing, Napalm } CannonController(QObject *parent = 0); void shoot(); public slots: void missed(); void hit(); signals: void aimCannon(int dir, int elev); void shootCannon(int power); void setAmmunitionType(AmmunitionType type); }; Q_DECLARE_USER_METATYPE_ENUM(CannonController::AmmunitionType) ... // Implementation: Q_IMPLEMENT_USER_METATYPE_ENUM(CannonController::AmmunitionType)
In order to shoot our cannon we now need to send three messages to the remote application. For such a complex and mission critical system we should really send only one message for efficiency purposes. We define a new class that encapsulates the entire shoot order:
class CannonFireOrders
{
public:
enum AmmunitionType { Explosive = 0, ArmorPiercing, Napalm }
CannonFireOrders( AmmunitionType type, int direction, int elevation, int power);
int direction() const;
int elevation() const;
int power() const;
AmmunitionType type() const;
private:
int m_dir;
int m_elev;
int m_power;
AmmunitionType m_type;
};
Q_DECLARE_USER_METATYPE_ENUM(CannonFireOrders::AmmunitionType)
Q_DECLARE_USER_METATYPE(CannonFireOrders)
We also modify the CannonController class in the following manner:
class CannonController : public QtopiaIpcAdaptor
{
Q_OBJECT
public:
CannonController(QObject *parent = 0);
void shoot();
public slots:
void missed();
void hit();
signals:
void shootCannon(const CannonFireOrders &orders);
};
The only thing missing is a way to tell the Qtopia IPC API how to serialize the CannonFireOrders class so it can be shipped to a remote applications, and how the other side would deserialize the information and create a CannonFireOrders. To accomplish this, we must define two new methods in the CannonFireOrders class:
class CannonFireOrders
{
public:
....
template <typename Stream> void serialize(Stream &stream) const;
template <typename Stream> void deserialize(Stream &stream);
...
};
And in the implementation file we implement both methods. It is important to note this fact, as usually templates are defined in the header files!
Q_IMPLEMENT_USER_METATYPE_ENUM(CannonFireOrders::AmmunitionType)
Q_IMPLEMENT_USER_METATYPE(CannonFireOrders)
....
template <typename Stream> void CannonFireOrders::serialize(Stream &s) const
{
s << m_dir;
s << m_elev;
s << m_power;
s << m_type;
}
template <typename Stream> void CannonFireOrders::deserialize(Stream &s)
{
s >> m_dir;
s >> m_elev;
s >> m_power;
s >> m_type;
}
We can now successfully use custom classes across applications! Please note that both applications must link in the implementation of the custom class.
Developers are encouraged to use the new QtopiaIpcAdapter API. It is extremely powerful and can easily support custom classes. However, to support legacy applications and make it easier to port such applications to the new IPC system, Qtopia introduces two new classes. QtopiaChannel and QtopiaIpcEnvelope. Both of these classes work exactly as their QCop counterparts.
Each application listens on a channel called QPE/Application/appname , where appname is the executable name (the application identifier). Standard messages on this channel are:
QPE/Application/appname
The QPE/Application/appname channel has a special property: when messages are sent to these channels via QtopiaIpcEnvelope, the message is delivered even if the application is not yet running (the application is run and the message is then sent).
The qcop command-line tool can be used to access the Qtopia IPC system from shell scripts and for debugging purposes. Running qcop without any arguments will print a help message. The other commands are as follows:
| Command | Description |
|---|---|
| qcop send channel message [parameters] | Send message on channel with the specified parameters. |
| qcop service send name message [parameters] | Send message to the application that implements the service name with the specified parameters. |
| qcop watch channel [channel ...] | Watch all messages on channel and print them to standard output. If channel has the form QPE/Application/foo, this will watch all messages to the application foo. |
| qcop service watch name [name ...] | Same as above, but watches the application associated with the service name instead. |
| qcop wait channel message | Waits for message to appear on channel and then prints the message arguments to standard output. |
| qcop service wait name message | Same as above, but waits on the application channel for the application associated with the service. This can be used to wait for someone to send a message to the service. |
| qcop query channel | Query all messages that the application on channel can respond to. The application needs to handle the special message __query_qcop_messages(QString) for this to work correctly. The easiest method is to use the QtopiaIpcAdaptor or QtopiaAbstractService classes to implement your service. These classes will automatically respond to the query message. The argument to the message is the name of the channel to respond on. The qcop query command has a default timeout of 1 second unless overridden by the timeout flag. |
| qcop service query name | Same as above, but for the service called name. |
| qcop list | List all services and the Qtopia IPC channels that they map to. |
The following flags can be specified before a command to modify its behavior:
| Flag | Description |
|---|---|
| hex | Print the contents of QByteArray parameters in hexadecimal (default). |
| nohex | Print the contents of QByteArray parameters as Latin-1 strings. |
| timeout msecs | Set the timeout for the command to msecs milliseconds. |
| notimeout | Disable the timeout for the command. This is the default for most commands, except qcop query which has a default timeout of 1 second. |
| enum name | Recognise name as an enumerated type in message names. |
The following example will watch for all Qtopia IPC messages on the QPE/System channel:
qcop watch QPE/System
The channel name for qcop watch can include wildcards. For example, the following will watch all network-related channels:
qcop watch 'QPE/Network*'
It is usually necessary to quote channel names when they include wildcards, especially when using qcop watch '*'.
The following example sends a Time::editTime() message to the Time service:
qcop service send Time 'editTime()'
Multiple commands can be separated by -- on the command line. The following example sends the dial() message to the QPE/pppd channel and then waits for up to 30 seconds for a dialResult(bool) message on the same channel.
qcop send QPE/pppd 'dial()' -- timeout 30000 wait QPE/pppd 'dialResult(bool)'
| Copyright © 2007 Trolltech | Trademarks | Qtopia 4.2.5 |