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) dbus.DBusException.__init__(self)
self._dbus_error_name = _PRESENCE_INTERFACE + '.NotFound' 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 """Represents another person on the network and keeps track of the
activities and resources they make available for sharing.""" 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: if not bus_name:
raise ValueError("DBus bus name must be valid") raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int): if not object_id or not isinstance(object_id, int):
raise ValueError("object id must be a valid number") raise ValueError("object id must be a valid number")
if not key:
raise ValueError("key must be valid")
self._bus_name = bus_name self._bus_name = bus_name
self._object_id = object_id self._object_id = object_id
self._object_path = _BUDDY_PATH + str(self._object_id) self._object_path = _BUDDY_PATH + str(self._object_id)
dbus.service.Object.__init__(self, self._bus_name, self._object_path) dbus.service.Object.__init__(self, self._bus_name, self._object_path)
self._activities = {} # Activity ID -> Activity self._activities = {} # Activity ID -> Activity
self.handles = {} # tp client -> handle self.handles = {} # tp client -> handle
self._key = key self._valid = False
self._icon = None self._owner = False
self._nick = None self._key = None
self._color = None
self._current_activity = 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 signals
@dbus.service.signal(_BUDDY_INTERFACE, @dbus.service.signal(_BUDDY_INTERFACE,
@ -80,10 +139,9 @@ class Buddy(dbus.service.Object):
@dbus.service.method(_BUDDY_INTERFACE, @dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="ay") in_signature="", out_signature="ay")
def GetIcon(self): def GetIcon(self):
icon = self.get_icon() if not self.props.icon:
if not icon:
return "" return ""
return icon return self.props.icon
@dbus.service.method(_BUDDY_INTERFACE, @dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="ao") in_signature="", out_signature="ao")
@ -97,10 +155,10 @@ class Buddy(dbus.service.Object):
in_signature="", out_signature="a{sv}") in_signature="", out_signature="a{sv}")
def GetProperties(self): def GetProperties(self):
props = {} props = {}
props['nick'] = self.get_nick() props['nick'] = self.props.nick
props['owner'] = self.is_owner() props['owner'] = self.props.owner
props['key'] = self.get_key() props['key'] = self.props.key
color = self.get_color() color = self.props.color
if color: if color:
props['color'] = color props['color'] = color
return props return props
@ -132,78 +190,53 @@ class Buddy(dbus.service.Object):
acts.append(act) acts.append(act)
return acts 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): def set_properties(self, properties):
if "nick" in properties.keys(): if "nick" in properties.keys():
self._set_nick(properties["nick"]) self._nick = properties["nick"]
if "color" in properties.keys(): if "color" in properties.keys():
self._set_color(properties["color"]) self._color = properties["color"]
self.PropertyChanged(properties)
def is_owner(self): # Try emitting PropertyChanged before updating validity
return False # 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._update_validity()
self._key = key
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 Owner(Buddy):
"""Class representing the owner of the machine. This is the client """Class representing the owner of the machine. This is the client
portion of the Owner, paired with the server portion in Owner.py.""" 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() 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 Buddy.__init__(self, bus_name, object_id, key=key, nick=nick, color=color)
self._name = profile.get_nick_name() self._owner = True
self._color = profile.get_color().to_string()
# dbus methods # dbus methods
@dbus.service.method(_OWNER_INTERFACE, @dbus.service.method(_OWNER_INTERFACE,
in_signature="ay", out_signature="") in_signature="ay", out_signature="")
def SetIcon(self, icon_data): def SetIcon(self, icon_data):
self.set_icon(icon_data) self.props.icon = icon_data
@dbus.service.method(_OWNER_INTERFACE, @dbus.service.method(_OWNER_INTERFACE,
in_signature="a{sv}", out_signature="") in_signature="a{sv}", out_signature="")
def SetProperties(self, prop): def SetProperties(self, prop):
self.set_properties(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): def __init__(self, registry):
gobject.GObject.__init__(self) gobject.GObject.__init__(self)
self._registry = registry self._registry = registry
def cleanup(self):
pass

View File

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

View File

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