Difference between revisions of "Development/Tutorials/D-Bus/CustomTypes"

Jump to: navigation, search
(first step in migrating an internal HOWTO to Techbase... I still need to learn how to do certain things on this wiki (like linking and code formatting))
 
(Change the annotations from com.trolltech to org.qtproject)
 
(15 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{Template:I18n/Language Navigation Bar|Development/Tutorials/D-Bus/Intermediate D-Bus}}
+
 
  
 
{{TutorialBrowser|
 
{{TutorialBrowser|
Line 7: Line 7:
 
name=Using Custom Types with DBus|
 
name=Using Custom Types with DBus|
  
pre=[[../Creating Interfaces|Creating Interfaces]]|
+
pre=[[../Creating Interfaces|Creating Interfaces]], [[../Intermediate_D-Bus|Intermediate DBus]]|
 
+
next=[[../Creating Interfaces|Creating Interfaces]]|
+
 
}}
 
}}
  
DBus-based IPC has been intregrated nicely in the Qt framework. Qt also provides [[http://doc.trolltech.com/4.6/qdbusabstractadaptor.html|Adaptors]] and [[http://doc.trolltech.com/4.6/qdbusabstractinterface.html|Interfaces]] to communicate with remote objects, so you don't have to manually construct [[http://doc.trolltech.com/4.6/qdbusmessage.html|QDBusMessages]].
+
DBus-based IPC has been intregrated nicely in the Qt framework. Qt also provides [http://doc.trolltech.com/4.6/qdbusabstractadaptor.html Adaptors] and [http://doc.trolltech.com/4.6/qdbusabstractinterface.html Interfaces] to communicate with remote objects, so you don't have to manually construct [http://doc.trolltech.com/4.6/qdbusmessage.html QDBusMessages].
These Adaptors and Interfaces are very similar to the Stubs and Proxies used in other RPC mechanisms like [[http://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture|Corba]] and [[http://java.sun.com/javase/technologies/core/basic/rmi/index.jsp|RMI]].
+
These Adaptors and Interfaces are very similar to the Stubs and Proxies used in other RPC mechanisms like [http://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture Corba] and [http://java.sun.com/javase/technologies/core/basic/rmi/index.jsp RMI].
  
 
So, Qt provides a way to talk to objects in a different process and it contains Adaptors and Interfaces to do so in a nice, object-oriented way.
 
So, Qt provides a way to talk to objects in a different process and it contains Adaptors and Interfaces to do so in a nice, object-oriented way.
Line 21: Line 19:
 
That is what this tutorial is about.
 
That is what this tutorial is about.
  
An [[attachment:DBusChat.tar.bz2|example]] was created to go along with this HOWTO. It is a very basic chat application that communicates through the session DBus.
+
An [[#Example|example]] was created to go along with this tutorial. It is a very basic chat application that communicates through the session DBus.
  
 
= Write a class =
 
= Write a class =
Line 29: Line 27:
 
The class being published in the example is the following:
 
The class being published in the example is the following:
  
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
class Chat : public QObject
 
class Chat : public QObject
 
{
 
{
Line 58: Line 56:
 
     QStringList m_users;
 
     QStringList m_users;
 
};
 
};
}}}
+
</syntaxhighlight>
  
 
It contains simple user management and provides the means to send a message.
 
It contains simple user management and provides the means to send a message.
Line 64: Line 62:
 
a user and a text message. There is no reason why the methods in the Chat
 
a user and a text message. There is no reason why the methods in the Chat
 
class couldn't just take 2 QString parameters, but then Qt would be able to
 
class couldn't just take 2 QString parameters, but then Qt would be able to
do everything automatically and we need something irregular for this howto.
+
do everything automatically and we need something irregular for this tutorial.
  
 
To show that Qt supports a lot of types right out of the box, a QStringList
 
To show that Qt supports a lot of types right out of the box, a QStringList
Line 73: Line 71:
  
 
Typically, the company name is included in the name, resulting in declarations like:
 
Typically, the company name is included in the name, resulting in declarations like:
{{{
+
<syntaxhighlight lang="cpp-qt">
 
Q_CLASSINFO("D-Bus Interface", "com.firm.department.product.interface")
 
Q_CLASSINFO("D-Bus Interface", "com.firm.department.product.interface")
}}}
+
</syntaxhighlight>
  
 
= Generate XML =
 
= Generate XML =
Line 81: Line 79:
  
 
If you run '''qdbuscpp2xml''' on the Chat.hpp file in the example, you will get the following output:
 
If you run '''qdbuscpp2xml''' on the Chat.hpp file in the example, you will get the following output:
{{{#!highlight xml
+
<syntaxhighlight lang="xml" line>
 
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 
<node>
 
<node>
Line 100: Line 98:
 
   </interface>
 
   </interface>
 
</node>
 
</node>
}}}
+
</syntaxhighlight>
  
 
Note that this xml file contains all methods from the Chat class using standard Qt types.
 
Note that this xml file contains all methods from the Chat class using standard Qt types.
Line 111: Line 109:
 
modify the XML.
 
modify the XML.
  
{{{#!wiki tip
+
:'''Tip'''
'''Tip'''
+
:
 
+
:You may want to generate the Adaptor and Interface using only the automatically generated xml. It might be helpful to compare them to the versions we will generate further on.
You may want to generate the Adaptor and Interface using only the automatically generated xml. It might be helpful to compare
+
them to the versions we will generate further on.
+
}}}
+
 
+
  
 
= Edit the XML =
 
= Edit the XML =
Line 124: Line 118:
  
 
This is done by specifying annotations:
 
This is done by specifying annotations:
{{{#!highlight xml
+
<syntaxhighlight lang="xml" line>
<annotation name="com.trolltech.QtDBus.QtTypeName"
+
<annotation name="org.qtproject.QtDBus.QtTypeName"
 
value="*customType*"/>
 
value="*customType*"/>
}}}
+
</syntaxhighlight>
  
 
The syntax differs slightly depending on wheter you're using it in a signal/method or in
 
The syntax differs slightly depending on wheter you're using it in a signal/method or in
 
a property (the {{{.In0}}} is omitted for properties), but it is fairly straightforward.
 
a property (the {{{.In0}}} is omitted for properties), but it is fairly straightforward.
 
The following is an example dealing with the QRect type (it has no relation with the example):
 
The following is an example dealing with the QRect type (it has no relation with the example):
{{{#!highlight xml
+
<syntaxhighlight lang="xml" line>
 
<property name="rectangle" type="(iiii)" access="readwrite">
 
<property name="rectangle" type="(iiii)" access="readwrite">
   <annotation name="com.trolltech.QtDBus.QtTypeName" value="QRect"/>
+
   <annotation name="org.qtproject.QtDBus.QtTypeName" value="QRect"/>
 
</property>
 
</property>
  
 
<signal name="rectangleChanged">
 
<signal name="rectangleChanged">
 
   <arg name="rect" type="(iiii)" direction="out"/>
 
   <arg name="rect" type="(iiii)" direction="out"/>
   <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QRect"/>
+
   <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QRect"/>
 
</signal>
 
</signal>
  
 
<method name="changeRectangle">
 
<method name="changeRectangle">
 
   <arg name="message" type="(iiii)" direction="in"/>
 
   <arg name="message" type="(iiii)" direction="in"/>
   <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QRect"/>
+
   <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QRect"/>
 
</method>
 
</method>
}}}
+
</syntaxhighlight>
  
 
Don't worry too much about what type to specify; any type that is
 
Don't worry too much about what type to specify; any type that is
Line 154: Line 148:
  
 
If any of your methods returns a complex result, you need to add an
 
If any of your methods returns a complex result, you need to add an
annotation for com.trolltech.QtDBus.QtTypeName.'''Out'''0 as well.
+
annotation for org.qtproject.QtDBus.QtTypeName.'''Out'''0 as well.
  
{{{#!wiki note
+
:'''Note'''
'''Note'''
+
:
 
+
:The XML above uses QRects. QRect is supported by default, so the code above would be generated for you by '''qdbuscpp2xml'''.
The XML above uses QRects. QRect is supported by default, so the
+
code above would be generated for you by '''qdbuscpp2xml'''.
+
}}}
+
  
 
The complete interface used for the Chat interface in the example is as follows:
 
The complete interface used for the Chat interface in the example is as follows:
{{{#!highlight xml
+
<syntaxhighlight lang="xml" line>
 
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 
<node>
 
<node>
Line 177: Line 168:
 
     <signal name="messageSent">
 
     <signal name="messageSent">
 
       <arg name="message" type="a(ii)" direction="out"/>
 
       <arg name="message" type="a(ii)" direction="out"/>
       <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="Message"/>
+
       <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="Message"/>
 
     </signal>
 
     </signal>
 
     <method name="addUser">
 
     <method name="addUser">
Line 187: Line 178:
 
     <method name="sendMessage">
 
     <method name="sendMessage">
 
       <arg name="message" type="a(ii)" direction="in"/>
 
       <arg name="message" type="a(ii)" direction="in"/>
       <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="Message"/>
+
       <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="Message"/>
 
     </method>
 
     </method>
 
   </interface>
 
   </interface>
 
</node>
 
</node>
}}}
+
</syntaxhighlight>
  
 
= Generate Adaptor and Interface classes =
 
= Generate Adaptor and Interface classes =
Line 203: Line 194:
 
For the DBusChat example, the Adaptor and Interface classes were created
 
For the DBusChat example, the Adaptor and Interface classes were created
 
by calling
 
by calling
{{{
+
<syntaxhighlight lang="bash">
 
qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
 
qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
 
qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface
 
qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface
}}}
+
</syntaxhighlight>
  
 
= Qt Meta object magic =
 
= Qt Meta object magic =
Line 216: Line 207:
 
== Register the type ==
 
== Register the type ==
 
Declare the type as a Qt meta type by adding a
 
Declare the type as a Qt meta type by adding a
{{{ Q_DECLARE_METATYPE }}} statement to the header file containing
+
<syntaxhighlight lang="cpp-qt"> Q_DECLARE_METATYPE </syntaxhighlight> statement to the header file containing
 
your custom type definition.
 
your custom type definition.
  
Line 223: Line 214:
  
 
In the example's Message class, a static method is included to do this:
 
In the example's Message class, a static method is included to do this:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
void Message::registerMetaType()
 
void Message::registerMetaType()
 
{
 
{
Line 230: Line 221:
 
     qDBusRegisterMetaType<Message>();
 
     qDBusRegisterMetaType<Message>();
 
}
 
}
}}}
+
</syntaxhighlight>
 
+
{{{{#!wiki important
+
'''Important'''
+
 
+
You need to register the type both in the application publishing
+
the object and in the application using it, since both applications
+
need to be able to handle the custom type.
+
 
+
Also take care to register the custom types before calling methods
+
on the Adaptor/Interface that need them.
+
 
+
  
Qt will show error output if you are using types it cannot (yet)
+
:'''Important'''
handle, but you should be aware of it nonetheless.
+
:
}}}}
+
:You need to register the type both in the application publishing the object and in the application using it, since both applications need to be able to handle the custom type.
 +
:<br />
 +
:Also take care to register the custom types before calling methods on the Adaptor/Interface that need them.
 +
:<br />
 +
:Qt will show error output if you are using types it cannot (yet) handle, but you should be aware of it nonetheless.
  
 
== Provide QDBusArgument streaming operators ==
 
== Provide QDBusArgument streaming operators ==
Line 254: Line 238:
 
This is done by using the QDBusArgument stream operators, so you will
 
This is done by using the QDBusArgument stream operators, so you will
 
need to implement those operators for your custom types, as explained
 
need to implement those operators for your custom types, as explained
[[http://doc.trolltech.com/4.6/qdbusargument.html#details|here]].
+
[http://doc.trolltech.com/4.6/qdbusargument.html#details here].
  
 
For the Message type from the example, these operators are very simple,
 
For the Message type from the example, these operators are very simple,
 
since it only contains 2 strings:
 
since it only contains 2 strings:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
QDBusArgument &operator<<(QDBusArgument &argument, const Message& message)
 
QDBusArgument &operator<<(QDBusArgument &argument, const Message& message)
 
{
 
{
Line 278: Line 262:
 
     return argument;
 
     return argument;
 
}
 
}
}}}
+
</syntaxhighlight>
 
QDBusArgument provides functions to handle much more complex types
 
QDBusArgument provides functions to handle much more complex types
 
as well.
 
as well.
Line 292: Line 276:
 
This is the code that publishes a Chat object in the DBusChat
 
This is the code that publishes a Chat object in the DBusChat
 
example:
 
example:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
Chat* pChat = new Chat(&a);
 
Chat* pChat = new Chat(&a);
 
ChatAdaptor* pChatAdaptor = new ChatAdaptor(pChat);
 
ChatAdaptor* pChatAdaptor = new ChatAdaptor(pChat);
Line 305: Line 289:
 
     qFatal("Could not register Chat object!");
 
     qFatal("Could not register Chat object!");
 
}
 
}
}}}
+
</syntaxhighlight>
  
 
The ChatAdaptor instance will process any incoming DBus
 
