Development/Tutorials/Porting to D-Bus: Difference between revisions

From KDE TechBase
(trolls may not be the only ones using teambuilder. add icecream for completeness)
(Mark for archiving)
 
(25 intermediate revisions by 7 users not shown)
Line 1: Line 1:
== Compiling D-Bus ==
{{Archived}}
The KDE libraries require D-Bus version 0.62 at least. See http://www.freedesktop.org/wiki/Software_2fdbus for the latest release.


To compile:
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.
<code bash>
==Porting from DCOP to D-Bus==
% ./configure --disable-qt --disable-qt3 --prefix=$DBUSDIR
=== Converting from DCOPClient ===
% make
The most direct replacement of {{class|DCOPClient}} are {{qt|QDBusConnection}} and {{qt|QDBusConnectionInterface}}. The convenience method QDBusConnection::sessionBus() usually replaces all occurrences of KApplication::dcopClient().
% make install
</code>


To run the D-Bus daemon, type the command:
The methods in {{class|DCOPClient}} that are related to listing existing applications on the bus are in {{qt|QDBusBusService}} (which you can access with QDBusConnection::sessionBus().interface()).
<code bash>
% eval `PATH=$DBUSDIR/bin $DBUSDIR/bin/dbus-launch --auto-syntax`
</code>


== Compiling QtDBus ==
=== Converting Hand-written DCOPClient calls ===
QtDBus depends on D-Bus.
DCOPClient has a "call" method that takes two {{qt|QByteArray}}'s containing the data to be transmitted and the data that was received. The most direct replacement for this is using {{qt|QDBusMessage}} and QDBusConnection::send or call. You most likely don't want to use that.


D-Bus is found using {{program|pkg-config}}, so if you did not install it to a standard path in the previous step, set the PKG_CONFIG_PATH environment variable:
Instead, drop the {{qt|QByteArray}} and {{qt|QDataStream}} variables and use the dynamic call mode (see next section).
<code bash>
% PKG_CONFIG_PATH=$DBUSDIR/lib/pkgconfig; export PKG_CONFIG_PATH
</code>


{{Tip|you may want to install QtDBUS to $DBUSDIR too.}}
=== Converting Calls using DCOPRef and DCOPReply ===
 
The direct replacement for {{class|DCOPRef}} is {{qt|QDBusInterface}}. The replacement for DCOPReply is {{qt|QDBusReply}}.
QtDBus now lives in {{module|kdesupport}} and uses {{program|cmake}}. It also requires you to have Qt 4.1.3 in order to compile (qt-copy has it). To compile it:
<code bash>
% svn co $SVNROOT/trunk/kdesupport
% cd $your_preferred_objdir
% cmake $OLDPWD/kdesupport
% make
</code>
 
== Compiling kdelibs ==
Remember to set PKG_CONFIG_PATH to where you installed D-Bus to ({{path|$DBUSDIR/lib/pkgconfig}}), where you installed QtDBUS and where Qt is installed ({{path|$QTDIR/lib}}). This is necessary because QtDBUS depends on Qt and is found using {{program|pkg-config}}.
 
Commands:
<code bash>
% cd kdelibs
% export PKG_CONFIG_PATH=$QTDIR/lib:$DBUSDIR/lib/pkgconfig
% cd $objdir
% cmake $OLDPWD
% make
</code>
 
To use teambuilder to distribute the build: pass {{program|cmake}} the flags
<code>
-DCMAKE_C_COMPILER=/opt/teambuilder2/bin/gcc -DCMAKE_CXX_COMPILER=/opt/teambuilder2/bin/g++
</code>
 
To use icecream to distribute the build: pass {{program|cmake}} the flags
<code>
-DCMAKE_C_COMPILER=/opt/icecream/bin/gcc -DCMAKE_CXX_COMPILER=/opt/icecream/bin/g++
</code>
 
