如何在不阻塞主线程的情况下使用Qt-Dbus绑定

时间:2020-12-12 17:33:07

My goal is to create a library using the Qt's DBus bindings.

我的目标是使用Qt的DBus绑定创建一个库。

I tried to create a Qt application without launching the QEventLoop (provided by the QCoreApplication class) in the main thread.

我试图创建一个Qt应用程序而不在主线程中启动QEventLoop(由QCoreApplication类提供)。

Here is a minimalistic application sample, working fine using QT-4.6.2 version but blocking on introspection using QT-4.8 or higher.

这是一个简约的应用程序示例,使用QT-4.6.2版本正常工作但使用QT-4.8或更高版本阻止内省。

DBusHandler.hpp

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

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

As you can see in the main.cpp file, app.exec() is commented out, but makes the application working fine on QT-4.8 or higher versions (5.3.0).

正如您在main.cpp文件中看到的那样,app.exec()已被注释掉,但使应用程序在QT-4.8或更高版本(5.3.0)上正常工作。

My question is the following: Is it possible to use the Qt's DBus bindings calling app.exec() in an other thread than the main one, on Qt-4.8 or 5.3 ?

我的问题如下:在Qt-4.8或5.3上,是否可以使用Qt的DBus绑定调用另一个主线程中的app.exec()?

1 个解决方案

#1


8  

Background: There is a private class called QDBusConnectionPrivate which inherits from QObject and handles all networking. Unfortunately, if you look at qdbusconnection.cpp:1116 you'll see that Qt hard codes the moveToThread to QCoreApplication::instance().

背景:有一个名为QDBusConnectionPrivate的私有类,它继承自QObject并处理所有网络。不幸的是,如果你看一下qdbusconnection.cpp:1116,你会看到Qt硬编码moveToThread到QCoreApplication :: instance()。

You should probably submit an enhancement request to allow the user to create a QDBusConnection that uses a user specified thread or event loop. See update below.

您应该提交一个增强请求,以允许用户创建使用用户指定的线程或事件循环的QDBusConnection。请参阅下面的更新

In the meantime, if you're comfortable doing some dangerous things, you can hack it in yourself by creating your own QDbusConnection subclass (I called mine SpecializedDBusConnection) that takes QThread as a third argument of where you want the QDbusConnectionPrivate instance to be moved to. Then use that class to create the connection instead of the default QDbusConnection::sessionBus().

与此同时,如果你喜欢做一些危险的事情,你可以通过创建自己的QDbusConnection子类(我称之为SpecializedDBusConnection)来自行破解它,它将QThread作为你想要将QDbusConnectionPrivate实例移动到的位置的第三个参数。然后使用该类创建连接而不是默认的QDbusConnection :: sessionBus()。

As this is using some private classes, it requires the inclusion of some private header files (noted in the code below) which in turn will attempt to include various dbus library headers which will necessitate the modifying of INCLUDEPATH of the project to include the dbus library include path.

由于这是使用一些私有类,它需要包含一些私有头文件(在下面的代码中注明),这反过来将尝试包括各种dbus库头,这将需要修改项目的INCLUDEPATH以包含dbus库包括路径。

I've verified this works on Qt 5.3.0 and Qt 4.8.6.

我已经在Qt 5.3.0和Qt 4.8.6上验证了这一点。

Update: In Qt 5.6, QtDBus was refactored to use threads for incoming/outgoing message processing; no more blocking of the main thread!

更新:在Qt 5.6中,QtDBus被重构为使用线程进行传入/传出消息处理;不再阻塞主线程!

DBusHandler.hpp

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]

#1


8  

Background: There is a private class called QDBusConnectionPrivate which inherits from QObject and handles all networking. Unfortunately, if you look at qdbusconnection.cpp:1116 you'll see that Qt hard codes the moveToThread to QCoreApplication::instance().

背景:有一个名为QDBusConnectionPrivate的私有类,它继承自QObject并处理所有网络。不幸的是,如果你看一下qdbusconnection.cpp:1116,你会看到Qt硬编码moveToThread到QCoreApplication :: instance()。

You should probably submit an enhancement request to allow the user to create a QDBusConnection that uses a user specified thread or event loop. See update below.

您应该提交一个增强请求,以允许用户创建使用用户指定的线程或事件循环的QDBusConnection。请参阅下面的更新

In the meantime, if you're comfortable doing some dangerous things, you can hack it in yourself by creating your own QDbusConnection subclass (I called mine SpecializedDBusConnection) that takes QThread as a third argument of where you want the QDbusConnectionPrivate instance to be moved to. Then use that class to create the connection instead of the default QDbusConnection::sessionBus().

与此同时,如果你喜欢做一些危险的事情,你可以通过创建自己的QDbusConnection子类(我称之为SpecializedDBusConnection)来自行破解它,它将QThread作为你想要将QDbusConnectionPrivate实例移动到的位置的第三个参数。然后使用该类创建连接而不是默认的QDbusConnection :: sessionBus()。

As this is using some private classes, it requires the inclusion of some private header files (noted in the code below) which in turn will attempt to include various dbus library headers which will necessitate the modifying of INCLUDEPATH of the project to include the dbus library include path.

由于这是使用一些私有类,它需要包含一些私有头文件(在下面的代码中注明),这反过来将尝试包括各种dbus库头,这将需要修改项目的INCLUDEPATH以包含dbus库包括路径。

I've verified this works on Qt 5.3.0 and Qt 4.8.6.

我已经在Qt 5.3.0和Qt 4.8.6上验证了这一点。

Update: In Qt 5.6, QtDBus was refactored to use threads for incoming/outgoing message processing; no more blocking of the main thread!

更新:在Qt 5.6中,QtDBus被重构为使用线程进行传入/传出消息处理;不再阻塞主线程!

DBusHandler.hpp

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]