The ChatAdaptor instance will process any incoming DBus
Line 320: Line 304:
 
In the DBusChat example, a connection is made with a
 
In the DBusChat example, a connection is made with a
 
remote Chat object like this:
 
remote Chat object like this:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt">
 
demo::Chat chatInterface(CHAT_SERVICE, CHAT_PATH, connection);
 
demo::Chat chatInterface(CHAT_SERVICE, CHAT_PATH, connection);
}}}
+
</syntaxhighlight>
  
 
== Using the remote object in your application ==
 
== Using the remote object in your application ==
Line 331: Line 315:
 
The ChatWindow class in the DBusChat example, for
 
The ChatWindow class in the DBusChat example, for
 
instance, adds a user simply by calling
 
instance, adds a user simply by calling
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt">
 
m_chatInterface.addUser(m_userName);
 
m_chatInterface.addUser(m_userName);
}}}
+
</syntaxhighlight>
  
 
Furthermore, ChatWindow is able to respond to signals
 
Furthermore, ChatWindow is able to respond to signals
 
emitted by the Chat object by connecting to its
 
emitted by the Chat object by connecting to its
 
Interface class just like with any other QObject:
 
Interface class just like with any other QObject:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt">
 
connect(&m_chatInterface, SIGNAL(userAdded(QString)), SLOT(chat_userAdded(QString)));
 
connect(&m_chatInterface, SIGNAL(userAdded(QString)), SLOT(chat_userAdded(QString)));
 
connect(&m_chatInterface, SIGNAL(userRemoved(QString)), SLOT(chat_userRemoved(QString)));
 
connect(&m_chatInterface, SIGNAL(userRemoved(QString)), SLOT(chat_userRemoved(QString)));
 
connect(&m_chatInterface, SIGNAL(messageSent(Message)), SLOT(chat_messageSent(Message)));
 
connect(&m_chatInterface, SIGNAL(messageSent(Message)), SLOT(chat_messageSent(Message)));
}}}
+
</syntaxhighlight>
  
 
= Varia =
 
= Varia =
Line 356: Line 340:
 
the header files passed to them, so they would be able to recognize the
 
the header files passed to them, so they would be able to recognize the
 
custom types and add methods/signals/properties to the XML accordingly.
 
custom types and add methods/signals/properties to the XML accordingly.
 +
One potential strategy for extending '''qdbuscpp2xml''' is with plugins,
 +
and this is presented at [[Development/Tutorials/D-Bus/Cpp2XmlPlugins|Cpp2XmlPlugins]].
  
 
Alternatively, a feature could be added to Qt Creator to support the kind
 
Alternatively, a feature could be added to Qt Creator to support the kind
Line 367: Line 353:
 
enumeration. A basic implementation could simply cast the enum to and from an int,
 
enumeration. A basic implementation could simply cast the enum to and from an int,
 
resulting in code looking like this:
 
resulting in code looking like this:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
QDBusArgument &operator<<(QDBusArgument &argument, const EnumerationType& source)
 
QDBusArgument &operator<<(QDBusArgument &argument, const EnumerationType& source)
 
{
 
{
Line 387: Line 373:
 
     return argument;
 
     return argument;
 
}
 
}
}}}
+
</syntaxhighlight>
  
 
You will notice that this code will be much the same for all
 
You will notice that this code will be much the same for all
Line 401: Line 387:
 
you want for all types, especially not for complex ones.
 
you want for all types, especially not for complex ones.
  
This is where the [[http://www.boost.org/|boost library]] comes to our aid. With some
+
This is where the [http://www.boost.org/ boost library] comes to our aid. With some magic, it supports conditionals within template definitions.
magic, it supports conditionals within template definitions.
+
  
 
That way, we can provide QDBusArgument marshaling and
 
That way, we can provide QDBusArgument marshaling and
Line 410: Line 395:
 
This is what the marshaling and unmarshaling code actually
 
This is what the marshaling and unmarshaling code actually
 
looks like:
 
looks like:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
template<typename T, typename TEnum>
 
template<typename T, typename TEnum>
 
class QDBusEnumMarshal;
 
class QDBusEnumMarshal;
Line 438: Line 423:
 
}
 
}
 
}
 
}
}}}
+
</syntaxhighlight>
  
 
The marshal implementation can now be called by invoking:
 
The marshal implementation can now be called by invoking:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt">
 
QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::marshal(argument, source);
 
QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::marshal(argument, source);
}}}
+
</syntaxhighlight>
with T being the type we want to marshal
+
with T being the type we want to marshal.
  
 
Some further glue code is needed to link the implementation to
 
Some further glue code is needed to link the implementation to
 
the QDbusArgument stream operators:
 
the QDbusArgument stream operators:
{{{#!highlight cpp
+
<syntaxhighlight lang="cpp-qt" line>
 
template<typename T>
 
template<typename T>
 
QDBusArgument& operator<<(QDBusArgument &argument, const T& source)
 
QDBusArgument& operator<<(QDBusArgument &argument, const T& source)
Line 460: Line 445:
 
     return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::unmarshal(argument, source);
 
     return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::unmarshal(argument, source);
 
}
 
}
}}}
+
</syntaxhighlight>
  
