Как использовать привязки Qt-Dbus без блокировки основного потока

Моя цель — создать библиотеку, используя Связывания Qt с DBus.

Я пытался создать приложение Qt без запуска QEventLoop (предоставлено QCoreApplication класс) в основной теме.

Вот минималистичный пример приложения, отлично работающий с версией QT-4.6.2, но блокирующий самоанализ с использованием QT-4.8 или выше.

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());
}

Как вы можете видеть в main.cpp файл, app.exec() закомментировано, но приложение работает нормально на QT-4.8 или более поздних версиях (5.3.0).

Мой вопрос заключается в следующем: возможно ли использовать привязки DBus в Qt app.exec() в другая нить чем основной, на Qt-4.8 или 5.3?

9

Решение

Фон: Есть частный класс QDBusConnectionPrivate которая наследуется от QObject и обрабатывает все сети. К сожалению, если вы посмотрите на qdbusconnection.cpp:1116 вы увидите, что Qt жестко кодирует moveToThread в QCoreApplication::instance(),

Вероятно, вам следует отправить запрос на расширение, чтобы позволить пользователю создать QDBusConnection, который использует указанный пользователем поток или цикл обработки событий. Смотрите обновление ниже.

В это время, если вам удобно делать какие-то опасные вещи, вы можете взломать это сами, создав собственную QDbusConnection подкласс (я назвал мой SpecializedDBusConnection) что занимает QThread в качестве третьего аргумента, где вы хотите QDbusConnectionPrivate экземпляр для перемещения. Затем используйте этот класс для создания соединения вместо стандартного QDbusConnection::sessionBus(),

Поскольку это использует некоторые частные классы, это требует включения некоторых частных заголовочных файлов (отмеченных в приведенном ниже коде), которые, в свою очередь, будут пытаться включить различные заголовки библиотеки dbus, что потребует изменения INCLUDEPATH проекта для включения в библиотеку dbus include path.

Я проверял это работает на Qt 5.3.0 и Qt 4.8.6.

Обновить: В Qt 5.6, QtDBus был реорганизован для использования потоков для обработки входящих / исходящих сообщений; больше не блокировка основного потока!

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]
8

Другие решения


По вопросам рекламы [email protected]