qdbus - can't connect to dbus signal with struct argument - qt

I'm trying to connect to amarok d-bus signal StatusChange (reference: https://xmms2.org/wiki/MPRIS#StatusChange). Interface and struct is ok because I can connect to simple signal CapsChange(int) in same interface and can get status by GetStatus dbus method, so this marshall struct is ok:
struct AmarokStatus {
int st1;
int st2;
int st3;
int st4;
};
Q_DECLARE_METATYPE(AmarokStatus)
qDBusRegisterMetaType<AmarokStatus>();
But when call:
mInf = new QDBusInterface("org.mpris.MediaPlayer2.amarok", "/Player",
"org.freedesktop.MediaPlayer", QDBusConnection::sessionBus(),this);
connect(mInf, SIGNAL(StatusChange(AmarokStatus)), this, SLOT(statusChanged(AmarokStatus)));
connect(mInf, SIGNAL(CapsChange(int)), this, SLOT(capsChange(int)));
I got message:
Object::connect: No such signal
org::freedesktop::MediaPlayer::StatusChange(AmarokStatus)
I have tried with SIGNAL(StatusChange(struct)) and SIGNAL(StatusChange(QDbusargument)) and other types but same message
D-Feet is saying that definition of StatusChange is: StatusChange(Struct of (Int32, Int32, Int32, Int32)), same with dbus-monitor. And same problem with signal TrackChange(array of struct). So I'm definitely messing something with connect() method.

There are two options:
Ensure the emitter which implements QDBusAbstractInterface defines the signal you are connecting to. This solution is a bit cleaner.
or
Use QDBusConnection::connect to connect to an anonymous signal. This solution works in a pinch but in my experience can be particularly error-prone:
if (!QDBusConnection::systemBus().connect("org.mpris.MediaPlayer2.amarok", "/Player", "org.freedesktop.MediaPlayer", "StatusChange", this, SLOT(statusChanged(AmarokStatus)))) {
qWarning() << "Failed to connect";
}

Related

How to get QDBusConnection::connect() failure reason

I'm trying to connect to a D-Bus signal this way:
bool result = QDBusConnection::systemBus().connect(
"foo.bar", // service
"/foo/bar", // path
"foo.bar", // interface
"SignalSomething",
this,
SLOT(SignalSomethingSlot()));
if( !result )
{
// Why!?
}
QDBusConnection::connect() returns a boolean, how do I get extended error information? If a check QDBusConnection::lastError() it returns no useful information (as QDBusError::isValid() is false).
I had the same issue and it turned out that the slot I connected to had the wrong parameter types. They must match according to Qt's documentation and it looks like connect() verifies that, despite not explicitly mentioned.
Warning: The signal will only be delivered to the slot if the parameters match.
I suggest d-feet to list signals and check their parameter types. dbus-monitor does list signals, paths and such too, but not always the exact type of parameters.
One important observation though: I fixed the issue in my particular case by using different slot parameters than the actual signal has!
I wanted to connect to a com.ubuntu.Upstart0_6 signal mentioned here to detect when the screen in Ubuntu is locked/unlocked. dbusmonitor prints the following and d-feet shows parameters (String, Array of [String])
// dbusmonitor output
signal time=1529077633.579984 sender=:1.0 -> destination=(null destination) serial=809 path=/com/ubuntu/Upstart; interface=com.ubuntu.Upstart0_6; member=EventEmitted
string "desktop-unlock"
array [
]
Hence the signal should be of type
void screenLockChangedUbuntu(QString event, QVector<QString> args) // connect() -> false
This however made connect() return false. The solution was to remove the array parameter from the slot:
void screenLockChangedUbuntu(QString event) // works
I am aware that the array parameter was always empty, but I cannot explain why it only worked when removing it.
You could try these tricks:
1) Set QDBUS_DEBUG environment variable before running your application.
export QDBUS_DEBUG=1
2) Start dbus-monitor to see what's happening on the bus. You may need to set a global policy to be able to eavesdrop system bus depending on your distro.
Update:
Are you sure connecting to the system bus succeeded? If it fails you should probably check system.conf policy and possibly create own conf in system.d. This post might be helpful.
You could first connect to the system bus with QDBusConnection::connectToBus and check if it succeeded with QDBusConnection::isConnected. Only after that you try to connect to the signal and check if that succeeded.
QDBusConnection bus = QDBusConnection::connectToBus(QDBusConnection::systemBus, myConnectionName);
if (bus.isConnected())
{
if(!bus.connect( ... ))
{
// Connecting to signal failed
}
}
else
{
// Connecting to system bus failed
}