Put all that in [[attachment:enumDBus.hpp|a header file]] and include it where you declare the
+
Put all that in [[#enumDBus.hpp|a header file]] and include it where you declare the
 
enumerations that need to pass across DBus. The compiler is now able
 
enumerations that need to pass across DBus. The compiler is now able
 
to find the streaming operators all on its own.
 
to find the streaming operators all on its own.
Line 469: Line 454:
 
register it with the Qt meta object system though.
 
register it with the Qt meta object system though.
  
{{{#!wiki note
+
:'''Note'''
'''Note'''
+
:
 +
:You only need a boost header file to do this ('''boost/type_traits/is_enum.hpp''', to be exact). There is no need to have the entire library installed, which is rather nice on an embedded platform, since the boost library takes up quite a bit of space.
 +
 
 +
=== enumDBus.hpp ===
 +
 
 +
This is the entire header file needed to marshal / unmarshal any enumeration:
 +
<syntaxhighlight lang="cpp-qt">
 +
#ifndef _ENUM_DBUS_HPP
 +
#define _ENUM_DBUS_HPP
 +
 
 +
#include <QtDBus/QDBusArgument>
 +
 
 +
#include <boost/type_traits/is_enum.hpp>
 +
 
 +
using namespace std;
 +
 
 +
template<typename T, typename TEnum>
 +
class QDBusEnumMarshal;
 +
 
 +
template<typename T>
 +
class QDBusEnumMarshal<T, boost::true_type>
 +
{
 +
public:
 +
static QDBusArgument& marshal(QDBusArgument &argument, const T& source)
 +
{
 +
argument.beginStructure();
 +
argument << static_cast<int>(source);
 +
argument.endStructure();
 +
return argument;
 +
}
 +
 
 +
static const QDBusArgument& unmarshal(const QDBusArgument &argument, T &source)
 +
{
 +
int a;
 +
argument.beginStructure();
 +
argument >> a;
 +
argument.endStructure();
 +
 +
source = static_cast<T>(a);
 +
 +
return argument;
 +
}
 +
};
 +
 
 +
template<typename T>
 +
QDBusArgument& operator<<(QDBusArgument &argument, const T& source)
 +
{
 +
return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::marshal(argument, source);
 +
}
 +
 
 +
template<typename T>
 +
const QDBusArgument& operator>>(const QDBusArgument &argument, T &source)
 +
{
 +
return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::unmarshal(argument, source);
 +
}
 +
 
 +
#endif //_ENUM_DBUS_HPP
 +
</syntaxhighlight>
 +
 
 +
= Example =
 +
 
 +
This section contains the sources for the chat example. The wiki does not allow the upload of files other than images, so they've been posted as text.
 +
 
 +
Chat is the interface being published. ChatAdaptor and ChatInterface were generated using Chat.xml.
 +
 
 +
Put all these files in a directory and run
 +
<syntaxhighlight lang="bash">
 +
qmake
 +
 
 +
make
 +
</syntaxhighlight>
 +
 
 +
== Chat.hpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#ifndef CHAT_HPP
 +
#define CHAT_HPP
 +
 
 +
#include <QObject>
 +
#include <QStringList>
 +
 
 +
class Message;
 +
 
 +
class Chat : public QObject
 +
{
 +
    Q_OBJECT
 +
 
 +
    Q_CLASSINFO("D-Bus Interface", "demo.Chat")
 +
    Q_PROPERTY( QStringList users READ users)
 +
 
 +
signals:
 +
    void userAdded(const QString& user);
 +
    void userRemoved(const QString& user);
 +
 
 +
    void messageSent(const Message &message);
 +
 
 +
public slots:
 +
    void addUser(const QString &user);
 +
    void removeUser(const QString &user);
 +
 
 +
    void sendMessage(const Message &message);
 +
 
 +
public:
 +
    Chat(QObject* parent = 0);
 +
    virtual ~Chat();
 +
 
 +
    QStringList users() const;
 +
 
 +
private:
 +
    QStringList m_users;
 +
};
 +
#endif // CHAT_HPP
 +
</syntaxhighlight>
 +
 
 +
== Chat.cpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#include "Chat.hpp"
 +
 
 +
Chat::Chat(QObject* parent) :
 +
        QObject(parent)
 +
{
 +
}
 +
 
 +
Chat::~Chat()
 +
{
 +
}
 +
 
 +
void Chat::addUser(const QString &user)
 +
{
 +
    if (!m_users.contains(user))
 +
    {
 +
        m_users.append(user);
 +
 
 +
        emit userAdded(user);
 +
    }
 +
}
 +
 
 +
void Chat::removeUser(const QString &user)
 +
{
 +
    if (m_users.contains(user))
 +
    {
 +
        m_users.removeOne(user);
 +
 
 +
        emit userRemoved(user);
 +
    }
 +
}
 +
 
 +
void Chat::sendMessage(const Message &message)
 +
{
 +
    Q_EMIT messageSent(message);
 +
}
 +
 
 +
QStringList Chat::users() const
 +
{
 +
    return m_users;
 +
}
 +
</syntaxhighlight>
 +
 
 +
== Chat.xml ==
 +
<syntaxhighlight lang="xml">
 +
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
 +
<node>
 +
  <interface name="demo.Chat">
 +
    <property name="users" type="as" access="read"/>
 +
    <signal name="userAdded">
 +
      <arg name="user" type="s" direction="out"/>
 +
    </signal>
 +
    <signal name="userRemoved">
 +
      <arg name="user" type="s" direction="out"/>
 +
    </signal>
 +
    <signal name="messageSent">
 +
      <arg name="message" type="a(ii)" direction="out"/>
 +
      <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="Message"/>
 +
    </signal>
 +
    <method name="addUser">
 +
      <arg name="user" type="s" direction="in"/>
 +
    </method>
 +
    <method name="removeUser">
 +
      <arg name="user" type="s" direction="in"/>
 +
    </method>
 +
    <method name="sendMessage">
 +
      <arg name="message" type="a(ii)" direction="in"/>
 +
      <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="Message"/>
 +
    </method>
 +
  </interface>
 +
</node>
 +
</syntaxhighlight>
 +
 
 +
== ChatAdaptor.h ==
 +
<syntaxhighlight lang="cpp-qt">
 +
/*
 +
* This file was generated by qdbusxml2cpp version 0.7
 +
* Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
 +
*
 +
* qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 +
*
 +
* This is an auto-generated file.
 +
* This file may have been hand-edited. Look for HAND-EDIT comments
 +
* before re-generating it.
 +
*/
 +
 
 +
#ifndef CHATADAPTOR_H_1270658274
 +
#define CHATADAPTOR_H_1270658274
 +
 
 +
#include <QtCore/QObject>
 +
#include <QtDBus/QtDBus>
 +
#include "Message.hpp"
 +
class QByteArray;
 +
template<class T> class QList;
 +
template<class Key, class Value> class QMap;
 +
class QString;
 +
class QStringList;
 +
class QVariant;
 +
 
 +
/*
 +
* Adaptor class for interface demo.Chat
 +
*/
 +
class ChatAdaptor: public QDBusAbstractAdaptor
 +
{
 +
    Q_OBJECT
 +
    Q_CLASSINFO("D-Bus Interface", "demo.Chat")
 +
    Q_CLASSINFO("D-Bus Introspection", ""
 +
"  <interface name=\"demo.Chat\">\n"
 +
"    <property access=\"read\" type=\"as\" name=\"users\"/>\n"
 +
"    <signal name=\"userAdded\">\n"
 +
"      <arg direction=\"out\" type=\"s\" name=\"user\"/>\n"
 +
"    </signal>\n"
 +
"    <signal name=\"userRemoved\">\n"
 +
"      <arg direction=\"out\" type=\"s\" name=\"user\"/>\n"
 +
"    </signal>\n"
 +
"    <signal name=\"messageSent\">\n"
 +
"      <arg direction=\"out\" type=\"a(ii)\" name=\"message\"/>\n"
 +
"      <annotation value=\"Message\" name=\"com.trolltech.QtDBus.QtTypeName.In0\"/>\n"
 +
"    </signal>\n"
 +
"    <method name=\"addUser\">\n"
 +
"      <arg direction=\"in\" type=\"s\" name=\"user\"/>\n"
 +
"    </method>\n"
 +
"    <method name=\"removeUser\">\n"
 +
"      <arg direction=\"in\" type=\"s\" name=\"user\"/>\n"
 +
"    </method>\n"
 +
"    <method name=\"sendMessage\">\n"
 +
"      <arg direction=\"in\" type=\"a(ii)\" name=\"message\"/>\n"
 +
"      <annotation value=\"Message\" name=\"com.trolltech.QtDBus.QtTypeName.In0\"/>\n"
 +
"    </method>\n"
 +
"  </interface>\n"
 +
        "")
 +
public:
 +
    ChatAdaptor(QObject *parent);
 +
    virtual ~ChatAdaptor();
 +
 
 +
public: // PROPERTIES
 +
    Q_PROPERTY(QStringList users READ users)
 +
    QStringList users() const;
 +
 
 +
public Q_SLOTS: // METHODS
 +
    void addUser(const QString &user);
 +
    void removeUser(const QString &user);
 +
    void sendMessage(Message message);
 +
Q_SIGNALS: // SIGNALS
 +
    void messageSent(Message message);
 +
    void userAdded(const QString &user);
 +
    void userRemoved(const QString &user);
 +
};
 +
 
 +
#endif
 +
</syntaxhighlight>
 +
 
 +
== ChatAdaptor.cpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
/*
 +
* This file was generated by qdbusxml2cpp version 0.7
 +
* Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
 +
*
 +
* qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 +
*
 +
* This is an auto-generated file.
 +
* Do not edit! All changes made to it will be lost.
 +
*/
 +
 
 +
#include "ChatAdaptor.h"
 +
#include <QtCore/QMetaObject>
 +
#include <QtCore/QByteArray>
 +
#include <QtCore/QList>
 +
#include <QtCore/QMap>
 +
#include <QtCore/QString>
 +
#include <QtCore/QStringList>
 +
#include <QtCore/QVariant>
 +
 
 +
/*
 +
* Implementation of adaptor class ChatAdaptor
 +
*/
 +
 
 +
ChatAdaptor::ChatAdaptor(QObject *parent)
 +
    : QDBusAbstractAdaptor(parent)
 +
{
 +
    // constructor
 +
    setAutoRelaySignals(true);
 +
}
 +
 
 +
ChatAdaptor::~ChatAdaptor()
 +
{
 +
    // destructor
 +
}
 +
 
 +
QStringList ChatAdaptor::users() const
 +
{
 +
    // get the value of property users
 +
    return qvariant_cast< QStringList >(parent()->property("users"));
 +
}
 +
 
 +
void ChatAdaptor::addUser(const QString &user)
 +
{
 +
    // handle method call demo.Chat.addUser
 +
    QMetaObject::invokeMethod(parent(), "addUser", Q_ARG(QString, user));
 +
}
 +
 
 +
void ChatAdaptor::removeUser(const QString &user)
 +
{
 +
    // handle method call demo.Chat.removeUser
 +
    QMetaObject::invokeMethod(parent(), "removeUser", Q_ARG(QString, user));
 +
}
 +
 
 +
void ChatAdaptor::sendMessage(Message message)
 +
{
 +
    // handle method call demo.Chat.sendMessage
 +
    QMetaObject::invokeMethod(parent(), "sendMessage", Q_ARG(Message, message));
 +
}
 +
</syntaxhighlight>
 +
 
 +
== ChatInterface.h ==
 +
<syntaxhighlight lang="cpp-qt">
 +
/*
 +
* This file was generated by qdbusxml2cpp version 0.7
 +
* Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface
 +
*
 +
* qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 +
*
 +
* This is an auto-generated file.
 +
* Do not edit! All changes made to it will be lost.
 +
*/
 +
 
 +
#ifndef CHATINTERFACE_H_1270658265
 +
#define CHATINTERFACE_H_1270658265
 +
 
 +
#include <QtCore/QObject>
 +
#include <QtCore/QByteArray>
 +
#include <QtCore/QList>
 +
#include <QtCore/QMap>
 +
#include <QtCore/QString>
 +
#include <QtCore/QStringList>
 +
#include <QtCore/QVariant>
 +
#include <QtDBus/QtDBus>
 +
#include "Message.hpp"
 +
 
 +
/*
 +
* Proxy class for interface demo.Chat
 +
*/
 +
class DemoChatInterface: public QDBusAbstractInterface
 +
{
 +
    Q_OBJECT
 +
public:
 +
    static inline const char *staticInterfaceName()
 +
    { return "demo.Chat"; }
 +
 
 +
public:
 +
    DemoChatInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
 +
 
 +
    ~DemoChatInterface();
 +
 
 +
    Q_PROPERTY(QStringList users READ users)
 +
    inline QStringList users() const
 +
    { return qvariant_cast< QStringList >(property("users")); }
 +
 
 +
public Q_SLOTS: // METHODS
 +
    inline QDBusPendingReply<> addUser(const QString &user)
 +
    {
 +
        QList<QVariant> argumentList;
 +
        argumentList << qVariantFromValue(user);
 +
        return asyncCallWithArgumentList(QLatin1String("addUser"), argumentList);
 +
    }
 +
 
 +
    inline QDBusPendingReply<> removeUser(const QString &user)
 +
    {
 +
        QList<QVariant> argumentList;
 +
        argumentList << qVariantFromValue(user);
 +
        return asyncCallWithArgumentList(QLatin1String("removeUser"), argumentList);
 +
    }
 +
 
 +
    inline QDBusPendingReply<> sendMessage(Message message)
 +
    {
 +
        QList<QVariant> argumentList;
 +
        argumentList << qVariantFromValue(message);
 +
        return asyncCallWithArgumentList(QLatin1String("sendMessage"), argumentList);
 +
    }
 +
 
 +
Q_SIGNALS: // SIGNALS
 +
    void messageSent(Message message);
 +
    void userAdded(const QString &user);
 +
    void userRemoved(const QString &user);
 +
};
 +
 
 +
namespace demo {
 +
  typedef ::DemoChatInterface Chat;
 +
}
 +
#endif
 +
</syntaxhighlight>
 +
 
 +
== ChatInterface.cpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
/*
 +
* This file was generated by qdbusxml2cpp version 0.7
 +
* Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface
 +
*
 +
* qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 +
*
 +
* This is an auto-generated file.
 +
* This file may have been hand-edited. Look for HAND-EDIT comments
 +
* before re-generating it.
 +
*/
 +
 
 +
#include "ChatInterface.h"
 +
 
 +
/*
 +
* Implementation of interface class DemoChatInterface
 +
*/
 +
 
 +
DemoChatInterface::DemoChatInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
 +
    : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
 +
{
 +
}
 +
 
 +
DemoChatInterface::~DemoChatInterface()
 +
{
 +
}
 +
</syntaxhighlight>
 +
 
 +
== ChatWindow.hpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#ifndef CHATWINDOW_HPP
 +
#define CHATWINDOW_HPP
 +
 
 +
#include <QtGui/QMainWindow>
 +
#include <QtGui/QStringListModel>
 +
 
 +
#include "ChatInterface.h"
 +
 
 +
namespace Ui
 +
{
 +
    class ChatWindow;
 +
}
 +
 
 +
class QCloseEvent;
 +
 
 +
class ChatWindow : public QMainWindow
 +
{
 +
    Q_OBJECT
 +
 
 +
public:
 +
    ChatWindow(demo::Chat& chatInterface, QWidget *parent = 0);
 +
    virtual ~ChatWindow();
 +
 
 +
protected:
 +
    virtual void closeEvent(QCloseEvent *event);
 +
 
 +
private:
 +
    ChatWindow(const ChatWindow &other);
 +
    ChatWindow& operator=(const ChatWindow &other);
 +
 
 +
    Ui::ChatWindow *ui;
 +
 
 +
    QString m_userName;
 +
 
 +
    QStringListModel m_users;
 +
 
 +
    demo::Chat &m_chatInterface;
 +
 
 +
private slots:
 +
    void sendMessage();
 +
 
 +
    void chat_userAdded(const QString &user);
 +
    void chat_userRemoved(const QString &user);
 +
    void chat_messageSent(const Message &message);
 +
};
 +
 
 +
#endif // CHATWINDOW_HPP
 +
</syntaxhighlight>
 +
 
 +
== ChatWindow.cpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#include "ChatWindow.hpp"
 +
#include "ui_ChatWindow.h"
 +
 
 +
#include <QtGui/QCloseEvent>
 +
#include <QtGui/QInputDialog>
 +
#include <QtGui/QMessageBox>
 +
 
 +
#include "Message.hpp"
 +
 
 +
ChatWindow::ChatWindow(demo::Chat& chatInterface, QWidget *parent) :
 +
    QMainWindow(parent),
 +
    ui(new Ui::ChatWindow),
 +
    m_userName(),
 +
    m_users(),
 +
    m_chatInterface(chatInterface)
 +
{
 +
    ui->setupUi(this);
 +
 
 +
    QStringList userList = chatInterface.users();
 +
    m_users.setStringList(userList);
 +
    m_users.sort(0);
 +
 
 +
    connect(&m_chatInterface, SIGNAL(userAdded(QString)), SLOT(chat_userAdded(QString)));
 +
    connect(&m_chatInterface, SIGNAL(userRemoved(QString)), SLOT(chat_userRemoved(QString)));
 +
    connect(&m_chatInterface, SIGNAL(messageSent(Message)), SLOT(chat_messageSent(Message)));
 +
 
 +
    connect(ui->lneMessage, SIGNAL(returnPressed()), SLOT(sendMessage()));
 +
    connect(ui->btnSend, SIGNAL(released()), SLOT(sendMessage()));
 +
 
 +
    bool ok;
 +
    m_userName = QInputDialog::getText(this, "Username?", "User name:", QLineEdit::Normal, QDir::home().dirName(), &ok, Qt::Dialog);
 +
 
 +
    if (!ok)
 +
    {
 +
        QApplication::exit();
 +
        qFatal("Cancelled");
 +
    }
 +
 
 +
    if (userList.contains(m_userName))
 +
    {
 +
        QMessageBox::critical(this, "Invalid Username", "Username " + m_userName + " is already taken");
 +
        QApplication::exit();
 +
        qFatal("Duplicate username");
 +
    }
 +
 
 +
    m_chatInterface.addUser(m_userName);
 +
 
 +
    setWindowTitle("Chat -- " + m_userName);
 +
 
 +
    ui->lstUsers->setModel(&m_users);
 +
}
 +
 
 +
ChatWindow::~ChatWindow()
 +
{
 +
    delete ui;
 +
}
 +
 
 +
void ChatWindow::closeEvent(QCloseEvent *event)
 +
{
 +
    QMainWindow::closeEvent(event);
 +
 
 +
    if (event->isAccepted())
 +
    {
 +
        m_chatInterface.removeUser(m_userName);
 +
    }
 +
}
 +
 
 +
void ChatWindow::sendMessage()
 +
{
 +
    Message message(m_userName, ui->lneMessage->text());
 +
    m_chatInterface.sendMessage(message);
 +
 
 +
    ui->lneMessage->clear();
 +
}
 +
 
 +
void ChatWindow::chat_userAdded(const QString &user)
 +
{
 +
    QStringList users = m_users.stringList();
 +
    users.append(user);
 +
    users.sort();
 +
    m_users.setStringList(users);
 +
}
 +
 
 +
void ChatWindow::chat_userRemoved(const QString &user)
 +
{
 +
    QStringList users = m_users.stringList();
 +
    users.removeOne(user);
 +
    m_users.setStringList(users);
 +
}
 +
 
 +
void ChatWindow::chat_messageSent(const Message &message)
 +
{
 +
    ui->txtChat->appendPlainText(message.getUser() + " : " + message.getText());
 +
}
 +
</syntaxhighlight>
 +
 
 +
== ChatWindow.ui ==
 +
<syntaxhighlight lang="xml">
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
<ui version="4.0">
 +
<class>ChatWindow</class>
 +
<widget class="QMainWindow" name="ChatWindow">
 +
  <property name="geometry">
 +
  <rect>
 +
    <x>0</x>
 +
    <y>0</y>
 +
    <width>600</width>
 +
    <height>400</height>
 +
  </rect>
 +
  </property>
 +
  <property name="windowTitle">
 +
  <string>ChatWindow</string>
 +
  </property>
 +
  <widget class="QWidget" name="centralWidget">
 +
  <layout class="QVBoxLayout" name="verticalLayout">
 +
    <item>
 +
    <widget class="QWidget" name="widget_2" native="true">
 +
      <layout class="QHBoxLayout" name="horizontalLayout_2">
 +
      <item>
 +
        <widget class="QListView" name="lstUsers">
 +
        <property name="maximumSize">
 +
          <size>
 +
          <width>150</width>
 +
          <height>16777215</height>
 +
          </size>
 +
        </property>
 +
        <property name="focusPolicy">
 +
          <enum>Qt::NoFocus</enum>
 +
        </property>
 +
        <property name="editTriggers">
 +
          <set>QAbstractItemView::NoEditTriggers</set>
 +
        </property>
 +
        <property name="selectionMode">
 +
          <enum>QAbstractItemView::NoSelection</enum>
 +
        </property>
 +
        </widget>
 +
      </item>
 +
      <item>
 +
        <widget class="QPlainTextEdit" name="txtChat">
 +
        <property name="focusPolicy">
 +
          <enum>Qt::NoFocus</enum>
 +
        </property>
 +
        </widget>
 +
      </item>
 +
      </layout>
 +
    </widget>
 +
    </item>
 +
    <item>
 +
    <widget class="QWidget" name="widget" native="true">
 +
      <layout class="QHBoxLayout" name="horizontalLayout">
 +
      <item>
 +
        <widget class="QLineEdit" name="lneMessage"/>
 +
      </item>
 +
      <item>
 +
        <widget class="QPushButton" name="btnSend">
 +
        <property name="text">
 +
          <string>Send</string>
 +
        </property>
 +
        </widget>
 +
      </item>
 +
      </layout>
 +
    </widget>
 +
    </item>
 +
  </layout>
 +
  </widget>
 +
  <widget class="QMenuBar" name="menuBar">
 +
  <property name="geometry">
 +
    <rect>
 +
    <x>0</x>
 +
    <y>0</y>
 +
    <width>600</width>
 +
    <height>25</height>
 +
    </rect>
 +
  </property>
 +
  </widget>
 +
  <widget class="QToolBar" name="mainToolBar">
 +
  <attribute name="toolBarArea">
 +
    <enum>TopToolBarArea</enum>
 +
  </attribute>
 +
  <attribute name="toolBarBreak">
 +
    <bool>false</bool>
 +
  </attribute>
 +
  </widget>
 +
  <widget class="QStatusBar" name="statusBar"/>
 +
</widget>
 +
<layoutdefault spacing="6" margin="11"/>
 +
<resources/>
 +
<connections/>
 +
</ui>
 +
</syntaxhighlight>
 +
 
 +
== Message.hpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#ifndef MESSAGE_HPP
 +
#define MESSAGE_HPP
 +
 
 +
#include <QtDBus>
 +
 
 +
class Message
 +
{
 +
public:
 +
    Message();
 +
    Message(const QString& user, const QString &message);
 +
    Message(const Message &other);
 +
    Message& operator=(const Message &other);
 +
    ~Message();
 +
 
 +
    friend QDBusArgument &operator<<(QDBusArgument &argument, const Message &message);
 +
    friend const QDBusArgument &operator>>(const QDBusArgument &argument, Message &message);
 +
 
 +
    QString getUser() const;
 +
    QString getText() const;
 +
 
 +
    //register Message with the Qt type system
 +
    static void registerMetaType();
 +
 
 +
private:
 +
    QString m_user;
 +
    QString m_text;
 +
};
 +
 
 +
Q_DECLARE_METATYPE(Message)
 +
 
 +
#endif // MESSAGE_HPP
 +
</syntaxhighlight>
 +
 
 +
== Message.cpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#include "Message.hpp"
 +
 
 +
Message::Message() :
 +
        m_user(),
 +
        m_text()
 +
{
 +
}
 +
 
 +
Message::Message(const QString &user, const QString &text) :
 +
        m_user(user),
 +
        m_text(text)
 +
{
 +
}
 +
 
 +
Message::Message(const Message &other) :
 +
        m_user(other.m_user),
 +
        m_text(other.m_text)
 +
{
 +
}
 +
 
 +
Message& Message::operator=(const Message &other)
 +
{
 +
    m_user = other.m_user;
 +
    m_text = other.m_text;
 +
 
 +
    return *this;
 +
}
 +
 
 +
Message::~Message()
 +
{
 +
}
 +
 
 +
QString Message::getUser() const
 +
{
 +
    return m_user;
 +
}
 +
 
 +
QString Message::getText() const
 +
{
 +
    return m_text;
 +
}
 +
 
 +
void Message::registerMetaType()
 +
{
 +
    qRegisterMetaType<Message>("Message");
 +
 
 +
    qDBusRegisterMetaType<Message>();
 +
}
 +
 
 +
QDBusArgument &operator<<(QDBusArgument &argument, const Message& message)
 +
{
 +
    argument.beginStructure();
 +
    argument << message.m_user;
 +
    argument << message.m_text;
 +
    argument.endStructure();
 +
 
 +
    return argument;
 +
}
 +
 
 +
const QDBusArgument &operator>>(const QDBusArgument &argument, Message &message)
 +
{
 +
    argument.beginStructure();
 +
    argument >> message.m_user;
 +
    argument >> message.m_text;
 +
    argument.endStructure();
 +
 
 +
    return argument;
 +
}
 +
</syntaxhighlight>
 +
 
 +
== main.cpp ==
 +
<syntaxhighlight lang="cpp-qt">
 +
#include <QtDBus/QDBusConnection>
 +
#include <QtDBus/QDBusConnectionInterface>
 +
#include <QtGui/QApplication>
 +
 
 +
#include "Chat.hpp"
 +
#include "ChatAdaptor.h"
 +
#include "ChatInterface.h"
 +
#include "ChatWindow.hpp"
 +
#include "Message.hpp"
 +
 
 +
#define CHAT_SERVICE "demo.dbus.chat"
 +
#define CHAT_PATH "/chat"
 +
 
 +
int main(int argc, char *argv[])
 +
{
 +
    /*
 +
      Register the Message type first thing, so Qt knows how to handle
 +
      it before an Adaptor/Interface is even constructed.
 +
 
 +
      It should be ok to register it further down, as long as no
 +
      Message marshaling or unmarshaling takes place, but this is
 +
      definitely the safest way of doing things.
 +
    */
 +
    Message::registerMetaType();
 +
 
 +
    QApplication a(argc, argv);
 +
    Chat* pChat = NULL;
 +
    ChatAdaptor* pChatAdaptor = NULL;
 +
 
 +
    /*
 +
      Create a Chat instance and register it with the session bus only if
 +
      the service isn't already available.
 +
    */
 +
    QDBusConnection connection = QDBusConnection::sessionBus();
 +
    if (!connection.interface()->isServiceRegistered(CHAT_SERVICE))
 +
    {
 +
        pChat = new Chat(&a);
 +
        pChatAdaptor = new ChatAdaptor(pChat);
 +
 
 +
        if (!connection.registerService(CHAT_SERVICE))
 +
        {
 +
            qFatal("Could not register service!");
 +
        }
 +
 
 +
        if (!connection.registerObject(CHAT_PATH, pChat))
 +
        {
 +
            qFatal("Could not register Chat object!");
 +
        }
 +
    }
 +
 
 +
    demo::Chat chatInterface(CHAT_SERVICE, CHAT_PATH, connection);
 +
 
 +
    ChatWindow w(chatInterface);
 +
    w.show();
 +
 
 +
    int ret = a.exec();
 +
 
 +
    //cleanup
 +
    if (pChat)
 +
    {
 +
        delete pChat;
 +
    }
 +
    if (pChatAdaptor)
 +
    {
 +
        delete pChatAdaptor;
 +
    }
 +
 
 +
    return ret;
 +
}
 +
</syntaxhighlight>
  
You only need a boost header file to do this
+
== DBusChat.pro ==
('''boost/type_traits/is_enum.hpp''', to be exact). There is no need
+
<syntaxhighlight lang="bash">
to have the entire library installed, which is rather nice on an
+
# -------------------------------------------------
embedded platform, since the boost library takes up quite a bit of
+
# Project created by QtCreator 2010-04-06T21:08:22
space.
+
# -------------------------------------------------
}}}
+
QT += dbus
 +
TARGET = DBusChat
 +
TEMPLATE = app
 +
SOURCES += main.cpp \
 +
    ChatWindow.cpp \
 +
    Chat.cpp \
 +
    Message.cpp \
 +
    ChatAdaptor.cpp \
 +
    ChatInterface.cpp
 +
HEADERS += ChatWindow.hpp \
 +
    Chat.hpp \
 +
    Message.hpp \
 +
    ChatAdaptor.h \
 +
    ChatInterface.h
 +
FORMS += ChatWindow.ui
 +
</syntaxhighlight>

Latest revision as of 01:10, 9 December 2013


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

DBus-based IPC has been intregrated nicely in the Qt framework. Qt also provides Adaptors and Interfaces to communicate with remote objects, so you don't have to manually construct QDBusMessages. These Adaptors and Interfaces are very similar to the Stubs and Proxies used in other RPC mechanisms like Corba and RMI.

So, Qt provides a way to talk to objects in a different process and it contains Adaptors and Interfaces to do so in a nice, object-oriented way. A number of types can be marshaled and unmarshaled by default, (QString, QPoint, QRect, etc.), so you can just use those in DBus-enabled classes without extra effort.

However, using a custom type is somewhat more complicated, as you need to tell Qt how to handle it. That is what this tutorial is about.

An example was created to go along with this tutorial. It is a very basic chat application that communicates through the session DBus.

Contents

[edit] Write a class

Write the class you would like to publish, complete with signals, slots and properties, using custom types as you go along.

The class being published in the example is the following:

  1. class Chat : public QObject
  2. {
  3.     Q_OBJECT
  4.  
  5.     Q_CLASSINFO("D-Bus Interface", "demo.Chat")
  6.     Q_PROPERTY( QStringList users READ users)
  7.  
  8. signals:
  9.     void userAdded(const QString& user);
  10.     void userRemoved(const QString& user);
  11.  
  12.     void messageSent(const Message &message);
  13.  
  14. public slots:
  15.     void addUser(const QString &user);
  16.     void removeUser(const QString &user);
  17.  
  18.     void sendMessage(const Message &message);
  19.  
  20. public:
  21.     Chat(QObject* parent = 0);
  22.     virtual ~Chat();
  23.  
  24.     QStringList users() const;
  25.  
  26. private:
  27.     QStringList m_users;
  28. };

It contains simple user management and provides the means to send a message. The Message class is the custom type in the example. It contains only a user and a text message. There is no reason why the methods in the Chat class couldn't just take 2 QString parameters, but then Qt would be able to do everything automatically and we need something irregular for this tutorial.

To show that Qt supports a lot of types right out of the box, a QStringList was used. This will become clear further on.

[edit] Q_CLASSINFO

The Q_CLASSINFO declaration provides a means to specify an interface name that will be take into account by the xml tools.

Typically, the company name is included in the name, resulting in declarations like:

Q_CLASSINFO("D-Bus Interface", "com.firm.department.product.interface")

[edit] Generate XML

Generate the xml description of the class using the qdbuscpp2xml tool provided by Qt.

If you run qdbuscpp2xml on the Chat.hpp file in the example, you will get the following output:

  1. <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
  2. <node>
  3.   <interface name="demo.Chat">
  4.     <property name="users" type="as" access="read"/>
  5.     <signal name="userAdded">
  6.       <arg name="user" type="s" direction="out"/>
  7.     </signal>
  8.     <signal name="userRemoved">
  9.       <arg name="user" type="s" direction="out"/>
  10.     </signal>
  11.     <method name="addUser">
  12.       <arg name="user" type="s" direction="in"/>
  13.     </method>
  14.     <method name="removeUser">
  15.       <arg name="user" type="s" direction="in"/>
  16.     </method>
  17.   </interface>
  18. </node>

Note that this xml file contains all methods from the Chat class using standard Qt types. If you were to generate Adaptor and Interface classes based on this xml, you would be able to add and remove users, get the list of users and receive the userAdded and userRemoved signals. Even the QStringList type is handled automatically.

The methods dealing with the Message type, however, are not available. To generate an Adaptor and an Interface capable of dealing with the Message type, you need to modify the XML.

Tip
You may want to generate the Adaptor and Interface using only the automatically generated xml. It might be helpful to compare them to the versions we will generate further on.

[edit] Edit the XML

The qdbusxml2cpp tool needs to be told about the custom types, so you need to add some type information to the XML.

This is done by specifying annotations:

  1. <annotation name="org.qtproject.QtDBus.QtTypeName"
  2. value="*customType*"/>

The syntax differs slightly depending on wheter you're using it in a signal/method or in a property (the {{{.In0}}} is omitted for properties), but it is fairly straightforward. The following is an example dealing with the QRect type (it has no relation with the example):

  1. <property name="rectangle" type="(iiii)" access="readwrite">
  2.   <annotation name="org.qtproject.QtDBus.QtTypeName" value="QRect"/>
  3. </property>
  4.  
  5. <signal name="rectangleChanged">
  6.   <arg name="rect" type="(iiii)" direction="out"/>
  7.   <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QRect"/>
  8. </signal>
  9.  
  10. <method name="changeRectangle">
  11.   <arg name="message" type="(iiii)" direction="in"/>
  12.   <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QRect"/>
  13. </method>

Don't worry too much about what type to specify; any type that is somewhat too complex to get marshaled/unmarshaled by default handlers will be processed using the custom type, so it really doesn't matter what type you use. You could even use {{{"(iiii)"}}} for everything.

If any of your methods returns a complex result, you need to add an annotation for org.qtproject.QtDBus.QtTypeName.Out0 as well.

Note
The XML above uses QRects. QRect is supported by default, so the code above would be generated for you by qdbuscpp2xml.

The complete interface used for the Chat interface in the example is as follows:

  1. <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
  2. <node>
  3.   <interface name="demo.Chat">
  4.     <property name="users" type="as" access="read"/>
  5.     <signal name="userAdded">
  6.       <arg name="user" type="s" direction="out"/>
  7.     </signal>
  8.     <signal name="userRemoved">
  9.       <arg name="user" type="s" direction="out"/>
  10.     </signal>
  11.     <signal name="messageSent">
  12.       <arg name="message" type="a(ii)" direction="out"/>
  13.       <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="Message"/>
  14.     </signal>
  15.     <method name="addUser">
  16.       <arg name="user" type="s" direction="in"/>
  17.     </method>
  18.     <method name="removeUser">
  19.       <arg name="user" type="s" direction="in"/>
  20.     </method>
  21.     <method name="sendMessage">
  22.       <arg name="message" type="a(ii)" direction="in"/>
  23.       <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="Message"/>
  24.     </method>
  25.   </interface>
  26. </node>

[edit] Generate Adaptor and Interface classes

Now that the XML contains all the information qdbusxml2cpp needs, it's time to generate the Adaptor and Interface classes.

Since custom types are involved, you will need to specify some extra includes, so the Adaptor and Interface classes can find all the type information.

For the DBusChat example, the Adaptor and Interface classes were created by calling

qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface

[edit] Qt Meta object magic

Even though we now have an Adaptor to wrap an object and an Interface to talk to it, you will not be able to compile them as some extra things are necessary for the Qt Meta object system to be able to process the custom type.

[edit] Register the type

Declare the type as a Qt meta type by adding a

 Q_DECLARE_METATYPE
statement to the header file containing

your custom type definition.

Add qRegisterMetaType and qDBusRegisterMetaType calls to enable the framework to work with the custom type.

In the example's Message class, a static method is included to do this:

  1. void Message::registerMetaType()
  2. {
  3.     qRegisterMetaType<Message>("Message");
  4.  
  5.     qDBusRegisterMetaType<Message>();
  6. }
Important
You need to register the type both in the application publishing the object and in the application using it, since both applications need to be able to handle the custom type.

Also take care to register the custom types before calling methods on the Adaptor/Interface that need them.

Qt will show error output if you are using types it cannot (yet) handle, but you should be aware of it nonetheless.

[edit] Provide QDBusArgument streaming operators

When a DBus call is executed that uses a custom type, the Qt framework will need to marshal (serialize) or unmarshal (unserialize) the instance.

This is done by using the QDBusArgument stream operators, so you will need to implement those operators for your custom types, as explained here.

For the Message type from the example, these operators are very simple, since it only contains 2 strings:

  1. QDBusArgument &operator<<(QDBusArgument &argument, const Message& message)
  2. {
  3.     argument.beginStructure();
  4.     argument << message.m_user;
  5.     argument << message.m_text;
  6.     argument.endStructure();
  7.  
  8.     return argument;
  9. }
  10.  
  11. const QDBusArgument &operator>>(const QDBusArgument &argument, Message &message)
  12. {
  13.     argument.beginStructure();
  14.     argument >> message.m_user;
  15.     argument >> message.m_text;
  16.     argument.endStructure();
  17.  
  18.     return argument;
  19. }

QDBusArgument provides functions to handle much more complex types as well.

[edit] Use the adaptor and interface classes

You are now ready to start using the Adaptor and Interface classes and have your custom type handled automatically.

[edit] Publishing an object using an Adaptor

To publish an object, you should instantiate an Adaptor for it and then register the object with the DBus you are using.

This is the code that publishes a Chat object in the DBusChat example:

  1. Chat* pChat = new Chat(&a);
  2. ChatAdaptor* pChatAdaptor = new ChatAdaptor(pChat);
  3.  
  4. if (!connection.registerService(CHAT_SERVICE))
  5. {
  6.     qFatal("Could not register service!");
  7. }
  8.  
  9. if (!connection.registerObject(CHAT_PATH, pChat))
  10. {
  11.     qFatal("Could not register Chat object!");
  12. }

The ChatAdaptor instance will process any incoming DBus requests for the Chat object. When a method is invoked, it will call the matching slot on the Chat object. When the Chat object emits a signal, the ChatAdaptor will transmit it across DBus.

[edit] Talking to a remote object using an Interface

To talk to a remote object, you need only instantiate the matching Interface and pass the correct service name and object path.

In the DBusChat example, a connection is made with a remote Chat object like this:

demo::Chat chatInterface(CHAT_SERVICE, CHAT_PATH, connection);

[edit] Using the remote object in your application

Once you have an instance of the Interface class, you should be able to interact with the remote object like you would with any other QObject.

The ChatWindow class in the DBusChat example, for instance, adds a user simply by calling

m_chatInterface.addUser(m_userName);

Furthermore, ChatWindow is able to respond to signals emitted by the Chat object by connecting to its Interface class just like with any other QObject:

connect(&m_chatInterface, SIGNAL(userAdded(QString)), SLOT(chat_userAdded(QString)));
connect(&m_chatInterface, SIGNAL(userRemoved(QString)), SLOT(chat_userRemoved(QString)));
connect(&m_chatInterface, SIGNAL(messageSent(Message)), SLOT(chat_messageSent(Message)));

[edit] Varia

[edit] Automation

We're not too wild about having to do some of this stuff manually, but using the Adaptors and Interfaces is much more convenient than writing code that manually constructs dbus calls and processes the replies.

One might hope qdbuscpp2xml and qdbusxml2cpp will be extended to support custom types by analyzing an entire code tree instead of just the header files passed to them, so they would be able to recognize the custom types and add methods/signals/properties to the XML accordingly. One potential strategy for extending qdbuscpp2xml is with plugins, and this is presented at Cpp2XmlPlugins.

Alternatively, a feature could be added to Qt Creator to support the kind of solution presented here in a automated fashion. Since Qt Creator already needs to be aware of custom types used in a project, this might be more feasible (or at least easier) than modifying the qdbus tools.

[edit] Adventurous serialization of enumerations

If you happen to use a lot of enumerations and you need them to be exported across DBus, you will likely end up with QDBusArgument stream operators for every enumeration. A basic implementation could simply cast the enum to and from an int, resulting in code looking like this:

  1. QDBusArgument &operator<<(QDBusArgument &argument, const EnumerationType& source)
  2. {
  3.     argument.beginStructure();
  4.     argument << static_cast<int>(source);
  5.     argument.endStructure();
  6.     return argument;
  7. }
  8.  
  9. const QDBusArgument &operator>>(const QDBusArgument &argument, EnumerationType &dest)
  10. {
  11.     int a;
  12.     argument.beginStructure();
  13.     argument >> a;
  14.     argument.endStructure();
  15.  
  16.     dest = static_cast<EnumerationType>(a);
  17.  
  18.     return argument;
  19. }

You will notice that this code will be much the same for all enumerations. The only part that will differ is the "EnumerationType".

Thankfully, C++ knows how to handle templates, so we should be able to write just the one template-based implementation. However, we need that one implementation to handle only enumeration types, not the other custom types we want to send across DBus. Otherwise, any custom type would be cast from and to an integer value, which is probably not what you want for all types, especially not for complex ones.

This is where the boost library comes to our aid. With some magic, it supports conditionals within template definitions.

That way, we can provide QDBusArgument marshaling and unmarshalling implementations that will be used only in situations when such a conditional is true.

This is what the marshaling and unmarshaling code actually looks like:

  1. template<typename T, typename TEnum>
  2. class QDBusEnumMarshal;
  3.  
  4. template<typename T>
  5. class QDBusEnumMarshal<T, boost::true_type>
  6. {
  7. public:
  8. 	static QDBusArgument& marshal(QDBusArgument &argument, const T& source)
  9. 	{
  10. 		argument.beginStructure();
  11. 		argument << static_cast<int>(source);
  12. 		argument.endStructure();
  13. 		return argument;
  14. 	}
  15.  
  16. 	static const QDBusArgument& unmarshal(const QDBusArgument &argument, T &source)
  17. 	{
  18. 		int a;
  19. 		argument.beginStructure();
  20. 		argument >> a;
  21. 		argument.endStructure();
  22.  
  23. 		source = static_cast<T>(a);
  24.  
  25. 		return argument;
  26. 	}
  27. }

The marshal implementation can now be called by invoking:

QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::marshal(argument, source);

with T being the type we want to marshal.

Some further glue code is needed to link the implementation to the QDbusArgument stream operators:

  1. template<typename T>
  2. QDBusArgument& operator<<(QDBusArgument &argument, const T& source)
  3. {
  4.     return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::marshal(argument, source);
  5. }
  6.  
  7. template<typename T>
  8. const QDBusArgument& operator>>(const QDBusArgument &argument, T &source)
  9. {
  10.     return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::unmarshal(argument, source);
  11. }

Put all that in a header file and include it where you declare the enumerations that need to pass across DBus. The compiler is now able to find the streaming operators all on its own.

You do still need to declare the enumeration as a metatype and register it with the Qt meta object system though.

Note
You only need a boost header file to do this (boost/type_traits/is_enum.hpp, to be exact). There is no need to have the entire library installed, which is rather nice on an embedded platform, since the boost library takes up quite a bit of space.

[edit] enumDBus.hpp

This is the entire header file needed to marshal / unmarshal any enumeration:

#ifndef _ENUM_DBUS_HPP
#define _ENUM_DBUS_HPP
 
#include <QtDBus/QDBusArgument>
 
#include <boost/type_traits/is_enum.hpp>
 
using namespace std;
 
template<typename T, typename TEnum>
class QDBusEnumMarshal;
 
template<typename T>
class QDBusEnumMarshal<T, boost::true_type>
{
public:
	static QDBusArgument& marshal(QDBusArgument &argument, const T& source)
	{
		argument.beginStructure();
		argument << static_cast<int>(source);
		argument.endStructure();
		return argument;
	}
 
	static const QDBusArgument& unmarshal(const QDBusArgument &argument, T &source)
	{
		int a;
		argument.beginStructure();
		argument >> a;
		argument.endStructure();
 
		source = static_cast<T>(a);
 
		return argument;
	}
};
 
template<typename T>
QDBusArgument& operator<<(QDBusArgument &argument, const T& source)
{
	return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::marshal(argument, source);
}
 
template<typename T>
const QDBusArgument& operator>>(const QDBusArgument &argument, T &source)
{
	return QDBusEnumMarshal<T, typename boost::is_enum<T>::type>::unmarshal(argument, source);
}
 
#endif //_ENUM_DBUS_HPP

[edit] Example

This section contains the sources for the chat example. The wiki does not allow the upload of files other than images, so they've been posted as text.

Chat is the interface being published. ChatAdaptor and ChatInterface were generated using Chat.xml.

Put all these files in a directory and run

qmake
 
make

[edit] Chat.hpp

#ifndef CHAT_HPP
#define CHAT_HPP
 
#include <QObject>
#include <QStringList>
 
class Message;
 
class Chat : public QObject
{
    Q_OBJECT
 
    Q_CLASSINFO("D-Bus Interface", "demo.Chat")
    Q_PROPERTY( QStringList users READ users)
 
signals:
    void userAdded(const QString& user);
    void userRemoved(const QString& user);
 
    void messageSent(const Message &message);
 
public slots:
    void addUser(const QString &user);
    void removeUser(const QString &user);
 
    void sendMessage(const Message &message);
 
public:
    Chat(QObject* parent = 0);
    virtual ~Chat();
 
    QStringList users() const;
 
private:
    QStringList m_users;
};
#endif // CHAT_HPP

[edit] Chat.cpp

#include "Chat.hpp"
 
Chat::Chat(QObject* parent) :
        QObject(parent)
{
}
 
Chat::~Chat()
{
}
 
void Chat::addUser(const QString &user)
{
    if (!m_users.contains(user))
    {
        m_users.append(user);
 
        emit userAdded(user);
    }
}
 
void Chat::removeUser(const QString &user)
{
    if (m_users.contains(user))
    {
        m_users.removeOne(user);
 
        emit userRemoved(user);
    }
}
 
void Chat::sendMessage(const Message &message)
{
    Q_EMIT messageSent(message);
}
 
QStringList Chat::users() const
{
    return m_users;
}

[edit] Chat.xml

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <interface name="demo.Chat">
    <property name="users" type="as" access="read"/>
    <signal name="userAdded">
      <arg name="user" type="s" direction="out"/>
    </signal>
    <signal name="userRemoved">
      <arg name="user" type="s" direction="out"/>
    </signal>
    <signal name="messageSent">
      <arg name="message" type="a(ii)" direction="out"/>
      <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="Message"/>
    </signal>
    <method name="addUser">
      <arg name="user" type="s" direction="in"/>
    </method>
    <method name="removeUser">
      <arg name="user" type="s" direction="in"/>
    </method>
    <method name="sendMessage">
      <arg name="message" type="a(ii)" direction="in"/>
      <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="Message"/>
    </method>
  </interface>
</node>

[edit] ChatAdaptor.h

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
 *
 * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * This file may have been hand-edited. Look for HAND-EDIT comments
 * before re-generating it.
 */
 
#ifndef CHATADAPTOR_H_1270658274
#define CHATADAPTOR_H_1270658274
 
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
#include "Message.hpp"
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
 
/*
 * Adaptor class for interface demo.Chat
 */
class ChatAdaptor: public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "demo.Chat")
    Q_CLASSINFO("D-Bus Introspection", ""
"  <interface name=\"demo.Chat\">\n"
"    <property access=\"read\" type=\"as\" name=\"users\"/>\n"
"    <signal name=\"userAdded\">\n"
"      <arg direction=\"out\" type=\"s\" name=\"user\"/>\n"
"    </signal>\n"
"    <signal name=\"userRemoved\">\n"
"      <arg direction=\"out\" type=\"s\" name=\"user\"/>\n"
"    </signal>\n"
"    <signal name=\"messageSent\">\n"
"      <arg direction=\"out\" type=\"a(ii)\" name=\"message\"/>\n"
"      <annotation value=\"Message\" name=\"com.trolltech.QtDBus.QtTypeName.In0\"/>\n"
"    </signal>\n"
"    <method name=\"addUser\">\n"
"      <arg direction=\"in\" type=\"s\" name=\"user\"/>\n"
"    </method>\n"
"    <method name=\"removeUser\">\n"
"      <arg direction=\"in\" type=\"s\" name=\"user\"/>\n"
"    </method>\n"
"    <method name=\"sendMessage\">\n"
"      <arg direction=\"in\" type=\"a(ii)\" name=\"message\"/>\n"
"      <annotation value=\"Message\" name=\"com.trolltech.QtDBus.QtTypeName.In0\"/>\n"
"    </method>\n"
"  </interface>\n"
        "")
public:
    ChatAdaptor(QObject *parent);
    virtual ~ChatAdaptor();
 
public: // PROPERTIES
    Q_PROPERTY(QStringList users READ users)
    QStringList users() const;
 
public Q_SLOTS: // METHODS
    void addUser(const QString &user);
    void removeUser(const QString &user);
    void sendMessage(Message message);
Q_SIGNALS: // SIGNALS
    void messageSent(Message message);
    void userAdded(const QString &user);
    void userRemoved(const QString &user);
};
 
