[solved] Receiving D-Bus signals in QML

I’m trying to receive a signal over D-Bus but I can’t see the method being invoked:

DBusInterface {
  bus: DBus.SessionBus
  service: 'com.example.apple'
  path: '/'
  iface: 'com.example.apple'

  signalsEnabled: true

  function orange(int_value) {
    console.log("############### DEBUG SIGNAL orange ", int_value);
  }

  function send_pear() {
    typedCall("Pear", [
      {"type": "s", "value": "hello world"},
      ],
      function (result) {
        console.log("DEBUG Pear:", result);
      },
      function (error) {
        console.log("############### ERROR Pear failed:", error);
      }
    );
  }

  Component.onCompleted: {
    console.log("############### DEBUG DBUS completed");
    send_pear();
  }
}

If I run dbus-monitor I see the signals being emitted:

[nemo@Sailfish ~]$ dbus-monitor --session "type='signal',sender='com.example.apple'"
signal time=1601199482.354399 sender=:1.83 -> destination=(null destination) serial=788 path=/; interface=com.example.apple; member=orange
   uint16 780
signal time=1601199483.357265 sender=:1.83 -> destination=(null destination) serial=789 path=/; interface=com.example.apple; member=orange
   uint16 781

Executing methods (e.g. the “Pear” method above) works perfectly fine.

What am I doing wrong?

3 Likes

It looks like you’re just missing a signalsEnabled: true statement inside the DBusInterfaace structure. There’s a decent explanation about handling signals in the sailfish dbus docs.

There can be other gotchas as well. For example, if the service drops in and out, you may have to set the watchServiceStatus property to true (it’s in the code, but not in the docs; I’m not sure why).

1 Like

A second signalsEnabled: true ? Where should it go?

1 Like

Oh, no, sorry, obviously not a second one; I’m just being blind. Basically I’m projecting the sort of mistakes I make onto you.

In that case I’d try adding the watchServiceStatus as well, but beyond that I’m not sure I’m afraid.

1 Like

Thank you anyway.
I didn’t know about watchServiceStatus so this will be definitely added to my code.

There must be something about this specific signal I’m generating. When I change it to e.g. the following, it works:

service: "uk.co.piggz.amazfish"
path: "/application"
iface: "uk.co.piggz.amazfish"

function informationChanged(a, b) {
  console.log("1 informationChanged:", a, b);
}

Now I have to find the differences between the two signals…

I’m glad you made progress. Having been unhelpful before I’m a bit reticent about giving useless advice again, but just in case, the three things that spring to mind are datatype conversion, name mangling and name clashes.

If the datatype being passed via the signal is complex/unsupported, then I believe that can cause problems.

The name mangling code may change the case of the first letter.

Finally I think there can be name-clashes as well (e.g. if the signal matches one of the existing DBusInterface methods.

I haven’t used any exotic data types:
I’ve changed the code to closely resemble the one from amazfisch that does work but no progress yet,
despite the signals being almost identical:

works:

D-Bus
    Header
        Endianness Flag: l
        Message Type: Signal emission (4)
        Message Flags: 0x01
        Protocol Version: 1
        Message body Length: 14
        Message Serial (cookie): 13931
        Header fields Length: 110
    Header Field: PATH
        Field code: PATH (1)
        Type signature: o
        OBJECT_PATH: /application
    Header Field: INTERFACE
        Field code: INTERFACE (2)
        Type signature: s
        STRING: uk.co.piggz.amazfish
    Header Field: MEMBER
        Field code: MEMBER (3)
        Type signature: s
        STRING: informationChanged
    Header Field: SIGNATURE
        Field code: SIGNATURE (8)
        Type signature: g
        SIGNATURE: is
    Header Field: SENDER
        Field code: SENDER (7)
        Type signature: s
        STRING: :1.44
    Body
        INT32: 8
        STRING: 17217

does not work:

D-Bus
    Header
        Endianness Flag: l
        Message Type: Signal emission (4)
        Message Flags: 0x01
        Protocol Version: 1
        Message body Length: 12
        Message Serial (cookie): 46
        Header fields Length: 111
    Header Field: PATH
        Field code: PATH (1)
        Type signature: o
        OBJECT_PATH: /application
    Header Field: INTERFACE
        Field code: INTERFACE (2)
        Type signature: s
        STRING: uk.co.piggz.amazfish2
    Header Field: MEMBER
        Field code: MEMBER (3)
        Type signature: s
        STRING: informationChanged
    Header Field: SIGNATURE
        Field code: SIGNATURE (8)
        Type signature: g
        SIGNATURE: is
    Header Field: SENDER
        Field code: SENDER (7)
        Type signature: s
        STRING: :1.446
    Body
        INT32: 43
        STRING: xxx
1 Like

busctl along with dbus-monitor do show the signals, it seem to be just Qt that does something differently:

[nemo@Sailfish ~]$ busctl --user --verbose monitor --match  "type='signal',sender='uk.co.piggz.amazfish2',interface='uk.co.piggz.amazfish2'"
Monitoring bus message stream.
‣ Type=signal  Endian=l  Flags=1  Version=1  Priority=0 Cookie=2
  Sender=org.freedesktop.DBus  Destination=:1.545  Path=/org/freedesktop/DBus  Interface=org.freedesktop.DBus  Member=NameAcquired
  MESSAGE "s" {
          STRING ":1.545";
  };



‣ Type=signal  Endian=l  Flags=1  Version=1  Priority=0 Cookie=3
  Sender=:1.546  Path=/application  Interface=uk.co.piggz.amazfish2  Member=informationChanged
  UniqueName=:1.546
  MESSAGE "is" {
          INT32 0;
          STRING "xxx";
  };
1 Like

Suspecting there may be something weird about the code in my initial program I’ve created one (from an example) in a completely different language to ensure it’s not something with the particular d-bus library I’m using.

Still no luck even in Python:

import dbus
import dbus.service
import dbus.mainloop.glib
import time

class TestObject(dbus.service.Object):
    def __init__(self, conn, object_path='/application'):
        dbus.service.Object.__init__(self, conn, object_path)

    @dbus.service.signal('uk.co.piggz.amazfish2', signature='is')
    def informationChanged(self, value, message):
      pass


if __name__ == '__main__':
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    session_bus = dbus.SessionBus()
    name = dbus.service.BusName('uk.co.piggz.amazfish2', session_bus)
    object = TestObject(session_bus)

    while True:
      time.sleep(1)
      object.informationChanged(1, "test")
1 Like

After comparing session bus traces over-and-over again I think I’ve found the issue.
It seems like having an error in the org.freedesktop.DBus.Introspectable method (or not having one as in the code snippet above) would prevent signals from being recognized by the QML interpreter. If I think about it It kind of makes sense if (at the start) it tries to match the function names in QML to signal names that may occur on the bus later.

3 Likes