I seem to have fixed the bug I mentioned in my last post. This is what I had:
class MetaPlayer (type (QObject), type (dbus.service.Object)):
"""Dummy metaclass that allows us to inherit from both QObject and
d.s.Object"""
pass
class Player (QObject, dbus.service.Object):
__metaclass__= MetaPlayer
[...]
Notice that MetaPlayer doesn't have a explicit
__init__() method; one would spect that Python would
take are of that. Here's the fixing code:
MetaQObject= type (QObject)
MetaObject= type (dbus.service.Object)
class MetaPlayer (MetaQObject, MetaObject):
"""Dummy metaclass that allows us to inherit from both QObject and d.s.Object"""
def __init__(cls, name, bases, dct):
MetaObject.__init__ (cls, name, bases, dct)
MetaQObject.__init__ (cls, name, bases, dct)
I really don't understand why I have to be so explicit. Maybe
it's because the metaclass for dbus.service.Object,
dbus.service.InterfaceType, inherits from the
type type[1]; this type is a new style class[2], but
doesn't inherits from object. Thus, I think, the
inherited __init__() methods are not called
automatically.
In any case, now I can mix QObject and
dbus.service.Object, and it works fine. For instance,
this call works:
$ qdbus org.kde.satyr /player quit
[1] the type type is of type type!
here:
In [1]: type (type)
Out[1]: <type 'type'>
[2] its type is not instance but type,
as mentioned above.
In
my last post I said «The next step is to make my
Player class to export its methods via
DBus and that's it!». Well, tell you what: is not that
easy. If you try to inherit from QObject and
dbus.service.Object you get this error:
In [3]: class Klass (QtCore.QObject, dbus.service.Object): pass
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a
(non-strict) subclass of the metaclasses of all its bases
This occurs when both ancestors have their own metaclasses.
Unluckily Python doesn't resolve it for you. The
answer is to create a intermediate metaclass which inherits from
both metaclasses (which we can obtain with type()) and
make it the metaclass of our class. In code:
class MetaPlayer (type (QObject), type (dbus.service.Object)):
"""Dummy metaclass that allows us to inherit from both QObject and
d.s.Object"""
pass
class Player (QObject, dbus.service.Object):
__metaclass__= MetaPlayer
[...]
Is that it now? Can I go and do my code? Unfortunately no. See this:
qdbus org.kde.satyr
/
/player
Cannot introspect object /player at org.kde.satyr:
org.freedesktop.DBus.Python.KeyError (Traceback (most recent call last):
File "/usr/lib/pymodules/python2.5/dbus/service.py", line 702, in _message_cb
retval = candidate_method(self, *args, **keywords)
File "/usr/lib/pymodules/python2.5/dbus/service.py", line 759, in Introspect
interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
KeyError: '__main__.Player'
)
This is the class dbus.service.Object complaining
something else. It's getting late here and I'm tired, so I'll
continue tomorrow.
Continuing with the development of Satyr, which
doesn't have any GUI. I thought it would be faster to make a
DBus interface that a good GUI, not to mention more
interesting.
The code snippet of today is this:
class DBusServer (dbus.service.Object):
def __init__ (self):
bus= dbus.SessionBus ()
bus_name= dbus.service.BusName ('org.kde.satyr.dbus.test', bus=bus)
dbus.service.Object.__init__ (self, bus_name, "/server")
@dbus.service.method('org.kde.satyr.dbus.test', in_signature='', out_signature='')
def bing (self):
print "bing!"
@dbus.service.method('org.kde.satyr.dbus.test', in_signature='', out_signature='')
def quit (self):
app.quit ()
dbus.mainloop.qt.DBusQtMainLoop (set_as_default=True)
dbs= DBusServer ()
sys.exit (app.exec_ ())
This simply defines a class which registers itself with the
session bus under the name org.kde.satyr.dbus.test,
exporting itself under the path /server and then
defining a method that goes bing! :) and another
one that quits the app. Note the decorator for the methods.
You might notice the dbus.mainloop.qt.DBusQtMainLoop
(set_as_default=True) call. This is needed because both Qt
and DBus in asyncronous mode (which is the one we're using and the
only one that works under Qt or Gtk, AFAIK) both have their own
event loops, and that makes some kind of magic that let both loops
coexist without blocking the other. This must be called before
connecting to the bus; otherwise, you get this error:
RuntimeError: To make asynchronous calls, receive signals or export objects,
D-Bus connections must be attached to a main loop by passing mainloop=...
to the constructor or calling dbus.set_default_main_loop(...)
So, let's test the beast. We run the script in one terminal and in the other:
mdione@mustang:~/src/projects/satyr/live$ qdbus | grep satyr
org.kde.satyr.py-10154
org.kde.satyr.dbus.test
mdione@mustang:~/src/projects/satyr/live$ qdbus org.kde.satyr.dbus.test
/
/server
mdione@mustang:~/src/projects/satyr/live$ qdbus org.kde.satyr.dbus.test /server
method void org.kde.satyr.dbus.test.bing()
method QString org.freedesktop.DBus.Introspectable.Introspect()
mdione@mustang:~/src/projects/satyr/live$ qdbus org.kde.satyr.dbus.test /server org.kde.satyr.dbus.test.bing
mdione@mustang:~/src/projects/satyr/live$ qdbus org.kde.satyr.dbus.test /server org.kde.satyr.dbus.test.quit
And in the other console:
mdione@mustang:~/src/projects/satyr/live$ python dbus_test.py
bing!
It goes bing!... and then finishes. The next step is to make my
Player class to export its methods via
DBus and that's it!. More info in the Python
DBus tutorial.