#endif

[edit] ChatAdaptor.cpp

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -a ChatAdaptor
 *
 * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * Do not edit! All changes made to it will be lost.
 */
 
#include "ChatAdaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
 
/*
 * Implementation of adaptor class ChatAdaptor
 */
 
ChatAdaptor::ChatAdaptor(QObject *parent)
    : QDBusAbstractAdaptor(parent)
{
    // constructor
    setAutoRelaySignals(true);
}
 
ChatAdaptor::~ChatAdaptor()
{
    // destructor
}
 
QStringList ChatAdaptor::users() const
{
    // get the value of property users
    return qvariant_cast< QStringList >(parent()->property("users"));
}
 
void ChatAdaptor::addUser(const QString &user)
{
    // handle method call demo.Chat.addUser
    QMetaObject::invokeMethod(parent(), "addUser", Q_ARG(QString, user));
}
 
void ChatAdaptor::removeUser(const QString &user)
{
    // handle method call demo.Chat.removeUser
    QMetaObject::invokeMethod(parent(), "removeUser", Q_ARG(QString, user));
}
 
void ChatAdaptor::sendMessage(Message message)
{
    // handle method call demo.Chat.sendMessage
    QMetaObject::invokeMethod(parent(), "sendMessage", Q_ARG(Message, message));
}

