Ожидание предоставления услуги DBus в Qt

С прокси-сервером Qt DBus, построенным на QDbusAbstractInterface (через qdbusxml2cpp), какой лучший способ обрабатывать сервис/объект, с которым вы хотите взаимодействовать, чтобы не быть доступным при запуске? Примечание. Мне не интересно просто знать это (вы можете использовать BlahService.isValid(), чтобы узнать это); Я хочу знать, действительно ли он, и знать, когда он станет действительным, поэтому я могу изменить состояние (и транслировать это изменение состояния с помощью сигнала), и при этом изменении состояния делать другие вещи. И наоборот, я хочу знать, когда это не действует по аналогичным причинам.

Без отслеживания состояния службы:

#define CONNECT_DBUS_SIG(x,y) connect(blah,SIGNAL(x),this,SLOT(y))

// FIX - should watch for service, and also handle it going away and
// coming back
blah = new BlahService("com.xyzzy.BlahService", "/com/xyzzy/BlahService",
                           QDBusConnection::sessionBus(), this);
if (!blah)
    return 0;
if (blah.isValid())
{
    CONNECT_DBUS_SIG(foo(),Event_foo());
}
else
{
    // Since we aren't watching for registration, what can we do but exit?
}

Вероятно, нам нужно наблюдать за NameOwnerChanged на объекте соединения DBus - если код QT dbus не сделает это для нас - и тогда, когда мы получим это состояние изменения сигнала, и при необходимости подключим или отключим сигналы от объекта.

Все примеры, которые я нахожу, либо игнорируют проблему, либо просто выходят, если объект сервера не существует и не справляется с этим. Пример Qt Car/Controller, по крайней мере, замечает, если сервер уходит и печатает "Отключен", если isValid() становится ложным во время использования, но опрос isValid().

Добавлено:

Обратите внимание, что QtDbusAbtractInterface регистрирует изменения прав собственности на сервер (NameOwnerChanged) и обновляет isValid(), когда происходят изменения. Поэтому я подозреваю, что вы можете напрямую подключиться к этому сигналу serverOwnerChanged, чтобы узнать об изменениях в собственности и использовать это как индикатор, чтобы повторить попытку - хотя вы не сможете доверять isValid, поскольку он может быть обновлен до или после того, как вы получите сигнал.

В качестве альтернативы (уродливый) вы можете настроить таймер и опрос для isValid().

Ответ 1

Хорошо, так как никто не ответил, я нашел ответ тем временем:

Вы хотите посмотреть NameOwnerChanged:

// subscribe to notifications about when a service is registered/unregistered
   connect(QDBusConnection::sessionBus().interface(),
           SIGNAL(serviceOwnerChanged(QString,QString,QString)),
           this,SLOT(serviceOwnerChanged(QString,QString,QString)));

и

void 
VcsApplicationController::serviceOwnerChanged(const QString &name,
                                              const QString &oldOwner,
                                              const QString &newOwner)
{
    Q_UNUSED(oldOwner);
    if (name == "com.foo.bar.FooService")
    {
        qLog(Whatever) << "serviceOwnerChanged" << name << oldOwner << newOwner;
        if (!newOwner.isEmpty())
        {
            // New owner in town
            emit Initialized();
            // or if you control the interface and both sides, you can wait for
            // a "Ready()" signal before declaring FooService ready for business.
        }
        else
        {
            // indicate we've lost connection, etc
            emit Uninitialized();
        }
    }
}

Обратите внимание, что может участвовать в гонках с помощью методов FooService изнутри serviceOwnerChanged. Я еще не уверен, что это побочный эффект привязки (dbus-С++ в моем тесте случай) или присущий конструкции dbus (возможно - нет в списке рассылки dbus, ответит на вопрос). Если существует реальное состояние гонки, вы можете подождать на сигнале Ready()/whatever, если вы управляете DBus API. Если вы не контролируете другой конец, вы можете добавить очень короткую задержку или вы также можете посмотреть AddMatch(), чтобы убедиться, что новый владелец добавил совпадение имени.

Ответ 2

С Qt 5.3, serviceOwnerChanged устарел. Используйте QDBusServiceWatcher, который позволяет смотреть определенную услугу, а не все.