<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.sugarlabs.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jedierikb</id>
	<title>Sugar Labs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.sugarlabs.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Jedierikb"/>
	<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/go/Special:Contributions/Jedierikb"/>
	<updated>2026-05-13T04:50:36Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/Sugar.presence&amp;diff=18555</id>
		<title>Development Team/Almanac/Sugar.presence</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/Sugar.presence&amp;diff=18555"/>
		<updated>2008-11-24T22:52:52Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* Which threading patterns are most appropriate for use on the sugar platform? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
=== How do I setup a D-Bus Tube? ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s first look at what conceptually happens to make activity sharing work. The diagram below shows two instances of the same activity: the &amp;quot;Sharing Activity&amp;quot; and the &amp;quot;Joining Activity&amp;quot;. The sharing activity is the activity that is initially run and shared with other buddies. The &amp;quot;Joining Activity&amp;quot; refers to other instances of the activity that are created once buddies decide to join an activity that has been shared. You can allow an activity to be shared (making it a sharing activity) by  [[Sugar.presence.presenceservice#How do I share an activity with other buddies in my neighborhood? | including the standard Sugar Activity Toolbar]].&lt;br /&gt;
&lt;br /&gt;
[[Image:tube-setup.jpg | How Tubes are Setup]]&lt;br /&gt;
&lt;br /&gt;
Once the user decides to share his activity with others, that activity becomes the sharing activity. The activity will receive a &amp;quot;shared&amp;quot; signal from the connection manager indicating the activity has been shared. The sharing activity must then save information about the channel on which the tube will exist and call the OfferDBusTube() method to offer a tube that can be accessed by other XOs that join the activity. The process is similar for XOs that join an activity except they will need to call the ListTubes() method instead of OfferDBusTubes() to find a tube that has been offered by the sharing activity. &lt;br /&gt;
&lt;br /&gt;
Once a tube has been set up by the underlying connection manager, both the sharing and joining XOs get a &amp;quot;NewTube&amp;quot; signal. Upon receiving this signal, each tube can instantiate a new Tube class that has been designed and implemented for the needs of each specific activity. The code example below shows how all of the steps above are actually accomplished (only the code directly relevant to tube setup has been included). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
from sugar import profile&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Sample&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class SampleActivity(activity.Activity):&lt;br /&gt;
    &lt;br /&gt;
    _ps = presenceservice.get_instance()&lt;br /&gt;
    &lt;br /&gt;
    ############################ METHODS INVOLVED IN TUBE SETUP ##########################################&lt;br /&gt;
&lt;br /&gt;
    #### Method: __init__, initialize this SampleActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        activity.Activity.__init__(self, handle)&lt;br /&gt;
        ...&lt;br /&gt;
        #When you initialize your activity, wait for receipt of &amp;quot;joined&amp;quot; or &amp;quot;shared&amp;quot; signals&lt;br /&gt;
        #and also keep a variable called self.initiating to indicating whether this instance&lt;br /&gt;
        #is the sharing or joining activity. &lt;br /&gt;
        self.initiating = None #indicates whether this instance initiated sharing. &lt;br /&gt;
        self.connect(&#039;shared&#039;, self._shared_cb)&lt;br /&gt;
        self.connect(&#039;joined&#039;, self._joined_cb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method self._shared_cb, which is called whenever this activity has been successfully &lt;br /&gt;
    # shared with other XOs on the mesh&lt;br /&gt;
    def _shared_cb(self, activity):&lt;br /&gt;
        &lt;br /&gt;
        #Ensure that this activity is indeed being shared&lt;br /&gt;
        if self._shared_activity is None:&lt;br /&gt;
            _logger.error(&amp;quot;Failed to share or join activity ... _shared_activity is null in _shared_cb()&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        #This activity initiated sharing, so set self.initiating to be true&lt;br /&gt;
        self.initiating = True&lt;br /&gt;
 &lt;br /&gt;
        #Save information about the telepathy connection, telepathy tubes channel and the text channel&lt;br /&gt;
        #associated with this shared activity&lt;br /&gt;
        self.conn = self._shared_activity.telepathy_conn&lt;br /&gt;
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        self.text_chan = self._shared_activity.telepathy_text_chan&lt;br /&gt;
&lt;br /&gt;
        #Set up a callback for when we receive the &amp;quot;NewTube&amp;quot; signal later on&lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&lt;br /&gt;
             &#039;NewTube&#039;, self._new_tube_cb)&lt;br /&gt;
&lt;br /&gt;
        #Call the OfferDBusTubes() method &lt;br /&gt;
        _id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(SERVICE, {})&lt;br /&gt;
&lt;br /&gt;
    #### Method self._joined_cb, which is called whenever this XO has successfully joined another&lt;br /&gt;
    # activity. &lt;br /&gt;
    def _joined_cb(self, activity):&lt;br /&gt;
&lt;br /&gt;
        #Ensure that this activity is indeed being shared&lt;br /&gt;
        if self._shared_activity is None:&lt;br /&gt;
            _logger.error(&amp;quot;Failed to share or join activity ... _shared_activity is null in _shared_cb()&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        #This activity joined an existing shared activity, so set self.initiating to False&lt;br /&gt;
        self.initiating = False&lt;br /&gt;
&lt;br /&gt;
        #Save information about the telepathy connection, telepathy tubes channel and the text channel&lt;br /&gt;
        #associated with this shared activity&lt;br /&gt;
        self.conn = self._shared_activity.telepathy_conn&lt;br /&gt;
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        self.text_chan = self._shared_activity.telepathy_text_chan&lt;br /&gt;
&lt;br /&gt;
        #Set up a callback for when we receive the &amp;quot;NewTube&amp;quot; signal later on&lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&lt;br /&gt;
             &#039;NewTube&#039;, self._new_tube_cb)&lt;br /&gt;
&lt;br /&gt;
        #For joining activities, call ListTubes and connect to a callback once a set &lt;br /&gt;
        #shared tubes have been found (list_tubes_reply_cb). &lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _list_tubes_reply_cb, which is called once ListTubes successfully finds&lt;br /&gt;
    # tubes available for a joining activity. &lt;br /&gt;
    def _list_tubes_reply_cb(self, tubes):&lt;br /&gt;
        for tube_info in tubes:&lt;br /&gt;
            self._new_tube_cb(*tube_info)&lt;br /&gt;
            &lt;br /&gt;
    #### Method _list_tubes_error_cb, which is needed in case there was some error in ListTubes&lt;br /&gt;
    # for the joining activity. &lt;br /&gt;
    def _list_tubes_error_cb(self, e):&lt;br /&gt;
        print &amp;quot;Error&amp;quot; + str(e)&lt;br /&gt;
&lt;br /&gt;
    #### Method _new_tube_cb, which is called for both joining and sharing activities once a tube&lt;br /&gt;
    # is available in the underlying presence system for communication. Several parameters will be &lt;br /&gt;
    # passed to this callback with information about the tube that has been set up. &lt;br /&gt;
    def _new_tube_cb(self, id, initiator, type, service, params, state):&lt;br /&gt;
        &lt;br /&gt;
        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):&lt;br /&gt;
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:&lt;br /&gt;
                #Accept the new tube that has been created &lt;br /&gt;
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)&lt;br /&gt;
&lt;br /&gt;
            # The tube connection object gives a handle to the new tube. &lt;br /&gt;
            tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])&lt;br /&gt;
&lt;br /&gt;
            # Using the handle saved above, create an instance of your own customized Tube wrapper that &lt;br /&gt;
            # will handle sending and receiving text from the underlying tube conneciton (ChatTube is defined&lt;br /&gt;
            # at the end below this activity class). &lt;br /&gt;
            self.chattube = ChatTube(tube_conn, self.initiating, self.text_received_cb)&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
    ... &lt;br /&gt;
    ############################# METHODS USED ONCE TUBE IS SET UP ###################################&lt;br /&gt;
    &lt;br /&gt;
    #### Method text_received_cb, which is called once a ChatTube instance is set up and that class&lt;br /&gt;
    # receives any text over the tube. You will pass this method as an argument to ChatTube.__init__()&lt;br /&gt;
    # (see _new_tube_cb() method above). &lt;br /&gt;
    def text_received_cb(self, text):&lt;br /&gt;
        self._chat += &amp;quot;\nSomeone Else&amp;quot; + &amp;quot;:: &amp;quot; + text + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
    #### Method: _speak_cb, which is called whenever user decides to send whatever chat text&lt;br /&gt;
    # he has written in self._chat_input to the public room. &lt;br /&gt;
    def _speak_cb(self, widget, entry):&lt;br /&gt;
        nick = profile.get_nick_name()&lt;br /&gt;
        nick = nick.upper()&lt;br /&gt;
        self._chat += &amp;quot;\n&amp;quot; + nick + &amp;quot;:: &amp;quot; + entry.get_text() + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        if self.chattube is not None:&lt;br /&gt;
            self.chattube.SendText(entry.get_text())&lt;br /&gt;
        entry.set_text(&amp;quot;&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
######################## CHATTUBE CLASS - WRAPS LOGIC FOR TUBE COMMUNICATION #####################&lt;br /&gt;
class ChatTube(ExportedGObject):&lt;br /&gt;
    #### Method __init__, which sets up a new ChatTube instance given the underlying tube connection &lt;br /&gt;
    # object (tube). &lt;br /&gt;
    def __init__(self, tube, is_initiator, text_received_cb):&lt;br /&gt;
        super(ChatTube, self).__init__(tube, PATH)&lt;br /&gt;
        self.tube = tube&lt;br /&gt;
        self.is_initiator = is_initiator #is this the sharing or joining activity?&lt;br /&gt;
        self.text_received_cb = text_received_cb #callback in main activity when text is received&lt;br /&gt;
        self.text = &#039;&#039;&lt;br /&gt;
 &lt;br /&gt;
        #The sendtext_cb method is called once someone else has sent text to this end of the tube. &lt;br /&gt;
        self.tube.add_signal_receiver(self.sendtext_cb, &#039;SendText&#039;, IFACE, path=PATH, sender_keyword=&#039;sender&#039;)&lt;br /&gt;
&lt;br /&gt;
    #### Method sendtext_cb, which is called once this tube receives text that is&lt;br /&gt;
    # sent by someone else&lt;br /&gt;
    def sendtext_cb(self, text, sender=None):&lt;br /&gt;
        if sender == self.tube.get_unique_name():&lt;br /&gt;
            return&lt;br /&gt;
        self.text = text&lt;br /&gt;
        self.text_received_cb(text)&lt;br /&gt;
&lt;br /&gt;
    #### Method SendText, which uses a DBus signal to actually send text to others over the tube. &lt;br /&gt;
    @signal(dbus_interface=IFACE, signature=&#039;s&#039;)&lt;br /&gt;
    def SendText(self, text):&lt;br /&gt;
        self.text = text&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How is data shared between activities through a D-Bus tube? ===&lt;br /&gt;
&lt;br /&gt;
Communication in Sugar is achieved through several layers of technologies, including Sugar&#039;s own Presence Service, the D-Bus service for process communication, and various implementations of the telepathy system used for real time conversations. In order to get activity communication working between activities, your goal is to ultimately create a D-Bus tube that will handle text messaging between activities (there are also stream tubes which are discussed elsewhere). &lt;br /&gt;
&lt;br /&gt;
The figure below shows a simplified conceptualization of what happens after you set up a D-Bus tube correctly. &lt;br /&gt;
&lt;br /&gt;
[[Image:tubes-all-setup.jpg | How things work once the tubes are set up. ]]&lt;br /&gt;
&lt;br /&gt;
The activity code for each XO should handle sending text to the tube and also receiving text through a callback method that is supplied to the tube. The Activity developer will also need to define the Tube Object itself, which should be designed to send and receive text appropriately based on the type of sharing that is desired. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s consider a simple, but concrete example of how a D-Bus tube is used (assuming it has already been setup correctly). The code below is from a chat application where users type text and send it to others who are sharing the activity. Only the minimal code needed to send and receive text is included for clarity. If you want to know how the tube is set up from start to finish, then read the section on [[#How do I setup a D-Bus Tube? | setting up a D-Bus tube]]. The comments should make clear what code does what in this example. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
...&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Sample&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
############## MAIN ACTIVITY CLASS ####################&lt;br /&gt;
class SampleActivity(activity.Activity):&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for text received from tube -- update activity state&lt;br /&gt;
    def text_received_cb(self, text):&lt;br /&gt;
        self._chat += &amp;quot;\nSomeone Else Said&amp;quot; + &amp;quot;:: &amp;quot; + text + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
    #### Method: _speak_cb, which is called whenever user decides to send whatever chat text&lt;br /&gt;
    # he has written in self._chat_input to the public. &lt;br /&gt;
    def _speak_cb(self, widget, entry):&lt;br /&gt;
        nick = profile.get_nick_name()&lt;br /&gt;
        nick = nick.upper()&lt;br /&gt;
        self._chat += &amp;quot;\n&amp;quot; + nick + &amp;quot;:: &amp;quot; + entry.get_text() + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        if self.chattube is not None:&lt;br /&gt;
            self.chattube.SendText(entry.get_text())&lt;br /&gt;
        entry.set_text(&amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
     ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###################### TUBE CLASS ######################&lt;br /&gt;
class ChatTube(ExportedGObject):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, tube, is_initiator, text_received_cb):&lt;br /&gt;
        super(ChatTube, self).__init__(tube, PATH)&lt;br /&gt;
        ...&lt;br /&gt;
        self.text_received_cb = text_received_cb&lt;br /&gt;
        self.text = &#039;&#039;&lt;br /&gt;
        self.tube.add_signal_receiver(self.sendtext_cb, &#039;SendText&#039;, IFACE, path=PATH, sender_keyword=&#039;sender&#039;)&lt;br /&gt;
        ...        &lt;br /&gt;
&lt;br /&gt;
    # This method is called when the XO receives some text through the D-bus tube&lt;br /&gt;
    def sendtext_cb(self, text, sender=None):&lt;br /&gt;
        # Ignore any text that this XO sent to itself. &lt;br /&gt;
        if sender == self.tube.get_unique_name():&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        self.text = text&lt;br /&gt;
        self.text_received_cb(text)&lt;br /&gt;
&lt;br /&gt;
    # This method is used to actually send text to all other XO&#039;s who are sharing. &lt;br /&gt;
    @signal(dbus_interface=IFACE, signature=&#039;s&#039;)&lt;br /&gt;
    def SendText(self, text):&lt;br /&gt;
        self.text = text&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I set up a simple stream tube that can send data one-way between two instances of an activity? ===&lt;br /&gt;
&lt;br /&gt;
As with D-Bus tubes, there are a series of coordinated steps that must happen between a sharing and joining activity before data can be shared over a tube. The example we discuss here specifically concerns sharing data one way - from a &amp;quot;sharing&amp;quot; activity to a &amp;quot;joining&amp;quot; activity. Stream tubes are especially useful if you want to share non-text data or if you want to take advantage of communication protocols like TCP/IP and UDP to transfer data between instances of an activity running on separate XOs. &lt;br /&gt;
&lt;br /&gt;
==== Step 1: Understand how to architect your stream tubes to achieve the goals of your activity. ====&lt;br /&gt;
&lt;br /&gt;
Before trying to create any working code, you should understand exactly what you want your stream tube to do - when and how will sharing of data occur over the tube during the life of your activity? The example we discuss below is predicated on a specific model of communication. &lt;br /&gt;
&lt;br /&gt;
In particular, the code we use in this section is adapted from the [http://wiki.laptop.org/go/Read Read activity], but it has been incorporated in to an example activity called &amp;quot;Annotate&amp;quot;. In activities like Read or Annotate, the goal is to set up a one way communication between the &amp;quot;sharing&amp;quot; activity and the &amp;quot;joining&amp;quot; activity so that the joining activity can download and take part in whatever document is being edited by the sharer. The document to be sent over the stream tube is shared immediately upon startup and requires no user action to initiate it. &lt;br /&gt;
&lt;br /&gt;
Given such a paradigm for using our stream tube, we can draw a rough picture of what our communication architecture will look like in this case:&lt;br /&gt;
&lt;br /&gt;
[[Image:stream-tube-in-annotate-activity.jpg | How annotate&#039;s stream tube will be used]]&lt;br /&gt;
&lt;br /&gt;
Notice that the stream tube exists on top of the [http://en.wikipedia.org/wiki/Internet_socket socket architecture] used by Unix systems to facilitate internet communication.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Define or identify the classes you will use to serve data and receive data through your stream tube. ====&lt;br /&gt;
&lt;br /&gt;
In our case, we want the sharing activity to behave like a one-time server while the joining activity is a client that downloads any document it needs on startup. To accomplish serving, we create the following two classes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
from sugar import network&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Annotate&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Annotate&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
##########################################################################&lt;br /&gt;
REQUESTHANDLER AND HTTPSERVER WORK TOGETHER TO SERVE DATA OVER STREAM TUBE &lt;br /&gt;
##########################################################################&lt;br /&gt;
class AnnotateHTTPRequestHandler(network.ChunkedGlibHTTPRequestHandler):&lt;br /&gt;
    #### Method translate_path, which, for our simple activity, just returns the same&lt;br /&gt;
    # filepath for a document that will be shared. &lt;br /&gt;
    def translate_path(self, path):&lt;br /&gt;
        return self.server._filepath&lt;br /&gt;
&lt;br /&gt;
class AnnotateHTTPServer(network.GlibTCPServer):&lt;br /&gt;
    def __init__(self, server_address, filepath):&lt;br /&gt;
        self._filepath = filepath&lt;br /&gt;
        network.GlibTCPServer.__init__(self, server_address, AnnotateHTTPRequestHandler)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example code above, most of the work is done by the superclasses defined in sugar.network, namely ChunkedGlibHTTPRequestHandler and GLibTCPServer. However, if you want some custom behavior in how data is served, then it is a good idea to subclass those or other Server and RequestHandler classes.&lt;br /&gt;
&lt;br /&gt;
On the client side, there must be a class that handles receiving data from a server. In Annotate, this is accomplished with the standard sugar.network.GlibURLDownloader class. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Coordinate sharing and joining in your activity using stream tubes and the client/server classes defined above. ====&lt;br /&gt;
&lt;br /&gt;
On the sharing instance, the following must happen:&lt;br /&gt;
* Once the activity has been shared by the user, the sharing instance should detect the &amp;quot;shared&amp;quot; signal and setup for sharing. &lt;br /&gt;
* To setup for sharing, the sharing instance needs to instantiate a new server (in Annotate, this is an AnnotateHTTPServer) that will deliver content to any client activities. &lt;br /&gt;
* The newly instantiated server should listen on a given port and that port should be connected to a new stream tube. The stream tube can be created by calling the iface.OfferStreamTube() method. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For the joining activity, it must coordinate several steps once the &amp;quot;joined&amp;quot; signal has been detected:&lt;br /&gt;
* The joining activity needs to wait for a valid stream tube that is provided under the service ANNOTATE_STREAM_SERVICE. &lt;br /&gt;
* When such a tube is available, the joining activity should specify the path where the downloaded data will be saved. &lt;br /&gt;
* Create a listening client on the address and port associated with the stream tube that has been found. In our case, we create a sugar.network.GlibURLDownloader instance that will manage downloading from the stream tube. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ANNOTATE_STREAM_SERVICE = &#039;annotate-activity-http&#039;&lt;br /&gt;
&lt;br /&gt;
class AnnotateActivity(activity.Activity):&lt;br /&gt;
&lt;br /&gt;
    #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        #Joining activity needs to know which stream tubes are available for downloading &lt;br /&gt;
        #and if it still needs to download document. &lt;br /&gt;
        self.unused_download_tubes = set() &lt;br /&gt;
        self._want_document = True&lt;br /&gt;
&lt;br /&gt;
        #Set the port number on which sharing activity will serve data&lt;br /&gt;
        h = hash(self._activity_id)&lt;br /&gt;
        self.port = 1024 + (h % 64511)&lt;br /&gt;
&lt;br /&gt;
        self.connect(&#039;shared&#039;, self._shared_cb)&lt;br /&gt;
        if self._shared_activity:&lt;br /&gt;
            # We&#039;re joining&lt;br /&gt;
            if self.get_shared():&lt;br /&gt;
                # Already joined for some reason, just get the document&lt;br /&gt;
                self._joined_cb(self)&lt;br /&gt;
            else:&lt;br /&gt;
                # Wait for a successful join before trying to get the document&lt;br /&gt;
                self.connect(&amp;quot;joined&amp;quot;, self._joined_cb)&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    ######################## SETUP SERVER ON SHARING ACTIVITY ####################################&lt;br /&gt;
&lt;br /&gt;
    #### Method _shared_cb, which is called when this activity is successfully &lt;br /&gt;
    # shared on a mesh channel. &lt;br /&gt;
    def _shared_cb(self, activity):&lt;br /&gt;
        # We initiated this activity and have now shared it, so by&lt;br /&gt;
        # definition we have the file.&lt;br /&gt;
        _logger.debug(&#039;SYSTEM:&#039;, &#039;_shared_cb(): Activity being shared&#039;)&lt;br /&gt;
        self._want_document = False;&lt;br /&gt;
        self._share_document()&lt;br /&gt;
&lt;br /&gt;
    #### Method: _share_document, which sets up an http server that will serve a specific file to&lt;br /&gt;
    # any joining activities. &lt;br /&gt;
    def _share_document(self):&lt;br /&gt;
        _logger.debug(&#039;SYSTEM&#039;, &#039;_share_document() -- sharing file &#039; + str(object_path))&lt;br /&gt;
        object_path = os.path.join(self.get_activity_root(), &#039;data&#039;, &#039;sample-shared-file.doc&#039;)&lt;br /&gt;
&lt;br /&gt;
        _logger.debug(&#039;SYSTEM&#039;, &#039;_share_document() -- Starting HTTP server on port &#039;+str(self.port))&lt;br /&gt;
        self._fileserver = AnnotateHTTPServer((&amp;quot;&amp;quot;, self.port), object_path)&lt;br /&gt;
&lt;br /&gt;
        # Make a tube for the server&lt;br /&gt;
        chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        iface = chan[telepathy.CHANNEL_TYPE_TUBES]&lt;br /&gt;
        self._fileserver_tube_id = iface.OfferStreamTube(ANNOTATE_STREAM_SERVICE,&lt;br /&gt;
                {},&lt;br /&gt;
                telepathy.SOCKET_ADDRESS_TYPE_IPV4,&lt;br /&gt;
                (&#039;127.0.0.1&#039;, dbus.UInt16(self.port)),&lt;br /&gt;
                telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    ################### COORDINATE TUBE CREATION AND DOWNLOAD IN JOINING ACTIVITY  ##################&lt;br /&gt;
&lt;br /&gt;
    #### Method _joined_cb, which is called when this activity joins another&lt;br /&gt;
    # instance&lt;br /&gt;
    def _joined_cb(self, also_self):&lt;br /&gt;
        self.watch_for_tubes()&lt;br /&gt;
        self._want_document = True;&lt;br /&gt;
        gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
    #### Method watch_for_tubes, which sets up a callback to _new_tube_cb once&lt;br /&gt;
    # a stream tube is made available. &lt;br /&gt;
    def watch_for_tubes(self):&lt;br /&gt;
        tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
&lt;br /&gt;
        tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&#039;NewTube&#039;,&lt;br /&gt;
            self._new_tube_cb)&lt;br /&gt;
        tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(&lt;br /&gt;
            reply_handler=self._list_tubes_reply_cb,&lt;br /&gt;
            error_handler=self._list_tubes_error_cb)&lt;br /&gt;
&lt;br /&gt;
    def _list_tubes_reply_cb(self, tubes):&lt;br /&gt;
        for tube_info in tubes:&lt;br /&gt;
            self._new_tube_cb(*tube_info)&lt;br /&gt;
&lt;br /&gt;
    def _list_tubes_error_cb(self, e):&lt;br /&gt;
&lt;br /&gt;
    #### Method _new_tube_cb, which is called once a stream tube is available &lt;br /&gt;
    # to download from&lt;br /&gt;
    def _new_tube_cb(self, tube_id, initiator, tube_type, service, params,&lt;br /&gt;
                     state):&lt;br /&gt;
        # If the available tube is the stream tube we set up in sharing activity, then &lt;br /&gt;
        # let&#039;s download from it. &lt;br /&gt;
        if service == ANNOTATE_STREAM_SERVICE:            &lt;br /&gt;
            # Add the newly found stream tube to the available tubes we can download from&lt;br /&gt;
            self.unused_download_tubes.add(tube_id)&lt;br /&gt;
            &lt;br /&gt;
            # if no download is in progress, let&#039;s fetch the document&lt;br /&gt;
            if self._want_document:&lt;br /&gt;
                gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _get_document, which sets this activity instance up to start downloading&lt;br /&gt;
    # the document from the sharing activity. It is called once a stream tube has been &lt;br /&gt;
    # obtained and saved in self.unused_download_tubes. &lt;br /&gt;
    def _get_document(self):&lt;br /&gt;
        if not self._want_document:&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
        # Assign a file path to download to -- where downloaded doc will be saved. &lt;br /&gt;
        path = os.path.join(self.get_activity_root(), &#039;instance&#039;,&lt;br /&gt;
                                &#039;%i&#039; % time.time())&lt;br /&gt;
&lt;br /&gt;
        # Pick an available tube we can try to download the document from&lt;br /&gt;
        try:&lt;br /&gt;
            tube_id = self.unused_download_tubes.pop()&lt;br /&gt;
        except (ValueError, KeyError), e:&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
        # Avoid trying to download the document multiple times at once&lt;br /&gt;
        self._want_document = False&lt;br /&gt;
        gobject.idle_add(self._download_document, tube_id, path)&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_result_cb, which is called once downloading is complete. &lt;br /&gt;
    def _download_result_cb(self, getter, tempfile, suggested_name, tube_id):&lt;br /&gt;
        del self.unused_download_tubes&lt;br /&gt;
        self.save()&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_progress_cb, which is called as the file is being downloaded. &lt;br /&gt;
    def _download_progress_cb(self, getter, bytes_downloaded, tube_id):&lt;br /&gt;
        # FIXME: signal the expected size somehow, so we can draw a progress&lt;br /&gt;
        # bar&lt;br /&gt;
        return True;&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_error_cb, which is called if there was an error downloading. &lt;br /&gt;
    def _download_error_cb(self, getter, err, tube_id):&lt;br /&gt;
        _logger.debug(&amp;quot;Error getting document from tube %u: %s&amp;quot;,&lt;br /&gt;
                      tube_id, err)&lt;br /&gt;
        self._want_document = True&lt;br /&gt;
        gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def _download_document(self, tube_id, path):&lt;br /&gt;
        # FIXME: should ideally have the CM listen on a Unix socket&lt;br /&gt;
        # instead of IPv4 (might be more compatible with Rainbow)&lt;br /&gt;
        chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        iface = chan[telepathy.CHANNEL_TYPE_TUBES]&lt;br /&gt;
        addr = iface.AcceptStreamTube(tube_id,&lt;br /&gt;
                telepathy.SOCKET_ADDRESS_TYPE_IPV4,&lt;br /&gt;
                telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0,&lt;br /&gt;
                utf8_strings=True)&lt;br /&gt;
        _logger.debug(&#039;Accepted stream tube: listening address is %r&#039;, addr)&lt;br /&gt;
        # SOCKET_ADDRESS_TYPE_IPV4 is defined to have addresses of type &#039;(sq)&#039;&lt;br /&gt;
        assert isinstance(addr, dbus.Struct)&lt;br /&gt;
        assert len(addr) == 2&lt;br /&gt;
        assert isinstance(addr[0], str)&lt;br /&gt;
        assert isinstance(addr[1], (int, long))&lt;br /&gt;
        assert addr[1] &amp;gt; 0 and addr[1] &amp;lt; 65536&lt;br /&gt;
        port = int(addr[1])&lt;br /&gt;
&lt;br /&gt;
        getter = network.GlibURLDownloader(&amp;quot;http://%s:%d/document&amp;quot;&lt;br /&gt;
                                           % (addr[0], port))&lt;br /&gt;
        getter.connect(&amp;quot;finished&amp;quot;, self._download_result_cb, tube_id)&lt;br /&gt;
        getter.connect(&amp;quot;progress&amp;quot;, self._download_progress_cb, tube_id)&lt;br /&gt;
        getter.connect(&amp;quot;error&amp;quot;, self._download_error_cb, tube_id)&lt;br /&gt;
        getter.start(path)&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== When downloading data from a stream tube, how can I show the download progress? ===&lt;br /&gt;
&lt;br /&gt;
=== How do I control the file and path where data is saved when sharing through a stream tube? ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where do I get more information regarding sugar activity sharing and the technologies that support it? ===&lt;br /&gt;
* A [[Shared_Sugar_Activities | brief tutorial]] on activity sharing for the OLPC laptop.&lt;br /&gt;
&lt;br /&gt;
=== I can&#039;t get tubes to work.  Are there unresolved issues? ===&lt;br /&gt;
&lt;br /&gt;
==== Which threading patterns are most appropriate for use on the sugar platform? ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
There are issues regarding the best way to create activities that support media sharing. In particular, the structure of DBus tubes makes it easy to customize the communication process between activity instances running on multiple XOs. However, stream tubes seem to be tied closely to a client server architecture - the servers and handlers provided in sugar.network package are some form of client/server arrangement. &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
Setting up more advanced stream tubes functionality that would allow sharing of non-text data (eg. binary files, images, etc.) in a continuous manner in the way dbus tubes enable text communication is complicated.  To allow activities to communicate constantly over a stream tube, one needs to set up both a client and server side on each activity and coordinate communication between them. Furthermore, most activities would require threading as well, which means resolving best practices for threading. Ultimately, it seems that the existing servers/handlers to support stream tubes may not be adequate to support easy and relatively painless sharing of data between activities on a continuous basis. The code that is on the Almanac right now guides a user through the basic process of setting up a stream tube and allowing communication one time between the server and client. This is useful for sharing/joining activities that require sending binary files (which is why Record and Read use this). However, it does not extend easily to peer to peer communication. &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
One solution to sending non-text data between activities is to just use the DBus tube and take advantage of its flexibility. It&#039;s not clear how well these tubes will support binary data, however and whether they work well with larger media files. It would be helpful to get insight from sugar developers regarding how such communication should work and what facilities sugar needs to add to support this (perhaps more functionality for stream tubes in sugar.network). Given that peer to peer networking through mesh is probably one of the standout features of XO laptops, I think having better functionality (and better documentation) for developers to build rich communicative applications is a high priority. &lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18067</id>
		<title>Development Team/Almanac</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18067"/>
		<updated>2008-11-24T22:47:45Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* My Python activity wants to use threads; how do I do that? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
{{Sugar Almanac TOC}}&lt;br /&gt;
== How do I get additional help beyond this almanac? ==&lt;br /&gt;
* Looking to get started with the basics of Sugar development? Check out Christoph Derndorfer&#039;s [http://www.olpcaustria.org/mediawiki/index.php/Activity_handbook Activity Handbook]. &lt;br /&gt;
* See also [[Sugar Code Snippets]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, on to the actual almanac ...&lt;br /&gt;
&lt;br /&gt;
== Where can I see API changes? ==&lt;br /&gt;
API changes between OLPC releases can be seen here: [[API changes]]&lt;br /&gt;
&lt;br /&gt;
== Getting Started ==&lt;br /&gt;
=== How do I structure my files so that they are a valid sugar activity?  === &lt;br /&gt;
Information on activity bundle structure can be found here: [[Activity bundles]]&lt;br /&gt;
&lt;br /&gt;
=== How do I make an icon for my activity? ===&lt;br /&gt;
Information on what you need to do can be found here: [[Making Sugar Icons]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar ==&lt;br /&gt;
* [[sugar.env]]&lt;br /&gt;
* [[sugar.profile]]&lt;br /&gt;
* [[sugar.mime]]&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.activity ==&lt;br /&gt;
* [[sugar.activity.activity]]&lt;br /&gt;
* [[sugar.activity.activityfactory]]&lt;br /&gt;
* [[sugar.activity.registry]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.datastore ==&lt;br /&gt;
* [[sugar.datastore.datastore]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.graphics ==&lt;br /&gt;
* [[sugar.graphics.alert]]&lt;br /&gt;
* [[sugar.graphics.icon]]&lt;br /&gt;
* [[sugar.graphics.notebook]]&lt;br /&gt;
* [[sugar.graphics.toolbutton]]&lt;br /&gt;
* [[sugar.graphics.toolbox]]&lt;br /&gt;
* [[sugar.graphics.style]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.presence ==&lt;br /&gt;
* [[sugar.presence]]&lt;br /&gt;
* [[sugar.presence.activity]]&lt;br /&gt;
* [[sugar.presence.buddy]]&lt;br /&gt;
* [[sugar.presence.presenceservice]]&lt;br /&gt;
&lt;br /&gt;
== Clipboard ==&lt;br /&gt;
* Notes on using [[GTK&#039;s Clipboard Module in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
* Notes on using [[Python Standard Logging in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Internationalization ==&lt;br /&gt;
*[[Internationalization in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Text and Graphics for Sugar Activities ==&lt;br /&gt;
* [[Pango]]&lt;br /&gt;
&lt;br /&gt;
== Audio &amp;amp; Video ==&lt;br /&gt;
* [[Sugar Almanac GStreamer]]&lt;br /&gt;
&lt;br /&gt;
== Mouse ==&lt;br /&gt;
=== How do I change the mouse cursor in my activity to the wait cursor? ===&lt;br /&gt;
In your activity subclass:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( gtk.gdk.Cursor(gtk.gdk.WATCH) )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and to switch it back to the default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( None );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I track the position of the mouse? ===&lt;br /&gt;
There are many different reasons you might want to track the position of the mouse in your activity, ranging from the entertaining ([[http://en.wikipedia.org/wiki/Xeyes]]) to the functional (hiding certain windows when the mouse hasn&#039;t moved for a couple of seconds and making those ui elements re-appear when the mouse has moved again).  Here is one way you can implement this functionality:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		...&lt;br /&gt;
		self.hideWidgetsTime = time.time()&lt;br /&gt;
		self.mx = -1&lt;br /&gt;
		self.my = -1&lt;br /&gt;
		self.HIDE_WIDGET_TIMEOUT_ID = gobject.timeout_add( 500, self.mouseMightHaveMovedCb )&lt;br /&gt;
&lt;br /&gt;
	def _mouseMightHaveMovedCb( self ):&lt;br /&gt;
		x, y = self.get_pointer()&lt;br /&gt;
		passedTime = 0&lt;br /&gt;
&lt;br /&gt;
		if (x != self.mx or y != self.my):&lt;br /&gt;
			self.hideWidgetsTime = time.time()&lt;br /&gt;
			if (self.hiddenWidgets):&lt;br /&gt;
				self.showWidgets()&lt;br /&gt;
				self.hiddenWidgets = False&lt;br /&gt;
		else:&lt;br /&gt;
			passedTime = time.time() - self.hideWidgetsTime&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		if (passedTime &amp;gt;= 3):&lt;br /&gt;
			if (not self.hiddenWidgets):&lt;br /&gt;
				self.hideWidgets()&lt;br /&gt;
				self.hiddenWidgets = True&lt;br /&gt;
&lt;br /&gt;
		self.mx = x&lt;br /&gt;
		self.my = y&lt;br /&gt;
		return True&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous==&lt;br /&gt;
&lt;br /&gt;
The tasks below are random useful techniques that have come up as I write code and documentation for this reference. They have yet to be categorized, but will be as a sufficient set of related entries are written.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I know when my activity is &amp;quot;active&amp;quot; or not? ===&lt;br /&gt;
&lt;br /&gt;
You can set an event using the VISIBILITY_NOTIFY_MASK constant in order to know when your activity changes visibility. Then in the callback for this event, you simply compare the event&#039;s state to gtk-defined variables for activity visibility. See the [http://www.pygtk.org/docs/pygtk/gdk-constants.html#gdk-visibility-state-constants GDK Visibility State Constants] section of gtk.gdk.Constants for more information. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        # Notify when the visibility state changes by calling self.__visibility_notify_cb&lt;br /&gt;
        # (PUT THIS IN YOUR ACTIVITY CODE - EG. THE __init__() METHOD)&lt;br /&gt;
        self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK)&lt;br /&gt;
        self.connect(&amp;quot;visibility-notify-event&amp;quot;, self.__visibility_notify_cb)&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for when the activity&#039;s visibility changes&lt;br /&gt;
    def __visibility_notify_cb(self, window, event):&lt;br /&gt;
        if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:&lt;br /&gt;
            print &amp;quot;I am not visible&amp;quot;&lt;br /&gt;
        elif event.state in [gtk.gdk.VISIBILITY_UNOBSCURED, gtk.gdk.VISIBILITY_PARTIAL]:&lt;br /&gt;
            print &amp;quot;I am visible&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I get the amount of free space available on disk under the /home directory tree? ===&lt;br /&gt;
The following function uses the [http://docs.python.org/lib/module-statvfs.html statvfs] module. The following code demonstrates how to get the total amount of free space under /home. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Method: getFreespaceKb, returns the available freespace in kilobytes. &lt;br /&gt;
    def getFreespaceKb(self):&lt;br /&gt;
        stat = os.statvfs(&amp;quot;/home&amp;quot;)&lt;br /&gt;
        freebytes  = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]&lt;br /&gt;
        freekb = freebytes / 1024&lt;br /&gt;
        return freekb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, however, that assuming anything about &amp;quot;/home&amp;quot; is a bad idea, better use os.environ[&#039;HOME&#039;] instead.  Rainbow will put your actual files elsewhere,&lt;br /&gt;
some on ramdisks, some on flash.  Be clear about which filesystem&#039;s free space you actually care about.&lt;br /&gt;
&lt;br /&gt;
=== How do I know whether my activity is running on a physical XO? ===&lt;br /&gt;
Sugar runs on ordinary computers as well as on XO&#039;s.  While your activity is typically going to be run on a real XO, some people will indeed run it elsewhere.  Normally you shouldn&#039;t write your activity to care whether it&#039;s on an XO or not.  If for some odd reason, you need to care, the easiest way to tell if you are on a physical XO is to check whether /sys/power/olpc-pm, an essential power management file for the XO, exists. &amp;lt;ref&amp;gt;[http://lists.laptop.org/pipermail/devel/2008-June/015923.html reliably detecting if running on an XO]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;OLPC [[Power Management Interface]]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
      #Print out a boolean value that tells us whether we are on an XO or not. &lt;br /&gt;
      print os.path.exists(&#039;/sys/power/olpc-pm&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I know the current language setting on my XO? ===&lt;br /&gt;
The system variable &#039;LANG&#039; tells you which language is currently active on the XO. The following code shows how to look at the value of this variable. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
       _logger.debug(os.environ[&#039;LANG&#039;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I repeatedly call a specific method after N number of seconds? ===&lt;br /&gt;
The gobject.timeout_add() function allows you to invoke a callback method after a certain amount of time. If you want to repeatedly call a method, simply keep invoking the gobject.timeout_add function in your callback itself. The code below is a simple example, where the callback function is named repeatedly_call. Note that the timing of the callbacks are approximate. To get the process going, you should make an initial call to repeatedly_call() somewhere in your code. &lt;br /&gt;
&lt;br /&gt;
You can see a more substantive example of this pattern in use when we [[Pango#How_do_I_dynamically_set_the_text_in_a_pango_layout.3F | regularly update the time displayed on a pango layout object]]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	#This method calls itself ROUGHLY every 1 second &lt;br /&gt;
	def repeatedly_call(self):&lt;br /&gt;
		now = datetime.datetime.now()&lt;br /&gt;
		gobject.timeout_add(self.repeat_period_msec, self.repeatedly_update_time)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I update the current build version of code that is running on my XO? ===&lt;br /&gt;
&lt;br /&gt;
There are several pages that give you instructions on how to install/update your current build. &lt;br /&gt;
&lt;br /&gt;
* If you already have a working build installed and an internet connection, first try [[olpc-update]]. &lt;br /&gt;
* If that doesn&#039;t work, you can look at instructions for an [[Activated upgrade]] that can be done via USB] boot. &lt;br /&gt;
&lt;br /&gt;
As the instructions on the pages linked above note, make sure to install your activities separately after you have upgraded to a specific base build.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== I am developing on an XO laptop, but my keyboard and language settings are not ideal. How can I change them? ===&lt;br /&gt;
&lt;br /&gt;
Internationalized laptops will often have settings that might slow you down while developing. To change around the language settings so you can better understand environment messages, use the [[Sugar Control Panel]]&lt;br /&gt;
&lt;br /&gt;
Keyboard settings on internationalized laptops&amp;lt;ref&amp;gt;[[Keyboard layouts#OLPC keyboard layouts]]&amp;lt;/ref&amp;gt; can also be suboptimal, especially as characters like &amp;quot;-&amp;quot; and &amp;quot;/&amp;quot; are in unfamiliar positions. You can use the &amp;lt;tt&amp;gt;setxkbmap&amp;lt;/tt&amp;gt; command in the [[Terminal Activity]] to reset the type of keyboard input used and then attach a standard U.S. keyboard that will allow you to type normally. The command below sets the keyboard to the US mapping (it will reset to the default internationalized mapping upon restart). &lt;br /&gt;
&lt;br /&gt;
 setxkbmap us&lt;br /&gt;
&lt;br /&gt;
=== My Python activity wants to use threads; how do I do that? ===&lt;br /&gt;
&lt;br /&gt;
A question that has been answered with limited success is which threading patterns are most appropriate for use in Sugar.  The following pattern of code to work fine in basic instances:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.sample_thread = Thread(target=self.announce_thread, args=())&lt;br /&gt;
        self.sample_thread.setDaemon(0)&lt;br /&gt;
        self.sample_thread.start()&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    def announce_thread(self):&lt;br /&gt;
        while (self.Running):&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
            print &amp;quot;thread running&amp;quot;&lt;br /&gt;
            self._update_chat_text(&amp;quot;Thread&amp;quot;, &amp;quot;In here&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the basic series of steps that most online documentation on python suggests to use when trying to work with threads in python. The problem is that it is unclear how this pattern relates to code that worked in the SimCity activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gobject&lt;br /&gt;
gobject.threads_init()&lt;br /&gt;
#import dbus.mainloop.glib&lt;br /&gt;
#dbus.mainloop.glib.threads_init()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It should be noted that in the SimCity activity the pygame sound player would not produce sound reliably unless this setup was done.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should the two patterns always be used in tandem? It seems that the latter code is mainly to initiate gobject and other libraries to work with threading, but it is unclear what restrictions there are with using threading with these libraries. Does one take precedence over the other? It is not clear if there is any problem with using the standard python threading code on the sugar technology stack.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fact, experiments with threading on sugar leads to several different problems. For one thing, thread termination was tricky - using the can_close() method for sugar activities to terminate an activity only killed threads in some circumstances. It did not properly handle terminating threads in the case of CTRL-C or terminal interrupts. You can try to catch signals (SIGINT, SIGTERM or SIGHUP), but you will still be running in to errors in terminating child threads using these as well. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another set of errors with threading comes up when trying to combine with stream tubes. The bottom line is that it is unclear what the scope of threading in a Sugar activity should be - should it simply work if you do the standard python threading pattern, is the use of the glib.threads_init and gobject.threads_init calls necessary, are there other interactions with threads and dbus that need to be accounted for? With more clarity from sugar developers on how the platform envisions threading to work in an activity, we can be more comfortable writing entries in the Almanac to help developers write error-free code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I customize the title that is displayed for each instance of my activity? ===&lt;br /&gt;
&lt;br /&gt;
By default, activity titles are just the generic activity names that you specify in your activity.info file. In some applications, you may want the activity title to be more dynamic. &lt;br /&gt;
&lt;br /&gt;
For example, it makes sense to set the title for different browser sessions to the active web page being visited. That way, when you look back in the journal at the different browser sessions you have run in the previous few days, you can identify unique sessions based on the website you happened to be visiting at the time. &lt;br /&gt;
&lt;br /&gt;
The code below shows how you can set the metadata for your activity to reflect a dynamic title based on whatever session criteria you feel is important. This example is adapted from the Browse activity, which sets activity instance titles based on the title of the current web page being visited. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if self.metadata[&#039;mime_type&#039;] == &#039;text/plain&#039;:&lt;br /&gt;
            if not self._jobject.metadata[&#039;title_set_by_user&#039;] == &#039;1&#039;:&lt;br /&gt;
                if self._browser.props.title:&lt;br /&gt;
                    # Set the title of this activity to be the current &lt;br /&gt;
                    # title of the page being visited by the browser. &lt;br /&gt;
                    self.metadata[&#039;title&#039;] = self._browser.props.title&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What packages are available on sugar to support game development? ===&lt;br /&gt;
&lt;br /&gt;
If your activity will require tools that are typically needed to develop robust and clean video games, then you should utilize the [http://www.pygame.org/ pygame package]. It can be readily imported into any activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import pygame&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I detect when one of the game buttons on the laptop have been pressed? ===&lt;br /&gt;
&lt;br /&gt;
The laptop game buttons (the circle, square, x, and check buttons next to the LCD) are encoded as page up, home, page down and end respectively. So, you can detect their press by listening for these specific events. For example, the code below listens for button presses and then just writes to an output widget which button was pressed. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Page_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCircle Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Page_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nX Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Home&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nSquare Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_End&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCheck Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I detect if one of the joystick buttons has been pressed? ===&lt;br /&gt;
&lt;br /&gt;
This is the same process as detecting game buttons, except with different names for the keys. Again, you listen for &amp;quot;key-press-event&amp;quot; signals and then in your callback you check to see if the pressed button was one of the joystick keys. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nUp Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nDown Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Left&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nLeft Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Right&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nRight Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Documentation]]&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18066</id>
		<title>Development Team/Almanac</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18066"/>
		<updated>2008-11-24T22:47:16Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* My Python activity wants to use threads; how do I do that? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
{{Sugar Almanac TOC}}&lt;br /&gt;
== How do I get additional help beyond this almanac? ==&lt;br /&gt;
* Looking to get started with the basics of Sugar development? Check out Christoph Derndorfer&#039;s [http://www.olpcaustria.org/mediawiki/index.php/Activity_handbook Activity Handbook]. &lt;br /&gt;
* See also [[Sugar Code Snippets]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, on to the actual almanac ...&lt;br /&gt;
&lt;br /&gt;
== Where can I see API changes? ==&lt;br /&gt;
API changes between OLPC releases can be seen here: [[API changes]]&lt;br /&gt;
&lt;br /&gt;
== Getting Started ==&lt;br /&gt;
=== How do I structure my files so that they are a valid sugar activity?  === &lt;br /&gt;
Information on activity bundle structure can be found here: [[Activity bundles]]&lt;br /&gt;
&lt;br /&gt;
=== How do I make an icon for my activity? ===&lt;br /&gt;
Information on what you need to do can be found here: [[Making Sugar Icons]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar ==&lt;br /&gt;
* [[sugar.env]]&lt;br /&gt;
* [[sugar.profile]]&lt;br /&gt;
* [[sugar.mime]]&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.activity ==&lt;br /&gt;
* [[sugar.activity.activity]]&lt;br /&gt;
* [[sugar.activity.activityfactory]]&lt;br /&gt;
* [[sugar.activity.registry]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.datastore ==&lt;br /&gt;
* [[sugar.datastore.datastore]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.graphics ==&lt;br /&gt;
* [[sugar.graphics.alert]]&lt;br /&gt;
* [[sugar.graphics.icon]]&lt;br /&gt;
* [[sugar.graphics.notebook]]&lt;br /&gt;
* [[sugar.graphics.toolbutton]]&lt;br /&gt;
* [[sugar.graphics.toolbox]]&lt;br /&gt;
* [[sugar.graphics.style]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.presence ==&lt;br /&gt;
* [[sugar.presence]]&lt;br /&gt;
* [[sugar.presence.activity]]&lt;br /&gt;
* [[sugar.presence.buddy]]&lt;br /&gt;
* [[sugar.presence.presenceservice]]&lt;br /&gt;
&lt;br /&gt;
== Clipboard ==&lt;br /&gt;
* Notes on using [[GTK&#039;s Clipboard Module in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
* Notes on using [[Python Standard Logging in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Internationalization ==&lt;br /&gt;
*[[Internationalization in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Text and Graphics for Sugar Activities ==&lt;br /&gt;
* [[Pango]]&lt;br /&gt;
&lt;br /&gt;
== Audio &amp;amp; Video ==&lt;br /&gt;
* [[Sugar Almanac GStreamer]]&lt;br /&gt;
&lt;br /&gt;
== Mouse ==&lt;br /&gt;
=== How do I change the mouse cursor in my activity to the wait cursor? ===&lt;br /&gt;
In your activity subclass:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( gtk.gdk.Cursor(gtk.gdk.WATCH) )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and to switch it back to the default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( None );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I track the position of the mouse? ===&lt;br /&gt;
There are many different reasons you might want to track the position of the mouse in your activity, ranging from the entertaining ([[http://en.wikipedia.org/wiki/Xeyes]]) to the functional (hiding certain windows when the mouse hasn&#039;t moved for a couple of seconds and making those ui elements re-appear when the mouse has moved again).  Here is one way you can implement this functionality:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		...&lt;br /&gt;
		self.hideWidgetsTime = time.time()&lt;br /&gt;
		self.mx = -1&lt;br /&gt;
		self.my = -1&lt;br /&gt;
		self.HIDE_WIDGET_TIMEOUT_ID = gobject.timeout_add( 500, self.mouseMightHaveMovedCb )&lt;br /&gt;
&lt;br /&gt;
	def _mouseMightHaveMovedCb( self ):&lt;br /&gt;
		x, y = self.get_pointer()&lt;br /&gt;
		passedTime = 0&lt;br /&gt;
&lt;br /&gt;
		if (x != self.mx or y != self.my):&lt;br /&gt;
			self.hideWidgetsTime = time.time()&lt;br /&gt;
			if (self.hiddenWidgets):&lt;br /&gt;
				self.showWidgets()&lt;br /&gt;
				self.hiddenWidgets = False&lt;br /&gt;
		else:&lt;br /&gt;
			passedTime = time.time() - self.hideWidgetsTime&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		if (passedTime &amp;gt;= 3):&lt;br /&gt;
			if (not self.hiddenWidgets):&lt;br /&gt;
				self.hideWidgets()&lt;br /&gt;
				self.hiddenWidgets = True&lt;br /&gt;
&lt;br /&gt;
		self.mx = x&lt;br /&gt;
		self.my = y&lt;br /&gt;
		return True&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous==&lt;br /&gt;
&lt;br /&gt;
The tasks below are random useful techniques that have come up as I write code and documentation for this reference. They have yet to be categorized, but will be as a sufficient set of related entries are written.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I know when my activity is &amp;quot;active&amp;quot; or not? ===&lt;br /&gt;
&lt;br /&gt;
You can set an event using the VISIBILITY_NOTIFY_MASK constant in order to know when your activity changes visibility. Then in the callback for this event, you simply compare the event&#039;s state to gtk-defined variables for activity visibility. See the [http://www.pygtk.org/docs/pygtk/gdk-constants.html#gdk-visibility-state-constants GDK Visibility State Constants] section of gtk.gdk.Constants for more information. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        # Notify when the visibility state changes by calling self.__visibility_notify_cb&lt;br /&gt;
        # (PUT THIS IN YOUR ACTIVITY CODE - EG. THE __init__() METHOD)&lt;br /&gt;
        self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK)&lt;br /&gt;
        self.connect(&amp;quot;visibility-notify-event&amp;quot;, self.__visibility_notify_cb)&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for when the activity&#039;s visibility changes&lt;br /&gt;
    def __visibility_notify_cb(self, window, event):&lt;br /&gt;
        if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:&lt;br /&gt;
            print &amp;quot;I am not visible&amp;quot;&lt;br /&gt;
        elif event.state in [gtk.gdk.VISIBILITY_UNOBSCURED, gtk.gdk.VISIBILITY_PARTIAL]:&lt;br /&gt;
            print &amp;quot;I am visible&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I get the amount of free space available on disk under the /home directory tree? ===&lt;br /&gt;
The following function uses the [http://docs.python.org/lib/module-statvfs.html statvfs] module. The following code demonstrates how to get the total amount of free space under /home. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Method: getFreespaceKb, returns the available freespace in kilobytes. &lt;br /&gt;
    def getFreespaceKb(self):&lt;br /&gt;
        stat = os.statvfs(&amp;quot;/home&amp;quot;)&lt;br /&gt;
        freebytes  = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]&lt;br /&gt;
        freekb = freebytes / 1024&lt;br /&gt;
        return freekb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, however, that assuming anything about &amp;quot;/home&amp;quot; is a bad idea, better use os.environ[&#039;HOME&#039;] instead.  Rainbow will put your actual files elsewhere,&lt;br /&gt;
some on ramdisks, some on flash.  Be clear about which filesystem&#039;s free space you actually care about.&lt;br /&gt;
&lt;br /&gt;
=== How do I know whether my activity is running on a physical XO? ===&lt;br /&gt;
Sugar runs on ordinary computers as well as on XO&#039;s.  While your activity is typically going to be run on a real XO, some people will indeed run it elsewhere.  Normally you shouldn&#039;t write your activity to care whether it&#039;s on an XO or not.  If for some odd reason, you need to care, the easiest way to tell if you are on a physical XO is to check whether /sys/power/olpc-pm, an essential power management file for the XO, exists. &amp;lt;ref&amp;gt;[http://lists.laptop.org/pipermail/devel/2008-June/015923.html reliably detecting if running on an XO]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;OLPC [[Power Management Interface]]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
      #Print out a boolean value that tells us whether we are on an XO or not. &lt;br /&gt;
      print os.path.exists(&#039;/sys/power/olpc-pm&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I know the current language setting on my XO? ===&lt;br /&gt;
The system variable &#039;LANG&#039; tells you which language is currently active on the XO. The following code shows how to look at the value of this variable. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
       _logger.debug(os.environ[&#039;LANG&#039;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I repeatedly call a specific method after N number of seconds? ===&lt;br /&gt;
The gobject.timeout_add() function allows you to invoke a callback method after a certain amount of time. If you want to repeatedly call a method, simply keep invoking the gobject.timeout_add function in your callback itself. The code below is a simple example, where the callback function is named repeatedly_call. Note that the timing of the callbacks are approximate. To get the process going, you should make an initial call to repeatedly_call() somewhere in your code. &lt;br /&gt;
&lt;br /&gt;
You can see a more substantive example of this pattern in use when we [[Pango#How_do_I_dynamically_set_the_text_in_a_pango_layout.3F | regularly update the time displayed on a pango layout object]]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	#This method calls itself ROUGHLY every 1 second &lt;br /&gt;
	def repeatedly_call(self):&lt;br /&gt;
		now = datetime.datetime.now()&lt;br /&gt;
		gobject.timeout_add(self.repeat_period_msec, self.repeatedly_update_time)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I update the current build version of code that is running on my XO? ===&lt;br /&gt;
&lt;br /&gt;
There are several pages that give you instructions on how to install/update your current build. &lt;br /&gt;
&lt;br /&gt;
* If you already have a working build installed and an internet connection, first try [[olpc-update]]. &lt;br /&gt;
* If that doesn&#039;t work, you can look at instructions for an [[Activated upgrade]] that can be done via USB] boot. &lt;br /&gt;
&lt;br /&gt;
As the instructions on the pages linked above note, make sure to install your activities separately after you have upgraded to a specific base build.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== I am developing on an XO laptop, but my keyboard and language settings are not ideal. How can I change them? ===&lt;br /&gt;
&lt;br /&gt;
Internationalized laptops will often have settings that might slow you down while developing. To change around the language settings so you can better understand environment messages, use the [[Sugar Control Panel]]&lt;br /&gt;
&lt;br /&gt;
Keyboard settings on internationalized laptops&amp;lt;ref&amp;gt;[[Keyboard layouts#OLPC keyboard layouts]]&amp;lt;/ref&amp;gt; can also be suboptimal, especially as characters like &amp;quot;-&amp;quot; and &amp;quot;/&amp;quot; are in unfamiliar positions. You can use the &amp;lt;tt&amp;gt;setxkbmap&amp;lt;/tt&amp;gt; command in the [[Terminal Activity]] to reset the type of keyboard input used and then attach a standard U.S. keyboard that will allow you to type normally. The command below sets the keyboard to the US mapping (it will reset to the default internationalized mapping upon restart). &lt;br /&gt;
&lt;br /&gt;
 setxkbmap us&lt;br /&gt;
&lt;br /&gt;
=== My Python activity wants to use threads; how do I do that? ===&lt;br /&gt;
&lt;br /&gt;
A question that has been answered with limited success is which threading patterns are most appropriate for use in Sugar.  The following pattern of code to work fine in basic instances:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.sample_thread = Thread(target=self.announce_thread, args=())&lt;br /&gt;
        self.sample_thread.setDaemon(0)&lt;br /&gt;
        self.sample_thread.start()&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    def announce_thread(self):&lt;br /&gt;
        while (self.Running):&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
            print &amp;quot;thread running&amp;quot;&lt;br /&gt;
            self._update_chat_text(&amp;quot;Thread&amp;quot;, &amp;quot;In here&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the basic series of steps that most online documentation on python suggests to use when trying to work with threads in python. The problem is that it is unclear how this pattern relates to code that worked in the SimCity activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gobject&lt;br /&gt;
gobject.threads_init()&lt;br /&gt;
#import dbus.mainloop.glib&lt;br /&gt;
#dbus.mainloop.glib.threads_init()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It should be noted that in the SimCity activity the pygame sound player would not produce sound reliably unless this setup was done.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should the two patterns always be used in tandem? It seems that the latter code is mainly to initiate gobject and other libraries to work with threading, but it is unclear what restrictions there are with using threading with these libraries. Does one take precedence over the other? It is not clear if there is any problem with using the standard python threading code on the sugar technology stack.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fact, experiments with threading on sugar leads to several different problems. For one thing, thread termination was tricky - using the can_close() method for sugar activities to terminate an activity only killed threads in some circumstances. It did not properly handle terminating threads in the case of CTRL-C or terminal interrupts. You can try to catch signals (SIGINT, SIGTERM or SIGHUP), but you will still be running in to errors in terminating child threads using these as well. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another set of errors with threading comes up when trying to combine with stream tubes. The bottom line is that it is unclear what the scope of threading in a Sugar activity should be - should it simply work if you do the standard python threading pattern, is the use of the glib.threads_init and gobject.threads_init calls necessary, are there other interactions with threads and dbus that need to be accounted for? With more clarity from sugar developers on how the platform envisions threading to work in an activity, we can be more comfortable writing entries in the Almanac to help developers write error-free code.&lt;br /&gt;
&lt;br /&gt;
=== How do I customize the title that is displayed for each instance of my activity? ===&lt;br /&gt;
&lt;br /&gt;
By default, activity titles are just the generic activity names that you specify in your activity.info file. In some applications, you may want the activity title to be more dynamic. &lt;br /&gt;
&lt;br /&gt;
For example, it makes sense to set the title for different browser sessions to the active web page being visited. That way, when you look back in the journal at the different browser sessions you have run in the previous few days, you can identify unique sessions based on the website you happened to be visiting at the time. &lt;br /&gt;
&lt;br /&gt;
The code below shows how you can set the metadata for your activity to reflect a dynamic title based on whatever session criteria you feel is important. This example is adapted from the Browse activity, which sets activity instance titles based on the title of the current web page being visited. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if self.metadata[&#039;mime_type&#039;] == &#039;text/plain&#039;:&lt;br /&gt;
            if not self._jobject.metadata[&#039;title_set_by_user&#039;] == &#039;1&#039;:&lt;br /&gt;
                if self._browser.props.title:&lt;br /&gt;
                    # Set the title of this activity to be the current &lt;br /&gt;
                    # title of the page being visited by the browser. &lt;br /&gt;
                    self.metadata[&#039;title&#039;] = self._browser.props.title&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What packages are available on sugar to support game development? ===&lt;br /&gt;
&lt;br /&gt;
If your activity will require tools that are typically needed to develop robust and clean video games, then you should utilize the [http://www.pygame.org/ pygame package]. It can be readily imported into any activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import pygame&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I detect when one of the game buttons on the laptop have been pressed? ===&lt;br /&gt;
&lt;br /&gt;
The laptop game buttons (the circle, square, x, and check buttons next to the LCD) are encoded as page up, home, page down and end respectively. So, you can detect their press by listening for these specific events. For example, the code below listens for button presses and then just writes to an output widget which button was pressed. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Page_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCircle Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Page_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nX Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Home&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nSquare Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_End&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCheck Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I detect if one of the joystick buttons has been pressed? ===&lt;br /&gt;
&lt;br /&gt;
This is the same process as detecting game buttons, except with different names for the keys. Again, you listen for &amp;quot;key-press-event&amp;quot; signals and then in your callback you check to see if the pressed button was one of the joystick keys. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nUp Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nDown Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Left&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nLeft Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Right&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nRight Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Documentation]]&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18065</id>
		<title>Development Team/Almanac</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18065"/>
		<updated>2008-11-24T22:45:00Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* My Python activity wants to use threads; how do I do that? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
{{Sugar Almanac TOC}}&lt;br /&gt;
== How do I get additional help beyond this almanac? ==&lt;br /&gt;
* Looking to get started with the basics of Sugar development? Check out Christoph Derndorfer&#039;s [http://www.olpcaustria.org/mediawiki/index.php/Activity_handbook Activity Handbook]. &lt;br /&gt;
* See also [[Sugar Code Snippets]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, on to the actual almanac ...&lt;br /&gt;
&lt;br /&gt;
== Where can I see API changes? ==&lt;br /&gt;
API changes between OLPC releases can be seen here: [[API changes]]&lt;br /&gt;
&lt;br /&gt;
== Getting Started ==&lt;br /&gt;
=== How do I structure my files so that they are a valid sugar activity?  === &lt;br /&gt;
Information on activity bundle structure can be found here: [[Activity bundles]]&lt;br /&gt;
&lt;br /&gt;
=== How do I make an icon for my activity? ===&lt;br /&gt;
Information on what you need to do can be found here: [[Making Sugar Icons]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar ==&lt;br /&gt;
* [[sugar.env]]&lt;br /&gt;
* [[sugar.profile]]&lt;br /&gt;
* [[sugar.mime]]&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.activity ==&lt;br /&gt;
* [[sugar.activity.activity]]&lt;br /&gt;
* [[sugar.activity.activityfactory]]&lt;br /&gt;
* [[sugar.activity.registry]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.datastore ==&lt;br /&gt;
* [[sugar.datastore.datastore]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.graphics ==&lt;br /&gt;
* [[sugar.graphics.alert]]&lt;br /&gt;
* [[sugar.graphics.icon]]&lt;br /&gt;
* [[sugar.graphics.notebook]]&lt;br /&gt;
* [[sugar.graphics.toolbutton]]&lt;br /&gt;
* [[sugar.graphics.toolbox]]&lt;br /&gt;
* [[sugar.graphics.style]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.presence ==&lt;br /&gt;
* [[sugar.presence]]&lt;br /&gt;
* [[sugar.presence.activity]]&lt;br /&gt;
* [[sugar.presence.buddy]]&lt;br /&gt;
* [[sugar.presence.presenceservice]]&lt;br /&gt;
&lt;br /&gt;
== Clipboard ==&lt;br /&gt;
* Notes on using [[GTK&#039;s Clipboard Module in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
* Notes on using [[Python Standard Logging in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Internationalization ==&lt;br /&gt;
*[[Internationalization in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Text and Graphics for Sugar Activities ==&lt;br /&gt;
* [[Pango]]&lt;br /&gt;
&lt;br /&gt;
== Audio &amp;amp; Video ==&lt;br /&gt;
* [[Sugar Almanac GStreamer]]&lt;br /&gt;
&lt;br /&gt;
== Mouse ==&lt;br /&gt;
=== How do I change the mouse cursor in my activity to the wait cursor? ===&lt;br /&gt;
In your activity subclass:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( gtk.gdk.Cursor(gtk.gdk.WATCH) )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and to switch it back to the default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( None );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I track the position of the mouse? ===&lt;br /&gt;
There are many different reasons you might want to track the position of the mouse in your activity, ranging from the entertaining ([[http://en.wikipedia.org/wiki/Xeyes]]) to the functional (hiding certain windows when the mouse hasn&#039;t moved for a couple of seconds and making those ui elements re-appear when the mouse has moved again).  Here is one way you can implement this functionality:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		...&lt;br /&gt;
		self.hideWidgetsTime = time.time()&lt;br /&gt;
		self.mx = -1&lt;br /&gt;
		self.my = -1&lt;br /&gt;
		self.HIDE_WIDGET_TIMEOUT_ID = gobject.timeout_add( 500, self.mouseMightHaveMovedCb )&lt;br /&gt;
&lt;br /&gt;
	def _mouseMightHaveMovedCb( self ):&lt;br /&gt;
		x, y = self.get_pointer()&lt;br /&gt;
		passedTime = 0&lt;br /&gt;
&lt;br /&gt;
		if (x != self.mx or y != self.my):&lt;br /&gt;
			self.hideWidgetsTime = time.time()&lt;br /&gt;
			if (self.hiddenWidgets):&lt;br /&gt;
				self.showWidgets()&lt;br /&gt;
				self.hiddenWidgets = False&lt;br /&gt;
		else:&lt;br /&gt;
			passedTime = time.time() - self.hideWidgetsTime&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		if (passedTime &amp;gt;= 3):&lt;br /&gt;
			if (not self.hiddenWidgets):&lt;br /&gt;
				self.hideWidgets()&lt;br /&gt;
				self.hiddenWidgets = True&lt;br /&gt;
&lt;br /&gt;
		self.mx = x&lt;br /&gt;
		self.my = y&lt;br /&gt;
		return True&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous==&lt;br /&gt;
&lt;br /&gt;
The tasks below are random useful techniques that have come up as I write code and documentation for this reference. They have yet to be categorized, but will be as a sufficient set of related entries are written.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I know when my activity is &amp;quot;active&amp;quot; or not? ===&lt;br /&gt;
&lt;br /&gt;
You can set an event using the VISIBILITY_NOTIFY_MASK constant in order to know when your activity changes visibility. Then in the callback for this event, you simply compare the event&#039;s state to gtk-defined variables for activity visibility. See the [http://www.pygtk.org/docs/pygtk/gdk-constants.html#gdk-visibility-state-constants GDK Visibility State Constants] section of gtk.gdk.Constants for more information. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        # Notify when the visibility state changes by calling self.__visibility_notify_cb&lt;br /&gt;
        # (PUT THIS IN YOUR ACTIVITY CODE - EG. THE __init__() METHOD)&lt;br /&gt;
        self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK)&lt;br /&gt;
        self.connect(&amp;quot;visibility-notify-event&amp;quot;, self.__visibility_notify_cb)&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for when the activity&#039;s visibility changes&lt;br /&gt;
    def __visibility_notify_cb(self, window, event):&lt;br /&gt;
        if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:&lt;br /&gt;
            print &amp;quot;I am not visible&amp;quot;&lt;br /&gt;
        elif event.state in [gtk.gdk.VISIBILITY_UNOBSCURED, gtk.gdk.VISIBILITY_PARTIAL]:&lt;br /&gt;
            print &amp;quot;I am visible&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I get the amount of free space available on disk under the /home directory tree? ===&lt;br /&gt;
The following function uses the [http://docs.python.org/lib/module-statvfs.html statvfs] module. The following code demonstrates how to get the total amount of free space under /home. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Method: getFreespaceKb, returns the available freespace in kilobytes. &lt;br /&gt;
    def getFreespaceKb(self):&lt;br /&gt;
        stat = os.statvfs(&amp;quot;/home&amp;quot;)&lt;br /&gt;
        freebytes  = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]&lt;br /&gt;
        freekb = freebytes / 1024&lt;br /&gt;
        return freekb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, however, that assuming anything about &amp;quot;/home&amp;quot; is a bad idea, better use os.environ[&#039;HOME&#039;] instead.  Rainbow will put your actual files elsewhere,&lt;br /&gt;
some on ramdisks, some on flash.  Be clear about which filesystem&#039;s free space you actually care about.&lt;br /&gt;
&lt;br /&gt;
=== How do I know whether my activity is running on a physical XO? ===&lt;br /&gt;
Sugar runs on ordinary computers as well as on XO&#039;s.  While your activity is typically going to be run on a real XO, some people will indeed run it elsewhere.  Normally you shouldn&#039;t write your activity to care whether it&#039;s on an XO or not.  If for some odd reason, you need to care, the easiest way to tell if you are on a physical XO is to check whether /sys/power/olpc-pm, an essential power management file for the XO, exists. &amp;lt;ref&amp;gt;[http://lists.laptop.org/pipermail/devel/2008-June/015923.html reliably detecting if running on an XO]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;OLPC [[Power Management Interface]]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
      #Print out a boolean value that tells us whether we are on an XO or not. &lt;br /&gt;
      print os.path.exists(&#039;/sys/power/olpc-pm&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I know the current language setting on my XO? ===&lt;br /&gt;
The system variable &#039;LANG&#039; tells you which language is currently active on the XO. The following code shows how to look at the value of this variable. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
       _logger.debug(os.environ[&#039;LANG&#039;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I repeatedly call a specific method after N number of seconds? ===&lt;br /&gt;
The gobject.timeout_add() function allows you to invoke a callback method after a certain amount of time. If you want to repeatedly call a method, simply keep invoking the gobject.timeout_add function in your callback itself. The code below is a simple example, where the callback function is named repeatedly_call. Note that the timing of the callbacks are approximate. To get the process going, you should make an initial call to repeatedly_call() somewhere in your code. &lt;br /&gt;
&lt;br /&gt;
You can see a more substantive example of this pattern in use when we [[Pango#How_do_I_dynamically_set_the_text_in_a_pango_layout.3F | regularly update the time displayed on a pango layout object]]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	#This method calls itself ROUGHLY every 1 second &lt;br /&gt;
	def repeatedly_call(self):&lt;br /&gt;
		now = datetime.datetime.now()&lt;br /&gt;
		gobject.timeout_add(self.repeat_period_msec, self.repeatedly_update_time)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I update the current build version of code that is running on my XO? ===&lt;br /&gt;
&lt;br /&gt;
There are several pages that give you instructions on how to install/update your current build. &lt;br /&gt;
&lt;br /&gt;
* If you already have a working build installed and an internet connection, first try [[olpc-update]]. &lt;br /&gt;
* If that doesn&#039;t work, you can look at instructions for an [[Activated upgrade]] that can be done via USB] boot. &lt;br /&gt;
&lt;br /&gt;
As the instructions on the pages linked above note, make sure to install your activities separately after you have upgraded to a specific base build.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== I am developing on an XO laptop, but my keyboard and language settings are not ideal. How can I change them? ===&lt;br /&gt;
&lt;br /&gt;
Internationalized laptops will often have settings that might slow you down while developing. To change around the language settings so you can better understand environment messages, use the [[Sugar Control Panel]]&lt;br /&gt;
&lt;br /&gt;
Keyboard settings on internationalized laptops&amp;lt;ref&amp;gt;[[Keyboard layouts#OLPC keyboard layouts]]&amp;lt;/ref&amp;gt; can also be suboptimal, especially as characters like &amp;quot;-&amp;quot; and &amp;quot;/&amp;quot; are in unfamiliar positions. You can use the &amp;lt;tt&amp;gt;setxkbmap&amp;lt;/tt&amp;gt; command in the [[Terminal Activity]] to reset the type of keyboard input used and then attach a standard U.S. keyboard that will allow you to type normally. The command below sets the keyboard to the US mapping (it will reset to the default internationalized mapping upon restart). &lt;br /&gt;
&lt;br /&gt;
 setxkbmap us&lt;br /&gt;
&lt;br /&gt;
=== My Python activity wants to use threads; how do I do that? ===&lt;br /&gt;
&lt;br /&gt;
A question that has been answered with limited success is which threading patterns are most appropriate for use in Sugar.  The following pattern of code to work fine in basic instances:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.sample_thread = Thread(target=self.announce_thread, args=())&lt;br /&gt;
        self.sample_thread.setDaemon(0)&lt;br /&gt;
        self.sample_thread.start()&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    def announce_thread(self):&lt;br /&gt;
        while (self.Running):&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
            print &amp;quot;thread running&amp;quot;&lt;br /&gt;
            self._update_chat_text(&amp;quot;Thread&amp;quot;, &amp;quot;In here&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the basic series of steps that most online documentation on python suggests to use when trying to work with threads in python. The problem is that it is unclear how this pattern relates to code that worked in the SimCity activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gobject&lt;br /&gt;
gobject.threads_init()&lt;br /&gt;
#import dbus.mainloop.glib&lt;br /&gt;
#dbus.mainloop.glib.threads_init()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It should be noted that in the SimCity activity the pygame sound player would not produce sound reliably unless this setup was done.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should the two patterns always be used in tandem? It seems that the latter code is mainly to initiate gobject and other libraries to work with threading, but it is unclear what restrictions there are with using threading with these libraries. Does one take precedence over the other? It is not clear if there is any problem with using the standard python threading code on the sugar technology stack. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fact, experiments with threading on sugar leads to several different problems. For one thing, thread termination was tricky - using the can_close() method for sugar activities to terminate an activity only killed threads in some circumstances. It did not properly handle terminating threads in the case of CTRL-C or terminal interrupts. You can try to catch signals (SIGINT, SIGTERM or SIGHUP), but you will still be running in to errors in terminating child threads using these as well. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another set of errors with threading comes up when trying to combine with stream tubes. The bottom line is that it is unclear what the scope of threading in a Sugar activity should be - should it simply work if you do the standard python threading pattern, is the use of the glib.threads_init and gobject.threads_init calls necessary, are there other interactions with threads and dbus that need to be accounted for? With more clarity from sugar developers on how the platform envisions threading to work in an activity, we can be more comfortable writing entries in the Almanac to help developers write error-free code.&lt;br /&gt;
&lt;br /&gt;
=== How do I customize the title that is displayed for each instance of my activity? ===&lt;br /&gt;
&lt;br /&gt;
By default, activity titles are just the generic activity names that you specify in your activity.info file. In some applications, you may want the activity title to be more dynamic. &lt;br /&gt;
&lt;br /&gt;
For example, it makes sense to set the title for different browser sessions to the active web page being visited. That way, when you look back in the journal at the different browser sessions you have run in the previous few days, you can identify unique sessions based on the website you happened to be visiting at the time. &lt;br /&gt;
&lt;br /&gt;
The code below shows how you can set the metadata for your activity to reflect a dynamic title based on whatever session criteria you feel is important. This example is adapted from the Browse activity, which sets activity instance titles based on the title of the current web page being visited. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if self.metadata[&#039;mime_type&#039;] == &#039;text/plain&#039;:&lt;br /&gt;
            if not self._jobject.metadata[&#039;title_set_by_user&#039;] == &#039;1&#039;:&lt;br /&gt;
                if self._browser.props.title:&lt;br /&gt;
                    # Set the title of this activity to be the current &lt;br /&gt;
                    # title of the page being visited by the browser. &lt;br /&gt;
                    self.metadata[&#039;title&#039;] = self._browser.props.title&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What packages are available on sugar to support game development? ===&lt;br /&gt;
&lt;br /&gt;
If your activity will require tools that are typically needed to develop robust and clean video games, then you should utilize the [http://www.pygame.org/ pygame package]. It can be readily imported into any activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import pygame&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I detect when one of the game buttons on the laptop have been pressed? ===&lt;br /&gt;
&lt;br /&gt;
The laptop game buttons (the circle, square, x, and check buttons next to the LCD) are encoded as page up, home, page down and end respectively. So, you can detect their press by listening for these specific events. For example, the code below listens for button presses and then just writes to an output widget which button was pressed. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Page_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCircle Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Page_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nX Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Home&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nSquare Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_End&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCheck Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I detect if one of the joystick buttons has been pressed? ===&lt;br /&gt;
&lt;br /&gt;
This is the same process as detecting game buttons, except with different names for the keys. Again, you listen for &amp;quot;key-press-event&amp;quot; signals and then in your callback you check to see if the pressed button was one of the joystick keys. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nUp Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nDown Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Left&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nLeft Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Right&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nRight Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Documentation]]&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18064</id>
		<title>Development Team/Almanac</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac&amp;diff=18064"/>
		<updated>2008-11-24T22:44:33Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* Miscellaneous */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
{{Sugar Almanac TOC}}&lt;br /&gt;
== How do I get additional help beyond this almanac? ==&lt;br /&gt;
* Looking to get started with the basics of Sugar development? Check out Christoph Derndorfer&#039;s [http://www.olpcaustria.org/mediawiki/index.php/Activity_handbook Activity Handbook]. &lt;br /&gt;
* See also [[Sugar Code Snippets]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, on to the actual almanac ...&lt;br /&gt;
&lt;br /&gt;
== Where can I see API changes? ==&lt;br /&gt;
API changes between OLPC releases can be seen here: [[API changes]]&lt;br /&gt;
&lt;br /&gt;
== Getting Started ==&lt;br /&gt;
=== How do I structure my files so that they are a valid sugar activity?  === &lt;br /&gt;
Information on activity bundle structure can be found here: [[Activity bundles]]&lt;br /&gt;
&lt;br /&gt;
=== How do I make an icon for my activity? ===&lt;br /&gt;
Information on what you need to do can be found here: [[Making Sugar Icons]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar ==&lt;br /&gt;
* [[sugar.env]]&lt;br /&gt;
* [[sugar.profile]]&lt;br /&gt;
* [[sugar.mime]]&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.activity ==&lt;br /&gt;
* [[sugar.activity.activity]]&lt;br /&gt;
* [[sugar.activity.activityfactory]]&lt;br /&gt;
* [[sugar.activity.registry]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.datastore ==&lt;br /&gt;
* [[sugar.datastore.datastore]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.graphics ==&lt;br /&gt;
* [[sugar.graphics.alert]]&lt;br /&gt;
* [[sugar.graphics.icon]]&lt;br /&gt;
* [[sugar.graphics.notebook]]&lt;br /&gt;
* [[sugar.graphics.toolbutton]]&lt;br /&gt;
* [[sugar.graphics.toolbox]]&lt;br /&gt;
* [[sugar.graphics.style]]&lt;br /&gt;
&lt;br /&gt;
== Package: sugar.presence ==&lt;br /&gt;
* [[sugar.presence]]&lt;br /&gt;
* [[sugar.presence.activity]]&lt;br /&gt;
* [[sugar.presence.buddy]]&lt;br /&gt;
* [[sugar.presence.presenceservice]]&lt;br /&gt;
&lt;br /&gt;
== Clipboard ==&lt;br /&gt;
* Notes on using [[GTK&#039;s Clipboard Module in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
* [[sugar.logger]]&lt;br /&gt;
* Notes on using [[Python Standard Logging in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Internationalization ==&lt;br /&gt;
*[[Internationalization in Sugar]]&lt;br /&gt;
&lt;br /&gt;
== Text and Graphics for Sugar Activities ==&lt;br /&gt;
* [[Pango]]&lt;br /&gt;
&lt;br /&gt;
== Audio &amp;amp; Video ==&lt;br /&gt;
* [[Sugar Almanac GStreamer]]&lt;br /&gt;
&lt;br /&gt;
== Mouse ==&lt;br /&gt;
=== How do I change the mouse cursor in my activity to the wait cursor? ===&lt;br /&gt;
In your activity subclass:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( gtk.gdk.Cursor(gtk.gdk.WATCH) )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and to switch it back to the default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self.window.set_cursor( None );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I track the position of the mouse? ===&lt;br /&gt;
There are many different reasons you might want to track the position of the mouse in your activity, ranging from the entertaining ([[http://en.wikipedia.org/wiki/Xeyes]]) to the functional (hiding certain windows when the mouse hasn&#039;t moved for a couple of seconds and making those ui elements re-appear when the mouse has moved again).  Here is one way you can implement this functionality:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		...&lt;br /&gt;
		self.hideWidgetsTime = time.time()&lt;br /&gt;
		self.mx = -1&lt;br /&gt;
		self.my = -1&lt;br /&gt;
		self.HIDE_WIDGET_TIMEOUT_ID = gobject.timeout_add( 500, self.mouseMightHaveMovedCb )&lt;br /&gt;
&lt;br /&gt;
	def _mouseMightHaveMovedCb( self ):&lt;br /&gt;
		x, y = self.get_pointer()&lt;br /&gt;
		passedTime = 0&lt;br /&gt;
&lt;br /&gt;
		if (x != self.mx or y != self.my):&lt;br /&gt;
			self.hideWidgetsTime = time.time()&lt;br /&gt;
			if (self.hiddenWidgets):&lt;br /&gt;
				self.showWidgets()&lt;br /&gt;
				self.hiddenWidgets = False&lt;br /&gt;
		else:&lt;br /&gt;
			passedTime = time.time() - self.hideWidgetsTime&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		if (passedTime &amp;gt;= 3):&lt;br /&gt;
			if (not self.hiddenWidgets):&lt;br /&gt;
				self.hideWidgets()&lt;br /&gt;
				self.hiddenWidgets = True&lt;br /&gt;
&lt;br /&gt;
		self.mx = x&lt;br /&gt;
		self.my = y&lt;br /&gt;
		return True&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous==&lt;br /&gt;
&lt;br /&gt;
The tasks below are random useful techniques that have come up as I write code and documentation for this reference. They have yet to be categorized, but will be as a sufficient set of related entries are written.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I know when my activity is &amp;quot;active&amp;quot; or not? ===&lt;br /&gt;
&lt;br /&gt;
You can set an event using the VISIBILITY_NOTIFY_MASK constant in order to know when your activity changes visibility. Then in the callback for this event, you simply compare the event&#039;s state to gtk-defined variables for activity visibility. See the [http://www.pygtk.org/docs/pygtk/gdk-constants.html#gdk-visibility-state-constants GDK Visibility State Constants] section of gtk.gdk.Constants for more information. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        # Notify when the visibility state changes by calling self.__visibility_notify_cb&lt;br /&gt;
        # (PUT THIS IN YOUR ACTIVITY CODE - EG. THE __init__() METHOD)&lt;br /&gt;
        self.add_events(gtk.gdk.VISIBILITY_NOTIFY_MASK)&lt;br /&gt;
        self.connect(&amp;quot;visibility-notify-event&amp;quot;, self.__visibility_notify_cb)&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for when the activity&#039;s visibility changes&lt;br /&gt;
    def __visibility_notify_cb(self, window, event):&lt;br /&gt;
        if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:&lt;br /&gt;
            print &amp;quot;I am not visible&amp;quot;&lt;br /&gt;
        elif event.state in [gtk.gdk.VISIBILITY_UNOBSCURED, gtk.gdk.VISIBILITY_PARTIAL]:&lt;br /&gt;
            print &amp;quot;I am visible&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I get the amount of free space available on disk under the /home directory tree? ===&lt;br /&gt;
The following function uses the [http://docs.python.org/lib/module-statvfs.html statvfs] module. The following code demonstrates how to get the total amount of free space under /home. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Method: getFreespaceKb, returns the available freespace in kilobytes. &lt;br /&gt;
    def getFreespaceKb(self):&lt;br /&gt;
        stat = os.statvfs(&amp;quot;/home&amp;quot;)&lt;br /&gt;
        freebytes  = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL]&lt;br /&gt;
        freekb = freebytes / 1024&lt;br /&gt;
        return freekb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note, however, that assuming anything about &amp;quot;/home&amp;quot; is a bad idea, better use os.environ[&#039;HOME&#039;] instead.  Rainbow will put your actual files elsewhere,&lt;br /&gt;
some on ramdisks, some on flash.  Be clear about which filesystem&#039;s free space you actually care about.&lt;br /&gt;
&lt;br /&gt;
=== How do I know whether my activity is running on a physical XO? ===&lt;br /&gt;
Sugar runs on ordinary computers as well as on XO&#039;s.  While your activity is typically going to be run on a real XO, some people will indeed run it elsewhere.  Normally you shouldn&#039;t write your activity to care whether it&#039;s on an XO or not.  If for some odd reason, you need to care, the easiest way to tell if you are on a physical XO is to check whether /sys/power/olpc-pm, an essential power management file for the XO, exists. &amp;lt;ref&amp;gt;[http://lists.laptop.org/pipermail/devel/2008-June/015923.html reliably detecting if running on an XO]&amp;lt;/ref&amp;gt; &amp;lt;ref&amp;gt;OLPC [[Power Management Interface]]&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
      #Print out a boolean value that tells us whether we are on an XO or not. &lt;br /&gt;
      print os.path.exists(&#039;/sys/power/olpc-pm&#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I know the current language setting on my XO? ===&lt;br /&gt;
The system variable &#039;LANG&#039; tells you which language is currently active on the XO. The following code shows how to look at the value of this variable. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
...&lt;br /&gt;
       _logger.debug(os.environ[&#039;LANG&#039;])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I repeatedly call a specific method after N number of seconds? ===&lt;br /&gt;
The gobject.timeout_add() function allows you to invoke a callback method after a certain amount of time. If you want to repeatedly call a method, simply keep invoking the gobject.timeout_add function in your callback itself. The code below is a simple example, where the callback function is named repeatedly_call. Note that the timing of the callbacks are approximate. To get the process going, you should make an initial call to repeatedly_call() somewhere in your code. &lt;br /&gt;
&lt;br /&gt;
You can see a more substantive example of this pattern in use when we [[Pango#How_do_I_dynamically_set_the_text_in_a_pango_layout.3F | regularly update the time displayed on a pango layout object]]. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	#This method calls itself ROUGHLY every 1 second &lt;br /&gt;
	def repeatedly_call(self):&lt;br /&gt;
		now = datetime.datetime.now()&lt;br /&gt;
		gobject.timeout_add(self.repeat_period_msec, self.repeatedly_update_time)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I update the current build version of code that is running on my XO? ===&lt;br /&gt;
&lt;br /&gt;
There are several pages that give you instructions on how to install/update your current build. &lt;br /&gt;
&lt;br /&gt;
* If you already have a working build installed and an internet connection, first try [[olpc-update]]. &lt;br /&gt;
* If that doesn&#039;t work, you can look at instructions for an [[Activated upgrade]] that can be done via USB] boot. &lt;br /&gt;
&lt;br /&gt;
As the instructions on the pages linked above note, make sure to install your activities separately after you have upgraded to a specific base build.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== I am developing on an XO laptop, but my keyboard and language settings are not ideal. How can I change them? ===&lt;br /&gt;
&lt;br /&gt;
Internationalized laptops will often have settings that might slow you down while developing. To change around the language settings so you can better understand environment messages, use the [[Sugar Control Panel]]&lt;br /&gt;
&lt;br /&gt;
Keyboard settings on internationalized laptops&amp;lt;ref&amp;gt;[[Keyboard layouts#OLPC keyboard layouts]]&amp;lt;/ref&amp;gt; can also be suboptimal, especially as characters like &amp;quot;-&amp;quot; and &amp;quot;/&amp;quot; are in unfamiliar positions. You can use the &amp;lt;tt&amp;gt;setxkbmap&amp;lt;/tt&amp;gt; command in the [[Terminal Activity]] to reset the type of keyboard input used and then attach a standard U.S. keyboard that will allow you to type normally. The command below sets the keyboard to the US mapping (it will reset to the default internationalized mapping upon restart). &lt;br /&gt;
&lt;br /&gt;
 setxkbmap us&lt;br /&gt;
&lt;br /&gt;
=== My Python activity wants to use threads; how do I do that? ===&lt;br /&gt;
&lt;br /&gt;
A question that has been answered with limited success is which threading patterns are most appropriate for use in Sugar.  The following pattern of code to work fine in basic instances:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.sample_thread = Thread(target=self.announce_thread, args=())&lt;br /&gt;
        self.sample_thread.setDaemon(0)&lt;br /&gt;
        self.sample_thread.start()&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    def announce_thread(self):&lt;br /&gt;
        while (self.Running):&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
            print &amp;quot;thread running&amp;quot;&lt;br /&gt;
            self._update_chat_text(&amp;quot;Thread&amp;quot;, &amp;quot;In here&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the basic series of steps that most online documentation on python suggests to use when trying to work with threads in python. The problem is that it is unclear how this pattern relates to code that worked in the SimCity activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gobject&lt;br /&gt;
gobject.threads_init()&lt;br /&gt;
#import dbus.mainloop.glib&lt;br /&gt;
#dbus.mainloop.glib.threads_init()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It should be noted that in the SimCity activity the pygame sound player would not produce sound reliably unless this setup was done.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Should the two patterns always be used in tandem? It seems that the latter code is mainly to initiate gobject and other libraries to work with threading, but it is unclear what restrictions there are with using threading with these libraries. Does one take precedence over the other? It is not clear if there is any problem with using the standard python threading code on the sugar technology stack. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In fact, experiments with threading on sugar leads to several different problems. For one thing, thread termination was tricky - using the can_close() method for sugar activities to terminate an activity only killed threads in some circumstances. It did not properly handle terminating threads in the case of CTRL-C or terminal interrupts. You can try to catch signals (SIGINT, SIGTERM or SIGHUP), but you will still be running in to errors in terminating child threads using these as well. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;p/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another set of errors with threading comes up when trying to combine with stream tubes. The bottom line is that it is unclear what the scope of threading in a Sugar activity should be - should it simply work if you do the standard python threading pattern, is the use of the glib.threads_init and gobject.threads_init calls necessary, are there other interactions with threads and dbus that need to be accounted for? With more clarity from sugar developers on how the platform envisions threading to work in an activity, we can be more comfortable writing entries in the Almanac to help developers write error-free code. &lt;br /&gt;
&lt;br /&gt;
=== How do I customize the title that is displayed for each instance of my activity? ===&lt;br /&gt;
&lt;br /&gt;
By default, activity titles are just the generic activity names that you specify in your activity.info file. In some applications, you may want the activity title to be more dynamic. &lt;br /&gt;
&lt;br /&gt;
For example, it makes sense to set the title for different browser sessions to the active web page being visited. That way, when you look back in the journal at the different browser sessions you have run in the previous few days, you can identify unique sessions based on the website you happened to be visiting at the time. &lt;br /&gt;
&lt;br /&gt;
The code below shows how you can set the metadata for your activity to reflect a dynamic title based on whatever session criteria you feel is important. This example is adapted from the Browse activity, which sets activity instance titles based on the title of the current web page being visited. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if self.metadata[&#039;mime_type&#039;] == &#039;text/plain&#039;:&lt;br /&gt;
            if not self._jobject.metadata[&#039;title_set_by_user&#039;] == &#039;1&#039;:&lt;br /&gt;
                if self._browser.props.title:&lt;br /&gt;
                    # Set the title of this activity to be the current &lt;br /&gt;
                    # title of the page being visited by the browser. &lt;br /&gt;
                    self.metadata[&#039;title&#039;] = self._browser.props.title&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What packages are available on sugar to support game development? ===&lt;br /&gt;
&lt;br /&gt;
If your activity will require tools that are typically needed to develop robust and clean video games, then you should utilize the [http://www.pygame.org/ pygame package]. It can be readily imported into any activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import pygame&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I detect when one of the game buttons on the laptop have been pressed? ===&lt;br /&gt;
&lt;br /&gt;
The laptop game buttons (the circle, square, x, and check buttons next to the LCD) are encoded as page up, home, page down and end respectively. So, you can detect their press by listening for these specific events. For example, the code below listens for button presses and then just writes to an output widget which button was pressed. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Page_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCircle Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Page_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nX Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Home&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nSquare Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_End&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nCheck Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I detect if one of the joystick buttons has been pressed? ===&lt;br /&gt;
&lt;br /&gt;
This is the same process as detecting game buttons, except with different names for the keys. Again, you listen for &amp;quot;key-press-event&amp;quot; signals and then in your callback you check to see if the pressed button was one of the joystick keys. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #### Initialize this activity. &lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        ...&lt;br /&gt;
        self.connect(&#039;key-press-event&#039;, self._keyPressCb)&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    #### Method _keyPressCb, which catches any presses of the game buttons. &lt;br /&gt;
    def _keyPressCb(self, widget, event):&lt;br /&gt;
&lt;br /&gt;
        keyname = gtk.gdk.keyval_name(event.keyval)&lt;br /&gt;
        &lt;br /&gt;
        if (keyname == &#039;KP_Up&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nUp Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Down&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nDown Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Left&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nLeft Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        elif (keyname == &#039;KP_Right&#039;):&lt;br /&gt;
            self._chat += &amp;quot;\nRight Pressed!&amp;quot;&lt;br /&gt;
            self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
        return False;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Documentation]]&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/Sugar.presence&amp;diff=18554</id>
		<title>Development Team/Almanac/Sugar.presence</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/Sugar.presence&amp;diff=18554"/>
		<updated>2008-11-24T22:09:48Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* I can&amp;#039;t get tubes to work.  Are there unresolved issues? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
=== How do I setup a D-Bus Tube? ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s first look at what conceptually happens to make activity sharing work. The diagram below shows two instances of the same activity: the &amp;quot;Sharing Activity&amp;quot; and the &amp;quot;Joining Activity&amp;quot;. The sharing activity is the activity that is initially run and shared with other buddies. The &amp;quot;Joining Activity&amp;quot; refers to other instances of the activity that are created once buddies decide to join an activity that has been shared. You can allow an activity to be shared (making it a sharing activity) by  [[Sugar.presence.presenceservice#How do I share an activity with other buddies in my neighborhood? | including the standard Sugar Activity Toolbar]].&lt;br /&gt;
&lt;br /&gt;
[[Image:tube-setup.jpg | How Tubes are Setup]]&lt;br /&gt;
&lt;br /&gt;
Once the user decides to share his activity with others, that activity becomes the sharing activity. The activity will receive a &amp;quot;shared&amp;quot; signal from the connection manager indicating the activity has been shared. The sharing activity must then save information about the channel on which the tube will exist and call the OfferDBusTube() method to offer a tube that can be accessed by other XOs that join the activity. The process is similar for XOs that join an activity except they will need to call the ListTubes() method instead of OfferDBusTubes() to find a tube that has been offered by the sharing activity. &lt;br /&gt;
&lt;br /&gt;
Once a tube has been set up by the underlying connection manager, both the sharing and joining XOs get a &amp;quot;NewTube&amp;quot; signal. Upon receiving this signal, each tube can instantiate a new Tube class that has been designed and implemented for the needs of each specific activity. The code example below shows how all of the steps above are actually accomplished (only the code directly relevant to tube setup has been included). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
from sugar import profile&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Sample&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class SampleActivity(activity.Activity):&lt;br /&gt;
    &lt;br /&gt;
    _ps = presenceservice.get_instance()&lt;br /&gt;
    &lt;br /&gt;
    ############################ METHODS INVOLVED IN TUBE SETUP ##########################################&lt;br /&gt;
&lt;br /&gt;
    #### Method: __init__, initialize this SampleActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        activity.Activity.__init__(self, handle)&lt;br /&gt;
        ...&lt;br /&gt;
        #When you initialize your activity, wait for receipt of &amp;quot;joined&amp;quot; or &amp;quot;shared&amp;quot; signals&lt;br /&gt;
        #and also keep a variable called self.initiating to indicating whether this instance&lt;br /&gt;
        #is the sharing or joining activity. &lt;br /&gt;
        self.initiating = None #indicates whether this instance initiated sharing. &lt;br /&gt;
        self.connect(&#039;shared&#039;, self._shared_cb)&lt;br /&gt;
        self.connect(&#039;joined&#039;, self._joined_cb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method self._shared_cb, which is called whenever this activity has been successfully &lt;br /&gt;
    # shared with other XOs on the mesh&lt;br /&gt;
    def _shared_cb(self, activity):&lt;br /&gt;
        &lt;br /&gt;
        #Ensure that this activity is indeed being shared&lt;br /&gt;
        if self._shared_activity is None:&lt;br /&gt;
            _logger.error(&amp;quot;Failed to share or join activity ... _shared_activity is null in _shared_cb()&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        #This activity initiated sharing, so set self.initiating to be true&lt;br /&gt;
        self.initiating = True&lt;br /&gt;
 &lt;br /&gt;
        #Save information about the telepathy connection, telepathy tubes channel and the text channel&lt;br /&gt;
        #associated with this shared activity&lt;br /&gt;
        self.conn = self._shared_activity.telepathy_conn&lt;br /&gt;
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        self.text_chan = self._shared_activity.telepathy_text_chan&lt;br /&gt;
&lt;br /&gt;
        #Set up a callback for when we receive the &amp;quot;NewTube&amp;quot; signal later on&lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&lt;br /&gt;
             &#039;NewTube&#039;, self._new_tube_cb)&lt;br /&gt;
&lt;br /&gt;
        #Call the OfferDBusTubes() method &lt;br /&gt;
        _id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(SERVICE, {})&lt;br /&gt;
&lt;br /&gt;
    #### Method self._joined_cb, which is called whenever this XO has successfully joined another&lt;br /&gt;
    # activity. &lt;br /&gt;
    def _joined_cb(self, activity):&lt;br /&gt;
&lt;br /&gt;
        #Ensure that this activity is indeed being shared&lt;br /&gt;
        if self._shared_activity is None:&lt;br /&gt;
            _logger.error(&amp;quot;Failed to share or join activity ... _shared_activity is null in _shared_cb()&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        #This activity joined an existing shared activity, so set self.initiating to False&lt;br /&gt;
        self.initiating = False&lt;br /&gt;
&lt;br /&gt;
        #Save information about the telepathy connection, telepathy tubes channel and the text channel&lt;br /&gt;
        #associated with this shared activity&lt;br /&gt;
        self.conn = self._shared_activity.telepathy_conn&lt;br /&gt;
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        self.text_chan = self._shared_activity.telepathy_text_chan&lt;br /&gt;
&lt;br /&gt;
        #Set up a callback for when we receive the &amp;quot;NewTube&amp;quot; signal later on&lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&lt;br /&gt;
             &#039;NewTube&#039;, self._new_tube_cb)&lt;br /&gt;
&lt;br /&gt;
        #For joining activities, call ListTubes and connect to a callback once a set &lt;br /&gt;
        #shared tubes have been found (list_tubes_reply_cb). &lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _list_tubes_reply_cb, which is called once ListTubes successfully finds&lt;br /&gt;
    # tubes available for a joining activity. &lt;br /&gt;
    def _list_tubes_reply_cb(self, tubes):&lt;br /&gt;
        for tube_info in tubes:&lt;br /&gt;
            self._new_tube_cb(*tube_info)&lt;br /&gt;
            &lt;br /&gt;
    #### Method _list_tubes_error_cb, which is needed in case there was some error in ListTubes&lt;br /&gt;
    # for the joining activity. &lt;br /&gt;
    def _list_tubes_error_cb(self, e):&lt;br /&gt;
        print &amp;quot;Error&amp;quot; + str(e)&lt;br /&gt;
&lt;br /&gt;
    #### Method _new_tube_cb, which is called for both joining and sharing activities once a tube&lt;br /&gt;
    # is available in the underlying presence system for communication. Several parameters will be &lt;br /&gt;
    # passed to this callback with information about the tube that has been set up. &lt;br /&gt;
    def _new_tube_cb(self, id, initiator, type, service, params, state):&lt;br /&gt;
        &lt;br /&gt;
        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):&lt;br /&gt;
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:&lt;br /&gt;
                #Accept the new tube that has been created &lt;br /&gt;
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)&lt;br /&gt;
&lt;br /&gt;
            # The tube connection object gives a handle to the new tube. &lt;br /&gt;
            tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])&lt;br /&gt;
&lt;br /&gt;
            # Using the handle saved above, create an instance of your own customized Tube wrapper that &lt;br /&gt;
            # will handle sending and receiving text from the underlying tube conneciton (ChatTube is defined&lt;br /&gt;
            # at the end below this activity class). &lt;br /&gt;
            self.chattube = ChatTube(tube_conn, self.initiating, self.text_received_cb)&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
    ... &lt;br /&gt;
    ############################# METHODS USED ONCE TUBE IS SET UP ###################################&lt;br /&gt;
    &lt;br /&gt;
    #### Method text_received_cb, which is called once a ChatTube instance is set up and that class&lt;br /&gt;
    # receives any text over the tube. You will pass this method as an argument to ChatTube.__init__()&lt;br /&gt;
    # (see _new_tube_cb() method above). &lt;br /&gt;
    def text_received_cb(self, text):&lt;br /&gt;
        self._chat += &amp;quot;\nSomeone Else&amp;quot; + &amp;quot;:: &amp;quot; + text + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
    #### Method: _speak_cb, which is called whenever user decides to send whatever chat text&lt;br /&gt;
    # he has written in self._chat_input to the public room. &lt;br /&gt;
    def _speak_cb(self, widget, entry):&lt;br /&gt;
        nick = profile.get_nick_name()&lt;br /&gt;
        nick = nick.upper()&lt;br /&gt;
        self._chat += &amp;quot;\n&amp;quot; + nick + &amp;quot;:: &amp;quot; + entry.get_text() + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        if self.chattube is not None:&lt;br /&gt;
            self.chattube.SendText(entry.get_text())&lt;br /&gt;
        entry.set_text(&amp;quot;&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
######################## CHATTUBE CLASS - WRAPS LOGIC FOR TUBE COMMUNICATION #####################&lt;br /&gt;
class ChatTube(ExportedGObject):&lt;br /&gt;
    #### Method __init__, which sets up a new ChatTube instance given the underlying tube connection &lt;br /&gt;
    # object (tube). &lt;br /&gt;
    def __init__(self, tube, is_initiator, text_received_cb):&lt;br /&gt;
        super(ChatTube, self).__init__(tube, PATH)&lt;br /&gt;
        self.tube = tube&lt;br /&gt;
        self.is_initiator = is_initiator #is this the sharing or joining activity?&lt;br /&gt;
        self.text_received_cb = text_received_cb #callback in main activity when text is received&lt;br /&gt;
        self.text = &#039;&#039;&lt;br /&gt;
 &lt;br /&gt;
        #The sendtext_cb method is called once someone else has sent text to this end of the tube. &lt;br /&gt;
        self.tube.add_signal_receiver(self.sendtext_cb, &#039;SendText&#039;, IFACE, path=PATH, sender_keyword=&#039;sender&#039;)&lt;br /&gt;
&lt;br /&gt;
    #### Method sendtext_cb, which is called once this tube receives text that is&lt;br /&gt;
    # sent by someone else&lt;br /&gt;
    def sendtext_cb(self, text, sender=None):&lt;br /&gt;
        if sender == self.tube.get_unique_name():&lt;br /&gt;
            return&lt;br /&gt;
        self.text = text&lt;br /&gt;
        self.text_received_cb(text)&lt;br /&gt;
&lt;br /&gt;
    #### Method SendText, which uses a DBus signal to actually send text to others over the tube. &lt;br /&gt;
    @signal(dbus_interface=IFACE, signature=&#039;s&#039;)&lt;br /&gt;
    def SendText(self, text):&lt;br /&gt;
        self.text = text&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How is data shared between activities through a D-Bus tube? ===&lt;br /&gt;
&lt;br /&gt;
Communication in Sugar is achieved through several layers of technologies, including Sugar&#039;s own Presence Service, the D-Bus service for process communication, and various implementations of the telepathy system used for real time conversations. In order to get activity communication working between activities, your goal is to ultimately create a D-Bus tube that will handle text messaging between activities (there are also stream tubes which are discussed elsewhere). &lt;br /&gt;
&lt;br /&gt;
The figure below shows a simplified conceptualization of what happens after you set up a D-Bus tube correctly. &lt;br /&gt;
&lt;br /&gt;
[[Image:tubes-all-setup.jpg | How things work once the tubes are set up. ]]&lt;br /&gt;
&lt;br /&gt;
The activity code for each XO should handle sending text to the tube and also receiving text through a callback method that is supplied to the tube. The Activity developer will also need to define the Tube Object itself, which should be designed to send and receive text appropriately based on the type of sharing that is desired. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s consider a simple, but concrete example of how a D-Bus tube is used (assuming it has already been setup correctly). The code below is from a chat application where users type text and send it to others who are sharing the activity. Only the minimal code needed to send and receive text is included for clarity. If you want to know how the tube is set up from start to finish, then read the section on [[#How do I setup a D-Bus Tube? | setting up a D-Bus tube]]. The comments should make clear what code does what in this example. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
...&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Sample&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
############## MAIN ACTIVITY CLASS ####################&lt;br /&gt;
class SampleActivity(activity.Activity):&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for text received from tube -- update activity state&lt;br /&gt;
    def text_received_cb(self, text):&lt;br /&gt;
        self._chat += &amp;quot;\nSomeone Else Said&amp;quot; + &amp;quot;:: &amp;quot; + text + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
    #### Method: _speak_cb, which is called whenever user decides to send whatever chat text&lt;br /&gt;
    # he has written in self._chat_input to the public. &lt;br /&gt;
    def _speak_cb(self, widget, entry):&lt;br /&gt;
        nick = profile.get_nick_name()&lt;br /&gt;
        nick = nick.upper()&lt;br /&gt;
        self._chat += &amp;quot;\n&amp;quot; + nick + &amp;quot;:: &amp;quot; + entry.get_text() + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        if self.chattube is not None:&lt;br /&gt;
            self.chattube.SendText(entry.get_text())&lt;br /&gt;
        entry.set_text(&amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
     ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###################### TUBE CLASS ######################&lt;br /&gt;
class ChatTube(ExportedGObject):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, tube, is_initiator, text_received_cb):&lt;br /&gt;
        super(ChatTube, self).__init__(tube, PATH)&lt;br /&gt;
        ...&lt;br /&gt;
        self.text_received_cb = text_received_cb&lt;br /&gt;
        self.text = &#039;&#039;&lt;br /&gt;
        self.tube.add_signal_receiver(self.sendtext_cb, &#039;SendText&#039;, IFACE, path=PATH, sender_keyword=&#039;sender&#039;)&lt;br /&gt;
        ...        &lt;br /&gt;
&lt;br /&gt;
    # This method is called when the XO receives some text through the D-bus tube&lt;br /&gt;
    def sendtext_cb(self, text, sender=None):&lt;br /&gt;
        # Ignore any text that this XO sent to itself. &lt;br /&gt;
        if sender == self.tube.get_unique_name():&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        self.text = text&lt;br /&gt;
        self.text_received_cb(text)&lt;br /&gt;
&lt;br /&gt;
    # This method is used to actually send text to all other XO&#039;s who are sharing. &lt;br /&gt;
    @signal(dbus_interface=IFACE, signature=&#039;s&#039;)&lt;br /&gt;
    def SendText(self, text):&lt;br /&gt;
        self.text = text&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I set up a simple stream tube that can send data one-way between two instances of an activity? ===&lt;br /&gt;
&lt;br /&gt;
As with D-Bus tubes, there are a series of coordinated steps that must happen between a sharing and joining activity before data can be shared over a tube. The example we discuss here specifically concerns sharing data one way - from a &amp;quot;sharing&amp;quot; activity to a &amp;quot;joining&amp;quot; activity. Stream tubes are especially useful if you want to share non-text data or if you want to take advantage of communication protocols like TCP/IP and UDP to transfer data between instances of an activity running on separate XOs. &lt;br /&gt;
&lt;br /&gt;
==== Step 1: Understand how to architect your stream tubes to achieve the goals of your activity. ====&lt;br /&gt;
&lt;br /&gt;
Before trying to create any working code, you should understand exactly what you want your stream tube to do - when and how will sharing of data occur over the tube during the life of your activity? The example we discuss below is predicated on a specific model of communication. &lt;br /&gt;
&lt;br /&gt;
In particular, the code we use in this section is adapted from the [http://wiki.laptop.org/go/Read Read activity], but it has been incorporated in to an example activity called &amp;quot;Annotate&amp;quot;. In activities like Read or Annotate, the goal is to set up a one way communication between the &amp;quot;sharing&amp;quot; activity and the &amp;quot;joining&amp;quot; activity so that the joining activity can download and take part in whatever document is being edited by the sharer. The document to be sent over the stream tube is shared immediately upon startup and requires no user action to initiate it. &lt;br /&gt;
&lt;br /&gt;
Given such a paradigm for using our stream tube, we can draw a rough picture of what our communication architecture will look like in this case:&lt;br /&gt;
&lt;br /&gt;
[[Image:stream-tube-in-annotate-activity.jpg | How annotate&#039;s stream tube will be used]]&lt;br /&gt;
&lt;br /&gt;
Notice that the stream tube exists on top of the [http://en.wikipedia.org/wiki/Internet_socket socket architecture] used by Unix systems to facilitate internet communication.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Define or identify the classes you will use to serve data and receive data through your stream tube. ====&lt;br /&gt;
&lt;br /&gt;
In our case, we want the sharing activity to behave like a one-time server while the joining activity is a client that downloads any document it needs on startup. To accomplish serving, we create the following two classes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
from sugar import network&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Annotate&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Annotate&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
##########################################################################&lt;br /&gt;
REQUESTHANDLER AND HTTPSERVER WORK TOGETHER TO SERVE DATA OVER STREAM TUBE &lt;br /&gt;
##########################################################################&lt;br /&gt;
class AnnotateHTTPRequestHandler(network.ChunkedGlibHTTPRequestHandler):&lt;br /&gt;
    #### Method translate_path, which, for our simple activity, just returns the same&lt;br /&gt;
    # filepath for a document that will be shared. &lt;br /&gt;
    def translate_path(self, path):&lt;br /&gt;
        return self.server._filepath&lt;br /&gt;
&lt;br /&gt;
class AnnotateHTTPServer(network.GlibTCPServer):&lt;br /&gt;
    def __init__(self, server_address, filepath):&lt;br /&gt;
        self._filepath = filepath&lt;br /&gt;
        network.GlibTCPServer.__init__(self, server_address, AnnotateHTTPRequestHandler)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example code above, most of the work is done by the superclasses defined in sugar.network, namely ChunkedGlibHTTPRequestHandler and GLibTCPServer. However, if you want some custom behavior in how data is served, then it is a good idea to subclass those or other Server and RequestHandler classes.&lt;br /&gt;
&lt;br /&gt;
On the client side, there must be a class that handles receiving data from a server. In Annotate, this is accomplished with the standard sugar.network.GlibURLDownloader class. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Coordinate sharing and joining in your activity using stream tubes and the client/server classes defined above. ====&lt;br /&gt;
&lt;br /&gt;
On the sharing instance, the following must happen:&lt;br /&gt;
* Once the activity has been shared by the user, the sharing instance should detect the &amp;quot;shared&amp;quot; signal and setup for sharing. &lt;br /&gt;
* To setup for sharing, the sharing instance needs to instantiate a new server (in Annotate, this is an AnnotateHTTPServer) that will deliver content to any client activities. &lt;br /&gt;
* The newly instantiated server should listen on a given port and that port should be connected to a new stream tube. The stream tube can be created by calling the iface.OfferStreamTube() method. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For the joining activity, it must coordinate several steps once the &amp;quot;joined&amp;quot; signal has been detected:&lt;br /&gt;
* The joining activity needs to wait for a valid stream tube that is provided under the service ANNOTATE_STREAM_SERVICE. &lt;br /&gt;
* When such a tube is available, the joining activity should specify the path where the downloaded data will be saved. &lt;br /&gt;
* Create a listening client on the address and port associated with the stream tube that has been found. In our case, we create a sugar.network.GlibURLDownloader instance that will manage downloading from the stream tube. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ANNOTATE_STREAM_SERVICE = &#039;annotate-activity-http&#039;&lt;br /&gt;
&lt;br /&gt;
class AnnotateActivity(activity.Activity):&lt;br /&gt;
&lt;br /&gt;
    #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        #Joining activity needs to know which stream tubes are available for downloading &lt;br /&gt;
        #and if it still needs to download document. &lt;br /&gt;
        self.unused_download_tubes = set() &lt;br /&gt;
        self._want_document = True&lt;br /&gt;
&lt;br /&gt;
        #Set the port number on which sharing activity will serve data&lt;br /&gt;
        h = hash(self._activity_id)&lt;br /&gt;
        self.port = 1024 + (h % 64511)&lt;br /&gt;
&lt;br /&gt;
        self.connect(&#039;shared&#039;, self._shared_cb)&lt;br /&gt;
        if self._shared_activity:&lt;br /&gt;
            # We&#039;re joining&lt;br /&gt;
            if self.get_shared():&lt;br /&gt;
                # Already joined for some reason, just get the document&lt;br /&gt;
                self._joined_cb(self)&lt;br /&gt;
            else:&lt;br /&gt;
                # Wait for a successful join before trying to get the document&lt;br /&gt;
                self.connect(&amp;quot;joined&amp;quot;, self._joined_cb)&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    ######################## SETUP SERVER ON SHARING ACTIVITY ####################################&lt;br /&gt;
&lt;br /&gt;
    #### Method _shared_cb, which is called when this activity is successfully &lt;br /&gt;
    # shared on a mesh channel. &lt;br /&gt;
    def _shared_cb(self, activity):&lt;br /&gt;
        # We initiated this activity and have now shared it, so by&lt;br /&gt;
        # definition we have the file.&lt;br /&gt;
        _logger.debug(&#039;SYSTEM:&#039;, &#039;_shared_cb(): Activity being shared&#039;)&lt;br /&gt;
        self._want_document = False;&lt;br /&gt;
        self._share_document()&lt;br /&gt;
&lt;br /&gt;
    #### Method: _share_document, which sets up an http server that will serve a specific file to&lt;br /&gt;
    # any joining activities. &lt;br /&gt;
    def _share_document(self):&lt;br /&gt;
        _logger.debug(&#039;SYSTEM&#039;, &#039;_share_document() -- sharing file &#039; + str(object_path))&lt;br /&gt;
        object_path = os.path.join(self.get_activity_root(), &#039;data&#039;, &#039;sample-shared-file.doc&#039;)&lt;br /&gt;
&lt;br /&gt;
        _logger.debug(&#039;SYSTEM&#039;, &#039;_share_document() -- Starting HTTP server on port &#039;+str(self.port))&lt;br /&gt;
        self._fileserver = AnnotateHTTPServer((&amp;quot;&amp;quot;, self.port), object_path)&lt;br /&gt;
&lt;br /&gt;
        # Make a tube for the server&lt;br /&gt;
        chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        iface = chan[telepathy.CHANNEL_TYPE_TUBES]&lt;br /&gt;
        self._fileserver_tube_id = iface.OfferStreamTube(ANNOTATE_STREAM_SERVICE,&lt;br /&gt;
                {},&lt;br /&gt;
                telepathy.SOCKET_ADDRESS_TYPE_IPV4,&lt;br /&gt;
                (&#039;127.0.0.1&#039;, dbus.UInt16(self.port)),&lt;br /&gt;
                telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    ################### COORDINATE TUBE CREATION AND DOWNLOAD IN JOINING ACTIVITY  ##################&lt;br /&gt;
&lt;br /&gt;
    #### Method _joined_cb, which is called when this activity joins another&lt;br /&gt;
    # instance&lt;br /&gt;
    def _joined_cb(self, also_self):&lt;br /&gt;
        self.watch_for_tubes()&lt;br /&gt;
        self._want_document = True;&lt;br /&gt;
        gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
    #### Method watch_for_tubes, which sets up a callback to _new_tube_cb once&lt;br /&gt;
    # a stream tube is made available. &lt;br /&gt;
    def watch_for_tubes(self):&lt;br /&gt;
        tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
&lt;br /&gt;
        tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&#039;NewTube&#039;,&lt;br /&gt;
            self._new_tube_cb)&lt;br /&gt;
        tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(&lt;br /&gt;
            reply_handler=self._list_tubes_reply_cb,&lt;br /&gt;
            error_handler=self._list_tubes_error_cb)&lt;br /&gt;
&lt;br /&gt;
    def _list_tubes_reply_cb(self, tubes):&lt;br /&gt;
        for tube_info in tubes:&lt;br /&gt;
            self._new_tube_cb(*tube_info)&lt;br /&gt;
&lt;br /&gt;
    def _list_tubes_error_cb(self, e):&lt;br /&gt;
&lt;br /&gt;
    #### Method _new_tube_cb, which is called once a stream tube is available &lt;br /&gt;
    # to download from&lt;br /&gt;
    def _new_tube_cb(self, tube_id, initiator, tube_type, service, params,&lt;br /&gt;
                     state):&lt;br /&gt;
        # If the available tube is the stream tube we set up in sharing activity, then &lt;br /&gt;
        # let&#039;s download from it. &lt;br /&gt;
        if service == ANNOTATE_STREAM_SERVICE:            &lt;br /&gt;
            # Add the newly found stream tube to the available tubes we can download from&lt;br /&gt;
            self.unused_download_tubes.add(tube_id)&lt;br /&gt;
            &lt;br /&gt;
            # if no download is in progress, let&#039;s fetch the document&lt;br /&gt;
            if self._want_document:&lt;br /&gt;
                gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _get_document, which sets this activity instance up to start downloading&lt;br /&gt;
    # the document from the sharing activity. It is called once a stream tube has been &lt;br /&gt;
    # obtained and saved in self.unused_download_tubes. &lt;br /&gt;
    def _get_document(self):&lt;br /&gt;
        if not self._want_document:&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
        # Assign a file path to download to -- where downloaded doc will be saved. &lt;br /&gt;
        path = os.path.join(self.get_activity_root(), &#039;instance&#039;,&lt;br /&gt;
                                &#039;%i&#039; % time.time())&lt;br /&gt;
&lt;br /&gt;
        # Pick an available tube we can try to download the document from&lt;br /&gt;
        try:&lt;br /&gt;
            tube_id = self.unused_download_tubes.pop()&lt;br /&gt;
        except (ValueError, KeyError), e:&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
        # Avoid trying to download the document multiple times at once&lt;br /&gt;
        self._want_document = False&lt;br /&gt;
        gobject.idle_add(self._download_document, tube_id, path)&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_result_cb, which is called once downloading is complete. &lt;br /&gt;
    def _download_result_cb(self, getter, tempfile, suggested_name, tube_id):&lt;br /&gt;
        del self.unused_download_tubes&lt;br /&gt;
        self.save()&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_progress_cb, which is called as the file is being downloaded. &lt;br /&gt;
    def _download_progress_cb(self, getter, bytes_downloaded, tube_id):&lt;br /&gt;
        # FIXME: signal the expected size somehow, so we can draw a progress&lt;br /&gt;
        # bar&lt;br /&gt;
        return True;&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_error_cb, which is called if there was an error downloading. &lt;br /&gt;
    def _download_error_cb(self, getter, err, tube_id):&lt;br /&gt;
        _logger.debug(&amp;quot;Error getting document from tube %u: %s&amp;quot;,&lt;br /&gt;
                      tube_id, err)&lt;br /&gt;
        self._want_document = True&lt;br /&gt;
        gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def _download_document(self, tube_id, path):&lt;br /&gt;
        # FIXME: should ideally have the CM listen on a Unix socket&lt;br /&gt;
        # instead of IPv4 (might be more compatible with Rainbow)&lt;br /&gt;
        chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        iface = chan[telepathy.CHANNEL_TYPE_TUBES]&lt;br /&gt;
        addr = iface.AcceptStreamTube(tube_id,&lt;br /&gt;
                telepathy.SOCKET_ADDRESS_TYPE_IPV4,&lt;br /&gt;
                telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0,&lt;br /&gt;
                utf8_strings=True)&lt;br /&gt;
        _logger.debug(&#039;Accepted stream tube: listening address is %r&#039;, addr)&lt;br /&gt;
        # SOCKET_ADDRESS_TYPE_IPV4 is defined to have addresses of type &#039;(sq)&#039;&lt;br /&gt;
        assert isinstance(addr, dbus.Struct)&lt;br /&gt;
        assert len(addr) == 2&lt;br /&gt;
        assert isinstance(addr[0], str)&lt;br /&gt;
        assert isinstance(addr[1], (int, long))&lt;br /&gt;
        assert addr[1] &amp;gt; 0 and addr[1] &amp;lt; 65536&lt;br /&gt;
        port = int(addr[1])&lt;br /&gt;
&lt;br /&gt;
        getter = network.GlibURLDownloader(&amp;quot;http://%s:%d/document&amp;quot;&lt;br /&gt;
                                           % (addr[0], port))&lt;br /&gt;
        getter.connect(&amp;quot;finished&amp;quot;, self._download_result_cb, tube_id)&lt;br /&gt;
        getter.connect(&amp;quot;progress&amp;quot;, self._download_progress_cb, tube_id)&lt;br /&gt;
        getter.connect(&amp;quot;error&amp;quot;, self._download_error_cb, tube_id)&lt;br /&gt;
        getter.start(path)&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== When downloading data from a stream tube, how can I show the download progress? ===&lt;br /&gt;
&lt;br /&gt;
=== How do I control the file and path where data is saved when sharing through a stream tube? ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where do I get more information regarding sugar activity sharing and the technologies that support it? ===&lt;br /&gt;
* A [[Shared_Sugar_Activities | brief tutorial]] on activity sharing for the OLPC laptop.&lt;br /&gt;
&lt;br /&gt;
=== I can&#039;t get tubes to work.  Are there unresolved issues? ===&lt;br /&gt;
&lt;br /&gt;
==== Which threading patterns are most appropriate for use on the sugar platform? ====&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/Sugar.presence&amp;diff=18553</id>
		<title>Development Team/Almanac/Sugar.presence</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/Sugar.presence&amp;diff=18553"/>
		<updated>2008-11-24T22:08:20Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: new heading for unanswered&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
=== How do I setup a D-Bus Tube? ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s first look at what conceptually happens to make activity sharing work. The diagram below shows two instances of the same activity: the &amp;quot;Sharing Activity&amp;quot; and the &amp;quot;Joining Activity&amp;quot;. The sharing activity is the activity that is initially run and shared with other buddies. The &amp;quot;Joining Activity&amp;quot; refers to other instances of the activity that are created once buddies decide to join an activity that has been shared. You can allow an activity to be shared (making it a sharing activity) by  [[Sugar.presence.presenceservice#How do I share an activity with other buddies in my neighborhood? | including the standard Sugar Activity Toolbar]].&lt;br /&gt;
&lt;br /&gt;
[[Image:tube-setup.jpg | How Tubes are Setup]]&lt;br /&gt;
&lt;br /&gt;
Once the user decides to share his activity with others, that activity becomes the sharing activity. The activity will receive a &amp;quot;shared&amp;quot; signal from the connection manager indicating the activity has been shared. The sharing activity must then save information about the channel on which the tube will exist and call the OfferDBusTube() method to offer a tube that can be accessed by other XOs that join the activity. The process is similar for XOs that join an activity except they will need to call the ListTubes() method instead of OfferDBusTubes() to find a tube that has been offered by the sharing activity. &lt;br /&gt;
&lt;br /&gt;
Once a tube has been set up by the underlying connection manager, both the sharing and joining XOs get a &amp;quot;NewTube&amp;quot; signal. Upon receiving this signal, each tube can instantiate a new Tube class that has been designed and implemented for the needs of each specific activity. The code example below shows how all of the steps above are actually accomplished (only the code directly relevant to tube setup has been included). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
from sugar import profile&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Sample&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class SampleActivity(activity.Activity):&lt;br /&gt;
    &lt;br /&gt;
    _ps = presenceservice.get_instance()&lt;br /&gt;
    &lt;br /&gt;
    ############################ METHODS INVOLVED IN TUBE SETUP ##########################################&lt;br /&gt;
&lt;br /&gt;
    #### Method: __init__, initialize this SampleActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
        activity.Activity.__init__(self, handle)&lt;br /&gt;
        ...&lt;br /&gt;
        #When you initialize your activity, wait for receipt of &amp;quot;joined&amp;quot; or &amp;quot;shared&amp;quot; signals&lt;br /&gt;
        #and also keep a variable called self.initiating to indicating whether this instance&lt;br /&gt;
        #is the sharing or joining activity. &lt;br /&gt;
        self.initiating = None #indicates whether this instance initiated sharing. &lt;br /&gt;
        self.connect(&#039;shared&#039;, self._shared_cb)&lt;br /&gt;
        self.connect(&#039;joined&#039;, self._joined_cb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method self._shared_cb, which is called whenever this activity has been successfully &lt;br /&gt;
    # shared with other XOs on the mesh&lt;br /&gt;
    def _shared_cb(self, activity):&lt;br /&gt;
        &lt;br /&gt;
        #Ensure that this activity is indeed being shared&lt;br /&gt;
        if self._shared_activity is None:&lt;br /&gt;
            _logger.error(&amp;quot;Failed to share or join activity ... _shared_activity is null in _shared_cb()&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        #This activity initiated sharing, so set self.initiating to be true&lt;br /&gt;
        self.initiating = True&lt;br /&gt;
 &lt;br /&gt;
        #Save information about the telepathy connection, telepathy tubes channel and the text channel&lt;br /&gt;
        #associated with this shared activity&lt;br /&gt;
        self.conn = self._shared_activity.telepathy_conn&lt;br /&gt;
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        self.text_chan = self._shared_activity.telepathy_text_chan&lt;br /&gt;
&lt;br /&gt;
        #Set up a callback for when we receive the &amp;quot;NewTube&amp;quot; signal later on&lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&lt;br /&gt;
             &#039;NewTube&#039;, self._new_tube_cb)&lt;br /&gt;
&lt;br /&gt;
        #Call the OfferDBusTubes() method &lt;br /&gt;
        _id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(SERVICE, {})&lt;br /&gt;
&lt;br /&gt;
    #### Method self._joined_cb, which is called whenever this XO has successfully joined another&lt;br /&gt;
    # activity. &lt;br /&gt;
    def _joined_cb(self, activity):&lt;br /&gt;
&lt;br /&gt;
        #Ensure that this activity is indeed being shared&lt;br /&gt;
        if self._shared_activity is None:&lt;br /&gt;
            _logger.error(&amp;quot;Failed to share or join activity ... _shared_activity is null in _shared_cb()&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        #This activity joined an existing shared activity, so set self.initiating to False&lt;br /&gt;
        self.initiating = False&lt;br /&gt;
&lt;br /&gt;
        #Save information about the telepathy connection, telepathy tubes channel and the text channel&lt;br /&gt;
        #associated with this shared activity&lt;br /&gt;
        self.conn = self._shared_activity.telepathy_conn&lt;br /&gt;
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        self.text_chan = self._shared_activity.telepathy_text_chan&lt;br /&gt;
&lt;br /&gt;
        #Set up a callback for when we receive the &amp;quot;NewTube&amp;quot; signal later on&lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&lt;br /&gt;
             &#039;NewTube&#039;, self._new_tube_cb)&lt;br /&gt;
&lt;br /&gt;
        #For joining activities, call ListTubes and connect to a callback once a set &lt;br /&gt;
        #shared tubes have been found (list_tubes_reply_cb). &lt;br /&gt;
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _list_tubes_reply_cb, which is called once ListTubes successfully finds&lt;br /&gt;
    # tubes available for a joining activity. &lt;br /&gt;
    def _list_tubes_reply_cb(self, tubes):&lt;br /&gt;
        for tube_info in tubes:&lt;br /&gt;
            self._new_tube_cb(*tube_info)&lt;br /&gt;
            &lt;br /&gt;
    #### Method _list_tubes_error_cb, which is needed in case there was some error in ListTubes&lt;br /&gt;
    # for the joining activity. &lt;br /&gt;
    def _list_tubes_error_cb(self, e):&lt;br /&gt;
        print &amp;quot;Error&amp;quot; + str(e)&lt;br /&gt;
&lt;br /&gt;
    #### Method _new_tube_cb, which is called for both joining and sharing activities once a tube&lt;br /&gt;
    # is available in the underlying presence system for communication. Several parameters will be &lt;br /&gt;
    # passed to this callback with information about the tube that has been set up. &lt;br /&gt;
    def _new_tube_cb(self, id, initiator, type, service, params, state):&lt;br /&gt;
        &lt;br /&gt;
        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):&lt;br /&gt;
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:&lt;br /&gt;
                #Accept the new tube that has been created &lt;br /&gt;
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)&lt;br /&gt;
&lt;br /&gt;
            # The tube connection object gives a handle to the new tube. &lt;br /&gt;
            tube_conn = TubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])&lt;br /&gt;
&lt;br /&gt;
            # Using the handle saved above, create an instance of your own customized Tube wrapper that &lt;br /&gt;
            # will handle sending and receiving text from the underlying tube conneciton (ChatTube is defined&lt;br /&gt;
            # at the end below this activity class). &lt;br /&gt;
            self.chattube = ChatTube(tube_conn, self.initiating, self.text_received_cb)&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
    ... &lt;br /&gt;
    ############################# METHODS USED ONCE TUBE IS SET UP ###################################&lt;br /&gt;
    &lt;br /&gt;
    #### Method text_received_cb, which is called once a ChatTube instance is set up and that class&lt;br /&gt;
    # receives any text over the tube. You will pass this method as an argument to ChatTube.__init__()&lt;br /&gt;
    # (see _new_tube_cb() method above). &lt;br /&gt;
    def text_received_cb(self, text):&lt;br /&gt;
        self._chat += &amp;quot;\nSomeone Else&amp;quot; + &amp;quot;:: &amp;quot; + text + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
    #### Method: _speak_cb, which is called whenever user decides to send whatever chat text&lt;br /&gt;
    # he has written in self._chat_input to the public room. &lt;br /&gt;
    def _speak_cb(self, widget, entry):&lt;br /&gt;
        nick = profile.get_nick_name()&lt;br /&gt;
        nick = nick.upper()&lt;br /&gt;
        self._chat += &amp;quot;\n&amp;quot; + nick + &amp;quot;:: &amp;quot; + entry.get_text() + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        if self.chattube is not None:&lt;br /&gt;
            self.chattube.SendText(entry.get_text())&lt;br /&gt;
        entry.set_text(&amp;quot;&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
######################## CHATTUBE CLASS - WRAPS LOGIC FOR TUBE COMMUNICATION #####################&lt;br /&gt;
class ChatTube(ExportedGObject):&lt;br /&gt;
    #### Method __init__, which sets up a new ChatTube instance given the underlying tube connection &lt;br /&gt;
    # object (tube). &lt;br /&gt;
    def __init__(self, tube, is_initiator, text_received_cb):&lt;br /&gt;
        super(ChatTube, self).__init__(tube, PATH)&lt;br /&gt;
        self.tube = tube&lt;br /&gt;
        self.is_initiator = is_initiator #is this the sharing or joining activity?&lt;br /&gt;
        self.text_received_cb = text_received_cb #callback in main activity when text is received&lt;br /&gt;
        self.text = &#039;&#039;&lt;br /&gt;
 &lt;br /&gt;
        #The sendtext_cb method is called once someone else has sent text to this end of the tube. &lt;br /&gt;
        self.tube.add_signal_receiver(self.sendtext_cb, &#039;SendText&#039;, IFACE, path=PATH, sender_keyword=&#039;sender&#039;)&lt;br /&gt;
&lt;br /&gt;
    #### Method sendtext_cb, which is called once this tube receives text that is&lt;br /&gt;
    # sent by someone else&lt;br /&gt;
    def sendtext_cb(self, text, sender=None):&lt;br /&gt;
        if sender == self.tube.get_unique_name():&lt;br /&gt;
            return&lt;br /&gt;
        self.text = text&lt;br /&gt;
        self.text_received_cb(text)&lt;br /&gt;
&lt;br /&gt;
    #### Method SendText, which uses a DBus signal to actually send text to others over the tube. &lt;br /&gt;
    @signal(dbus_interface=IFACE, signature=&#039;s&#039;)&lt;br /&gt;
    def SendText(self, text):&lt;br /&gt;
        self.text = text&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How is data shared between activities through a D-Bus tube? ===&lt;br /&gt;
&lt;br /&gt;
Communication in Sugar is achieved through several layers of technologies, including Sugar&#039;s own Presence Service, the D-Bus service for process communication, and various implementations of the telepathy system used for real time conversations. In order to get activity communication working between activities, your goal is to ultimately create a D-Bus tube that will handle text messaging between activities (there are also stream tubes which are discussed elsewhere). &lt;br /&gt;
&lt;br /&gt;
The figure below shows a simplified conceptualization of what happens after you set up a D-Bus tube correctly. &lt;br /&gt;
&lt;br /&gt;
[[Image:tubes-all-setup.jpg | How things work once the tubes are set up. ]]&lt;br /&gt;
&lt;br /&gt;
The activity code for each XO should handle sending text to the tube and also receiving text through a callback method that is supplied to the tube. The Activity developer will also need to define the Tube Object itself, which should be designed to send and receive text appropriately based on the type of sharing that is desired. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s consider a simple, but concrete example of how a D-Bus tube is used (assuming it has already been setup correctly). The code below is from a chat application where users type text and send it to others who are sharing the activity. Only the minimal code needed to send and receive text is included for clarity. If you want to know how the tube is set up from start to finish, then read the section on [[#How do I setup a D-Bus Tube? | setting up a D-Bus tube]]. The comments should make clear what code does what in this example. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
...&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Sample&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Sample&amp;quot;&lt;br /&gt;
&lt;br /&gt;
############## MAIN ACTIVITY CLASS ####################&lt;br /&gt;
class SampleActivity(activity.Activity):&lt;br /&gt;
    ...&lt;br /&gt;
    # Callback method for text received from tube -- update activity state&lt;br /&gt;
    def text_received_cb(self, text):&lt;br /&gt;
        self._chat += &amp;quot;\nSomeone Else Said&amp;quot; + &amp;quot;:: &amp;quot; + text + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
&lt;br /&gt;
    #### Method: _speak_cb, which is called whenever user decides to send whatever chat text&lt;br /&gt;
    # he has written in self._chat_input to the public. &lt;br /&gt;
    def _speak_cb(self, widget, entry):&lt;br /&gt;
        nick = profile.get_nick_name()&lt;br /&gt;
        nick = nick.upper()&lt;br /&gt;
        self._chat += &amp;quot;\n&amp;quot; + nick + &amp;quot;:: &amp;quot; + entry.get_text() + &amp;quot;\n&amp;quot;&lt;br /&gt;
        self._chat_buffer.set_text(self._chat)&lt;br /&gt;
        if self.chattube is not None:&lt;br /&gt;
            self.chattube.SendText(entry.get_text())&lt;br /&gt;
        entry.set_text(&amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
     ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###################### TUBE CLASS ######################&lt;br /&gt;
class ChatTube(ExportedGObject):&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, tube, is_initiator, text_received_cb):&lt;br /&gt;
        super(ChatTube, self).__init__(tube, PATH)&lt;br /&gt;
        ...&lt;br /&gt;
        self.text_received_cb = text_received_cb&lt;br /&gt;
        self.text = &#039;&#039;&lt;br /&gt;
        self.tube.add_signal_receiver(self.sendtext_cb, &#039;SendText&#039;, IFACE, path=PATH, sender_keyword=&#039;sender&#039;)&lt;br /&gt;
        ...        &lt;br /&gt;
&lt;br /&gt;
    # This method is called when the XO receives some text through the D-bus tube&lt;br /&gt;
    def sendtext_cb(self, text, sender=None):&lt;br /&gt;
        # Ignore any text that this XO sent to itself. &lt;br /&gt;
        if sender == self.tube.get_unique_name():&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        self.text = text&lt;br /&gt;
        self.text_received_cb(text)&lt;br /&gt;
&lt;br /&gt;
    # This method is used to actually send text to all other XO&#039;s who are sharing. &lt;br /&gt;
    @signal(dbus_interface=IFACE, signature=&#039;s&#039;)&lt;br /&gt;
    def SendText(self, text):&lt;br /&gt;
        self.text = text&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How do I set up a simple stream tube that can send data one-way between two instances of an activity? ===&lt;br /&gt;
&lt;br /&gt;
As with D-Bus tubes, there are a series of coordinated steps that must happen between a sharing and joining activity before data can be shared over a tube. The example we discuss here specifically concerns sharing data one way - from a &amp;quot;sharing&amp;quot; activity to a &amp;quot;joining&amp;quot; activity. Stream tubes are especially useful if you want to share non-text data or if you want to take advantage of communication protocols like TCP/IP and UDP to transfer data between instances of an activity running on separate XOs. &lt;br /&gt;
&lt;br /&gt;
==== Step 1: Understand how to architect your stream tubes to achieve the goals of your activity. ====&lt;br /&gt;
&lt;br /&gt;
Before trying to create any working code, you should understand exactly what you want your stream tube to do - when and how will sharing of data occur over the tube during the life of your activity? The example we discuss below is predicated on a specific model of communication. &lt;br /&gt;
&lt;br /&gt;
In particular, the code we use in this section is adapted from the [http://wiki.laptop.org/go/Read Read activity], but it has been incorporated in to an example activity called &amp;quot;Annotate&amp;quot;. In activities like Read or Annotate, the goal is to set up a one way communication between the &amp;quot;sharing&amp;quot; activity and the &amp;quot;joining&amp;quot; activity so that the joining activity can download and take part in whatever document is being edited by the sharer. The document to be sent over the stream tube is shared immediately upon startup and requires no user action to initiate it. &lt;br /&gt;
&lt;br /&gt;
Given such a paradigm for using our stream tube, we can draw a rough picture of what our communication architecture will look like in this case:&lt;br /&gt;
&lt;br /&gt;
[[Image:stream-tube-in-annotate-activity.jpg | How annotate&#039;s stream tube will be used]]&lt;br /&gt;
&lt;br /&gt;
Notice that the stream tube exists on top of the [http://en.wikipedia.org/wiki/Internet_socket socket architecture] used by Unix systems to facilitate internet communication.&lt;br /&gt;
&lt;br /&gt;
==== Step 2: Define or identify the classes you will use to serve data and receive data through your stream tube. ====&lt;br /&gt;
&lt;br /&gt;
In our case, we want the sharing activity to behave like a one-time server while the joining activity is a client that downloads any document it needs on startup. To accomplish serving, we create the following two classes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
import telepathy&lt;br /&gt;
from dbus.service import method, signal&lt;br /&gt;
from dbus.gobject_service import ExportedGObject&lt;br /&gt;
from sugar.presence import presenceservice&lt;br /&gt;
from sugar.presence.tubeconn import TubeConnection&lt;br /&gt;
from sugar import network&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
SERVICE = &amp;quot;org.laptop.Annotate&amp;quot;&lt;br /&gt;
IFACE = SERVICE&lt;br /&gt;
PATH = &amp;quot;/org/laptop/Annotate&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
##########################################################################&lt;br /&gt;
REQUESTHANDLER AND HTTPSERVER WORK TOGETHER TO SERVE DATA OVER STREAM TUBE &lt;br /&gt;
##########################################################################&lt;br /&gt;
class AnnotateHTTPRequestHandler(network.ChunkedGlibHTTPRequestHandler):&lt;br /&gt;
    #### Method translate_path, which, for our simple activity, just returns the same&lt;br /&gt;
    # filepath for a document that will be shared. &lt;br /&gt;
    def translate_path(self, path):&lt;br /&gt;
        return self.server._filepath&lt;br /&gt;
&lt;br /&gt;
class AnnotateHTTPServer(network.GlibTCPServer):&lt;br /&gt;
    def __init__(self, server_address, filepath):&lt;br /&gt;
        self._filepath = filepath&lt;br /&gt;
        network.GlibTCPServer.__init__(self, server_address, AnnotateHTTPRequestHandler)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example code above, most of the work is done by the superclasses defined in sugar.network, namely ChunkedGlibHTTPRequestHandler and GLibTCPServer. However, if you want some custom behavior in how data is served, then it is a good idea to subclass those or other Server and RequestHandler classes.&lt;br /&gt;
&lt;br /&gt;
On the client side, there must be a class that handles receiving data from a server. In Annotate, this is accomplished with the standard sugar.network.GlibURLDownloader class. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Step 3: Coordinate sharing and joining in your activity using stream tubes and the client/server classes defined above. ====&lt;br /&gt;
&lt;br /&gt;
On the sharing instance, the following must happen:&lt;br /&gt;
* Once the activity has been shared by the user, the sharing instance should detect the &amp;quot;shared&amp;quot; signal and setup for sharing. &lt;br /&gt;
* To setup for sharing, the sharing instance needs to instantiate a new server (in Annotate, this is an AnnotateHTTPServer) that will deliver content to any client activities. &lt;br /&gt;
* The newly instantiated server should listen on a given port and that port should be connected to a new stream tube. The stream tube can be created by calling the iface.OfferStreamTube() method. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For the joining activity, it must coordinate several steps once the &amp;quot;joined&amp;quot; signal has been detected:&lt;br /&gt;
* The joining activity needs to wait for a valid stream tube that is provided under the service ANNOTATE_STREAM_SERVICE. &lt;br /&gt;
* When such a tube is available, the joining activity should specify the path where the downloaded data will be saved. &lt;br /&gt;
* Create a listening client on the address and port associated with the stream tube that has been found. In our case, we create a sugar.network.GlibURLDownloader instance that will manage downloading from the stream tube. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ANNOTATE_STREAM_SERVICE = &#039;annotate-activity-http&#039;&lt;br /&gt;
&lt;br /&gt;
class AnnotateActivity(activity.Activity):&lt;br /&gt;
&lt;br /&gt;
    #### Method: __init__, initialize this AnnotateActivity instance&lt;br /&gt;
    def __init__(self, handle):&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        #Joining activity needs to know which stream tubes are available for downloading &lt;br /&gt;
        #and if it still needs to download document. &lt;br /&gt;
        self.unused_download_tubes = set() &lt;br /&gt;
        self._want_document = True&lt;br /&gt;
&lt;br /&gt;
        #Set the port number on which sharing activity will serve data&lt;br /&gt;
        h = hash(self._activity_id)&lt;br /&gt;
        self.port = 1024 + (h % 64511)&lt;br /&gt;
&lt;br /&gt;
        self.connect(&#039;shared&#039;, self._shared_cb)&lt;br /&gt;
        if self._shared_activity:&lt;br /&gt;
            # We&#039;re joining&lt;br /&gt;
            if self.get_shared():&lt;br /&gt;
                # Already joined for some reason, just get the document&lt;br /&gt;
                self._joined_cb(self)&lt;br /&gt;
            else:&lt;br /&gt;
                # Wait for a successful join before trying to get the document&lt;br /&gt;
                self.connect(&amp;quot;joined&amp;quot;, self._joined_cb)&lt;br /&gt;
&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
    ######################## SETUP SERVER ON SHARING ACTIVITY ####################################&lt;br /&gt;
&lt;br /&gt;
    #### Method _shared_cb, which is called when this activity is successfully &lt;br /&gt;
    # shared on a mesh channel. &lt;br /&gt;
    def _shared_cb(self, activity):&lt;br /&gt;
        # We initiated this activity and have now shared it, so by&lt;br /&gt;
        # definition we have the file.&lt;br /&gt;
        _logger.debug(&#039;SYSTEM:&#039;, &#039;_shared_cb(): Activity being shared&#039;)&lt;br /&gt;
        self._want_document = False;&lt;br /&gt;
        self._share_document()&lt;br /&gt;
&lt;br /&gt;
    #### Method: _share_document, which sets up an http server that will serve a specific file to&lt;br /&gt;
    # any joining activities. &lt;br /&gt;
    def _share_document(self):&lt;br /&gt;
        _logger.debug(&#039;SYSTEM&#039;, &#039;_share_document() -- sharing file &#039; + str(object_path))&lt;br /&gt;
        object_path = os.path.join(self.get_activity_root(), &#039;data&#039;, &#039;sample-shared-file.doc&#039;)&lt;br /&gt;
&lt;br /&gt;
        _logger.debug(&#039;SYSTEM&#039;, &#039;_share_document() -- Starting HTTP server on port &#039;+str(self.port))&lt;br /&gt;
        self._fileserver = AnnotateHTTPServer((&amp;quot;&amp;quot;, self.port), object_path)&lt;br /&gt;
&lt;br /&gt;
        # Make a tube for the server&lt;br /&gt;
        chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        iface = chan[telepathy.CHANNEL_TYPE_TUBES]&lt;br /&gt;
        self._fileserver_tube_id = iface.OfferStreamTube(ANNOTATE_STREAM_SERVICE,&lt;br /&gt;
                {},&lt;br /&gt;
                telepathy.SOCKET_ADDRESS_TYPE_IPV4,&lt;br /&gt;
                (&#039;127.0.0.1&#039;, dbus.UInt16(self.port)),&lt;br /&gt;
                telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    ################### COORDINATE TUBE CREATION AND DOWNLOAD IN JOINING ACTIVITY  ##################&lt;br /&gt;
&lt;br /&gt;
    #### Method _joined_cb, which is called when this activity joins another&lt;br /&gt;
    # instance&lt;br /&gt;
    def _joined_cb(self, also_self):&lt;br /&gt;
        self.watch_for_tubes()&lt;br /&gt;
        self._want_document = True;&lt;br /&gt;
        gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
    #### Method watch_for_tubes, which sets up a callback to _new_tube_cb once&lt;br /&gt;
    # a stream tube is made available. &lt;br /&gt;
    def watch_for_tubes(self):&lt;br /&gt;
        tubes_chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
&lt;br /&gt;
        tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(&#039;NewTube&#039;,&lt;br /&gt;
            self._new_tube_cb)&lt;br /&gt;
        tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(&lt;br /&gt;
            reply_handler=self._list_tubes_reply_cb,&lt;br /&gt;
            error_handler=self._list_tubes_error_cb)&lt;br /&gt;
&lt;br /&gt;
    def _list_tubes_reply_cb(self, tubes):&lt;br /&gt;
        for tube_info in tubes:&lt;br /&gt;
            self._new_tube_cb(*tube_info)&lt;br /&gt;
&lt;br /&gt;
    def _list_tubes_error_cb(self, e):&lt;br /&gt;
&lt;br /&gt;
    #### Method _new_tube_cb, which is called once a stream tube is available &lt;br /&gt;
    # to download from&lt;br /&gt;
    def _new_tube_cb(self, tube_id, initiator, tube_type, service, params,&lt;br /&gt;
                     state):&lt;br /&gt;
        # If the available tube is the stream tube we set up in sharing activity, then &lt;br /&gt;
        # let&#039;s download from it. &lt;br /&gt;
        if service == ANNOTATE_STREAM_SERVICE:            &lt;br /&gt;
            # Add the newly found stream tube to the available tubes we can download from&lt;br /&gt;
            self.unused_download_tubes.add(tube_id)&lt;br /&gt;
            &lt;br /&gt;
            # if no download is in progress, let&#039;s fetch the document&lt;br /&gt;
            if self._want_document:&lt;br /&gt;
                gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _get_document, which sets this activity instance up to start downloading&lt;br /&gt;
    # the document from the sharing activity. It is called once a stream tube has been &lt;br /&gt;
    # obtained and saved in self.unused_download_tubes. &lt;br /&gt;
    def _get_document(self):&lt;br /&gt;
        if not self._want_document:&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
        # Assign a file path to download to -- where downloaded doc will be saved. &lt;br /&gt;
        path = os.path.join(self.get_activity_root(), &#039;instance&#039;,&lt;br /&gt;
                                &#039;%i&#039; % time.time())&lt;br /&gt;
&lt;br /&gt;
        # Pick an available tube we can try to download the document from&lt;br /&gt;
        try:&lt;br /&gt;
            tube_id = self.unused_download_tubes.pop()&lt;br /&gt;
        except (ValueError, KeyError), e:&lt;br /&gt;
            return False&lt;br /&gt;
&lt;br /&gt;
        # Avoid trying to download the document multiple times at once&lt;br /&gt;
        self._want_document = False&lt;br /&gt;
        gobject.idle_add(self._download_document, tube_id, path)&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_result_cb, which is called once downloading is complete. &lt;br /&gt;
    def _download_result_cb(self, getter, tempfile, suggested_name, tube_id):&lt;br /&gt;
        del self.unused_download_tubes&lt;br /&gt;
        self.save()&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_progress_cb, which is called as the file is being downloaded. &lt;br /&gt;
    def _download_progress_cb(self, getter, bytes_downloaded, tube_id):&lt;br /&gt;
        # FIXME: signal the expected size somehow, so we can draw a progress&lt;br /&gt;
        # bar&lt;br /&gt;
        return True;&lt;br /&gt;
&lt;br /&gt;
    #### Method _download_error_cb, which is called if there was an error downloading. &lt;br /&gt;
    def _download_error_cb(self, getter, err, tube_id):&lt;br /&gt;
        _logger.debug(&amp;quot;Error getting document from tube %u: %s&amp;quot;,&lt;br /&gt;
                      tube_id, err)&lt;br /&gt;
        self._want_document = True&lt;br /&gt;
        gobject.idle_add(self._get_document)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    def _download_document(self, tube_id, path):&lt;br /&gt;
        # FIXME: should ideally have the CM listen on a Unix socket&lt;br /&gt;
        # instead of IPv4 (might be more compatible with Rainbow)&lt;br /&gt;
        chan = self._shared_activity.telepathy_tubes_chan&lt;br /&gt;
        iface = chan[telepathy.CHANNEL_TYPE_TUBES]&lt;br /&gt;
        addr = iface.AcceptStreamTube(tube_id,&lt;br /&gt;
                telepathy.SOCKET_ADDRESS_TYPE_IPV4,&lt;br /&gt;
                telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0,&lt;br /&gt;
                utf8_strings=True)&lt;br /&gt;
        _logger.debug(&#039;Accepted stream tube: listening address is %r&#039;, addr)&lt;br /&gt;
        # SOCKET_ADDRESS_TYPE_IPV4 is defined to have addresses of type &#039;(sq)&#039;&lt;br /&gt;
        assert isinstance(addr, dbus.Struct)&lt;br /&gt;
        assert len(addr) == 2&lt;br /&gt;
        assert isinstance(addr[0], str)&lt;br /&gt;
        assert isinstance(addr[1], (int, long))&lt;br /&gt;
        assert addr[1] &amp;gt; 0 and addr[1] &amp;lt; 65536&lt;br /&gt;
        port = int(addr[1])&lt;br /&gt;
&lt;br /&gt;
        getter = network.GlibURLDownloader(&amp;quot;http://%s:%d/document&amp;quot;&lt;br /&gt;
                                           % (addr[0], port))&lt;br /&gt;
        getter.connect(&amp;quot;finished&amp;quot;, self._download_result_cb, tube_id)&lt;br /&gt;
        getter.connect(&amp;quot;progress&amp;quot;, self._download_progress_cb, tube_id)&lt;br /&gt;
        getter.connect(&amp;quot;error&amp;quot;, self._download_error_cb, tube_id)&lt;br /&gt;
        getter.start(path)&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== When downloading data from a stream tube, how can I show the download progress? ===&lt;br /&gt;
&lt;br /&gt;
=== How do I control the file and path where data is saved when sharing through a stream tube? ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where do I get more information regarding sugar activity sharing and the technologies that support it? ===&lt;br /&gt;
* A [[Shared_Sugar_Activities | brief tutorial]] on activity sharing for the OLPC laptop.&lt;br /&gt;
&lt;br /&gt;
=== I can&#039;t get tubes to work.  Are there unresolved issues? ===&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Marketing_Team/Events/Sugarcamp_Boston_2008/hackathon&amp;diff=11758</id>
		<title>Marketing Team/Events/Sugarcamp Boston 2008/hackathon</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Marketing_Team/Events/Sugarcamp_Boston_2008/hackathon&amp;diff=11758"/>
		<updated>2008-11-18T03:47:41Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: adding myself to attendees&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Details ==&lt;br /&gt;
&lt;br /&gt;
* Monday, November 17, 2008&lt;br /&gt;
* 6:30pm - 10pm (we will order food together at the beginning; bring your own $ for dinner)&lt;br /&gt;
* One Broadway, Cambridge MA 02142 (at the corner of Broadway and 3rd, by the Kendall T stop)&lt;br /&gt;
* Call [[User:Mchua|Mel Chua]] to be let in when you arrive - 847.970.8484&lt;br /&gt;
&lt;br /&gt;
== About ==&lt;br /&gt;
&lt;br /&gt;
=== For developers ===&lt;br /&gt;
&lt;br /&gt;
This is a &amp;quot;new Sugar coders hackfest&amp;quot; evening, where people can come to learn about making Activities - bring a project you want to work on, and we&#039;ll have people from the core Sugar team around to help, teach, answer questions, run impromptu tutorials, and make tools/patches as needed to ease the entry route for new coders.&lt;br /&gt;
&lt;br /&gt;
=== For non-developers ===&lt;br /&gt;
&lt;br /&gt;
We&#039;re also looking for people who &#039;&#039;won&#039;t&#039;&#039; be coding that night to sprint with David Farning on community infrastructure setup. Problems to tackle include building a Newbie Welcome Wagon package, handling [http://dev.laptop.org/ticket/8630 licensing] of both content and Activity bundles, documentation and testing systems, and more.&lt;br /&gt;
&lt;br /&gt;
== Attendees ==&lt;br /&gt;
&lt;br /&gt;
Sign up if you&#039;re coming, so we know how many to expect- tentatives and drop-ins are ok, but make things much harder to plan for. Also let us know (in parentheses) what you&#039;ll be doing!&lt;br /&gt;
&lt;br /&gt;
* Marco (on-site mentor, Sugar Activity development)&lt;br /&gt;
* Tomeu (on-site mentor, Sugar Activity development)&lt;br /&gt;
* [[User:Dfarning|Dfarning]] (on-site mentor, infrastructure and documentation)&lt;br /&gt;
* [[User:Mchua|Mchua]] (hackathon coordinator, working on Larry, a language-learning Activity)&lt;br /&gt;
* Yifan + others from Olin, may drop by to check things out&lt;br /&gt;
* Owen Williams (PenguinTV, may be a little late)&lt;br /&gt;
* Tom Boyle (StarChart, focusing on localization)&lt;br /&gt;
* [[User:Wade|Wade]] (Sugar Activity development)&lt;br /&gt;
* Brian Jordan (Sugar Activity development, Pippy love)&lt;br /&gt;
* Amanda Peyton (MIT Sloan project looking at the SugarLabs organizational structure)&lt;br /&gt;
* Ryan Kabir (new development contributor to Sugar, looking for a place to start)&lt;br /&gt;
* Brendan Powers (new contributor,looking for something simple to get used to the project)&lt;br /&gt;
* Chris Ball (from 7:30)&lt;br /&gt;
* Erik Blankinship (updating the Map Activity)&lt;br /&gt;
&lt;br /&gt;
== Stuff to hack on ==&lt;br /&gt;
&lt;br /&gt;
* Spreadsheet activity: http://lists.laptop.org/pipermail/sugar/2008-February/004225.html&lt;br /&gt;
* Mind mapping activity: http://lists.laptop.org/pipermail/olpc-sur/2008-November/001173.html&lt;br /&gt;
* Make the Sugar shell react to changes in the ~/Activities dir using inotify.&lt;br /&gt;
* A quiz game with competitive collaboration (cjb) -- e.g. generate arithmetic problems, have a shared countdown, and then the first person to answer correctly gets the most points.  Generalizable to quizes about non-math stuff too.&lt;br /&gt;
&lt;br /&gt;
== To do ==&lt;br /&gt;
&lt;br /&gt;
Let [[User:Mchua|Mchua]] (mel at laptop dot org) know if you can help!&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;s&amp;gt;Get a location with wifi, projectors, and ready access to food (which probably means &amp;quot;order pizza, then ask everyone if they can chip in a few dollars when it comes).&amp;lt;/s&amp;gt;&lt;br /&gt;
* &amp;lt;s&amp;gt;make invitations and send them out&amp;lt;/s&amp;gt;&lt;br /&gt;
* &amp;lt;s&amp;gt;find some way to make nametags for everybody&amp;lt;/s&amp;gt; (Actually, we probably won&#039;t need these - we&#039;re small enough.)&lt;br /&gt;
* make sure that the location gets set up (chairs, tables, etc.) and taken down/cleaned-up afterwards&lt;br /&gt;
* send thank-you follow up emails after the event encouraging people to come to the rest of Sugarcamp&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/GStreamer&amp;diff=18705</id>
		<title>Development Team/Almanac/GStreamer</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/GStreamer&amp;diff=18705"/>
		<updated>2008-10-03T15:55:40Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: /* How can I add play/pause buttons and a scrubber to my audio or video? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
&lt;br /&gt;
=== Where can I get additional resources and sample code on using the camera? ===&lt;br /&gt;
* [[Programming_the_camera]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How can I embed video files in my activity? ===&lt;br /&gt;
Sugar, by default, can play back audio (encoded in Vorbis) and video (encoded in Theora) files in ogg containers.&lt;br /&gt;
&lt;br /&gt;
The following file will be helpful for embedding video or audio files into your activity.  After the code posting is a code snippet showing how to use this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gtk&lt;br /&gt;
import pygtk&lt;br /&gt;
pygtk.require(&#039;2.0&#039;)&lt;br /&gt;
import sys&lt;br /&gt;
import pygst&lt;br /&gt;
pygst.require(&#039;0.10&#039;)&lt;br /&gt;
import gst&lt;br /&gt;
import gst.interfaces&lt;br /&gt;
import gobject&lt;br /&gt;
import time&lt;br /&gt;
gobject.threads_init()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class Gplay:&lt;br /&gt;
&lt;br /&gt;
	def __init__(self):&lt;br /&gt;
		self.window = None&lt;br /&gt;
		self.playing = False&lt;br /&gt;
&lt;br /&gt;
		self.player = gst.element_factory_make(&amp;quot;playbin&amp;quot;, &amp;quot;playbin&amp;quot;)&lt;br /&gt;
		xis = gst.element_factory_make(&amp;quot;xvimagesink&amp;quot;, &amp;quot;xvimagesink&amp;quot;)&lt;br /&gt;
		self.player.set_property(&amp;quot;video-sink&amp;quot;, xis)&lt;br /&gt;
		bus = self.player.get_bus()&lt;br /&gt;
		bus.enable_sync_message_emission()&lt;br /&gt;
		bus.add_signal_watch()&lt;br /&gt;
		self.SYNC_ID = bus.connect(&#039;sync-message::element&#039;, self._onSyncMessageCb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _onSyncMessageCb(self, bus, message):&lt;br /&gt;
		if message.structure is None:&lt;br /&gt;
			return True&lt;br /&gt;
		if message.structure.get_name() == &#039;prepare-xwindow-id&#039;:&lt;br /&gt;
			self.window.set_sink(message.src)&lt;br /&gt;
			message.src.set_property(&#039;force-aspect-ratio&#039;, True)&lt;br /&gt;
			return True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def setFile(self, path):&lt;br /&gt;
		uri = &amp;quot;file://&amp;quot; + str( path )&lt;br /&gt;
		if (self.player.get_property(&#039;uri&#039;) == uri):&lt;br /&gt;
			self.seek(gst.SECOND*0)&lt;br /&gt;
			return&lt;br /&gt;
&lt;br /&gt;
		self.player.set_state(gst.STATE_READY)&lt;br /&gt;
		self.player.set_property(&#039;uri&#039;, uri)&lt;br /&gt;
		ext = uri[len(uri)-3:]&lt;br /&gt;
		if (ext == &amp;quot;jpg&amp;quot;):&lt;br /&gt;
			self.pause()&lt;br /&gt;
		else:&lt;br /&gt;
			self.play()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def queryPosition(self):&lt;br /&gt;
		#&amp;quot;Returns a (position, duration) tuple&amp;quot;&lt;br /&gt;
		try:&lt;br /&gt;
			position, format = self.player.query_position(gst.FORMAT_TIME)&lt;br /&gt;
		except:&lt;br /&gt;
			position = gst.CLOCK_TIME_NONE&lt;br /&gt;
&lt;br /&gt;
		try:&lt;br /&gt;
			duration, format = self.player.query_duration(gst.FORMAT_TIME)&lt;br /&gt;
		except:&lt;br /&gt;
			duration = gst.CLOCK_TIME_NONE&lt;br /&gt;
&lt;br /&gt;
		return (position, duration)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def seek(self, time):&lt;br /&gt;
		event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, time, gst.SEEK_TYPE_NONE, 0)&lt;br /&gt;
		res = self.player.send_event(event)&lt;br /&gt;
		if res:&lt;br /&gt;
			self.player.set_new_stream_time(0L)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def pause(self):&lt;br /&gt;
		self.playing = False&lt;br /&gt;
		self.player.set_state(gst.STATE_PAUSED)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def play(self):&lt;br /&gt;
		self.playing = True&lt;br /&gt;
		self.player.set_state(gst.STATE_PLAYING)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def stop(self):&lt;br /&gt;
		self.playing = False&lt;br /&gt;
		self.player.set_state(gst.STATE_NULL)&lt;br /&gt;
		self.nextMovie()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def get_state(self, timeout=1):&lt;br /&gt;
		return self.player.get_state(timeout=timeout)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def is_playing(self):&lt;br /&gt;
		return self.playing&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class PlayVideoWindow(gtk.Window):&lt;br /&gt;
	def __init__(self):&lt;br /&gt;
		gtk.Window.__init__(self)&lt;br /&gt;
&lt;br /&gt;
		self.imagesink = None&lt;br /&gt;
		self.unset_flags(gtk.DOUBLE_BUFFERED)&lt;br /&gt;
		self.set_flags(gtk.APP_PAINTABLE)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def set_sink(self, sink):&lt;br /&gt;
		if (self.imagesink != None):&lt;br /&gt;
			assert self.window.xid&lt;br /&gt;
			self.imagesink = None&lt;br /&gt;
			del self.imagesink&lt;br /&gt;
&lt;br /&gt;
		self.imagesink = sink&lt;br /&gt;
		self.imagesink.set_xwindow_id(self.window.xid)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is a code snippet showing how you would use this file to embed media playback in your activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		#this is our video playback window&lt;br /&gt;
		self.gplay = Gplay( )&lt;br /&gt;
		self.gplayWin = PlayVideoWindow( )&lt;br /&gt;
		self.gplay.window = self.gplayWin&lt;br /&gt;
&lt;br /&gt;
		self.gplayWin.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG )&lt;br /&gt;
		self.gplayWin.set_decorated( False )&lt;br /&gt;
		self.gplayWin.set_transient_for( self ) #activity subclass&lt;br /&gt;
		self.gplayWin.move( 100, 100 )&lt;br /&gt;
		self.gplayWin.resize( 300, 400 )&lt;br /&gt;
		self.gplayWin.show_all( )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How can I add play/pause buttons and a scrubber to my audio or video? ===&lt;br /&gt;
&lt;br /&gt;
The widget below is pre-configured to work with the gplay class above.  This widget takes two gtk.Images in its constructor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gtk&lt;br /&gt;
import pygtk&lt;br /&gt;
pygtk.require(&#039;2.0&#039;)&lt;br /&gt;
import pygst&lt;br /&gt;
pygst.require(&#039;0.10&#039;)&lt;br /&gt;
import gst&lt;br /&gt;
import gst.interfaces&lt;br /&gt;
import gobject&lt;br /&gt;
&lt;br /&gt;
import sugar.graphics.style&lt;br /&gt;
&lt;br /&gt;
class GScrub(gtk.Window):&lt;br /&gt;
&lt;br /&gt;
	def __init__(self, gplay, playImg, pauseImg):&lt;br /&gt;
		gtk.Window.__init__(self)&lt;br /&gt;
		self._gplay = gplay&lt;br /&gt;
		self._playImg = playImg&lt;br /&gt;
		self._pauseImg = pauseImg&lt;br /&gt;
&lt;br /&gt;
		self.UPDATE_INTERVAL = 500&lt;br /&gt;
		self.UPDATE_SCALE_ID = 0&lt;br /&gt;
		self.CHANGED_ID = 0&lt;br /&gt;
		self.was_playing = False&lt;br /&gt;
		self.p_position = gst.CLOCK_TIME_NONE&lt;br /&gt;
		self.p_duration = gst.CLOCK_TIME_NONE&lt;br /&gt;
&lt;br /&gt;
		self.hbox = gtk.HBox()&lt;br /&gt;
		self.hbox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.hbox.modify_bg( gtk.STATE_INSENSITIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.add( self.hbox )&lt;br /&gt;
&lt;br /&gt;
		self.button = gtk.Button()&lt;br /&gt;
		buttBox = gtk.EventBox()&lt;br /&gt;
		buttBox.add(self.button)&lt;br /&gt;
		buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.button.set_image( self._playImg )&lt;br /&gt;
		self.button.set_property(&#039;can-default&#039;, True)&lt;br /&gt;
		self.button.set_relief(gtk.RELIEF_NONE)&lt;br /&gt;
		self.button.set_size_request( 55, 55 )&lt;br /&gt;
		buttBox.set_size_request( 55, 55 )&lt;br /&gt;
		self.button.show()&lt;br /&gt;
&lt;br /&gt;
		buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.button.modify_bg( gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
&lt;br /&gt;
		self.button.connect(&#039;clicked&#039;, self._buttonClickedCb)&lt;br /&gt;
		self.hbox.pack_start(buttBox, expand=False)&lt;br /&gt;
&lt;br /&gt;
		self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)&lt;br /&gt;
		self.hscale = gtk.HScale(self.adjustment)&lt;br /&gt;
		self.hscale.set_draw_value(False)&lt;br /&gt;
		self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)&lt;br /&gt;
		hscaleBox = gtk.EventBox()&lt;br /&gt;
		hscaleBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		hscaleBox.add( self.hscale )&lt;br /&gt;
		self.hscale.connect(&#039;button-press-event&#039;, self._scaleButtonPressCb)&lt;br /&gt;
		self.hscale.connect(&#039;button-release-event&#039;, self._scaleButtonReleaseCb)&lt;br /&gt;
		self.hbox.pack_start(hscaleBox, expand=True)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def removeCallbacks( self ):&lt;br /&gt;
		if (self.UPDATE_SCALE_ID != 0):&lt;br /&gt;
			gobject.source_remove(self.UPDATE_SCALE_ID)&lt;br /&gt;
			self.UPDATE_SCALE_ID = 0&lt;br /&gt;
		if (self.CHANGED_ID != 0):&lt;br /&gt;
			gobject.source_remove(self.CHANGED_ID)&lt;br /&gt;
			self.CHANGED_ID = 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def reset(self):&lt;br /&gt;
		self.adjustment.set_value(0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _buttonClickedCb(self, widget):&lt;br /&gt;
		self.play_toggled()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def set_button_play(self):&lt;br /&gt;
		self.button.set_image(self._playImg)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def set_button_pause(self):&lt;br /&gt;
		self.button.set_image(self._pauseImg)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def play_toggled(self):&lt;br /&gt;
		self.p_position, self.p_duration = self._gplay.queryPosition()&lt;br /&gt;
		if (self.p_position == self.p_duration):&lt;br /&gt;
			self._gplay.seek(0)&lt;br /&gt;
			self._gplay.pause()&lt;br /&gt;
&lt;br /&gt;
		if self._gplay.is_playing():&lt;br /&gt;
			self._gplay.pause()&lt;br /&gt;
			self.set_button_play()&lt;br /&gt;
		else:&lt;br /&gt;
			#if self._gplay.error:&lt;br /&gt;
			#	#todo: check if we have &amp;quot;error&amp;quot;, and also to disable everything&lt;br /&gt;
			#	self.button.set_disabled()&lt;br /&gt;
			#else:&lt;br /&gt;
			self.doPlay()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def doPlay(self):&lt;br /&gt;
		self._gplay.play()&lt;br /&gt;
		if self.UPDATE_SCALE_ID == 0:&lt;br /&gt;
			self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)&lt;br /&gt;
		self.set_button_pause()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _scaleButtonPressCb(self, widget, event):&lt;br /&gt;
		#self.button.set_sensitive(False)&lt;br /&gt;
		self.was_playing = self._gplay.is_playing()&lt;br /&gt;
		if self.was_playing:&lt;br /&gt;
			self._gplay.pause()&lt;br /&gt;
&lt;br /&gt;
		# don&#039;t timeout-update position during seek&lt;br /&gt;
		if self.UPDATE_SCALE_ID != 0:&lt;br /&gt;
			gobject.source_remove(self.UPDATE_SCALE_ID)&lt;br /&gt;
			self.UPDATE_SCALE_ID = 0&lt;br /&gt;
&lt;br /&gt;
		# make sure we get changed notifies&lt;br /&gt;
		if self.CHANGED_ID == 0:&lt;br /&gt;
			self.CHANGED_ID = self.hscale.connect(&#039;value-changed&#039;, self._scaleValueChangedCb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _scaleButtonReleaseCb(self, widget, event):&lt;br /&gt;
		# see seek.cstop_seek&lt;br /&gt;
		widget.disconnect(self.CHANGED_ID)&lt;br /&gt;
		self.CHANGED_ID = 0&lt;br /&gt;
&lt;br /&gt;
		#self.button.set_sensitive(True)&lt;br /&gt;
		if self.was_playing:&lt;br /&gt;
			self._gplay.play()&lt;br /&gt;
&lt;br /&gt;
		if self.UPDATE_SCALE_ID != 0:&lt;br /&gt;
			pass&lt;br /&gt;
			#print(&#039;Had a previous update timeout id&#039;)&lt;br /&gt;
		else:&lt;br /&gt;
			self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _scaleValueChangedCb(self, scale):&lt;br /&gt;
		real = long(scale.get_value() * self.p_duration / 100) # in ns&lt;br /&gt;
		self._gplay.seek(real)&lt;br /&gt;
		# allow for a preroll&lt;br /&gt;
		self._gplay.get_state(timeout=50*gst.MSECOND) # 50 ms&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _updateScaleCb(self):&lt;br /&gt;
		self.p_position, self.p_duration = self._gplay.queryPosition()&lt;br /&gt;
		if self.p_position != gst.CLOCK_TIME_NONE:&lt;br /&gt;
			value = self.p_position * 100.0 / self.p_duration&lt;br /&gt;
			if (value &amp;gt; 99):&lt;br /&gt;
				value = 99&lt;br /&gt;
			elif (value &amp;lt; 0):&lt;br /&gt;
				value = 0&lt;br /&gt;
&lt;br /&gt;
			self.adjustment.set_value(value)&lt;br /&gt;
&lt;br /&gt;
			if self._gplay.is_playing() and (self.p_position == self.p_duration):&lt;br /&gt;
				self._gplay.pause()&lt;br /&gt;
				self.set_button_play()&lt;br /&gt;
&lt;br /&gt;
		return True&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
	<entry>
		<id>https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/GStreamer&amp;diff=18704</id>
		<title>Development Team/Almanac/GStreamer</title>
		<link rel="alternate" type="text/html" href="https://wiki.sugarlabs.org/index.php?title=Development_Team/Almanac/GStreamer&amp;diff=18704"/>
		<updated>2008-10-03T15:53:40Z</updated>

		<summary type="html">&lt;p&gt;Jedierikb: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Sugar Almanac}}&lt;br /&gt;
&lt;br /&gt;
=== Where can I get additional resources and sample code on using the camera? ===&lt;br /&gt;
* [[Programming_the_camera]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How can I embed video files in my activity? ===&lt;br /&gt;
Sugar, by default, can play back audio (encoded in Vorbis) and video (encoded in Theora) files in ogg containers.&lt;br /&gt;
&lt;br /&gt;
The following file will be helpful for embedding video or audio files into your activity.  After the code posting is a code snippet showing how to use this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gtk&lt;br /&gt;
import pygtk&lt;br /&gt;
pygtk.require(&#039;2.0&#039;)&lt;br /&gt;
import sys&lt;br /&gt;
import pygst&lt;br /&gt;
pygst.require(&#039;0.10&#039;)&lt;br /&gt;
import gst&lt;br /&gt;
import gst.interfaces&lt;br /&gt;
import gobject&lt;br /&gt;
import time&lt;br /&gt;
gobject.threads_init()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class Gplay:&lt;br /&gt;
&lt;br /&gt;
	def __init__(self):&lt;br /&gt;
		self.window = None&lt;br /&gt;
		self.playing = False&lt;br /&gt;
&lt;br /&gt;
		self.player = gst.element_factory_make(&amp;quot;playbin&amp;quot;, &amp;quot;playbin&amp;quot;)&lt;br /&gt;
		xis = gst.element_factory_make(&amp;quot;xvimagesink&amp;quot;, &amp;quot;xvimagesink&amp;quot;)&lt;br /&gt;
		self.player.set_property(&amp;quot;video-sink&amp;quot;, xis)&lt;br /&gt;
		bus = self.player.get_bus()&lt;br /&gt;
		bus.enable_sync_message_emission()&lt;br /&gt;
		bus.add_signal_watch()&lt;br /&gt;
		self.SYNC_ID = bus.connect(&#039;sync-message::element&#039;, self._onSyncMessageCb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _onSyncMessageCb(self, bus, message):&lt;br /&gt;
		if message.structure is None:&lt;br /&gt;
			return True&lt;br /&gt;
		if message.structure.get_name() == &#039;prepare-xwindow-id&#039;:&lt;br /&gt;
			self.window.set_sink(message.src)&lt;br /&gt;
			message.src.set_property(&#039;force-aspect-ratio&#039;, True)&lt;br /&gt;
			return True&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def setFile(self, path):&lt;br /&gt;
		uri = &amp;quot;file://&amp;quot; + str( path )&lt;br /&gt;
		if (self.player.get_property(&#039;uri&#039;) == uri):&lt;br /&gt;
			self.seek(gst.SECOND*0)&lt;br /&gt;
			return&lt;br /&gt;
&lt;br /&gt;
		self.player.set_state(gst.STATE_READY)&lt;br /&gt;
		self.player.set_property(&#039;uri&#039;, uri)&lt;br /&gt;
		ext = uri[len(uri)-3:]&lt;br /&gt;
		if (ext == &amp;quot;jpg&amp;quot;):&lt;br /&gt;
			self.pause()&lt;br /&gt;
		else:&lt;br /&gt;
			self.play()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def queryPosition(self):&lt;br /&gt;
		#&amp;quot;Returns a (position, duration) tuple&amp;quot;&lt;br /&gt;
		try:&lt;br /&gt;
			position, format = self.player.query_position(gst.FORMAT_TIME)&lt;br /&gt;
		except:&lt;br /&gt;
			position = gst.CLOCK_TIME_NONE&lt;br /&gt;
&lt;br /&gt;
		try:&lt;br /&gt;
			duration, format = self.player.query_duration(gst.FORMAT_TIME)&lt;br /&gt;
		except:&lt;br /&gt;
			duration = gst.CLOCK_TIME_NONE&lt;br /&gt;
&lt;br /&gt;
		return (position, duration)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def seek(self, time):&lt;br /&gt;
		event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, time, gst.SEEK_TYPE_NONE, 0)&lt;br /&gt;
		res = self.player.send_event(event)&lt;br /&gt;
		if res:&lt;br /&gt;
			self.player.set_new_stream_time(0L)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def pause(self):&lt;br /&gt;
		self.playing = False&lt;br /&gt;
		self.player.set_state(gst.STATE_PAUSED)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def play(self):&lt;br /&gt;
		self.playing = True&lt;br /&gt;
		self.player.set_state(gst.STATE_PLAYING)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def stop(self):&lt;br /&gt;
		self.playing = False&lt;br /&gt;
		self.player.set_state(gst.STATE_NULL)&lt;br /&gt;
		self.nextMovie()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def get_state(self, timeout=1):&lt;br /&gt;
		return self.player.get_state(timeout=timeout)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def is_playing(self):&lt;br /&gt;
		return self.playing&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
class PlayVideoWindow(gtk.Window):&lt;br /&gt;
	def __init__(self):&lt;br /&gt;
		gtk.Window.__init__(self)&lt;br /&gt;
&lt;br /&gt;
		self.imagesink = None&lt;br /&gt;
		self.unset_flags(gtk.DOUBLE_BUFFERED)&lt;br /&gt;
		self.set_flags(gtk.APP_PAINTABLE)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def set_sink(self, sink):&lt;br /&gt;
		if (self.imagesink != None):&lt;br /&gt;
			assert self.window.xid&lt;br /&gt;
			self.imagesink = None&lt;br /&gt;
			del self.imagesink&lt;br /&gt;
&lt;br /&gt;
		self.imagesink = sink&lt;br /&gt;
		self.imagesink.set_xwindow_id(self.window.xid)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is a code snippet showing how you would use this file to embed media playback in your activity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		#this is our video playback window&lt;br /&gt;
		self.gplay = Gplay( )&lt;br /&gt;
		self.gplayWin = PlayVideoWindow( )&lt;br /&gt;
		self.gplay.window = self.gplayWin&lt;br /&gt;
&lt;br /&gt;
		self.gplayWin.set_type_hint( gtk.gdk.WINDOW_TYPE_HINT_DIALOG )&lt;br /&gt;
		self.gplayWin.set_decorated( False )&lt;br /&gt;
		self.gplayWin.set_transient_for( self ) #activity subclass&lt;br /&gt;
		self.gplayWin.move( 100, 100 )&lt;br /&gt;
		self.gplayWin.resize( 300, 400 )&lt;br /&gt;
		self.gplayWin.show_all( )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== How can I add play/pause buttons and a scrubber to my audio or video? ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import gtk&lt;br /&gt;
import pygtk&lt;br /&gt;
pygtk.require(&#039;2.0&#039;)&lt;br /&gt;
import pygst&lt;br /&gt;
pygst.require(&#039;0.10&#039;)&lt;br /&gt;
import gst&lt;br /&gt;
import gst.interfaces&lt;br /&gt;
import gobject&lt;br /&gt;
&lt;br /&gt;
import sugar.graphics.style&lt;br /&gt;
&lt;br /&gt;
class GScrub(gtk.Window):&lt;br /&gt;
&lt;br /&gt;
	def __init__(self, gplay, playImg, pauseImg):&lt;br /&gt;
		gtk.Window.__init__(self)&lt;br /&gt;
		self._gplay = gplay&lt;br /&gt;
		self._playImg = playImg&lt;br /&gt;
		self._pauseImg = pauseImg&lt;br /&gt;
&lt;br /&gt;
		self.UPDATE_INTERVAL = 500&lt;br /&gt;
		self.UPDATE_SCALE_ID = 0&lt;br /&gt;
		self.CHANGED_ID = 0&lt;br /&gt;
		self.was_playing = False&lt;br /&gt;
		self.p_position = gst.CLOCK_TIME_NONE&lt;br /&gt;
		self.p_duration = gst.CLOCK_TIME_NONE&lt;br /&gt;
&lt;br /&gt;
		self.hbox = gtk.HBox()&lt;br /&gt;
		self.hbox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.hbox.modify_bg( gtk.STATE_INSENSITIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.add( self.hbox )&lt;br /&gt;
&lt;br /&gt;
		self.button = gtk.Button()&lt;br /&gt;
		buttBox = gtk.EventBox()&lt;br /&gt;
		buttBox.add(self.button)&lt;br /&gt;
		buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.button.set_image( self._playImg )&lt;br /&gt;
		self.button.set_property(&#039;can-default&#039;, True)&lt;br /&gt;
		self.button.set_relief(gtk.RELIEF_NONE)&lt;br /&gt;
		self.button.set_size_request( 55, 55 )&lt;br /&gt;
		buttBox.set_size_request( 55, 55 )&lt;br /&gt;
		self.button.show()&lt;br /&gt;
&lt;br /&gt;
		buttBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		self.button.modify_bg( gtk.STATE_ACTIVE, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
&lt;br /&gt;
		self.button.connect(&#039;clicked&#039;, self._buttonClickedCb)&lt;br /&gt;
		self.hbox.pack_start(buttBox, expand=False)&lt;br /&gt;
&lt;br /&gt;
		self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)&lt;br /&gt;
		self.hscale = gtk.HScale(self.adjustment)&lt;br /&gt;
		self.hscale.set_draw_value(False)&lt;br /&gt;
		self.hscale.set_update_policy(gtk.UPDATE_CONTINUOUS)&lt;br /&gt;
		hscaleBox = gtk.EventBox()&lt;br /&gt;
		hscaleBox.modify_bg( gtk.STATE_NORMAL, sugar.graphics.style.COLOR_BLACK.get_gdk_color() )&lt;br /&gt;
		hscaleBox.add( self.hscale )&lt;br /&gt;
		self.hscale.connect(&#039;button-press-event&#039;, self._scaleButtonPressCb)&lt;br /&gt;
		self.hscale.connect(&#039;button-release-event&#039;, self._scaleButtonReleaseCb)&lt;br /&gt;
		self.hbox.pack_start(hscaleBox, expand=True)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def removeCallbacks( self ):&lt;br /&gt;
		if (self.UPDATE_SCALE_ID != 0):&lt;br /&gt;
			gobject.source_remove(self.UPDATE_SCALE_ID)&lt;br /&gt;
			self.UPDATE_SCALE_ID = 0&lt;br /&gt;
		if (self.CHANGED_ID != 0):&lt;br /&gt;
			gobject.source_remove(self.CHANGED_ID)&lt;br /&gt;
			self.CHANGED_ID = 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def reset(self):&lt;br /&gt;
		self.adjustment.set_value(0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _buttonClickedCb(self, widget):&lt;br /&gt;
		self.play_toggled()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def set_button_play(self):&lt;br /&gt;
		self.button.set_image(self._playImg)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def set_button_pause(self):&lt;br /&gt;
		self.button.set_image(self._pauseImg)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def play_toggled(self):&lt;br /&gt;
		self.p_position, self.p_duration = self._gplay.queryPosition()&lt;br /&gt;
		if (self.p_position == self.p_duration):&lt;br /&gt;
			self._gplay.seek(0)&lt;br /&gt;
			self._gplay.pause()&lt;br /&gt;
&lt;br /&gt;
		if self._gplay.is_playing():&lt;br /&gt;
			self._gplay.pause()&lt;br /&gt;
			self.set_button_play()&lt;br /&gt;
		else:&lt;br /&gt;
			#if self._gplay.error:&lt;br /&gt;
			#	#todo: check if we have &amp;quot;error&amp;quot;, and also to disable everything&lt;br /&gt;
			#	self.button.set_disabled()&lt;br /&gt;
			#else:&lt;br /&gt;
			self.doPlay()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def doPlay(self):&lt;br /&gt;
		self._gplay.play()&lt;br /&gt;
		if self.UPDATE_SCALE_ID == 0:&lt;br /&gt;
			self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)&lt;br /&gt;
		self.set_button_pause()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _scaleButtonPressCb(self, widget, event):&lt;br /&gt;
		#self.button.set_sensitive(False)&lt;br /&gt;
		self.was_playing = self._gplay.is_playing()&lt;br /&gt;
		if self.was_playing:&lt;br /&gt;
			self._gplay.pause()&lt;br /&gt;
&lt;br /&gt;
		# don&#039;t timeout-update position during seek&lt;br /&gt;
		if self.UPDATE_SCALE_ID != 0:&lt;br /&gt;
			gobject.source_remove(self.UPDATE_SCALE_ID)&lt;br /&gt;
			self.UPDATE_SCALE_ID = 0&lt;br /&gt;
&lt;br /&gt;
		# make sure we get changed notifies&lt;br /&gt;
		if self.CHANGED_ID == 0:&lt;br /&gt;
			self.CHANGED_ID = self.hscale.connect(&#039;value-changed&#039;, self._scaleValueChangedCb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _scaleButtonReleaseCb(self, widget, event):&lt;br /&gt;
		# see seek.cstop_seek&lt;br /&gt;
		widget.disconnect(self.CHANGED_ID)&lt;br /&gt;
		self.CHANGED_ID = 0&lt;br /&gt;
&lt;br /&gt;
		#self.button.set_sensitive(True)&lt;br /&gt;
		if self.was_playing:&lt;br /&gt;
			self._gplay.play()&lt;br /&gt;
&lt;br /&gt;
		if self.UPDATE_SCALE_ID != 0:&lt;br /&gt;
			pass&lt;br /&gt;
			#print(&#039;Had a previous update timeout id&#039;)&lt;br /&gt;
		else:&lt;br /&gt;
			self.UPDATE_SCALE_ID = gobject.timeout_add(self.UPDATE_INTERVAL, self._updateScaleCb)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _scaleValueChangedCb(self, scale):&lt;br /&gt;
		real = long(scale.get_value() * self.p_duration / 100) # in ns&lt;br /&gt;
		self._gplay.seek(real)&lt;br /&gt;
		# allow for a preroll&lt;br /&gt;
		self._gplay.get_state(timeout=50*gst.MSECOND) # 50 ms&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	def _updateScaleCb(self):&lt;br /&gt;
		self.p_position, self.p_duration = self._gplay.queryPosition()&lt;br /&gt;
		if self.p_position != gst.CLOCK_TIME_NONE:&lt;br /&gt;
			value = self.p_position * 100.0 / self.p_duration&lt;br /&gt;
			if (value &amp;gt; 99):&lt;br /&gt;
				value = 99&lt;br /&gt;
			elif (value &amp;lt; 0):&lt;br /&gt;
				value = 0&lt;br /&gt;
&lt;br /&gt;
			self.adjustment.set_value(value)&lt;br /&gt;
&lt;br /&gt;
			if self._gplay.is_playing() and (self.p_position == self.p_duration):&lt;br /&gt;
				self._gplay.pause()&lt;br /&gt;
				self.set_button_play()&lt;br /&gt;
&lt;br /&gt;
		return True&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Jedierikb</name></author>
	</entry>
</feed>