[edit] ChatInterface.h

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface
 *
 * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * Do not edit! All changes made to it will be lost.
 */
 
#ifndef CHATINTERFACE_H_1270658265
#define CHATINTERFACE_H_1270658265
 
#include <QtCore/QObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include <QtDBus/QtDBus>
#include "Message.hpp"
 
/*
 * Proxy class for interface demo.Chat
 */
class DemoChatInterface: public QDBusAbstractInterface
{
    Q_OBJECT
public:
    static inline const char *staticInterfaceName()
    { return "demo.Chat"; }
 
public:
    DemoChatInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = 0);
 
    ~DemoChatInterface();
 
    Q_PROPERTY(QStringList users READ users)
    inline QStringList users() const
    { return qvariant_cast< QStringList >(property("users")); }
 
public Q_SLOTS: // METHODS
    inline QDBusPendingReply<> addUser(const QString &user)
    {
        QList<QVariant> argumentList;
        argumentList << qVariantFromValue(user);
        return asyncCallWithArgumentList(QLatin1String("addUser"), argumentList);
    }
 
    inline QDBusPendingReply<> removeUser(const QString &user)
    {
        QList<QVariant> argumentList;
        argumentList << qVariantFromValue(user);
        return asyncCallWithArgumentList(QLatin1String("removeUser"), argumentList);
    }
 
    inline QDBusPendingReply<> sendMessage(Message message)
    {
        QList<QVariant> argumentList;
        argumentList << qVariantFromValue(message);
        return asyncCallWithArgumentList(QLatin1String("sendMessage"), argumentList);
    }
 
