Convert buddies to GObjects; ensure signals don't emit before buddy is valid

This commit is contained in:
Dan Williams 2007-03-02 11:10:44 -05:00
parent 7b89672604
commit 08c791849f
4 changed files with 131 additions and 87 deletions

View File

@ -28,32 +28,91 @@ class NotFoundError(dbus.DBusException):
dbus.DBusException.__init__(self)
self._dbus_error_name = _PRESENCE_INTERFACE + '.NotFound'
class Buddy(dbus.service.Object):
class DBusGObjectMetaclass(gobject.GObjectMeta, dbus.service.InterfaceType): pass
class DBusGObject(dbus.service.Object, gobject.GObject): __metaclass__ = DBusGObjectMetaclass
class Buddy(DBusGObject):
"""Represents another person on the network and keeps track of the
activities and resources they make available for sharing."""
def __init__(self, bus_name, object_id, key=None):
__gtype_name__ = "Buddy"
__gsignals__ = {
'validity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_BOOLEAN]))
}
__gproperties__ = {
'key' : (str, None, None, None,
gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY),
'icon' : (str, None, None, None, gobject.PARAM_READWRITE),
'nick' : (str, None, None, None, gobject.PARAM_READWRITE),
'color' : (str, None, None, None, gobject.PARAM_READWRITE),
'current-activity' : (str, None, None, None, gobject.PARAM_READWRITE),
'valid' : (bool, None, None, False, gobject.PARAM_READABLE),
'owner' : (bool, None, None, False, gobject.PARAM_READABLE)
}
def __init__(self, bus_name, object_id, **kwargs):
if not bus_name:
raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int):
raise ValueError("object id must be a valid number")
if not key:
raise ValueError("key must be valid")
self._bus_name = bus_name
self._object_id = object_id
self._object_path = _BUDDY_PATH + str(self._object_id)
dbus.service.Object.__init__(self, self._bus_name, self._object_path)
self._activities = {} # Activity ID -> Activity
self.handles = {} # tp client -> handle
self._key = key
self._icon = None
self._nick = None
self._color = None
self._current_activity = None
self._valid = False
self._owner = False
self._key = None
gobject.GObject.__init__(self, **kwargs)
if not self._key:
raise RuntimeError("public key required")
def do_get_property(self, pspec):
if pspec.name == "key":
return self._key
elif pspec.name == "icon":
return self._icon
elif pspec.name == "nick":
return self._nick
elif pspec.name == "color":
return self._color
elif pspec.name == "current-activity":
if not self._current_activity:
return None
if not self._activities.has_key(self._current_activity):
return None
return self._activities[self._current_activity]
elif pspec.name == "valid":
return self._valid
elif pspec.name == "owner":
return self._owner
def do_set_property(self, pspec, value):
if pspec.name == "icon":
if value != self._icon:
self._icon = value
self.IconChanged(value)
elif pspec.name == "nick":
self._nick = value
elif pspec.name == "color":
self._color = value
elif pspec.name == "current-activity":
self._current_activity = value
elif pspec.name == "key":
if self._key:
raise RuntimeError("key already set")
self._key = value
self._update_validity()
# dbus signals
@dbus.service.signal(_BUDDY_INTERFACE,
@ -80,10 +139,9 @@ class Buddy(dbus.service.Object):
@dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="ay")
def GetIcon(self):
icon = self.get_icon()
if not icon:
if not self.props.icon:
return ""
return icon
return self.props.icon
@dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="ao")
@ -97,10 +155,10 @@ class Buddy(dbus.service.Object):
in_signature="", out_signature="a{sv}")
def GetProperties(self):
props = {}
props['nick'] = self.get_nick()
props['owner'] = self.is_owner()
props['key'] = self.get_key()
color = self.get_color()
props['nick'] = self.props.nick
props['owner'] = self.props.owner
props['key'] = self.props.key
color = self.props.color
if color:
props['color'] = color
return props
@ -132,78 +190,53 @@ class Buddy(dbus.service.Object):
acts.append(act)
return acts
def get_icon(self):
"""Return the buddies icon, if any."""
return self._icon
def get_nick(self):
return self._nick
def get_color(self):
return self._color
def get_current_activity(self):
if not self._current_activity:
return None
if not self._activities.has_key(self._current_activity):
return None
return self._activities[self._current_activity]
def set_icon(self, icon):
"""Can only set icon for other buddies. The Owner
takes care of setting it's own icon."""
if icon != self._icon:
self._icon = icon
self.IconChanged(icon)
def _set_nick(self, nick):
self._nick = nick
def _set_color(self, color):
self._color = color
def set_properties(self, properties):
if "nick" in properties.keys():
self._set_nick(properties["nick"])
self._nick = properties["nick"]
if "color" in properties.keys():
self._set_color(properties["color"])
self.PropertyChanged(properties)
self._color = properties["color"]
def is_owner(self):
return False
# Try emitting PropertyChanged before updating validity
# to avoid leaking a PropertyChanged signal before the buddy is
# actually valid the first time after creation
if self._valid:
self.PropertyChanged(properties)
def set_key(self, key):
self._key = key
self._update_validity()
def _update_validity(self):
try:
old_valid = self._valid
if self._color and self._nick and self._key:
self._valid = True
else:
self._valid = False
if old_valid != self._valid:
self.emit("validity-changed", self._valid)
except AttributeError:
self._valid = False
def get_key(self):
return self._key
class Owner(Buddy):
"""Class representing the owner of the machine. This is the client
portion of the Owner, paired with the server portion in Owner.py."""
def __init__(self, ps, bus_name, object_id):
def __init__(self, bus_name, object_id):
key = profile.get_pubkey()
Buddy.__init__(self, bus_name, object_id, key=key)
nick = profile.get_nick_name()
color = profile.get_color().to_string()
self._ps = ps
self._name = profile.get_nick_name()
self._color = profile.get_color().to_string()
Buddy.__init__(self, bus_name, object_id, key=key, nick=nick, color=color)
self._owner = True
# dbus methods
@dbus.service.method(_OWNER_INTERFACE,
in_signature="ay", out_signature="")
def SetIcon(self, icon_data):
self.set_icon(icon_data)
self.props.icon = icon_data
@dbus.service.method(_OWNER_INTERFACE,
in_signature="a{sv}", out_signature="")
def SetProperties(self, prop):
self.set_properties(self, prop)
# methods
def is_owner(self):
return True
def set_icon(self, icon):
self._icon = icon