Error while connecting lambda function to QProcess::error

In following code I want to connect lambda function to QProcess::error signal:
void Updater::start() {
QProcess process;
QObject::connect(&process, &QProcess::error, [=] (QProcess::ProcessError error) {
qWarning() << "error " << error;
});
process.start("MyProgram");
process.waitForFinished();
}
But I get strange error:
error: no matching function for call to 'Updater::connect(QProcess*
[unresolved overloaded function type],
Updater::start()::)' });
What I do wrong here? The code executes inside method of class derived from QObject. The project configured to work with c++11.
I use Qt 5.3.1 on Linux x32 with gcc 4.9.2
Problem is that the QProcess has another error() method, so compiler just doesn't know which method use. If you want to deal with overloaded methods, you should use next:
QProcess process;
connect(&process, static_cast<void (QProcess::*)(QProcess::ProcessError)>
(&QProcess::error), [=](QProcess::ProcessError pError) {
qWarning() << "error " << pError;
});
process.start("MyProgram");
process.waitForFinished();
Yes, it looks ugly, but there is no another way (only old syntax?).
This special line tells compiler that you want to use void QProcess::error(QProcess::ProcessError error), so now there is no any ambiguity
More information you can find here.
For those who are using Qt 5.6 or later, the QProcess::error signal is deprecated. You can use the QProcess::errorOccurred signal instead to avoid the naming ambiguity and complicated casting.
QProcess process;
connect(&process, &QProcess::errorOccurred, [=](QProcess::ProcessError error) {
qWarning() << error;
});
process.start("MyProgram");
process.waitForFinished();

Qt TCP/IP socket connection check

I am writing Qt TCP/IP client. I want to check the connection state with server before send data to sever.
As far my knowledge I can do this by following methods
Use a bool 'ConnectionState', set this variable when connected with
sever and reset this variable on disconnected() signal. Now before
sending data to server (client->write()) check the value of this
variable.
use this 'client->state() == QTcpSocket::ConnectedState' way to check the connection state.
Which is good practice. Or any other method to this.
Thanks In advance.
QTCPSocket is derived from QAbstractSocket, which provides a state() function. This returns one of the following enums: -
enum SocketState { UnconnectedState, HostLookupState, ConnectingState, ConnectedState, ..., ListeningState }
So, assuming m_pSocket is a QTcpSocket, you would simply do this to check if it is connected:-
bool connected = (m_pSocket->state() == QTcpSocket::ConnectedState);
You could add a boolean and keep track of the state, but if a network error occurs you need to ensure that it is always in-sync with the actual connection state.
You can use errorOccurred signal and It's just enough for this signal define a slot in client. when an error occurs, a signal trigger and you can receive notify in slot function.
In client.h
/* define a slot for client */
public slots:
void errorOccurred(QAbstractSocket::SocketError error);
In client.c
/*client constructor*/
Client::Client(QObject *parent) {
/*some other code here*/
connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
this, SLOT(errorOccurred(QAbstractSocket::SocketError)));
/*and maybe some other code here*/
}
and in client.c write implementation for errorOccurred:
void Client::errorOccurred(QAbstractSocket::SocketError error) {
qDebug() << "error in connection: " << socket->errorString();
}
OUTPUT:
error in connection: "Connection refused"

Connecting to QNetworkReply::error signal

