[Commits] groupthink branch master updated.
bens
bens at bemasc.localdomain
Mon Jan 12 00:07:20 EST 2009
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "/home/bemasc/public_git/groupthink".
The branch, master has been updated
via 09689e2370808fb21cbe228165100df761d0e42e (commit)
from bb633dac532aa6908ac5d8eaafcbc72b04dab1cc (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
__init__.py | 1 +
groupthink.py => groupthink_base.py | 319 ++++++++++++++++++++++++++---------
gtk.py | 26 +++
sugar.py | 200 ++++++++++++++++++++++
4 files changed, 464 insertions(+), 82 deletions(-)
create mode 100644 __init__.py
rename groupthink.py => groupthink_base.py (81%)
create mode 100644 gtk.py
create mode 100644 sugar.py
- Log -----------------------------------------------------------------
commit 09689e2370808fb21cbe228165100df761d0e42e
Author: bens <bens at bemasc.localdomain>
Date: Sun Jan 11 23:27:05 2009 -0500
Dont lose files...
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..628753a
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1 @@
+from groupthink_base import *
diff --git a/groupthink.py b/groupthink_base.py
similarity index 81%
rename from groupthink.py
rename to groupthink_base.py
index 3f707ed..a2ab685 100644
--- a/groupthink.py
+++ b/groupthink_base.py
@@ -24,7 +24,7 @@ import logging
import threading
import thread
import random
-from dobject_helpers import *
+from listset import ListSet
"""
DObject is a library of components useful for constructing distributed
@@ -40,6 +40,38 @@ def PassFunction(*args):
def ReturnFunction(x):
return x
+class Group:
+ """A Group is a simple tool for organizing DObjects. Once it is set up
+ with a tubebox, the user may simply add objects to it, e.g.
+
+ self.group = Group(tb)
+ ...
+ self.group['mydict1'] = HighScore('No one', 0)
+
+ and the group will take care of assigning a handler to the object with
+ the specified name.
+ For a Group g, g['a'] is equivalent in almost all ways to g.a, for
+ programmer convenience.
+ """
+ def __init__(self, tubebox):
+ self.tubebox = tubebox
+ self._d = dict()
+
+ def __setitem__(self, name, dobj):
+ h = dobj.HANDLER_TYPE(name, self.tubebox)
+ dobj.set_handler(h)
+ self._d[name] = dobj
+
+ __setattr__ = __setitem__
+
+ def __getitem__(self, name):
+ return self._d[name]
+
+ __getattr__ = __getitem__
+
+ def __delattr(self, name):
+ raise #Deletion is not supported
+
class TubeBox:
""" A TubeBox is a box that either contains a Tube or does not.
The purpose of a TubeBox is to solve this problem: Activities are not
@@ -175,37 +207,12 @@ class TimeHandler(dbus.gobject_service.ExportedGObject):
self._know_offset = True
self._offset_lock.release()
-
class UnorderedHandler(dbus.gobject_service.ExportedGObject):
- """ The most basic DObject is the Unordered Object (UO). A UO has the
- property that any changes to its state can be encapsulated as messages, and
- these messages have no intrinsic ordering. Different instances of the same
- UO, after receiving the same messages in different orders, should reach the
- same state.
-
- Any UO could be implemented as a set of all messages received so far, and
- coherency could be maintained by sending all messages ever transmitted to
- each new joining member. However, many UOs will have the property that most
- messages are obsolete, and need not be transmitted. Therefore, as an
- optimization, UOs manage their own state structures for synchronizing state
- with joining/merging users.
-
- Each UO should accept a UnorderedHandler as one of its constructor's arguments
- Whenever an action is taken on the local UO (e.g. a method call that changes
- the object's state), the UO must call handler.send() with an appropriately
- encoded message. Every UO must implement three methods:
-
- receive_message(msg):
- This method accepts and processes a message sent via handler.send().
- Because objects are sent over DBus, it is advisable to DBus-ify the message
- before calling send, and de-DBus-ify it inside receive_message.
-
- get_history():
- This method returns an encoded copy of all non-obsolete state, ready to be
- sent over DBus.
-
- add_history(state):
- This method accepts and processes the state object returned by get_history()
+ """The UnorderedHandler serves as the interface between a local UnorderedObject
+ (a pure python entity) and the d-bus/network system. Each UnorderedObject
+ is associated with a single Handler, and vice-versa. It is the Handler that
+ is actually exposed over D-Bus. The purpose of this system is to minimize
+ the amount of networking code required for each additional UnorderedObject.
"""
IFACE = "org.dobject.Unordered"
BASEPATH = "/org/dobject/Unordered/"
@@ -314,10 +321,67 @@ class UnorderedHandler(dbus.gobject_service.ExportedGObject):
with a different name every time."""
return UnorderedHandler(self._myname + "/" + name, self._tube_box)
+class HandlerAcceptor:
+ HANDLER_TYPE = NotImplementedError
+ def set_handler(self, handler):
+ raise NotImplementedError
+
+class UnorderedObject(HandlerAcceptor):
+ """ The most basic DObject is the Unordered Object (UO). A UO has the
+ property that any changes to its state can be encapsulated as messages, and
+ these messages have no intrinsic ordering. Different instances of the same
+ UO, after receiving the same messages in different orders, should reach the
+ same state.
+
+ Any UO could be implemented as a set of all messages received so far, and
+ coherency could be maintained by sending all messages ever transmitted to
+ each new joining member. However, many UOs will have the property that most
+ messages are obsolete, and need not be transmitted. Therefore, as an
+ optimization, UOs manage their own state structures for synchronizing state
+ with joining/merging users.
+
+ The following code is an abstract class for UnorderedObject, serving
+ primarily as documentation for the concept.
+ """
+
+ HANDLER_TYPE = UnorderedHandler
+ handler = None
+
+ def set_handler(self, handler):
+ """Each UO must accept an UnorderedHandler via set_handler
+ Whenever an action is taken on the local UO (e.g. a method call that changes
+ the object's state), the UO must call handler.send() with an appropriately
+ encoded message.
+
+ Subclasses may override this method if they wish to perform more actions
+ when a handler is set."""
+ if self.handler:
+ raise
+ else:
+ self.handler = handler
+ self.handler.register(self)
+
+
+ def receive_message(self,msg):
+ """This method accepts and processes a message sent via handler.send().
+ Because objects are sent over DBus, it is advisable to DBus-ify the message
+ before calling send, and de-DBus-ify it inside receive_message."""
+ raise NotImplementedError
+
+ def get_history(self):
+ """This method returns an encoded copy of all non-obsolete state, ready to be
+ sent over DBus."""
+ raise NotImplementedError
+
+ def add_history(self, state):
+ """This method accepts and processes the state object returned by get_history()"""
+ raise NotImplementedError
+
+
def empty_translator(x, pack):
return x
-class HighScore:
+class HighScore(UnorderedObject):
""" A HighScore is the simplest nontrivial DObject. A HighScore's state consists
of a value and a score. The user may suggest a new value and score. If the new
score is higher than the current score, then the value and score are updated.
@@ -334,7 +398,7 @@ class HighScore:
random number to each message, and thereby reduces the probability of a tie
by a factor of 2**52.
"""
- def __init__(self, handler, initval, initscore, value_translator=empty_translator, score_translator=empty_translator, break_ties=False):
+ def __init__(self, initval, initscore, value_translator=empty_translator, score_translator=empty_translator, break_ties=False):
self._logger = logging.getLogger('stopwatch.HighScore')
self._lock = threading.Lock()
self._value = initval
@@ -349,10 +413,8 @@ class HighScore:
self._val_trans = value_translator
self._score_trans = score_translator
- self._handler = handler
- self._handler.register(self)
-
self._listeners = []
+
def _set_value_from_net(self, val, score, tiebreaker):
self._logger.debug("set_value_from_net " + str(val) + " " + str(score))
@@ -375,8 +437,8 @@ class HighScore:
score will be broadcast to all other participants.
"""
self._logger.debug("set_value " + str(val) + " " + str(score))
- if self._actually_set_value(val, score, None):
- self._handler.send(self.get_history())
+ if self._actually_set_value(val, score, None) and self.handler:
+ self.handler.send(self.get_history())
def _actually_set_value(self, value, score, tiebreaker):
self._logger.debug("_actually_set_value " + str(value)+ " " + str(score))
@@ -455,6 +517,13 @@ def float_translator(f, pack):
else:
return float(f)
+def uint_translator(f, pack):
+ """This translator packs and unpacks 64-bit uints for dbus serialization"""
+ if pack:
+ return dbus.UInt64(f)
+ else:
+ return int(f)
+
def string_translator(s, pack):
"""This translator packs and unpacks unicode strings for dbus serialization"""
if pack:
@@ -462,36 +531,81 @@ def string_translator(s, pack):
else:
return str(s)
-class Latest:
+class Latest(HandlerAcceptor):
""" Latest is a variation on HighScore, in which the score is the current
timestamp. Latest uses TimeHandler to provide a groupwide coherent clock.
Because TimeHandler's guarantees about synchronization and resilience are
weak, Latest is not as resilient to failures as a true DObject.
- The creator must provide a UnorderedHandler and the initial value. One may
+ The creator must provide the initial value. One may
optionally indicate the initial time (as a float in epoch-time), a
TimeHandler (otherwise a new one will be created), and a translator for
serialization of the values.
+
+ Note that if time_handler is not provided, the object will not be functional
+ until set_handler is called.
"""
- def __init__(self, handler, initval, inittime=float('-inf'), time_handler=None, translator=empty_translator):
- if time_handler is None:
- self._time_handler = TimeHandler(handler.get_path(), handler.get_tube())
- else:
- self._time_handler = time_handler
+ def __init__(self, initval, inittime=float('-inf'), time_handler=None, translator=empty_translator):
+ self._time_handler = time_handler
self._listeners = []
self._lock = threading.Lock()
- self._highscore = HighScore(handler, initval, inittime, translator, float_translator)
+ self._highscore = HighScore(initval, inittime, translator, float_translator)
self._highscore.register_listener(self._highscore_cb)
+ def set_handler(self, handler):
+ if self.handler:
+ raise
+ else:
+ if self._time_handler is None:
+ self._time_handler = TimeHandler(handler.get_path(), handler.get_tube())
+ self._highscore.set_handler(handler)
+
def get_value(self):
""" Returns the latest value """
return self._highscore.get_value()
def set_value(self, val):
""" Suggest a new value """
- self._highscore.set_value(val, self._time_handler.time())
+ if self._time_handler:
+ self._highscore.set_value(val, self._time_handler.time())
+ else:
+ raise #missing _time_handler
+
+ def register_listener(self, L):
+ """ Register a listener L(value), to be called whenever another user
+ adds a new latest value."""
+ self._lock.acquire()
+ self._listeners.append(L)
+ self._lock.release()
+ L(self.get_value())
+
+ def _highscore_cb(self, val, score):
+ for L in self._listeners:
+ L(val)
+
+class Recentest(HandlerAcceptor):
+ """ Recentest is like Latest, but without using a clock or TimeHandler.
+ As a result, it can only guarantee causality, not synchrony.
+ """
+ def __init__(self, initval, translator=empty_translator):
+ self._listeners = []
+ self._lock = threading.Lock()
+
+ self._highscore = HighScore(initval, 0, translator, uint_translator, break_ties=True)
+ self._highscore.register_listener(self._highscore_cb)
+
+ def set_handler(self, handler):
+ self._highscore.set_handler(handler)
+
+ def get_value(self):
+ """ Returns the current value """
+ return self._highscore.get_value()
+
+ def set_value(self, val):
+ """ Set a new value """
+ self._highscore.set_value(val, self._highscore.get_score() + 1)
def register_listener(self, L):
""" Register a listener L(value), to be called whenever another user
@@ -512,7 +626,7 @@ class AddOnlySet:
is perfectly coherent, since the order in which elements are added is not
important.
"""
- def __init__(self, handler, initset = (), translator=empty_translator):
+ def __init__(self, initset = (), translator=empty_translator):
self._logger = logging.getLogger('dobject.AddOnlySet')
self._set = set(initset)
@@ -521,9 +635,6 @@ class AddOnlySet:
self._trans = translator
self._listeners = [] #This must be done before registering with the handler
- self._handler = handler
- self._handler.register(self)
-
self.__and__ = self._set.__and__
self.__cmp__ = self._set.__cmp__
self.__contains__ = self._set.__contains__
@@ -587,8 +698,8 @@ class AddOnlySet:
self._send((y,))
def _send(self, els):
- if len(els) > 0:
- self._handler.send(dbus.Array([self._trans(el, True) for el in els]))
+ if len(els) > 0 and self.handler is not None:
+ self.handler.send(dbus.Array([self._trans(el, True) for el in els]))
def _net_update(self, y):
s = set(y)
@@ -619,7 +730,7 @@ class AddOnlySet:
L(s)
def __repr__(self):
- return 'AddOnlySet(' + repr(self._handler) + ', ' + repr(self._set) + ', ' + repr(self._trans) + ')'
+ return 'AddOnlySet(' + repr(self.handler) + ', ' + repr(self._set) + ', ' + repr(self._trans) + ')'
class AddOnlySortedSet:
""" AddOnlySortedSet is much like AddOnlySet, only backed by a ListSet, which
@@ -637,9 +748,6 @@ class AddOnlySortedSet:
self._trans = translator
self._listeners = [] #This must be done before registering with the handler
-
- self._handler = handler
- self._handler.register(self)
self.__and__ = self._set.__and__
self.__contains__ = self._set.__contains__
@@ -711,8 +819,8 @@ class AddOnlySortedSet:
self._send((y,))
def _send(self, els):
- if len(els) > 0:
- self._handler.send(dbus.Array([self._trans(el, True) for el in els]))
+ if len(els) > 0 and self.handler is not None:
+ self.handler.send(dbus.Array([self._trans(el, True) for el in els]))
def _net_update(self, y):
d = ListSet()
@@ -744,10 +852,9 @@ class AddOnlySortedSet:
L(s)
def __repr__(self):
- return 'AddOnlySortedSet(' + repr(self._handler) + ', ' + repr(self._set) + ', ' + repr(self._trans) + ')'
-
+ return 'AddOnlySortedSet(' + repr(self.handler) + ', ' + repr(self._set) + ', ' + repr(self._trans) + ')'
-def CausalHandler():
+class CausalHandler:
"""The CausalHandler is analogous to the UnorderedHandler, in that it
presents an interface with which to build a wide variety of objects with
distributed state. The CausalHandler is different from the Unordered in two
@@ -777,7 +884,7 @@ def CausalHandler():
_max64 = 2**64
def __init__(self, name, tube_box):
- self._unordered = UnorderedObject(name, tube_box)
+ self._unordered = UnorderedHandler(name, tube_box)
self._counter = 0
self._object = None
@@ -832,9 +939,53 @@ def CausalHandler():
hist = dbus.Tuple((h, self.index_trans(self.get_index(), True)))
return
-class CausalDict:
+class CausalObject:
+ """A CausalObject is almost precisely like an UnorderedObject, except
+ that whereas an UnorderedObject is completely specified by a set of messages,
+ a CausalObject is completely specified by an ordered list of messages,
+ sorted according to an opaque index associated with each message.
+ This index must be monotonically increasing in time for new messages as they
+ are created, but old messages may arrive long after they were created, and
+ are then inserted into the middle of the timestream.
+
+ The following code is an abstract class for CausalObject, serving
+ primarily as documentation for the concept.
+ """
+
+ HANDLER_TYPE = CausalHandler
+ handler = None
+
+ def set_handler(self, handler):
+ """Each CO must accept an CausalHandler via set_handler.
+
+ Subclasses may override this method if they wish to perform more actions
+ when a handler is set."""
+ if self.handler:
+ raise
+ else:
+ self.handler = handler
+ self.handler.register(self)
+
+ def receive_message(self, msg, index):
+ """This method accepts and processes a message sent via handler.send().
+ Because objects are sent over DBus, it is advisable to DBus-ify the message
+ before calling send, and de-DBus-ify it inside receive_message.
+
+ The index argument is an opaque index used for determining the ordering."""
+ raise NotImplementedError
+
+ def get_history(self):
+ """This method returns an encoded copy of all non-obsolete state, ready to be
+ sent over DBus."""
+ raise NotImplementedError
+
+ def add_history(self, state):
+ """This method accepts and processes the state object returned by get_history()"""
+ raise NotImplementedError
+
+class CausalDict(CausalObject):
"""NOTE: CausalDict is UNTESTED. Other things may be buggy, but CausalDict
- PROBABLY DOES NOT WORK.
+ PROBABLY DOES NOT WORK. A CausalDict WILL NOT WORK UNTIL set_handler IS CALLED.
CausalDict is a distributed version of a Dict (hash table). All users keep
a copy of the entire table, so this is not a "Distributed Hash Table"
@@ -864,12 +1015,8 @@ class CausalDict:
DELETE = 1
CLEAR = 2
- def __init__(self, handler, initdict=(), key_translator=empty_translator, value_translator=empty_translator):
- self._handler = handler
+ def __init__(self, initdict=(), key_translator=empty_translator, value_translator=empty_translator):
self._dict = dict(initdict)
- self._clear = self._handler.get_index() #this must happen before index_dict initialization, so that self._clear is less than any index in index_dict
- self._index_dict = dict(((k, self._handler.get_index()) for k in initdict))
-
self._listeners = []
self._key_trans = key_translator
@@ -901,26 +1048,34 @@ class CausalDict:
#special setdefault
#special update
self.values = self._dict.values
-
- self._handler.register(self)
+
+ def set_handler(self, handler):
+ if self.handler is not None:
+ raise
+ else:
+ self.handler = handler
+ self._clear = self.handler.get_index() #this must happen before index_dict initialization, so that self._clear is less than any index in index_dict
+ self._index_dict = dict(((k, self.handler.get_index()) for k in initdict))
+
+ self.handler.register(self)
def __delitem__(self, key):
"""Same as for dict"""
del self._dict[key]
- n = self._handler.send(((dbus.Int32(CausalDict.DELETE), self._key_trans(key, True))))
+ n = self.handler.send(((dbus.Int32(CausalDict.DELETE), self._key_trans(key, True))))
self._index_dict[key] = n
def __setitem__(self, key, value):
"""Same as for dict"""
self._dict[key] = value
- n = self._handler.send(dbus.Array([(dbus.Int32(CausalDict.ADD), self._key_trans(key, True), self._val_trans(value, True))]))
+ n = self.handler.send(dbus.Array([(dbus.Int32(CausalDict.ADD), self._key_trans(key, True), self._val_trans(value, True))]))
self._index_dict[key] = n
def clear(self):
"""Same as for dict"""
self._dict.clear()
self._index_dict.clear()
- n = self._handler.send(dbus.Array([(dbus.Int32(CausalDict.CLEAR))]))
+ n = self.handler.send(dbus.Array([(dbus.Int32(CausalDict.CLEAR))]))
self._clear = n
def pop(self, key, x=None):
@@ -932,7 +1087,7 @@ class CausalDict:
r = self._dict.pop(key, x)
if t:
- n = self._handler.send(dbus.Array([(dbus.Int32(CausalDict.DELETE), self._key_trans(key, True))]))
+ n = self.handler.send(dbus.Array([(dbus.Int32(CausalDict.DELETE), self._key_trans(key, True))]))
self._index_dict[key] = n
return r
@@ -941,7 +1096,7 @@ class CausalDict:
"""Same as for dict"""
p = self._dict.popitem()
key = p[0]
- n = self._handler.send(dbus.Array([(dbus.Int32(CausalDict.DELETE), self._key_trans(key, True))]))
+ n = self.handler.send(dbus.Array([(dbus.Int32(CausalDict.DELETE), self._key_trans(key, True))]))
self._index_dict[key] = n
return p
@@ -949,7 +1104,7 @@ class CausalDict:
"""Same as for dict"""
if key not in self._dict:
self._dict[key] = x
- n = self._handler.send(dbus.Array([(dbus.Int32(CausalDict.ADD), self._key_trans(key, True), self._val_trans(value, True))]))
+ n = self.handler.send(dbus.Array([(dbus.Int32(CausalDict.ADD), self._key_trans(key, True), self._val_trans(value, True))]))
self._index_dict[key] = n
def update(*args,**kargs):
@@ -961,7 +1116,7 @@ class CausalDict:
if (p[0] not in self._dict) or (self._dict[p[0]] != p[1]):
newpairs.append(p)
self._dict[p[0]] = p[1]
- n = self._handler.send(dbus.Array([(dbus.Int32(CausalDict.ADD), self._key_trans(p[0], True), self._val_trans(p[1], True)) for p in newpairs]))
+ n = self.handler.send(dbus.Array([(dbus.Int32(CausalDict.ADD), self._key_trans(p[0], True), self._val_trans(p[1], True)) for p in newpairs]))
for p in newpairs:
self._index_dict[p[0]] = n
@@ -1002,15 +1157,15 @@ class CausalDict:
self._trigger(a,r)
def get_history(self):
- c = self._handler.index_trans(self._clear, True)
+ c = self.handler.index_trans(self._clear, True)
d = dbus.Array([(self._key_trans(p[0], True), self._val_trans(p[1], True)) for p in self._dict.items()])
i = dbus.Array([(self._key_trans(p[0], True), self._handler.index_trans(p[1], True)) for p in self._index_dict.items()])
return dbus.Tuple((c,d,i))
def add_history(self, hist):
- c = self._handler.index_trans(hist[0], False)
+ c = self.handler.index_trans(hist[0], False)
d = dict(((self._key_trans(p[0], False), self._val_trans(p[1], False)) for p in hist[1]))
- i = [(self._key_trans(p[0], False), self._handler.index_trans(p[1], False)) for p in hist[2]]
+ i = [(self._key_trans(p[0], False), self.handler.index_trans(p[1], False)) for p in hist[2]]
a = dict()
r = dict()
diff --git a/gtk.py b/gtk.py
new file mode 100644
index 0000000..9f8cc97
--- /dev/null
+++ b/gtk.py
@@ -0,0 +1,26 @@
+import gtk
+import groupthink
+
+class RecentEntry(HandlerAcceptor, gtk.Entry):
+ """RecentEntry is an extension of gtk.Entry that, when attached to a group,
+ creates a unified Entry field for all participants"""
+ def __init__(self, text=""):
+ gtk.Entry.__init__(self, text)
+ self._text_changed_handler = self.connect('changed', self._local_change_cb)
+ self._recent = groupthink.Recentest(text, string_translator)
+ self._recent.register_listener(self._remote_change_cb)
+
+ def _local_change_cb(self, widget):
+ self._recent.set_value(self.get_text())
+
+ def set_handler(self, handler):
+ self._recent.set_handler(handler)
+
+ def _remote_change_cb(self, text):
+ if self.get_text() != text:
+ #The following code will break if running in any thread other than
+ #the main thread. I do not know how to make code that works with
+ #both multithreaded gtk _and_ single-threaded gtk.
+ self._name.handler_block(self._name_changed_handler)
+ self.set_text(text)
+ self._name.handler_unblock(self._name_changed_handler)
diff --git a/sugar.py b/sugar.py
new file mode 100644
index 0000000..1993222
--- /dev/null
+++ b/sugar.py
@@ -0,0 +1,200 @@
+# Copyright 2007 Collabora Ltd.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+import telepathy
+
+from sugar.activity.activity import Activity, ActivityToolbox
+from sugar.presence import presenceservice
+
+from sugar.presence.tubeconn import TubeConnection
+
+import gtk
+
+import groupthink
+
+class GroupActivity(Activity):
+
+ message_preparing = "Preparing user interface"
+ message_loading = "Loading object from Journal"
+ message_joining = "Joining shared activity"
+
+ """Abstract Class for Activities using Groupthink"""
+ def __init__(self, handle, service_name):
+ Activity.__init__(self, handle)
+ self.logger = logging.getLogger(service_name)
+ self.dbus_name = service_name
+
+ ##gobject.threads_init()
+
+ # top toolbar with share and close buttons:
+ toolbox = ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ # This variable indicates whether this instance has initiated sharing
+ # it is always false, initially, and only becomes true if the user
+ # shares the activity after launch
+ self.initiating = False
+
+ # This variable tracks whether the Activity's display is up and running
+ self.initialized = False
+ if self._shared_activity:
+ self.message = self.message_joining
+ elif handle.object_id:
+ self.message = self.message_loading
+ else:
+ self.message = self.message_preparing
+
+ v = gtk.VBox()
+ self.startup_label = gtk.Label(self.message)
+ v.pack_start(self.startup_label)
+ self.set_canvas(v)
+ self.show_all()
+
+ self.tubebox = groupthink.TubeBox()
+ self.timer = groupthink.TimeHandler("main", self.tubebox)
+ self.group = groupthink.Group(self.tubebox)
+ # self.group is extremely important. It is the unified reference point
+ # that contains all state in the system. Everything else is stateless.
+
+ # get the Presence Service
+ self.pservice = presenceservice.get_instance()
+ # Buddy object for you
+ owner = self.pservice.get_owner()
+ self.owner = owner
+
+ self.connect('shared', self._shared_cb)
+ self.connect('joined', self._joined_cb)
+
+ self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK)
+ self.connect("visibility-notify-event", self._visible_cb)
+ self.connect("notify::active", self._active_cb)
+
+ if not (self._shared_activity or handle.object_id):
+ self.initialize_firststart()
+ self.initialize_display()
+
+ def initialize_firststart(self):
+ """Any subclass that needs to take any extra action in the case where
+ the activity is launched locally without a sharing context or input
+ file should override this method"""
+ pass
+
+ def initialize_display(self):
+ """All subclasses should override this method, in order to display
+ their GUI."""
+ self.initialized = True
+
+ def _shared_cb(self, activity):
+ self.logger.debug('My activity was shared')
+ self.initiating = True
+ self._sharing_setup()
+
+ self.logger.debug('This is my activity: making a tube...')
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ self.dbus_name, {})
+
+ def _sharing_setup(self):
+ if self._shared_activity is None:
+ self.logger.error('Failed to share or join activity')
+ return
+
+ self.conn = self._shared_activity.telepathy_conn
+ self.tubes_chan = self._shared_activity.telepathy_tubes_chan
+ self.text_chan = self._shared_activity.telepathy_text_chan
+
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube',
+ self._new_tube_cb)
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ self.logger.error('ListTubes() failed: %s', e)
+
+ def _joined_cb(self, activity):
+ if not self._shared_activity:
+ return
+
+ self.logger.debug('Joined an existing shared activity')
+ self.initiating = False
+ self._sharing_setup()
+
+ if not self.initialized:
+ self.initialize_display()
+
+ self.logger.debug('This is not my activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ self.logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', id, initiator, type, service,
+ params, state)
+ if (type == telepathy.TUBE_TYPE_DBUS and
+ service == self.dbus_name):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+ tube_conn = TubeConnection(self.conn,
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES],
+ id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
+ self.tubebox.insert_tube(tube_conn, self.initiating)
+
+ """
+ #These functions require modifications to dbus-python to expose
+ #dbus_message_marshal and dbus_message_demarshal.
+ def read_file(self, file_path):
+ # This function should be implemented by deserializing a bunch of
+ # dbus messages derived from all the objects in self.group, and calling
+ # add_history on a matching set of blank objects to bring them up to
+ # present.
+ if not self.initialized:
+ self.initialize_display()
+ pass
+
+ def write_file(self, file_path):
+ # This method should be implemented by serializing the results of
+ # .get_history() from each object in self.group, along with the name
+ # and type of that object.
+ pass
+ """
+
+ def _active_cb(self, widget, event):
+ self.logger.debug("_active_cb")
+ if self.props.active:
+ self.resume()
+ else:
+ self.pause()
+
+ def _visible_cb(self, widget, event):
+ self.logger.debug("_visible_cb")
+ if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:
+ self.pause()
+ else:
+ self.resume()
+
+ def pause(self):
+ """Subclasses should override this function to stop updating the display
+ since it is not visible."""
+ pass
+
+ def resume(self):
+ """Subclasses should override this function to resume updating the
+ display, since it is now visible"""
+ pass
-----------------------------------------------------------------------
--
/home/bemasc/public_git/groupthink
More information about the Commits
mailing list