by Greg DeKoenigsberg
Last time, we talked about installing Sugar so that you could emulate the OLPC environment on your system. Now it’s time to explore how activities work on the XO.
Finding your activities
Let’s assume that you’ve installed Sugar on your system in
/tmp/SUGAR. One level down, you'll find a directory called
build, which replicates much of the file system of the XO -- or at least the parts that Sugar needs to work. The activities themselves would be found in
/tmp/SUGAR/build/share/activities. This corresponds to the path
/usr/share/activities on an actual XO.
Here's a list of the activities in my latest build of Sugar:
[gdk@localhost activities]$ pwd
[gdk@localhost activities]$ ll
drwxrwxr-x 3 gdk gdk 4096 Mar 17 18:43 BlockParty.activity
drwxrwxr-x 6 gdk gdk 4096 Mar 8 12:24 Develop.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 7 14:42 Etoys.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 8 19:03 GroupChat.activity
drwxr-xr-x 3 gdk gdk 4096 Mar 8 18:34 HelloWorld.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 7 14:43 Journal.activity
drwxrwxr-x 5 gdk gdk 4096 Mar 17 18:45 Kye.activity
drwxrwxr-x 6 gdk gdk 4096 Mar 17 18:36 Memosono.activity
drwxrwxr-x 7 gdk gdk 4096 Mar 7 14:43 News Reader.activity
drwxrwxr-x 11 gdk gdk 4096 Mar 7 14:43 TamTam.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 17 18:41 Web.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 17 18:44 Write.activity
drwxrwxr-x 3 gdk gdk 4096 Mar 7 14:43 Xbook.activity
Anatomy of an activity: HelloWorld
Activities are structured very simply. That's by design. The simpler the activity structure, the more likely it is that kids will be able to modify existing activities and create new ones. Let's take a quick look at the simplest of all activities: HelloWorld.
[gdk@localhost activities]$ ll HelloWorld.activity/
drwxr-xr-x 2 gdk gdk 4096 Mar 20 11:51 activity
-rw-r--r-- 1 gdk gdk 993 Mar 20 11:19 HelloWorldActivity.py
-rw-r--r-- 1 gdk gdk 1353 Mar 21 10:47 HelloWorldActivity.pyc
-rw-rw-r-- 1 gdk gdk 843 Mar 21 15:00 setup.py
This is the basic structure of an activity. There's the activity code itself, in this case
HelloWorldActivity.py; there's a boilerplate
setup.py script that bundles the activity for redistribution; and then there's the
activity directory, where all metadata about the activity is stored. Looking into the
activity, we see:
[gdk@localhost activities]$ ll HelloWorld.activity/activity/
-rw-r--r-- 1 gdk gdk 2350 Mar 8 12:09 activity-helloworld.svg
-rw-r--r-- 1 gdk gdk 186 Mar 8 12:23 activity.info
The icon for the activity is kept here in SVG format. This is the icon that shows up in the Sugar frame, and in the activity ring once the activity has been started.
We can see the metadata about the HelloWorld activity in the
name = HelloWorld
service_name = com.ywwg.HelloWorldActivity
class = HelloWorldActivity.HelloWorldActivity
icon = activity-helloworld
activity_version = 2
show_launcher = yes
Let's examine the
activity.info file line by line:
nameis obvious enough.
service_nameuniquely identifies this activity with a namespace identifier, which should be particularly familiar to Java developers. One should also perhaps expect to find the latest version of the activity at the listed namespace, but this has not yet been formalized.
classrefers to the class that will be invoked when the activity is started from the frame; in this case, we see that Sugar will look in
HelloWorldActivity.pyfor the Python class
iconspecifies the icon file to be used: in this case, the
activity-helloworld.svgfile we saw previously.
- For simplicity's sake,
activity_versionwill always be an integer. If two activities are trying to share, and one has a greater activity version, the old activity should simply be able to replace itself with the new activity.
show_launcherspecifies whether or not the activity will be available in the Sugar frame. As we see here, HelloWorld is available.
Next, let's examine the activity code itself from
HelloWorldActivity.py, with lots of comments inline in the code. If you're familiar with PyGTK -- and if you want to write Sugar activities, you should be -- then a lot of this code will look familiar. To learn more about PyGTK, check out the excellent tutorial at pygtk.org.
# Hello World opens a big window in Sugar. When the user clicks the button,
# the text "Hello World" appears in the Sugar log files.
from sugar.activity import activity
import sys, os
# hello is the function that writes 'Hello World' into the Sugar log.
def hello(self, widget, data=None):
def __init__(self, handle):
# Creates a new button with the label "Hello World".
self.button = gtk.Button("Hello World")
# When the button receives the "clicked" signal, it will call the
# function hello() passing it None as its argument. The hello()
# function is defined above.
self.button.connect("clicked", self.hello, None)
# This packs the button into ourselves (a Sugar window).
# The final step is to display this newly created widget.
Note that logs can be viewed in the developer console (Alt-0 opens it). The developer console is very useful, with all logs kept in one place. We can see the entry for the button push in the HelloWorld log:
It's useful to have access to the logs after your Sugar session is over, though. Logs are kept in the .sugar directory in your homedir:
[gdk@localhost activities]$ ll ~/.sugar/default/logs/
-rw-r--r-- 1 gdk gdk 35 Mar 21 15:06 clipboard.log
-rw-r--r-- 1 gdk gdk 38 Mar 21 15:06 HelloWorld.log
-rw-r--r-- 1 gdk gdk 871 Mar 21 15:06 presenceservice.log
-rw-rw-r-- 1 gdk gdk 931 Mar 21 15:06 shell.log
[gdk@localhost activities]$ cat ~/.sugar/default/logs/HelloWorld.log
INFO - Hello World
Things to Keep in Mind
So what's the difference, then, between writing a Sugar activity and a PyGTK desktop application? Not a lot of difference, really -- but there are a couple of key points to remember.
First of all, a Sugar activity has no dependencies on other activities. None. At all. As any user of RPM and/or dpkg knows, dependencies bring bloat. At a svelte 128M of memory, bloat is a luxury that the XO cannot afford. Besides, dependency resolution is an unwelcome bump in the user experience. If a kid wants to run that cool music activity right now, the last thing he should have to see is a dialogue that says, "coolmusicactivity requires sdl and csound-devel: download now?"
There are some tradeoffs to this approach. It means that the activity developer is strongly tied to the libraries that ship with the XO by default; if particular libraries are not available, the developer must ship them as part of the activity. One might argue that this approach causes bloat as well, since the same library might be present in a whole bunch of different activities. Ideally, that shouldn't be much of a problem; it should be simple to discard activities that aren't being used, and then download them again when they are needed. That's the goal, anyway.
The second thing to consider is the fundamental interactivity requirement for activities. Activities should be designed to be shared, and the process for sharing should be trivial. If a child sees a friend playing a neat new game, she should be able to click on that friend in her neighborhood view, receive her game code, and join in the game, immediately. In fact, Sugar practically demands this "share-ability", as we see from the following graphic:
Our version of HelloWorld can not currently be shared. There's no code in it that allows sharing. But the "share" link is still present, and will always be present -- which means that, ultimately, a good "HelloWorld" example will include sharing.
We're not quite there yet. A lot of the details around activity sharing are still being hammered out, but when those details are worked out, we'll bring them to you.
Next time, we'll look at an activity with a bit more meat. Until then, happy hacking. In the meantime, here's some great reading about Sugar from the OLPC wiki: