Development/Tutorials/D-Bus/Intermediate D-Bus: Difference between revisions

From KDE TechBase
(More detail on accessing properties without Introspect)
(Nothing needs to be changed since Qt4 here)
 
(15 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{{TutorialBrowser|
series=D-Bus|
name=Intermediate D-BUS|
pre=[[../Accessing Interfaces|Accessing Interfaces]]|
next=[[../Creating Interfaces|Creating Interfaces]], [[../CustomTypes|Using Custom Types with DBus]]|
}}
== Abstract ==
== Abstract ==
The basic techniques explained in [[../Accessing_Interfaces|Accessing Interfaces]] are suitable for using D-Bus methods with relatively simple signatures, but the more complex interfaces often found in the wild require additional techniques to address, explained in this article.
The basic techniques explained in [[../Accessing_Interfaces|Accessing Interfaces]] are suitable for using D-Bus methods with relatively simple signatures, but the more complex interfaces often found in the wild require additional techniques to address, explained in this article.
Line 5: Line 18:
QtDBus requires additional setup to deal with methods that return more complex return types than single primitives.  The return type needs to be declared to the Qt type system so that it can be demarshalled.
QtDBus requires additional setup to deal with methods that return more complex return types than single primitives.  The return type needs to be declared to the Qt type system so that it can be demarshalled.
=== Lists ===
=== Lists ===
Lists of values returned by D-Bus methods are mapped to {{qt|QList}} in QtDBus.  The appropriate specialisation of QList should be declared as a type to the Qt type system, for example:
Lists of values returned by D-Bus methods are mapped to {{qt|QList}} in QtDBus.  The appropriate specialisation of {{qt|QList}} should be declared as a type to the Qt type system, for example:


<code cppqt>
<syntaxhighlight lang="cpp-qt">
Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
</code>
</syntaxhighlight>


It is essential that the <tt>Q_DECLARE_METATYPE</tt> macro is used outside any code blocks or methods in source code.  The best place to use it is at the top of the file.
It is essential that the <tt>Q_DECLARE_METATYPE</tt> macro is used outside any code blocks or methods in source code.  The best place to use it is at the top of the file.
Line 15: Line 28:
The type should also be declared to QtDBus using:
The type should also be declared to QtDBus using:
      
      
<code cppqt>
<syntaxhighlight lang="cpp-qt">
qDBusRegisterMetaType<QList<QDBusObjectPath> >();
qDBusRegisterMetaType<QList<QDBusObjectPath> >();
</code>
</syntaxhighlight>


=== Dicts ===
=== Dicts ===
The DBus Dict type should map to QMap, example to follow..
The DBus Dict type should map to {{qt|QMap}}, example to follow..


=== Arbitrary sets of return types ===
=== Arbitrary sets of return types ===
Some D-Bus methods return an arbitrary tuple of values.  The {{qt|QDBusReply}} class can only handle the first value returned by a method, so to get the rest of the returned parameters we fall back to using {{qt|QDBusMessage}}.  Since QDBusAbstractInterface::call() and similar actually return QDBusMessage, when we use QDBusReply we are actually just constructing this from the QDBusMessage containing all the return values.
Some D-Bus methods return an arbitrary tuple of values.  The {{qt|QDBusReply}} class can only handle the first value returned by a method, so to get the rest of the returned parameters we fall back to using {{qt|QDBusMessage}}.  Since QDBusAbstractInterface::call() and similar actually return QDBusMessage, when we use QDBusReply we are actually just constructing this from the QDBusMessage containing all the return values.


Once we have the {{qt|QDBusMessage}}, we can access the return values using arguments() which returns a QList<QVariant>.
Once we have the {{qt|QDBusMessage}}, we can access the return values using arguments() which returns a {{qt|QList}}<{{qt|QVariant}}>.
 
For example, for a method  
For example, for a method  
<tt> org.kde.DBusTute.Favourites.Get( out INT32 number, out STRING colour, out STRING flavour )</tt>,  
<tt> org.kde.DBusTute.Favourites.Get( out INT32 number, out STRING colour, out STRING flavour )</tt>,  
Line 32: Line 45:




<code cppqt n>
<syntaxhighlight lang="cpp-qt" line>
QDBusInterface iface( "org.kde.DBusTute",
QDBusInterface iface( "org.kde.DBusTute",
                       "/org/kde/DBusTute/Favourites",
                       "/org/kde/DBusTute/Favourites",
Line 38: Line 51:
                       QDBus::sessionBus(), 0 );
                       QDBus::sessionBus(), 0 );
QDBusMessage reply = iface.call( "Get" );
QDBusMessage reply = iface.call( "Get" );
QList<QVariant> values = return.arguments();
QList<QVariant> values = reply.arguments();
int favouriteNumber = values.takeFirst().toInt();
int favouriteNumber = values.takeFirst().toInt();
QString favouriteColour = values.takeFirst().toString();
QString favouriteColour = values.takeFirst().toString();
QString favouriteFlavour = values.takeFirst().toString();
QString favouriteFlavour = values.takeFirst().toString();
</code>
</syntaxhighlight>


== Interfaces that don't support Introspect ==
== Interfaces that don't support Introspect ==
Line 50: Line 63:
Introspect is required to discover properties that are accessed via QObject::property().  If it is not present, but the names and signature of the properties are known by looking at the source code of the remote interface, the D-Bus property system can be used manually, with these methods:
Introspect is required to discover properties that are accessed via QObject::property().  If it is not present, but the names and signature of the properties are known by looking at the source code of the remote interface, the D-Bus property system can be used manually, with these methods:


<code>
<syntaxhighlight lang="cpp-qt">
org.freedesktop.DBus.Properties.Get (in STRING interface_name,
org.freedesktop.DBus.Properties.Get (in STRING interface_name,
                                     in STRING property_name,
                                     in STRING property_name,
Line 57: Line 70:
                                     in STRING property_name,
                                     in STRING property_name,
                                     in VARIANT value);
                                     in VARIANT value);
</code>
</syntaxhighlight>


=== Signals ===
=== Signals ===
If Introspect is not supported, QObject::connect() will get a 'no such signal' error at runtime.
If Introspect is not supported, QObject::connect() will get a 'no such signal' error at runtime.


It is still possible to connect to these signals with QtDBus, at a lower level, using QDBusConnection::connect().  If you are using {{qt|QDbusInterface}} for its convenient call() methods, get its connection and call connect() on this:
It is still possible to connect to these signals with QtDBus, at a lower level, using {{qt|QDBusConnection}}::connect().  If you are using {{qt|QDBusInterface}} for its convenient call() methods, get its connection and call connect() on this:


<code cppqt n >
<syntaxhighlight lang="cpp-qt" line>
QDBusInterface iface( "org.kde.DBusTute",
QDBusInterface iface( "org.kde.DBusTute",
                       "/org/kde/DBusTute/Favourites",
                       "/org/kde/DBusTute/Favourites",
Line 75: Line 88:
                             "FavouritesChanged", this,  
                             "FavouritesChanged", this,  
                             SLOT(favouritesChanged() ) );
                             SLOT(favouritesChanged() ) );
</code>
</syntaxhighlight>


The connection semantics are similar to a regular QObject::connect().
The connection semantics are similar to a regular QObject::connect().

Latest revision as of 12:06, 17 April 2020


Intermediate D-BUS
Tutorial Series   D-Bus
Previous   Accessing Interfaces
What's Next   Creating Interfaces, Using Custom Types with DBus
Further Reading   n/a

Abstract

The basic techniques explained in Accessing Interfaces are suitable for using D-Bus methods with relatively simple signatures, but the more complex interfaces often found in the wild require additional techniques to address, explained in this article.

Complex Return Types

QtDBus requires additional setup to deal with methods that return more complex return types than single primitives. The return type needs to be declared to the Qt type system so that it can be demarshalled.

Lists

Lists of values returned by D-Bus methods are mapped to QList in QtDBus. The appropriate specialisation of QList should be declared as a type to the Qt type system, for example:

Q_DECLARE_METATYPE(QList<QDBusObjectPath>)

It is essential that the Q_DECLARE_METATYPE macro is used outside any code blocks or methods in source code. The best place to use it is at the top of the file.

The type should also be declared to QtDBus using:

qDBusRegisterMetaType<QList<QDBusObjectPath> >();

Dicts

The DBus Dict type should map to QMap, example to follow..

Arbitrary sets of return types

Some D-Bus methods return an arbitrary tuple of values. The QDBusReply class can only handle the first value returned by a method, so to get the rest of the returned parameters we fall back to using QDBusMessage. Since QDBusAbstractInterface::call() and similar actually return QDBusMessage, when we use QDBusReply we are actually just constructing this from the QDBusMessage containing all the return values.

Once we have the QDBusMessage, we can access the return values using arguments() which returns a QList<QVariant>.

For example, for a method org.kde.DBusTute.Favourites.Get( out INT32 number, out STRING colour, out STRING flavour ), we would use the following code:


QDBusInterface iface( "org.kde.DBusTute",
                      "/org/kde/DBusTute/Favourites",
                      "org.kde.DBusTute.Favourites",
                      QDBus::sessionBus(), 0 );
QDBusMessage reply = iface.call( "Get" );
QList<QVariant> values = reply.arguments();
int favouriteNumber = values.takeFirst().toInt();
QString favouriteColour = values.takeFirst().toString();
QString favouriteFlavour = values.takeFirst().toString();

Interfaces that don't support Introspect

QDBusInterface, as a proxy for the remote D-Bus interface, makes use of introspection to provide high level access to D-Bus signals and properties. However, the object must support the interface org.freedesktop.DBus.Introspectable to do so, which is not mandatory.

Properties

Introspect is required to discover properties that are accessed via QObject::property(). If it is not present, but the names and signature of the properties are known by looking at the source code of the remote interface, the D-Bus property system can be used manually, with these methods:

org.freedesktop.DBus.Properties.Get (in STRING interface_name,
                                     in STRING property_name,
                                     out VARIANT value);
org.freedesktop.DBus.Properties.Set (in STRING interface_name,
                                     in STRING property_name,
                                     in VARIANT value);

Signals

If Introspect is not supported, QObject::connect() will get a 'no such signal' error at runtime.

It is still possible to connect to these signals with QtDBus, at a lower level, using QDBusConnection::connect(). If you are using QDBusInterface for its convenient call() methods, get its connection and call connect() on this:

QDBusInterface iface( "org.kde.DBusTute",
                      "/org/kde/DBusTute/Favourites",
                      "org.kde.DBusTute.Favourites",
                      QDBus::sessionBus(), 0 );

iface.connection().connect( "org.kde.DBusTute",
                            "/org/kde/DBusTute/Favourites",
                            "org.kde.DBusTute.Favourites",
                            "FavouritesChanged", this, 
                            SLOT(favouritesChanged() ) );

The connection semantics are similar to a regular QObject::connect().