DBus reply data accessed via QString
-
QDBusMessage is a struct that holds the reply and some further meta data like error codes etc.
CheckQDBusMessage::arguments()
to get a list of all arguments as list of QVariants andQVariant
provides atoString()
method. (see http://doc.qt.io/qt-5/qdbusmessage.html#arguments)QDBusMessage reply = dbusInterface->call(...); QList<QVariant> args = reply.arguments(); if (args.count() != 3) { qCritical("Got no valid DBus response"); } else { QString a0 = args.at(0).toString(); QString a1 = args.at(1).toString(); QStirng a2 = args.at(2).toString(); // .... }
QVariant
provides also methods to convert to other types if you expect an int or something else in the reply.And: If you expect just one value, you can assign the
QDBusMessage
directly to aQDbusReply
:QDBusReply<QString> reply = dbusInterface->call(...); if (reply.isValid()) { QString reply = reply.value()); // ... } else { qCritical("Got no valid DBus response"); }
btw, Qt brings a the
qdbusviewer
which is very useful to inspect the provided DBus interfaces and methods of running processes... -
Hi Micland, thank you a lot for your answer. However when I adapt your code to mine in that way:
iface_Clementine = new QDBusInterface("org.mpris.clementine", "/Player", "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus(), this); replyClementine = iface_Clementine->call("GetMetadata"); QList<QVariant> args = replyClementine.arguments(); if (args.count() == 0) { qCritical("Got no valid DBus response"); } else { QString a1 = args.at(1).toString(); qDebug() << a1; }
I get the following result:
""
, In other word an empty string. Sometimes my app crashes. Why? -
@amonR2 said:
I get the following result:
""
, In other word an empty string. Sometimes my app crashes. Why?Did you run the
qdbusviewer
(separate tool shipped with Qt) and inspect your interface manually? Does this viewer also get an empty string or anything else? If you get a valid answer using Qt I guess the backends is sending an empty string... -
@ Kshegunov, thanks for your help. To check if the obtained interface is valid, is this code correct:
iface_Clementine = new QDBusInterface("org.mpris.clementine", "/Player", "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus(), this); if (!iface_Clementine->isValid()) { qCritical("The player is not nactive"); } else{ replyClementine = iface_Clementine->call("GetMetadata"); QList<QVariant> args = replyClementine.arguments(); if (args.count() == 0) { qCritical("Got no valid DBus response"); } else { QString a1 = args.at(1).toString(); qDebug() << a1; } }
But how do you do to check if the reply is valid?
@ micland, yes I ran qdbusviewer and obtained a message like the one I put in my first post:
//Received reply from :1.156 Arguments: [Argument: a{sv} {"album" = [Variant(QString): "In Search of..."], "artist" = [Variant(QString): "N.E.R.D."],...]
. The only difference was the name of the service that I changed in my code but I still get an empty string as result. Why the backend sends an empty string instead of the actual result? And in my code, if you notice, I check if the number of items in the list is null. Does Qt counts an empty string as an item? -
To check if the obtained interface is valid, is this code correct
Yes.
But how do you do to check if the reply is valid?
I personally check it through a
QDBusReply
object (e.g. here). If using the dbus message class use QDBusAbstractInterface::lastError instead. -
@amonR2 said:
yes I ran qdbusviewer and obtained a message like the one I put in my first post:
//Received reply from :1.156 Arguments: [Argument: a{sv} {"album" = [Variant(QString): "In Search of..."], "artist" = [Variant(QString): "N.E.R.D."],...]
. The only difference was the name of the service that I changed in my code but I still get an empty string as result.Uhm, that's a bit strange. Can you try to inspect the reply with the debugger or send the arguments to debug out (
qDebug() << args;
) just to see if all arguments are empty or what Qt has interpreted from the message? -
@kshegunov , thank you very much.
@micland , I don't know how to inspect the reply with the debugger so I prefer to give you the result of
qDebug() << args;
which is:(QVariant(, ))
. Should I set Qt or my header in a certain way? So far I use the following includes:#include <QtGui> #include <QtDBus/QtDBus>
Is it ok? And what about the Q_DECLARE_METATYPE() that I don't use? Should I use it here? If yes, how?
-
@amonR2 said:
I don't know how to inspect the reply with the debugger so I prefer to give you the result of
qDebug() << args;
which is:(QVariant(, ))
. Should I set Qt or my header in a certain way?The order of the includes should not be relevant.
According to your debug output your program does not get the same answer asqdbusviewer
gets. But I don't think that it's a bug in Qt becauseqdbusviewer
uses the same logic and should be affected by that bug, too.
Could you check forreply->errorMessage();
and double check if there is any typo in the specified DBus interface / object / method in your code? -
@micland said:
Could you check for
reply->errorMessage();
There sorry but I am not sure what you are talking about, did you mean to do this instead:
qDebug() << replyClementine.errroMessage();
? If yes, the output is still an empty string (""
). For the typo in the DBus interface, objet and method in my code I don't think there is one asqDebug() << replyClementine
gives me a correct response. But why this response is not "translated" into a list of variants and strings? What may "corrupt" the process here? -
@amonR2
Yes, I wanted to see that there is no error message provided (or: I was hoping to see a message that gave us a hint ;-) ). I'm a bit confused that there is no error AND no usable reply.
Another idea: If you don't known how to debug the arguments, can you print outargs.count()
? Perhaps there is just one entry in the list and this entry might be a new list (or hash map) - and you're accessing the (not existing) second argument.... (the output(QVariant(, ))
might indicate that there is just one argument...). -
The output of
qDebug << args.count();
gives me1
. There is something I have changed in my code's environment: previously it was in release mode and I have just thought to put it in Debug one's which gives more info. With this code:QString a1 = args.at(1).toString(); qDebug() << a1;
my app crashes and the IDE gives me this message:
ASSERT failure in QList<T>::at: "index out of range", file /usr/include/qt4/QtCore/qlist.h, line 469
. But when I use this one:QString a1 = args.at(0).toString(); qDebug() << a1;
my app doesn't crash and
qDebug()
gives me an empty string. -
@amonR2
Ok, the argument list contains just one argument - that's why you get the "index out of range" error because you try to access the second argument. Please add the lineqDebug() << args.at(0).typeName();
to see the type of the first argument. Maybe it's a list where you find all your expected data in... -
@amonR2
Huh, a nestedQDBusArgument
? Well check the inner argument if it serves the expected information:
args.at(0).value<QDBusArgument>().args()
.
(You should really try to inspect the reply using the debugger and iterate through the object tree to see what's encapsulated in the arguments. That's a lot easier than poking withqDebug()
- perhaps this link will help you getting started? http://doc.qt.io/qtcreator/creator-debug-mode.html) -
@micland said:
Huh, a nested
QDBusArgument
?sorry I don't understand what you mean there but that's all qDebug displays.
Then when I try to qDebug thisargs.at(0).value<QDBusArgument>().args();
, the compiler says:'class QDBusArgument' has no member named 'args'
. So I tried with this:args.at(0).value<QDBusArgument>()
and received the following from the compiler:no match for 'operator<<' in 'qDebug()() << QVariant::value() const [with T = QDBusArgument]()' [...] no known conversion for argument 1 from 'QDebug' to 'QDBusArgument&'
.
Finally, by puting a breakpoint (thank you for the link) atQList<QVariant> args = replyClementine.arguments();
in my code and launching the debug-mode, the compiler stops and shows me this through the "Locals and Expressions" window:args <inaccessible> QList<QVariant> this @0x83f87d8 FenetreNNS QWidget QWidget Deviselabel 0x0 QLabel * MainLabel @0x844d210 QLabel Nextm @0x8451f28 QPushButton NoTrackLabel @0x8454628 QLabel Play @0x844f628 QPushButton Previous @0x8451088 QPushButton TemoinSousTitre false bool Volume @0x8453a48 QSlider VolumeLabel @0x844d160 QLabel boutonDeviseBaht @0x8439978 QPushButton boutonDeviseDenar @0x843cce0 QPushButton boutonDeviseDollar @0x843f968 QPushButton boutonDeviseEuro @0x8440418 QPushButton boutonDeviseLira @0x843e608 QPushButton boutonDevisePeso @0x8442098 QPushButton boutonDevisePound @0x8444e58 QPushButton boutonDeviseRuble @0x843b078 QPushButton boutonDeviseRupee @0x844a4b0 QPushButton boutonDeviseWon @0x844b2e0 QPushButton boutonDeviseYen @0x8448a28 QPushButton boutonDeviseYuan @0x8449e70 QPushButton iface_Clementine @0x845e788 QDBusInterface machine @0x8455420 QStateMachine principalefen @0xbffff7b4 FenPrincipale replyClementine QDBusMessage d_ptr @0x845d098 QDBusMessagePrivate signMapper @0x8454ac0 QSignalMapper signMapper2 @0x84553e8 QSignalMapper sliderSousTitre @0x844d998 QSlider state1 @0x8451f60 QState state2 @0x8451f70 QState str1 "Currency" QString tempo @0x84505b0 QTimer
Should I show the content of some other windows?
-
@amonR2 said:
Huh, a nested
QDBusArgument
?
sorry I don't understand what you mean there but that's all qDebug displays.Aye, sorry - I was wrong, read your answer too fast and got confused...
It's hard to find this error without reproducing it. If I find some time at the weekend I'll try to debug it. Can you tell me which software you installed? will say: what's that player your communicating with? -
@amonR2
I'm a little bit closer, but did non really succeed...
First: Qt 4.8 is some days older, I used 5.6 for my experiements. Inspecting the DBus interface usingqdbusviewer
(from Qt5.6) ended with the error message "Unable to find method GetMetadata on path /Player in interface org.freedesktop.MediaPlayer" (the method is listed but I can't call it) - but the cli clientqdbus
provided me the expected data. I can't say if that's a bug in Qt or a "conformity problem", but it shows that calling the clementine DBus interface from Qt works not straight forward.I tried to access the interface using a simple Qt program (like the example code you posted) and got the same reply as you did. Inspecting the received
QDBusArgument
showed that itscurrentyType()
is a MapType (4) which means that the arguments are organized as a key/value list (the same says the MPRIS spec: see "Metadata" is an array of dict entries in the form (string, variant) eg: {sv}., https://xmms2.org/wiki/MPRIS#.22Metadata.22)I think you have to extract the values manually and play arround with
beginMap()
,beginMapEntry()
,endMapEntry()
,endMap()
ofQDBusArgument
. (I spent just a little time but did not succeed ...)
But I'm interested if that's the right way so if you find a solution please share it here! (If I find some time I will try it again, too...) -
@amonR2
out of curiousity I was looking for a solution (I didn't know before that maps and structs are supported by DBus). And I found a way - using Qt 5.6, but should work with Qt 4.8 as well:// first the same code as you did already QDBusInterface *iface_Clementine = new QDBusInterface("org.mpris.clementine", "/Player", "org.freedesktop.MediaPlayer", QDBusConnection::sessionBus()); if(iface_Clementine->isValid()) { QDBusMessage replyClementine = iface_Clementine->call("GetMetadata"); qDebug() << replyClementine; // there is just one argument and this argument is a map QList<QVariant> args = replyClementine.arguments(); const QDBusArgument &arg = args[0].value<QDBusArgument>(); // the map has to be extracted entry by entry which is a tuple of key and value // in this case, the key is a string but the type of the value depends on the key (string, int, ...) arg.beginMap(); while (!arg.atEnd()) { QString key; QDBusVariant value; arg.beginMapEntry(); arg >> key >> value; arg.endMapEntry(); qDebug() << "Key:" << key << "\t\tValue:" << value.variant(); } arg.endMap(); }
You still have to inspect the type of the value (depends on what the entry represents: album or track number, or whatever). And the argument parsing can be wrapped in a stream operator, see http://doc.qt.io/qt-5/qdbusargument.html#beginMap-1
Some further helpul information can be found here: http://stackoverflow.com/questions/20206376/how-do-i-extract-the-returned-data-from-qdbusmessage-in-a-qt-dbus-call
I have no clue why the qdbusviewer of Qt 5.6 is unable to call the DBus method but the code snippet above is working fine.