Come utilizzare Qt-Dbus associazioni senza bloccare il thread principale
-
26-12-2019 - |
Domanda
Il mio obiettivo è quello di creare una libreria di utilizzo Qt DBus associazioni.
Ho provato a creare un'applicazione Qt senza lanciare il QEventLoop
(fornito dal QCoreApplication
classe) nel thread principale.
Qui è un minimalista applicazione di esempio, lavorando bene con le QT 4.6.2 versione, ma il blocco sull'introspezione utilizzando QT 4.8 o superiore.
DBusHandler.hpp
#pragma once
#include <iostream>
#include <QtCore/QThread>
#include <QtCore/QtCore>
#include <QtDBus/QDBusInterface>
class DBusHandler : public QThread
{
Q_OBJECT;
private:
void run(void)
{
QDBusConnection connection = QDBusConnection::sessionBus();
connection.registerService("my.qdbus.example");
connection.registerObject("/", this, QDBusConnection::ExportAllSlots);
exec();
}
public:
DBusHandler(void) {}
virtual ~DBusHandler(void) {}
void stop(void)
{
QDBusConnection connection = QDBusConnection::sessionBus();
connection.unregisterObject("/");
connection.unregisterService("my.qdbus.example");
connection.disconnectFromBus(connection.name());
QThread::quit();
}
public slots:
void remoteCall(QByteArray message)
{
std::cout << "Message size: " << message.size() << std::endl;
}
};
main.cpp
#include "DBusHandler.hpp"
int main(int ac, char **av)
{
QCoreApplication app(ac, av);
DBusHandler handler;
handler.moveToThread(&handler);
handler.start();
while (not handler.isRunning());
// app.exec();
sleep(10); // Gives time to call using the command line: "qdbus my.qdbus.example / local.DBusHandler.remoteCall a_message"
handler.stop();
while (handler.isRunning());
}
Come si può vedere nel main.cpp
file, app.exec()
non è commentata, ma rende l'applicazione funziona bene su QT 4.8 o versioni superiori (5.3.0).
La mia domanda è la seguente:È possibile utilizzare Qt DBus associazioni di chiamata app.exec()
in un altro thread da quello principale, su Qt 4.8 5.3 ?
Soluzione
Sfondo:C'è una classe privata denominato QDBusConnectionPrivate
che eredita da QObject, e gestisce tutta la rete.Purtroppo, se si guarda qdbusconnection.cpp:1116
vedrai che Qt hard codici moveToThread
per QCoreApplication::instance()
.
Probabilmente si dovrebbe presentare una richiesta di miglioramento per consentire all'utente di creare un QDBusConnection che utilizza un utente specificato il filetto o il ciclo di eventi. Vedere aggiornamento di seguito.
Nel frattempo, se sei comodo fare certe cose pericolose, è possibile hack è in te creando il proprio QDbusConnection
sottoclasse (io la mia chiamata SpecializedDBusConnection
) che si QThread
come terzo argomento di cui si desidera che il QDbusConnectionPrivate
istanza per essere spostato.Quindi utilizzare la classe per creare la connessione, invece di quella di default QDbusConnection::sessionBus()
.
Questo è l'utilizzo di alcune lezioni private, richiede l'inserimento di alcuni file di intestazione private (indicato nel codice riportato di seguito) che a sua volta cercherà di includere vari dbus header di libreria, che richiederà la modifica della INCLUDEPATH il progetto dbus libreria percorso di inclusione.
Ho verificato questo funziona su Qt 5.3.0 e Qt 4.8.6.
Aggiornamento: In Qt 5.6, QtDBus è stato riscritto per utilizzare i thread in arrivo/in uscita di elaborazione del messaggio;non più il blocco del thread principale!
DBusHandler.hpp
#pragma once
#include <iostream>
#include <QtCore/QThread>
#include <QtCore/QtCore>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnectionInterface>
#include "/path/to/Qt5.3.0/5.3/Src/qtbase/src/dbus/qdbusconnection_p.h"
class SpecializedDBusConnection : public QDBusConnection {
const char *ownName;
public:
inline SpecializedDBusConnection(BusType type, const char *name, QThread *thread)
: QDBusConnection(connectToBus(type, QString::fromLatin1(name))), ownName(name)
{
if (QDBusConnectionPrivate::d(*this)) {
QDBusConnectionPrivate::d(*this)->moveToThread(thread);
}
}
inline ~SpecializedDBusConnection()
{ disconnectFromBus(QString::fromLatin1(ownName)); }
};
class DBusHandler : public QThread
{
Q_OBJECT;
private:
void run(void)
{
QDBusConnection connection = SpecializedDBusConnection(QDBusConnection::SessionBus, "qt_default_session_bus", this);
connection.registerService("my.qdbus.example");
connection.registerObject("/", this, QDBusConnection::ExportAllSlots);
exec();
}
[snip]