View File

@ -21,3 +21,6 @@ class LinkLocalPlugin(gobject.GObject):
def __init__(self, registry):
gobject.GObject.__init__(self)
self._registry = registry
def cleanup(self):
pass

View File

@ -51,8 +51,8 @@ class PresenceService(dbus.service.Object):
# Create the Owner object
objid = self._get_next_object_id()
self._owner = Owner(self, self._bus_name, objid)
self._buddies[self._owner.get_key()] = self._owner
self._owner = Owner(self._bus_name, objid)
self._buddies[self._owner.props.key] = self._owner
self._registry = ManagerRegistry()
self._registry.LoadManagers()
@ -86,30 +86,34 @@ class PresenceService(dbus.service.Object):
# we don't know yet this buddy
objid = self._get_next_object_id()
buddy = Buddy(self._bus_name, objid, key=key)
print "New buddy %s" % key
buddy.connect("validity-changed", self._buddy_validity_changed_cb)
self._buddies[key] = buddy
new_buddy = True
buddies = self._handles[tp]
buddies[handle] = buddy
# store the handle of the buddy for this CM
buddy.handles[tp] = handle
if new_buddy:
self.BuddyAppeared(buddy.object_path())
buddy.set_properties(props)
print "New buddy properties %s" % props
def _buddy_validity_changed_cb(self, buddy, valid):
if valid:
self.BuddyAppeared(buddy.object_path())
print "New Buddy: %s (%s)" % (buddy.props.nick, buddy.props.color)
else:
self.BuddyDisappeared(buddy.object_path())
print "Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)
def _contact_offline(self, tp, handle):
buddy = self._handles[tp].pop(handle)
key = buddy.get_key()
key = buddy.props.key
# the handle of the buddy for this CM is not valid anymore
buddy.handles.pop(tp)
if not buddy.handles:
self.BuddyDisappeared(buddy.object_path())
print "Buddy %s gone" % buddy.get_key()
if buddy.props.valid:
self.BuddyDisappeared(buddy.object_path())
print "Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)
self._buddies.pop(key)
def _get_next_object_id(self):
@ -119,15 +123,15 @@ class PresenceService(dbus.service.Object):
def _avatar_updated(self, tp, handle, avatar):
buddy = self._handles[tp].get(handle)
if buddy and not buddy.is_owner():
print "Buddy %s icon updated" % buddy.get_key()
buddy.set_icon(avatar)
if buddy and not buddy.props.owner:
print "Buddy %s icon updated" % buddy.props.key
buddy.props.icon = avatar
def _properties_changed(self, tp, handle, prop):
buddy = self._handles[tp].get(handle)
if buddy:
buddy.set_properties(prop)
print "Buddy %s properties updated" % buddy.get_key()
print "Buddy %s properties updated" % buddy.props.key
def _activities_changed(self, tp, handle, prop):
pass
@ -185,6 +189,9 @@ class PresenceService(dbus.service.Object):
def ShareActivity(self, actid, atype, name, properties):
raise NotImplementedError("not implemented yet")
def cleanup(self):
for tp in self._handles:
tp.cleanup()
def main():
loop = gobject.MainLoop()
@ -192,6 +199,7 @@ def main():
try:
loop.run()
except KeyboardInterrupt:
ps.cleanup()
print 'Ctrl+C pressed, exiting...'
if __name__ == "__main__":

View File

@ -194,7 +194,7 @@ class ServerPlugin(gobject.GObject):
self._conn._valid_interfaces.add(CONN_INTERFACE_BUDDY_INFO)
if CONN_INTERFACE_BUDDY_INFO not in self._conn.get_valid_interfaces():
print 'OLPC information not available'
self.disconnect()
self.cleanup()
return
self._conn[CONN_INTERFACE_BUDDY_INFO].connect_to_signal('PropertiesChanged', self._properties_changed_cb)
@ -267,7 +267,7 @@ class ServerPlugin(gobject.GObject):
else:
self._conn[CONN_INTERFACE].Connect()
def disconnect(self):
def cleanup(self):
self._conn[CONN_INTERFACE].Disconnect()
def _contact_offline(self, handle):