Процесс QDBusPendingCallWatcher приводит к PyQt4?

Я хотел бы сделать неблокирующий вызов функции в удаленном сервисе D-Bus с использованием PyQt4.QtDBus. Адаптируясь из документации Qt C++, я разработал следующую тестовую программу:

from PyQt4 import QtCore, QtDBus

class DeviceInterface(QtDBus.QDBusAbstractInterface):

    def __init__(self, service, path, connection, parent=None):
        super().__init__(service, path, 'org.freedesktop.UDisks.Device',
                         connection, parent)

    @QtCore.pyqtSlot(QtDBus.QDBusArgument)
    def callFinishedSlot(self, arg):
        print("Got result:", arg)


if __name__ == '__main__':
    import sys
    app = QtCore.QCoreApplication(sys.argv)

    dev = DeviceInterface('org.freedesktop.UDisks',
                          '/org/freedesktop/UDisks/devices/sda1',
                          QtDBus.QDBusConnection.systemBus(), app)

    async = dev.asyncCall("FilesystemListOpenFiles");
    watcher = QtDBus.QDBusPendingCallWatcher(async, dev)
    watcher.finished.connect(dev.callFinishedSlot)
    sys.exit(app.exec_())

Вроде работает. Когда я запускаю его, он печатает:

Got result: <PyQt4.QtDBus.QDBusPendingCallWatcher object at 0xb740c77c>

Проблема в том, что я не знаю, как конвертировать QDBusPendingCallWatcher к чему-то (например, QDBusMessage) что я могу извлечь результаты. Пример из документации C++ делает это:

 void MyClass.callFinishedSlot(QDBusPendingCallWatcher *call)
 {
     QDBusPendingReply<QString, QByteArray> reply = *call;
     if (reply.isError()) {
         showError();
     } else {
         QString text = reply.argumentAt<0>();
         QByteArray data = reply.argumentAt<1>();
         showReply(text, data);
     }
     call->deleteLater();
 }

Может кто-нибудь сказать мне, как перевести слот C++ в то, что будет работать с PyQt4? (Я использую PyQt4.9.1 с Qt 4.8.1.)

1 ответ

Решение

Ладно, похоже, хитрость заключается в создании QDBusPendingReply от QDBusPendingCallWatcher экземпляр (большое спасибо Филу Томпсону за то, что он указал на это в списке рассылки PyQt). Оказывается, эта же техника работает и в C++. У меня был неправильный путь к объекту UDisk в моем исходном коде, плюс несколько других мелких опечаток, так что вот полный, рабочий пример для потомков:

from PyQt4 import QtCore, QtDBus

class DeviceInterface(QtDBus.QDBusAbstractInterface):

    def __init__(self, service, path, connection, parent=None):
        super().__init__(service, path, 'org.freedesktop.UDisks.Device',
                         connection, parent)

    def callFinishedSlot(self, call):
        # Construct a reply object from the QDBusPendingCallWatcher
        reply = QtDBus.QDBusPendingReply(call)
        if reply.isError():
            print(reply.error().message())
        else:
            print("  PID        UID     COMMAND")
            print("-------    -------   ------------------------------------")
            for pid, uid, cmd in reply.argumentAt(0):
                print("{0:>7d}    {1:>7d}   {2}".format(pid, uid, cmd))
        # Important: Tell Qt we are finished processing this message
        call.deleteLater()

if __name__ == '__main__':
    import sys
    import signal
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    app = QtCore.QCoreApplication(sys.argv)

    dev = DeviceInterface('org.freedesktop.UDisks',
                          '/org/freedesktop/UDisks/devices/sda1',
                          QtDBus.QDBusConnection.systemBus(), app)

    async = dev.asyncCall("FilesystemListOpenFiles");
    watcher = QtDBus.QDBusPendingCallWatcher(async, dev)
    watcher.finished.connect(dev.callFinishedSlot)
    sys.exit(app.exec_())

Это должно работать на большинстве последних дистрибутивов Linux и является хорошим примером использования PyQt для вызова метода D-Bus, который возвращает составной тип.

Другие вопросы по тегам