(Update to the API found in the released Qt 4.2) |
m (use the right classes for code highlighting) |
||
| Line 20: | Line 20: | ||
Sample code in DCOP: | Sample code in DCOP: | ||
| − | <code | + | <code cppqt3> |
DCOPRef kded("kded", "favicons") | DCOPRef kded("kded", "favicons") | ||
DCOPReply reply = kded.call("iconForURL(KUrl)", url); | DCOPReply reply = kded.call("iconForURL(KUrl)", url); | ||
| Line 31: | Line 31: | ||
Sample code in D-Bus: | Sample code in D-Bus: | ||
| − | <code | + | <code cppqt> |
QDBusInterface kded("org.kde.kded", "/modules/favicons", | QDBusInterface kded("org.kde.kded", "/modules/favicons", | ||
"org.kde.FaviconsModule"); | "org.kde.FaviconsModule"); | ||
| Line 48: | Line 48: | ||
Sample; | Sample; | ||
| − | <code | + | <code cppqt> |
QDBusInterface interface("org.kde.myapp", "/MyObject", | QDBusInterface interface("org.kde.myapp", "/MyObject", | ||
"org.kde.MyInterface"); | "org.kde.MyInterface"); | ||
| Line 67: | Line 67: | ||
* Remove the k_dcop method references. | * Remove the k_dcop method references. | ||
* Make the methods that were DCOP-accessible scriptable slots. That is, you must make it: | * Make the methods that were DCOP-accessible scriptable slots. That is, you must make it: | ||
| − | <code | + | <code cppqt> |
public Q_SLOTS: | public Q_SLOTS: | ||
Q_SCRIPTABLE void methodName(); | Q_SCRIPTABLE void methodName(); | ||
| Line 78: | Line 78: | ||
In order to register the object, you'll need to do: | In order to register the object, you'll need to do: | ||
| − | <code | + | <code cppqt> |
QDBusConnection::sessionBus().registerObject("<object-path>", this, | QDBusConnection::sessionBus().registerObject("<object-path>", this, | ||
QDBusConnection::ExportScriptableSlots); | QDBusConnection::ExportScriptableSlots); | ||
| Line 91: | Line 91: | ||
You'll declare it as: | You'll declare it as: | ||
| − | <code | + | <code cppqt> |
class MyAdaptor: public QDBusAbstractAdaptor | class MyAdaptor: public QDBusAbstractAdaptor | ||
{ | { | ||
| Line 106: | Line 106: | ||
And the implementation will look like: | And the implementation will look like: | ||
| − | <code | + | <code cppqt> |
MyAdaptor::MyAdaptor(QObject *parent) | MyAdaptor::MyAdaptor(QObject *parent) | ||
: QDBusAbstractAdaptor(parent) | : QDBusAbstractAdaptor(parent) | ||
| Line 119: | Line 119: | ||
In the class using the adaptor, do this: | In the class using the adaptor, do this: | ||
| − | <code | + | <code cppqt> |
new MyAdaptor(this); | new MyAdaptor(this); | ||
QDBusConnection::sessionBus().registerObject("/<object-path>", this, | QDBusConnection::sessionBus().registerObject("/<object-path>", this, | ||
| Line 138: | Line 138: | ||
Sample DCOP code: | Sample DCOP code: | ||
| − | <code | + | <code cppqt3> |
class MyClass: public QObject, public DCOPObject | class MyClass: public QObject, public DCOPObject | ||
{ | { | ||
| Line 165: | Line 165: | ||
The equivalent code with QtDBus would be: | The equivalent code with QtDBus would be: | ||
| − | <code | + | <code cppqt> |
class MyClass: public QObject | class MyClass: public QObject | ||
{ | { | ||
This tutorial will explain how to convert code that uses DCOP for its interprocess communication (IPC) to D-Bus. D-Bus is the new IPC system for KDE 4.
Contents |
The most direct replacement of DCOPClient are QDBusConnection and QDBusConnectionInterface. The convenience method QDBusConnection::sessionBus() usually replaces all occurrences of KApplication::dcopClient().
The methods in DCOPClient that are related to listing existing applications on the bus are in QDBusBusService (which you can access with QDBusConnection::sessionBus().interface()).
DCOPClient has a "call" method that takes two QByteArray's containing the data to be transmitted and the data that was received. The most direct replacement for this is using QDBusMessage and QDBusConnection::send or call. You most likely don't want to use that.
Instead, drop the QByteArray and QDataStream variables and use the dynamic call mode (see next section).
The direct replacement for DCOPRef is QDBusInterface. The replacement for DCOPReply is QDBusReply.
However, there are some important differences to be noticed:
Sample code in DCOP:
DCOPRef kded("kded", "favicons")
DCOPReply reply = kded.call("iconForURL(KUrl)", url);
QString icon;
if (reply.isValid())
reply.get(icon);
return icon;
Sample code in D-Bus:
QDBusInterface kded("org.kde.kded", "/modules/favicons",
"org.kde.FaviconsModule");
QDBusReply<QString> reply = kded.call("iconForURL", url.url()); return reply;
Things to note in the code:
D-Bus also supports multiple return values. You will normally not find this kind of construct in DCOP, since it didn't support that functionality. However, this may show up in the form of a struct being returned. In this case, you may want to use the functionality of multiple return arguments. You'll need to use QDBusMessage in this case:
Sample;
QDBusInterface interface("org.kde.myapp", "/MyObject",
"org.kde.MyInterface");
QDBusMessage reply = interface.call("myFunction", argument1, argument2); if (reply.type() == QDBusMessage::ReplyMessage) {
returnvalue1 = reply.at(0).toString();
returnvalue2 = reply.at(1).toInt();
/* etc. */
}
There is no direct replacement for DCOPObject. It's replaced by a normal QObject with explict registering. You may use this new QObject with or without an adaptor. So, in order to port, you need to follow these steps:
public Q_SLOTS:
Q_SCRIPTABLE void methodName();
Note that the Q_CLASSINFO macro is case-sensitive. Do not misspell "D-Bus Interface" (that's capital I and D-Bus has a dash).
In order to register the object, you'll need to do:
QDBusConnection::sessionBus().registerObject("<object-path>", this,
QDBusConnection::ExportScriptableSlots);
Normally, "<object-path>" will be the argument the class was passing to the DCOPObject constructor. Don't forget the leading slash. Another useful option to the third argument is QDBusConnection::ExportScriptableProperties, which will export the scriptable properties.
DCOP had broken support for signals. They were public methods, while normal signals in QObject are protected methods. The correct way to port from DCOP signals is to refactor the code to support signals correctly.
Adaptors are a special QObject that you attach to your normal QObject and whose sole purpose is to relay things to and from the bus. You'll generally use it to export more than one interface or when you need to translate the call arguments in any way.
You'll declare it as:
class MyAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.MyAdaptor")
public:
MyAdaptor(QObject *parent);
signals:
void signal1(); void signal2(const QString &argument);
};
And the implementation will look like:
MyAdaptor::MyAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
setAutoRelaySignals(true); /* alternative syntax: connect(parent, SIGNAL(signal1()), SIGNAL(signal1())); connect(parent, SIGNAL(signal2(QString)), SIGNAL(QString())); */
}
In the class using the adaptor, do this:
new MyAdaptor(this);
QDBusConnection::sessionBus().registerObject("/<object-path>", this,
QDBusConnection::ExportAdaptors);
Things to notice:
DCOP had the capability of doing transactions: that is, delay the reply from a called method until later on. QtDBus has the same functionality, under a different name.
Unlike DCOP, with QtDBus, you need a special parameter to your slot in order to receive the information about the call and set up the delayed reply. This parameter is of type QDBusMessage and must appear after the last input parameter. You declare that you want a delayed reply by creating a reply. You will be responsible for sending it later.
Sample DCOP code:
class MyClass: public QObject, public DCOPObject
{
DCOPTransaction *xact; DCOPClient *client;
k_dcop:
QString myMethod(const QString &arg1)
{
client = callingDcopClient();
xact = client->beginTransaction();
QTimer::singleShot(0, this, SLOT(processLater());
return QString(); // reply will be ignored
}
public Q_SLOTS:
void processLater()
{
QByteArray replyData;
QDataStream stream(&replyData, QIODevice::WriteOnly);
stream << QString(QLatin1String("foo"));
client->endTransaction(xact, "QString", replyData);
}
};
The equivalent code with QtDBus would be:
class MyClass: public QObject
{
QDBusMessage reply; QDBusConnection connection;
public Q_SLOTS:
Q_SCRIPTABLE QString myMethod(const QString &arg1,
const QDBusMessage &msg)
{
connection = QDBusConnection::sender();
reply = QDBusMessage::createReply(msg);
QTimer::singleShot(0, this, SLOT(processLater());
return QString(); // reply will be ignored
}
void processLater()
{
reply << QString(QLatin1String("foo"));
connection.send(reply);
}
};