Q_SIGNALS: // SIGNALS
    void messageSent(Message message);
    void userAdded(const QString &user);
    void userRemoved(const QString &user);
};
 
namespace demo {
  typedef ::DemoChatInterface Chat;
}
#endif

[edit] ChatInterface.cpp

/*
 * This file was generated by qdbusxml2cpp version 0.7
 * Command line was: qdbusxml2cpp Chat.xml -i Message.hpp -p ChatInterface
 *
 * qdbusxml2cpp is Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * This is an auto-generated file.
 * This file may have been hand-edited. Look for HAND-EDIT comments
 * before re-generating it.
 */
 
#include "ChatInterface.h"
 
/*
 * Implementation of interface class DemoChatInterface
 */
 
DemoChatInterface::DemoChatInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
    : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
{
}
 
DemoChatInterface::~DemoChatInterface()
{
}

[edit] ChatWindow.hpp

#ifndef CHATWINDOW_HPP
#define CHATWINDOW_HPP
 
#include <QtGui/QMainWindow>
#include <QtGui/QStringListModel>
 
#include "ChatInterface.h"
 
namespace Ui
{
    class ChatWindow;
}
 
class QCloseEvent;
 
class ChatWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    ChatWindow(demo::Chat& chatInterface, QWidget *parent = 0);
    virtual ~ChatWindow();
 
