DBus reply data accessed via QString
-
@ 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.
-
WOOW!!! Thank you sooo much micland!! It works!!!
I tried a code similar to yours but some data were missing. Just needed to do a very tiny modification on your code:qDebug() << key << value.variant().toString();
or directly
qDebug() << value.variant().toString();
Except it, it is perfect! Thank you again.
@micland said: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.
I hope it is going to change as I intend to use it or the 5.7 version very soon.
Cheers again.