I am using the new connect syntax for Qt5. QNetworkReply has a signal called error and also a function called error. This causes problems when attempting to connect to the signal:
connect(reply, &QNetworkReply::error, this, &MyClass::error);
error C2664: 'QMetaObject::Connection QObject::connect(const QObject *,const char *,const QObject *,const char *,Qt::ConnectionType)' : cannot convert parameter 2 from 'overloaded-function' to 'const char *'
Context does not allow for disambiguation of overloaded function
How do I tell the compiler (MSVC) that I want to connect to the signal rather than the function?
You need manually to cast the function pointer to address the correct function:
connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), this, &MyClass::error);
This is the noted consequence of using new syntax for overloaded functions:
cons:
Very complicated syntax in cases of overloads?
P.S. You can find similar example on the above link:
QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), [socket] (QAbstractSocket::SocketError) {
qDebug()<< "ERROR " << socket->errorString();
socket->deleteLater();
});
Start from Qt 5.15 QNetworkReply::error is not a signal any more. You can connect to &QNetworkReply::errorOccurred instead, fortunately, without type casting:
connect(reply, &QNetworkReply::errorOccurred, this,
[reply](QNetworkReply::NetworkError) {
qCDebug() << "Error " << reply->errorString();
});

Passing a QLocalSocket* to a method expecting a QIODevice*

Perhaps I'm being over-ambitious, but I'm trying to write a server program which can accept connections over both QLocalSockets and QTcpSockets. The concept is to have a 'nexus' object with both a QLocalServer and QTcpServer listening for new connections:
Nexus::Nexus(QObject *parent)
: QObject(parent)
{
// Establish a QLocalServer to deal with local connection requests:
localServer = new QLocalServer;
connect(localServer, SIGNAL(newConnection()),
this, SLOT(newLocalConnection()));
localServer -> listen("CalculationServer");
// Establish a UDP socket to deal with discovery requests:
udpServer = new QUdpSocket(this);
udpServer -> bind(QHostAddress::Any, SERVER_DISCOVERY_PORT);
connect(udpServer, SIGNAL(readyRead()),
this, SLOT(beDiscovered()));
// Establish a QTcpServer to deal with remote connection requests:
tcpServer = new QTcpServer;
connect(tcpServer, SIGNAL(newConnection()),
this, SLOT(newTcpConnection()));
tcpServer -> listen(QHostAddress::Any, SERVER_COMMAND_PORT);
}
... and then separate slots which establish a server object, whose constructor takes a pointer to a QIODevice. In theory, this ought to work because both QLocalSocket and QTcpSocket inherit QIODevice. Here is the newLocalConnection slot, for example:
void Nexus::newLocalConnection()
{
// Create a new CalculationServer connected to the newly-created local socket:
serverList.append(new CalculationServer(localServer -> nextPendingConnection()));
// We don't allow more than one local connection, so stop listening on the server:
localServer -> close();
}
The problem is that this won't compile, giving an error:
error C2664:
'CalculationServer::CalculationServer(QIODevice
*,QObject *)' : cannot convert parameter 1 from 'QLocalSocket *' to
'QIODevice *' 1> Types pointed
to are unrelated; conversion requires
reinterpret_cast, C-style cast or
function-style cast
Now the types pointed to are clearly not unrelated, and elsewhere in my code I have no problems at all with actions like:
QLocalSocket *socket = new QLocalSocket;
QIODevice *server = new QIODevice;
server = socket;
... so can anyone tell me why the compiler has a problem with this? Is there a way that I can make the constructor accept the QLocalServer*? I suppose there is the nuclear option of getting the constructor to take a void pointer plus an extra variable to tell it what it's being sent, so it can then recast the void pointer to either a QLocalSocket or QTcpSocket, but I feel uncomfortable resorting to reinterpret_cast on what looks like it ought to be a straightforward bit of C++ polymorphism.
Regards,
Stephen.
The most likely reason is that you have forgotten to #include <QLocalSocket> in the source file where the error occurs.

Resources