protected:
    virtual void closeEvent(QCloseEvent *event);
 
private:
    ChatWindow(const ChatWindow &other);
    ChatWindow& operator=(const ChatWindow &other);
 
    Ui::ChatWindow *ui;
 
    QString m_userName;
 
    QStringListModel m_users;
 
    demo::Chat &m_chatInterface;
 
private slots:
    void sendMessage();
 
    void chat_userAdded(const QString &user);
    void chat_userRemoved(const QString &user);
    void chat_messageSent(const Message &message);
};
 
#endif // CHATWINDOW_HPP

[edit] ChatWindow.cpp

#include "ChatWindow.hpp"
#include "ui_ChatWindow.h"
 
#include <QtGui/QCloseEvent>
#include <QtGui/QInputDialog>
#include <QtGui/QMessageBox>
 
#include "Message.hpp"
 
ChatWindow::ChatWindow(demo::Chat& chatInterface, QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::ChatWindow),
    m_userName(),
    m_users(),
    m_chatInterface(chatInterface)
{
    ui->setupUi(this);
 
    QStringList userList = chatInterface.users();
    m_users.setStringList(userList);
    m_users.sort(0);
 
    connect(&m_chatInterface, SIGNAL(userAdded(QString)), SLOT(chat_userAdded(QString)));
    connect(&m_chatInterface, SIGNAL(userRemoved(QString)), SLOT(chat_userRemoved(QString)));
    connect(&m_chatInterface, SIGNAL(messageSent(Message)), SLOT(chat_messageSent(Message)));
 
    connect(ui->lneMessage, SIGNAL(returnPressed()), SLOT(sendMessage()));
    connect(ui->btnSend, SIGNAL(released()), SLOT(sendMessage()));
 
    bool ok;
    m_userName = QInputDialog::getText(this, "Username?", "User name:", QLineEdit::Normal, QDir::home().dirName(), &ok, Qt::Dialog);
 
    if (!ok)
    {
        QApplication::exit();
        qFatal("Cancelled");
    }
 
    if (userList.contains(m_userName))
    {
        QMessageBox::critical(this, "Invalid Username", "Username " + m_userName + " is already taken");
        QApplication::exit();
        qFatal("Duplicate username");
    }
 
    m_chatInterface.addUser(m_userName);
 
    setWindowTitle("Chat -- " + m_userName);
 
    ui->lstUsers->setModel(&m_users);
}
 