== Porting existing DCOP code ==
=== Usage of DCOPClient ===
The most direct replacement of {{program|DCOPClient}} are [http://developer.kde.org/~thiago/QtDBus/qdbusconnection.html|QDBusConnection] and [http://developer.kde.org/~thiago/QtDBus/qdbusbusservice.html|QDBusBusService]. The convenience method QDBus::sessionBus() usually replaces all occurrences of KApplication::dcopClient().
 
The methods in {{class|DCOPClient}} that are related to listing existing applications on the bus are in {{class|QDBusBusService}} (which you can access with QDBus::sessionBus().busService()).
 
=== Hand-written calls using DCOPClient ===
DCOPClient has a "call" method that takes two {{class|QByteArray}}'s containing the data to be transmitted and the data that was received. The most direct replacement for this is using [http://developer.kde.org/~thiago/QtDBus/qdbusmessage.html QDBusMessage] and QDBusConnection::send or sendWithReply. You most likely don't want to use that.
 
Instead, drop the {{class|QByteArray}} and {{class|QDataStream}} variables and use the dynamic call mode (see next section).
 
=== Calls using DCOPRef and DCOPReply ===
The direct replacement for {{class|DCOPRef}} is {{class|QDBusInterfacePtr}}, which is just a wrapper around [http://developer.kde.org/~thiago/QtDBus/qdbusinterface.html QDBusInterface]. The replacement for [http://developer.kde.org/~thiago/QtDBus/qdbusreply.html|QDBusReply].


However, there are some important differences to be noticed:
However, there are some important differences to be noticed:
* {{class|QDBusInterface}} is not "cheap": it constructs an entire {{class|QObject}}. So, if you can, store the value somewhere for later reuse.
* {{qt|QDBusInterface}} is not "cheap": it constructs an entire {{qt|QObject}}. So, if you can, store the value somewhere for later reuse.
* QDBusReply is a template class, so you must know ahead of time what your reply type is.
* {{qt|QDBusReply}} is a template class, so you must know ahead of time what your reply type is.
* If you create a {{class|QDBusInterface}} without specifying the third argument (the interface name), this will generate a round-trip to the remote application and will allocate non-shared memory. So, wherever possible, use the interface name to make it cached.
* If you create a {{qt|QDBusInterface}} without specifying the third argument (the interface name), this will generate a round-trip to the remote application and will allocate non-shared memory. So, wherever possible, use the interface name to make it cached.


Sample code in DCOP:
Sample code in DCOP:
~pp~
<syntaxhighlight lang="cpp-qt">
    DCOPRef kded("kded", "favicons")
DCOPRef kded("kded", "favicons")
    DCOPReply reply = kded.call("iconForURL(KUrl)", url);
DCOPReply reply = kded.call("iconForURL(KUrl)", url);
    QString icon;
QString icon;
    if (reply.isValid())
if (reply.isValid())
        reply.get(icon);
    reply.get(icon);
    return icon;
return icon;
~/pp~
</syntaxhighlight>
 


Sample code in DBUS:
Sample code in D-Bus:
~pp~
<syntaxhighlight lang="cpp-qt">
    QDBusInterfacePtr kded("org.kde.kded", "/modules/favicons", "org.kde.FaviconsM
QDBusInterface kded("org.kde.kded", "/modules/favicons",    
odule");
                    "org.kde.FaviconsModule");
    QDBusReply<QString> reply = kded->call("iconForURL", url.url());
QDBusReply<QString> reply = kded.call("iconForURL", url.url());
    return reply;
return reply;
~/pp~
</syntaxhighlight>


Things to note in the code:
Things to note in the code:
# use the 3-argument for in QDBusInterfacePtr. The interface name you can usually
* use the 3-argument version of {{qt|QDBusInterface}}. The interface name you can usually obtain by calling "interfaces" on the existing DCOP object (in this case, "dcop kded favicons interfaces").
obtain by calling "interfaces" on the existing DCOP object (in this case, "dcop kd
* the "application id" becomes a "service name" and it should start with "org.kde"
ed favicons interfaces").
* the "object id" becomes an "object path" and must start with a slash
# the "application id" becomes a "service name" and it should start with "org.kde"
* you don't write the function signature: just the function name
# the "object id" becomes an "object path" and must start with a slash
* custom types are not supported (nor ever will be), so you need to convert them to basic types
# it's "kded->call", not "kded.call"
# you don't write the function signature: just the function name
# custom types are not supported (nor ever will be), so you need to convert them t
o basic types


DBUS also supports multiple return values. You will normally not find this kind of
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 {{qt|QDBusMessage}} in this case:
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;
Sample;
~pp~
<syntaxhighlight lang="cpp-qt">
    QDBusInterfacePtr interface("org.kde.myapp", "/MyObject", "org.kde.MyInterface
QDBusInterface interface("org.kde.myapp", "/MyObject",
");
                        "org.kde.MyInterface");
    QDBusMessage reply = interface->call("myFunction", argument1, argument2);
QDBusMessage reply = interface.call("myFunction", argument1, argument2);
    if (reply.type() == QDBusMessage::ReplyMessage) {
if (reply.type() == QDBusMessage::ReplyMessage)
        returnvalue1 = reply.at(0).toString();
{
        returnvalue2 = reply.at(1).toInt();
    returnvalue1 = reply.at(0).toString();
    returnvalue2 = reply.at(1).toInt();
         /* etc. */
         /* etc. */
    }
}
~/pp~
</syntaxhighlight>


<h3>Usage of DCOPObject</h3>
===Converting from DCOPObject===
There is no direct replacement of DCOPObject. It's replaced by normal QObject, wit
There is no direct replacement for {{class|DCOPObject}}. It's replaced by a normal {{qt|QObject}} with explict registering. You may use this new {{qt|QObject}} with or without an adaptor. So, in order to port, you need to follow these steps:
h or without an adaptor, with explicit registering. So, in order to port, you need
to follow these steps:


# Remove the "virtual public DCOPObject" from the class declaration
* Remove the "virtual public DCOPObject" from the class declaration
# Replace the K_DCOP macro with Q_CLASSINFO("D-Bus Interface", "<interfacename>")
* Replace the K_DCOP macro with Q_CLASSINFO("D-Bus Interface", "<interfacename>") where <interfacename> is the name of the interface you're declaring (generally, it'll be "org.kde" followed by the class name itself)
where <interfacename> is the name of the interface you're declaring (generally, it
* Remove the k_dcop method references.
'll be "org.kde" followed by the class name itself)
* Make the methods that were DCOP-accessible scriptable slots. That is, you must make it:
# Remove the k_dcop method references.
<syntaxhighlight lang="cpp-qt">
# Make the methods that were DCOP-accessible scriptable slots. That is, you must m
public Q_SLOTS:
ake it:
~pp~
  public Q_SLOTS:
     Q_SCRIPTABLE void methodName();
     Q_SCRIPTABLE void methodName();
~/pp~
</syntaxhighlight>
# Change "ASYNC" to "Q_NOREPLY void"
* Change "ASYNC" to "Q_NOREPLY void"
# Remove the call to the DCOPObject constructor in the class' constructor.
* Remove the call to the DCOPObject constructor in the class' constructor.
# Add the registering to the constructor.
* Register the {{qt|QObject}} with D-Bus in the constructor.


Note that the Q_CLASSINFO macro is case-sensitive. Do not misspell "D-Bus Interfac
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).
e" (that's capital I and D-Bus has a dash).


In order to register the object, you'll need to do:
In order to register the object, you'll need to do:
~pp~
<syntaxhighlight lang="cpp-qt">
    QDBus::sessionBus().registerObject("<object-path>", this, QDBusConnection::Exp
QDBusConnection::sessionBus().registerObject("<object-path>", this,
ortSlots);
                            QDBusConnection::ExportScriptableSlots);
~/pp~
</syntaxhighlight>
 
Normally, "<object-path>" will be the argument the class was passing to the DCOPOb
ject constructor. Don't forget the leading slash. Another useful option to the thi
rd argument is QDBusConnection::ExportProperties, which will export the scriptable
properties.


<h3>Signals</h3>
Normally, "<object-path>" will be the argument the class was passing to the {{class|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 a broken support for signals. They were public methods, while normal sign
als in QObject are protected methods. The correct way to port is to refactor the c
ode to support signals correctly.


On the current version of QtDBus, you cannot use QDBusConnection::ExportSignals. T
===Signals===
his is a known limitation and will be fixed in the future. In order to use signals, you'll need to write an adaptor.
DCOP had broken support for signals. They were public methods, while normal signals in {{qt|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 so
Adaptors are a special {{qt|QObject}} that you attach to your normal {{qt|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.
le purpose is to relay things to and from the bus. You'll generally use it to expo
rt more than one interface or when you need to translate the call arguments in any way.


You'll declare it as:
You'll declare it as:
~pp~
<syntaxhighlight lang="cpp-qt">
class MyAdaptor: public QDBusAbstractAdaptor
class MyAdaptor: public QDBusAbstractAdaptor
{
{
Line 179: Line 105:
     void signal2(const QString &argument);
     void signal2(const QString &argument);
};
};
~/pp~
</syntaxhighlight>


And the .cpp file will have:
And the implementation will look like:
~pp~
<syntaxhighlight lang="cpp-qt">
MyAdaptor::MyAdaptor(QObject *parent)
MyAdaptor::MyAdaptor(QObject *parent)
    : QDBusAbstractAdaptor(parent)
: QDBusAbstractAdaptor(parent)
{
{
     setAutoRelaySignals(true);
     setAutoRelaySignals(true);
Line 192: Line 118:
     */
     */
}
}
~/pp~
</syntaxhighlight>


In the class using the adaptor, do this:
In the class using the adaptor, do this:
~pp~
<syntaxhighlight lang="cpp-qt">
    new MyAdaptor(this);
new MyAdaptor(this);
    QDBus::sessionBus().registerObject("/<object-path>", this, QDBusConnection::Ex
QDBusConnection::sessionBus().registerObject("/<object-path>", this,
portAdaptors);
                                            QDBusConnection::ExportAdaptors);
~/pp~
</syntaxhighlight>


Things to notice:
Things to notice:
* MyAdaptor is not exported. This is a private class and the .h file should be a _p.h as well.
* MyAdaptor is not exported. This is a private class and the .h file should be a _p.h as well.
* There's no need to mark the signals Q_SCRIPTABLE. The same goes for slots and pr
* There's no need to mark the signals in the adaptor as Q_SCRIPTABLE. The same goes for the slots and properties in the adaptor
operties.
* The "setAutoRelaySignals" function just connects all signals in "parent" to the signals of the same name and arguments in the adaptor.
* The "setAutoRelaySignals" function just connects all signals in "parent" to the
* There's no need to store the adaptor pointer, it'll be deleted automatically when needed. Therefore, do not delete it and, especially, do '''not''' reparent it.
signals of the same name and arguments in the adaptor.
* You can create more adaptors later, if you need. There's no need to re-register the object.
* There's no need to store the adaptor pointer, it'll be deleted automatically whe
n needed. Therefore, do not delete it and, especially, do NOT reparent it.
* You can create more adaptors later, if you need. There's no need to
re-register the object.


<h3>DCOP Transactions</h3>
===DCOP Transactions===
DCOP had the capability of doing transactions: that is, delay the reply from a cal
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.
led method until later on. QtDBus has the same functionality, under a different na
me.


Unlike DCOP, with QtDBus, you need a special parameter to your slot in order to re
Unlike DCOP, with QtDBus, you need to declare that your class may want to do transactions. For that, you must make your object derive from {{qt|QDBusContext}} as well as from your your normal inheritance. This will provide you with a few extra methods in your class. You will want to call setDelayedReply to notify QtDBus that the reply will come at a later point in time. You also want to keep the original connection object and message so as to be able to send the proper reply on the proper connection.
ceive 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 decl
are that you want a delayed reply by creating a reply. You will be responsible for
sending it later.


Sample DCOP code:
Sample DCOP code:
~pp~
<syntaxhighlight lang="cpp-qt">
class MyClass: public QObject, public DCOPObject
class MyClass: public QObject, public DCOPObject
{
{
Line 248: Line 164:
     }
     }
};
};
~/pp~
</syntaxhighlight>


The equivalent code with QtDBus would be:
The equivalent code with QtDBus would be:
~pp~
<syntaxhighlight lang="cpp-qt">
class MyClass: public QObject
class MyClass: public QObject, private QDBusContext
{
{
     QDBusMessage reply;
     QDBusMessage reply;
    QDBusConnection connection;


public Q_SLOTS:
public Q_SLOTS:
     Q_SCRIPTABLE QString myMethod(const QString &arg1, const QDBusMessage &msg)
     Q_SCRIPTABLE QString myMethod(const QString &arg1)
     {
     {
         reply = QDBusMessage::methodReply(msg);
        // connection(), message() and setDelayedReply()
        // come from QDBusContext
        connection = connection();
         reply = QDBusMessage::createReply(message());
        setDelayedReply(true);
         QTimer::singleShot(0, this, SLOT(processLater());
         QTimer::singleShot(0, this, SLOT(processLater());
         return QString();      // reply will be ignored
         return QString();      // reply will be ignored
Line 266: Line 187:
     void processLater()
     void processLater()
     {
     {
         reply << QString(QLatin1String("foo"));
         reply << QString::fromLatin1("foo");
         reply.connection().send(reply);
         connection.send(reply);
     }
     }
};
};
~/pp~
</syntaxhighlight>


<h2>Caveats when porting</h2>
==Caveats when porting==


* You cannot pass "long" arguments, so remember to cast any WId parameters to qlon
* You cannot pass "long" arguments, so remember to cast any WId parameters to qlonglong.
glong.
* KDED object paths should start with "/modules/" (i.e., /modules/kssld, /modules/favicons, etc.)
* KDED object paths should start with "/modules/" (i.e., /modules/kssld, /modules/
favicons, etc.)

Latest revision as of 13:31, 30 May 2019


This page has been archived
The information on this page is outdated or no longer in use but is kept for historical purposes. Please see the Category:Archives for similar pages.

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.

Porting from DCOP to D-Bus

Converting from DCOPClient

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()).

Converting Hand-written DCOPClient calls

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).

Converting Calls using DCOPRef and DCOPReply

The direct replacement for DCOPRef is QDBusInterface. The replacement for DCOPReply is QDBusReply.

However, there are some important differences to be noticed:

  • QDBusInterface is not "cheap": it constructs an entire QObject. So, if you can, store the value somewhere for later reuse.
  • QDBusReply is a template class, so you must know ahead of time what your reply type is.
  • If you create a QDBusInterface without specifying the third argument (the interface name), this will generate a round-trip to the remote application and will allocate non-shared memory. So, wherever possible, use the interface name to make it cached.

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:

  • use the 3-argument version of QDBusInterface. The interface name you can usually obtain by calling "interfaces" on the existing DCOP object (in this case, "dcop kded favicons interfaces").
  • the "application id" becomes a "service name" and it should start with "org.kde"
  • the "object id" becomes an "object path" and must start with a slash
  • you don't write the function signature: just the function name
  • custom types are not supported (nor ever will be), so you need to convert them to basic types

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. */
}

Converting from DCOPObject

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:

  • Remove the "virtual public DCOPObject" from the class declaration
  • Replace the K_DCOP macro with Q_CLASSINFO("D-Bus Interface", "<interfacename>") where <interfacename> is the name of the interface you're declaring (generally, it'll be "org.kde" followed by the class name itself)
  • Remove the k_dcop method references.
  • Make the methods that were DCOP-accessible scriptable slots. That is, you must make it:
public Q_SLOTS:
    Q_SCRIPTABLE void methodName();
  • Change "ASYNC" to "Q_NOREPLY void"
  • Remove the call to the DCOPObject constructor in the class' constructor.
  • Register the QObject with D-Bus in the constructor.

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.

Signals

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:

  • MyAdaptor is not exported. This is a private class and the .h file should be a _p.h as well.
  • There's no need to mark the signals in the adaptor as Q_SCRIPTABLE. The same goes for the slots and properties in the adaptor
  • The "setAutoRelaySignals" function just connects all signals in "parent" to the signals of the same name and arguments in the adaptor.
  • There's no need to store the adaptor pointer, it'll be deleted automatically when needed. Therefore, do not delete it and, especially, do not reparent it.
  • You can create more adaptors later, if you need. There's no need to re-register the object.

DCOP Transactions

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 to declare that your class may want to do transactions. For that, you must make your object derive from QDBusContext as well as from your your normal inheritance. This will provide you with a few extra methods in your class. You will want to call setDelayedReply to notify QtDBus that the reply will come at a later point in time. You also want to keep the original connection object and message so as to be able to send the proper reply on the proper connection.

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, private QDBusContext
{
    QDBusMessage reply;
    QDBusConnection connection;

public Q_SLOTS:
    Q_SCRIPTABLE QString myMethod(const QString &arg1)
    {
        // connection(), message() and setDelayedReply() 
        // come from QDBusContext
        connection = connection();
        reply = QDBusMessage::createReply(message());
        setDelayedReply(true);
        QTimer::singleShot(0, this, SLOT(processLater());
        return QString();       // reply will be ignored
    }

    void processLater()
    {
        reply << QString::fromLatin1("foo");
        connection.send(reply);
    }
};

Caveats when porting

  • You cannot pass "long" arguments, so remember to cast any WId parameters to qlonglong.
  • KDED object paths should start with "/modules/" (i.e., /modules/kssld, /modules/favicons, etc.)