ChatWindow::~ChatWindow()
{
    delete ui;
}
 
void ChatWindow::closeEvent(QCloseEvent *event)
{
    QMainWindow::closeEvent(event);
 
    if (event->isAccepted())
    {
        m_chatInterface.removeUser(m_userName);
    }
}
 
void ChatWindow::sendMessage()
{
    Message message(m_userName, ui->lneMessage->text());
    m_chatInterface.sendMessage(message);
 
    ui->lneMessage->clear();
}
 
void ChatWindow::chat_userAdded(const QString &user)
{
    QStringList users = m_users.stringList();
    users.append(user);
    users.sort();
    m_users.setStringList(users);
}
 
void ChatWindow::chat_userRemoved(const QString &user)
{
    QStringList users = m_users.stringList();
    users.removeOne(user);
    m_users.setStringList(users);
}
 
void ChatWindow::chat_messageSent(const Message &message)
{
    ui->txtChat->appendPlainText(message.getUser() + " : " + message.getText());
}

[edit] ChatWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>ChatWindow</class>
 <widget class="QMainWindow" name="ChatWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>600</width>
    <height>400</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>ChatWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QWidget" name="widget_2" native="true">
      <layout class="QHBoxLayout" name="horizontalLayout_2">
       <item>
        <widget class="QListView" name="lstUsers">
         <property name="maximumSize">
          <size>
           <width>150</width>
           <height>16777215</height>
          </size>
         </property>
         <property name="focusPolicy">
          <enum>Qt::NoFocus</enum>
         </property>
         <property name="editTriggers">
          <set>QAbstractItemView::NoEditTriggers</set>
         </property>
         <property name="selectionMode">
          <enum>QAbstractItemView::NoSelection</enum>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QPlainTextEdit" name="txtChat">
         <property name="focusPolicy">
          <enum>Qt::NoFocus</enum>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item>
     <widget class="QWidget" name="widget" native="true">
      <layout class="QHBoxLayout" name="horizontalLayout">
       <item>
        <widget class="QLineEdit" name="lneMessage"/>
       </item>
       <item>
        <widget class="QPushButton" name="btnSend">
         <property name="text">
          <string>Send</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>600</width>
     <height>25</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

[edit] Message.hpp

#ifndef MESSAGE_HPP
#define MESSAGE_HPP
 
#include <QtDBus>
 
class Message
{
public:
    Message();
    Message(const QString& user, const QString &message);
    Message(const Message &other);
    Message& operator=(const Message &other);
    ~Message();
 
    friend QDBusArgument &operator<<(QDBusArgument &argument, const Message &message);
    friend const QDBusArgument &operator>>(const QDBusArgument &argument, Message &message);
 
    QString getUser() const;
    QString getText() const;
 
    //register Message with the Qt type system
    static void registerMetaType();
 
private:
    QString m_user;
    QString m_text;
};
 
Q_DECLARE_METATYPE(Message)
 
#endif // MESSAGE_HPP

[edit] Message.cpp

#include "Message.hpp"
 
Message::Message() :
        m_user(),
        m_text()
{
}
 
Message::Message(const QString &user, const QString &text) :
        m_user(user),
        m_text(text)
{
}
 
Message::Message(const Message &other) :
        m_user(other.m_user),
        m_text(other.m_text)
{
}
 
Message& Message::operator=(const Message &other)
{
    m_user = other.m_user;
    m_text = other.m_text;
 
    return *this;
}
 
Message::~Message()
{
}
 
QString Message::getUser() const
{
    return m_user;
}
 
QString Message::getText() const
{
    return m_text;
}
 
void Message::registerMetaType()
{
    qRegisterMetaType<Message>("Message");
 
    qDBusRegisterMetaType<Message>();
}
 
QDBusArgument &operator<<(QDBusArgument &argument, const Message& message)
{
    argument.beginStructure();
    argument << message.m_user;
    argument << message.m_text;
    argument.endStructure();
 
    return argument;
}
 
const QDBusArgument &operator>>(const QDBusArgument &argument, Message &message)
{
    argument.beginStructure();
    argument >> message.m_user;
    argument >> message.m_text;
    argument.endStructure();
 
    return argument;
}

[edit] main.cpp

#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
#include <QtGui/QApplication>
 
#include "Chat.hpp"
#include "ChatAdaptor.h"
#include "ChatInterface.h"
#include "ChatWindow.hpp"
#include "Message.hpp"
 
#define CHAT_SERVICE "demo.dbus.chat"
#define CHAT_PATH "/chat"
 
int main(int argc, char *argv[])
{
    /*
      Register the Message type first thing, so Qt knows how to handle
      it before an Adaptor/Interface is even constructed.
 
      It should be ok to register it further down, as long as no
      Message marshaling or unmarshaling takes place, but this is
      definitely the safest way of doing things.
    */
    Message::registerMetaType();
 
    QApplication a(argc, argv);
    Chat* pChat = NULL;
    ChatAdaptor* pChatAdaptor = NULL;
 
    /*
      Create a Chat instance and register it with the session bus only if
      the service isn't already available.
    */
    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.interface()->isServiceRegistered(CHAT_SERVICE))
    {
        pChat = new Chat(&a);
        pChatAdaptor = new ChatAdaptor(pChat);
 
        if (!connection.registerService(CHAT_SERVICE))
        {
            qFatal("Could not register service!");
        }
 
        if (!connection.registerObject(CHAT_PATH, pChat))
        {
            qFatal("Could not register Chat object!");
        }
    }
 
    demo::Chat chatInterface(CHAT_SERVICE, CHAT_PATH, connection);
 
    ChatWindow w(chatInterface);
    w.show();
 
    int ret = a.exec();
 
    //cleanup
    if (pChat)
    {
        delete pChat;
    }
    if (pChatAdaptor)
    {
        delete pChatAdaptor;
    }
 
    return ret;
}

[edit] DBusChat.pro

# -------------------------------------------------
# Project created by QtCreator 2010-04-06T21:08:22
# -------------------------------------------------
QT += dbus
TARGET = DBusChat
TEMPLATE = app
SOURCES += main.cpp \
    ChatWindow.cpp \
    Chat.cpp \
    Message.cpp \
    ChatAdaptor.cpp \
    ChatInterface.cpp
HEADERS += ChatWindow.hpp \
    Chat.hpp \
    Message.hpp \
    ChatAdaptor.h \
    ChatInterface.h
FORMS += ChatWindow.ui

This page was last modified on 9 December 2013, at 01:10. This page has been accessed 18,188 times. Content is available under Creative Commons License SA 3.0 as well as the GNU Free Documentation License 1.2.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V.Legal