Sugarizing: Difference between revisions

From OLPC
Jump to navigation Jump to search
(Another example)
(Added translation and developer templates)
 
(11 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Developers}}
{{Translations}}
Non-Python programs can be made to run in the Sugar environment. This code supports the required interactions with the Sugar UI. For example:

*The activity icon appears in the frame with the others.
*The icon appears in the donut when activated.
*Menus follow the Sugar UI guidelines.
*The activity has the proper suspend/resume behavior.
*If appropriate, the activity can be shared correctly.
*Projects can be saved to the Journal and resumed.

From most to least Python, methods include:

*Turn the non-Python code into something that can be controlled from Python, then write the activity in Python. The web browser (same engine as Firefox) and word processor (AbiWord) were done this way.
*Write a small Python wrapper. There have been problems with this approach.
*Avoid Python entirely, using the raw [[API]]. GTK will allow use of most Sugar UI elements. For some toolkits, such as [[SDL]], the [http://lists.laptop.org/pipermail/devel/2008-January/009387.html sugarize tool] may be required.

==[[Tux Paint]]==
Tux Paint uses the sugarize tool.

==Etoys==
==Etoys==


[[Etoys]] for Sugar is based on the squeakland.org version of Squeak. An earlier activity version used a Python activity wrapper that opened a GTKSocket window and forked off the Squeak virtual machine, which opened a window itself that was redirected into the wrapper. A pipe was used to continuously exchange commands between the wrapper and Etoys, e.g., when Python received a message on the D-Bus it was forwarded on the pipe into Etoys.
The current [[Etoys]] version for OLPC is based on the squeakland.org version. It runs on the latest [[Squeak]] VM with minimal [[Sugar]] glue code. The details of sugarizing Etoys were documented as the [[Low-level Activity API]].

Nowadays Etoys is a native Sugar activity. A tiny shell script in the activity bundle executes the Squeak virtual machine, which adds the required Sugar X properties to the main window. The Smalltalk code inside Etoys then creates a D-Bus service and talks to Sugar via D-Bus for Datastore access, Sharing, etc., according to the [[Low-level Activity API]].


==SimCity==
==SimCity==


You can examine the [http://dev.laptop.org/git?p=projects/simcity-activity;a=blob_plain;f=simcityactivity.py;hb=HEAD glue code used by SimCity].
You can examine the [http://dev.laptop.org/git?p=projects/simcity-activity;a=blob_plain;f=simcityactivity.py;hb=HEAD glue code used by SimCity], although it doesn't work very well, according to the developers.


# -*- mode: python; tab-width: 4 -*-
# -*- mode: python; tab-width: 4 -*-
#
#
# SimCity, Unix Version. This game was released for the Unix platform
# [[SimCity]], Unix Version. This game was released for the Unix platform
# in or about 1990 and has been modified for inclusion in the One Laptop
# in or about 1990 and has been modified for inclusion in the One Laptop
# Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
# Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If
Line 39: Line 61:
try:
try:
import pygame.mixer
import pygame.mixer
pygame.mixer.init()
pygame.mixer.init()
except: pass
except: pass
try:
try:
from sugar.presence import presenceservice
from sugar.presence import presenceservice
except ImportError:
except ImportError:
from sugar.presence import PresenceService as presenceservice
from sugar.presence import PresenceService as presenceservice
def QuoteTCL(s):
def QuoteTCL(s):
return s.replace('"', '\\"')
return s.replace('"', '\\"')
class SimCityActivity(activity.Activity):
class SimCityActivity(activity.Activity):
def __init__(self, handle):
def __init__(self, handle):
activity.Activity.__init__(self, handle)
activity.Activity.__init__(self, handle)
self.set_title(_('SimCity Activity'))
self.set_title(_('SimCity Activity'))
self.connect('destroy', self._destroy_cb)
self.connect('destroy', self._destroy_cb)
self.connect('focus-in-event', self._focus_in_cb)
self.connect('focus-in-event', self._focus_in_cb)
self.connect('focus-out-event', self._focus_out_cb)
self.connect('focus-out-event', self._focus_out_cb)
signal.signal(signal.SIGCHLD, self._sigchild_handler)
signal.signal(signal.SIGCHLD, self._sigchild_handler)
self._bundle_path = get_bundle_path()
self._bundle_path = get_bundle_path()
if False:
if False:
# FIXME: Plug SimCity's window into a gtk socket.
# FIXME: Plug SimCity's window into a gtk socket.
# Doesn't work yet, but it would be cool if it did.
# Doesn't work yet, but it would be cool if it did.
socket = gtk.Socket()
socket = gtk.Socket()
try:
try:
self.set_canvas(socket)
self.set_canvas(socket)
except AttributeError:
except AttributeError:
self.add(socket)
self.add(socket)
socket.show()
socket.show()
socket.connect('plug-added', self._plug_added_cb)
socket.connect('plug-added', self._plug_added_cb)
socket.connect('plug-removed', self._plug_removed_cb)
socket.connect('plug-removed', self._plug_removed_cb)
win = socket.get_id()
win = socket.get_id()
command = os.path.join(
command = os.path.join(
self._bundle_path,
self._bundle_path,
'SimCity')
'SimCity')
args = [
args = [
command,
command,
#'-R', str(win), # Set root window to socket window id
#'-R', str(win), # Set root window to socket window id
'-t', # Interactive tty mode, so we can send it commands.
'-t', # Interactive tty mode, so we can send it commands.
]
]
logging.debug("CWD: " + self._bundle_path)
logging.debug("CWD: " + self._bundle_path)
logging.debug("SIMCITY ARGS: " + repr(args))
logging.debug("SIMCITY ARGS: " + repr(args))
self._process = subprocess.Popen(
self._process = subprocess.Popen(
args,
args,
stdin=subprocess.PIPE,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stdout=subprocess.PIPE,
close_fds=True,
close_fds=True,
cwd=self._bundle_path,
cwd=self._bundle_path,
preexec_fn=lambda: os.chdir(self._bundle_path))
preexec_fn=lambda: os.chdir(self._bundle_path))
logging.debug("STARTING THREAD... " + str(self._stdout_thread_function))
logging.debug("STARTING THREAD... " + str(self._stdout_thread_function))
t = None
t = None
try:
try:
t = thread.start_new(
t = thread.start_new(
self._stdout_thread_function,
self._stdout_thread_function,
())
())
except Exception, e:
except Exception, e:
logging.debug("EXCEPTION " + str(e))
logging.debug("EXCEPTION " + str(e))
self._stdout_thread = t
self._stdout_thread = t
logging.debug("STARTED THREAD. " + str(t))
logging.debug("STARTED THREAD. " + str(t))
uri = handle.uri or ''
uri = handle.uri or ''
logging.debug("SIMCITYACTIVITY SUGARSTARTUP URI " + repr(uri))
logging.debug("SIMCITYACTIVITY SUGARSTARTUP URI " + repr(uri))
self.send_process(
self.send_process(
'SugarStartUp "' + QuoteTCL(uri) + '"\n')
'SugarStartUp "' + QuoteTCL(uri) + '"\n')
nick = profile.get_nick_name() or ''
nick = profile.get_nick_name() or ''
logging.debug("SIMCITYACTIVITY SUGARNICKNAME NICK " + repr(nick))
logging.debug("SIMCITYACTIVITY SUGARNICKNAME NICK " + repr(nick))
self.send_process(
self.send_process(
'SugarNickName "' + QuoteTCL(nick) + '"\n')
'SugarNickName "' + QuoteTCL(nick) + '"\n')
#logging.debug("started SimCity, pid " + repr(self._pid))
#logging.debug("started SimCity, pid " + repr(self._pid))
ps = presenceservice.get_instance()
ps = presenceservice.get_instance()
for buddy in ps.get_buddies():
for buddy in ps.get_buddies():
self._buddy_appeared_cb(ps, buddy)
self._buddy_appeared_cb(ps, buddy)
ps.connect("buddy-appeared", self._buddy_appeared_cb)
ps.connect("buddy-appeared", self._buddy_appeared_cb)
ps.connect("buddy-disappeared", self._buddy_disappeared_cb)
ps.connect("buddy-disappeared", self._buddy_disappeared_cb)
def _stdout_thread_function(self, *args, **keys):
def _stdout_thread_function(self, *args, **keys):
logging.debug("_stdout_thread_function BEGIN " + repr(args) + " " + repr(keys))
logging.debug("_stdout_thread_function BEGIN " + repr(args) + " " + repr(keys))
f = self._process.stdout
f = self._process.stdout
fcntl.fcntl(f.fileno(), fcntl.F_SETFD, 0)
fcntl.fcntl(f.fileno(), fcntl.F_SETFD, 0)
while True:
while True:
line = 'XXX'
line = 'XXX'
try:
try:
line = f.readline()
line = f.readline()
except Exception, e:
except Exception, e:
logging.debug("READLINE EXCEPTION " + str(e))
logging.debug("READLINE EXCEPTION " + str(e))
break
break
logging.debug("LINE: " + repr(line))
logging.debug("LINE: " + repr(line))
line = line.strip()
line = line.strip()
if not line:
if not line:
continue
continue
words = line.strip().split(' ')
words = line.strip().split(' ')
command = words[0]
command = words[0]
if command == 'PlaySound':
if command == 'PlaySound':
logging.debug("PLAYSOUND " + " ".join(words[1:]))
logging.debug("PLAYSOUND " + " ".join(words[1:]))
self.play_sound(words[1])
self.play_sound(words[1])
else:
else:
pass # logging.debug(">>> " + line)
pass # logging.debug(">>> " + line)
logging.debug("_stdout_thread_function END")
logging.debug("_stdout_thread_function END")
def play_sound(self, name):
def play_sound(self, name):
fileName = os.path.join(
fileName = os.path.join(
self._bundle_path,
self._bundle_path,
'res/sounds',
'res/sounds',
name.lower() + '.wav')
name.lower() + '.wav')
print "PLAY_SOUND " + fileName
print "PLAY_SOUND " + fileName
try:
try:
sound = pygame.mixer.Sound(fileName)
sound = pygame.mixer.Sound(fileName)
sound.play()
sound.play()
except Exception, e:
except Exception, e:
print "Can't play sound: " + fileName + " " + str(e)
print "Can't play sound: " + fileName + " " + str(e)
pass
pass
def send_process(self, message):
def send_process(self, message):
logging.debug("SEND_PROCESS " + message)
logging.debug("SEND_PROCESS " + message)
self._process.stdin.write(message)
self._process.stdin.write(message)
def share(self):
def share(self):
logging.debug("SHARE")
logging.debug("SHARE")
Activity.share(self)
Activity.share(self)
self.send_process(
self.send_process(
'SugarShare\n')
'SugarShare\n')
def quit_process(self):
def quit_process(self):
logging.debug("QUIT_PROCESS")
logging.debug("QUIT_PROCESS")
self.send_process(
self.send_process(
'SugarQuit\n')
'SugarQuit\n')
time.sleep(10)
time.sleep(10)
def _plug_added_cb(self, sock):
def _plug_added_cb(self, sock):
logging.debug("SimCity window opened")
logging.debug("SimCity window opened")
return False
return False
def _plug_removed_cb(self, sock):
def _plug_removed_cb(self, sock):
logging.debug("SimCity window closed")
logging.debug("SimCity window closed")
self.destroy()
self.destroy()
return False
return False
def _destroy_cb(self, window):
def _destroy_cb(self, window):
logging.debug("SimCity activity destroyed %r" % window)
logging.debug("SimCity activity destroyed %r" % window)
self.quit_process()
self.quit_process()
def _focus_in_cb(self, window, event):
def _focus_in_cb(self, window, event):
logging.debug("SimCity activated %r %r" % (window, event))
logging.debug("SimCity activated %r %r" % (window, event))
self.send_process(
self.send_process(
'SugarActivate\n')
'SugarActivate\n')
def _focus_out_cb(self, window, event):
def _focus_out_cb(self, window, event):
logging.debug("SimCity deactivated %r %r" % (window, event))
logging.debug("SimCity deactivated %r %r" % (window, event))
self.send_process(
self.send_process(
'SugarDeactivate\n')
'SugarDeactivate\n')
def _buddy_appeared_cb(self, ps, buddy):
def _buddy_appeared_cb(self, ps, buddy):
try:
try:
key = buddy.props.key or ''
key = buddy.props.key or ''
nick = buddy.props.nick or ''
nick = buddy.props.nick or ''
color = buddy.props.color or ''
color = buddy.props.color or ''
address = buddy.props.ip4_address or ''
address = buddy.props.ip4_address or ''
except AttributeError:
except AttributeError:
key = buddy.get_name() or ''
key = buddy.get_name() or ''
nick = buddy.get_name() or ''
nick = buddy.get_name() or ''
color = buddy.get_color() or ''
color = buddy.get_color() or ''
address = buddy.get_ip4_address() or ''
address = buddy.get_ip4_address() or ''
logging.debug("SIMCITYACTIVITY _BUDDY_APPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))
logging.debug("SIMCITYACTIVITY _BUDDY_APPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))
logging.debug("Buddy appeared " + repr(buddy.props.nick))
logging.debug("Buddy appeared " + repr(buddy.props.nick))
self.send_process(
self.send_process(
'SugarBuddyAdd "' +
'SugarBuddyAdd "' +
QuoteTCL(key) + '" "' +
QuoteTCL(key) + '" "' +
QuoteTCL(nick) + '" "' +
QuoteTCL(nick) + '" "' +
QuoteTCL(color) + '" "' +
QuoteTCL(color) + '" "' +
QuoteTCL(address) + '"\n')
QuoteTCL(address) + '"\n')
def _buddy_disappeared_cb(self, ps, buddy):
def _buddy_disappeared_cb(self, ps, buddy):
try:
try:
key = buddy.props.key or ''
key = buddy.props.key or ''
nick = buddy.props.nick or ''
nick = buddy.props.nick or ''
color = buddy.props.color or ''
color = buddy.props.color or ''
address = buddy.props.ip4_address or ''
address = buddy.props.ip4_address or ''
except AttributeError:
except AttributeError:
key = buddy.get_name() or ''
key = buddy.get_name() or ''
nick = buddy.get_name() or ''
nick = buddy.get_name() or ''
color = buddy.get_color() or ''
color = buddy.get_color() or ''
address = buddy.get_ip4_address() or ''
address = buddy.get_ip4_address() or ''
logging.debug("SIMCITYACTIVITY _BUDDY_DISAPPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))
logging.debug("SIMCITYACTIVITY _BUDDY_DISAPPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))
logging.debug("Buddy disappeared " + repr(buddy.props.nick))
logging.debug("Buddy disappeared " + repr(buddy.props.nick))
self.send_process(
self.send_process(
'SugarBuddyDel "' +
'SugarBuddyDel "' +
QuoteTCL(key) + '" "' +
QuoteTCL(key) + '" "' +
QuoteTCL(nick) + '" "' +
QuoteTCL(nick) + '" "' +
QuoteTCL(color) + '" "' +
QuoteTCL(color) + '" "' +
QuoteTCL(address) + '"\n')
QuoteTCL(address) + '"\n')
def _sigchild_handler(self, signum, frame):
def _sigchild_handler(self, signum, frame):
logging.debug("got signal %i %r %r" % (signum, frame, self._process))
logging.debug("got signal %i %r %r" % (signum, frame, self._process))
sys.exit(0)
sys.exit(0)




Line 281: Line 303:
micropolis-acivity.
micropolis-acivity.


The [ http://dev.laptop.org/git?p=projects/simcity-activity;a=summary SimCity project] is written in C with TCL
The [http://dev.laptop.org/git?p=projects/simcity-activity;a=summary SimCity project] is written in C with TCL
scripting.
scripting.


This isn't a very Sugarized app, but it runs, goes in the donut, quits properly, and plays sounds. It also tries to be installable as a .xo file (see the src/Makefile -- this works for initial installs, but not for upgrades due to Sugar bugs). It notices
This isn't a very Sugarized app, but it runs, goes in the donut, quits properly, and plays sounds. It also tries to be installable as a .xo file (see the src/Makefile -- this works for initial installs, but not for upgrades due to Sugar bugs). Sometimes a circle icon is left in the donut. It notices but ignores all the sharing stuff at the moment; we could barely figure out the UI syntax to share an app, let alone how to program it, nor what it would mean to the gameplay of SimCity. Sharing might mean that each person gets a piece of land, or that cities can sell resources like electricity.

but ignores all the sharing stuff at the moment; we could barely figure out the UI syntax to share an app, let alone how to program it, nor what it would mean to the gameplay of SimCity.
== See Also ==


*[[Low-level Activity API]]
==Tutorial==
*[[Sugar Factory]]
*http://www.catmoran.com/olpc/sug
*http://www.catmoran.com/olpc/#sugxterm


To come.


{{stub}}
{{stub}}
[[Category:Developers]]

Latest revision as of 07:13, 16 July 2011

  english | español HowTo [ID# 258112]  +/-  

Non-Python programs can be made to run in the Sugar environment. This code supports the required interactions with the Sugar UI. For example:

  • The activity icon appears in the frame with the others.
  • The icon appears in the donut when activated.
  • Menus follow the Sugar UI guidelines.
  • The activity has the proper suspend/resume behavior.
  • If appropriate, the activity can be shared correctly.
  • Projects can be saved to the Journal and resumed.

From most to least Python, methods include:

  • Turn the non-Python code into something that can be controlled from Python, then write the activity in Python. The web browser (same engine as Firefox) and word processor (AbiWord) were done this way.
  • Write a small Python wrapper. There have been problems with this approach.
  • Avoid Python entirely, using the raw API. GTK will allow use of most Sugar UI elements. For some toolkits, such as SDL, the sugarize tool may be required.

Tux Paint

Tux Paint uses the sugarize tool.

Etoys

Etoys for Sugar is based on the squeakland.org version of Squeak. An earlier activity version used a Python activity wrapper that opened a GTKSocket window and forked off the Squeak virtual machine, which opened a window itself that was redirected into the wrapper. A pipe was used to continuously exchange commands between the wrapper and Etoys, e.g., when Python received a message on the D-Bus it was forwarded on the pipe into Etoys.

Nowadays Etoys is a native Sugar activity. A tiny shell script in the activity bundle executes the Squeak virtual machine, which adds the required Sugar X properties to the main window. The Smalltalk code inside Etoys then creates a D-Bus service and talks to Sugar via D-Bus for Datastore access, Sharing, etc., according to the Low-level Activity API.

SimCity

You can examine the glue code used by SimCity, although it doesn't work very well, according to the developers.

# -*- mode: python; tab-width: 4 -*-
#
# SimCity, Unix Version.  This game was released for the Unix platform
# in or about 1990 and has been modified for inclusion in the One Laptop
# Per Child program.  Copyright (C) 1989 - 2007 Electronic Arts Inc.  If
# you need assistance with this program, you may contact:
#   http://wiki.laptop.org/go/SimCity  or email  simcity@laptop.org.
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#[Portions of license removed] 

import gtk
import os
import signal
import tempfile
import logging
import sys
import time
import subprocess
import thread
import fcntl

from sugar.activity import activity
from sugar.activity.activity import get_bundle_path
from sugar import profile
from gettext import gettext as _
from glob import glob

try:
    import pygame.mixer
    pygame.mixer.init()
except: pass

try:
    from sugar.presence import presenceservice
except ImportError:
    from sugar.presence import PresenceService as presenceservice


def QuoteTCL(s):
    return s.replace('"', '\\"')


class SimCityActivity(activity.Activity):

    def __init__(self, handle):

        activity.Activity.__init__(self, handle)
   
        self.set_title(_('SimCity Activity'))
        self.connect('destroy', self._destroy_cb)
        self.connect('focus-in-event', self._focus_in_cb)
        self.connect('focus-out-event', self._focus_out_cb)

        signal.signal(signal.SIGCHLD, self._sigchild_handler)

        self._bundle_path = get_bundle_path()

        if False:
            # FIXME: Plug SimCity's window into a gtk socket.
            # Doesn't work yet, but it would be cool if it did. 
            socket = gtk.Socket()
            try:
                self.set_canvas(socket)
            except AttributeError:
                self.add(socket)
            socket.show()
            socket.connect('plug-added', self._plug_added_cb)
            socket.connect('plug-removed', self._plug_removed_cb)

            win = socket.get_id()

        command = os.path.join(
            self._bundle_path,
            'SimCity')

        args = [
            command,
            #'-R', str(win), # Set root window to socket window id
            '-t', # Interactive tty mode, so we can send it commands.
        ]

        logging.debug("CWD: " + self._bundle_path)
        logging.debug("SIMCITY ARGS: " + repr(args))

        self._process = subprocess.Popen(
            args,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            close_fds=True,
            cwd=self._bundle_path,
            preexec_fn=lambda: os.chdir(self._bundle_path))

        logging.debug("STARTING THREAD... " + str(self._stdout_thread_function))
        t = None
        try:
            t = thread.start_new(
                self._stdout_thread_function,
                ())
        except Exception, e:
            logging.debug("EXCEPTION " + str(e))
        self._stdout_thread = t
        logging.debug("STARTED THREAD. " + str(t))

        uri = handle.uri or 
        logging.debug("SIMCITYACTIVITY SUGARSTARTUP URI " + repr(uri))
        self.send_process(
            'SugarStartUp "' + QuoteTCL(uri) + '"\n')

        nick = profile.get_nick_name() or 
        logging.debug("SIMCITYACTIVITY SUGARNICKNAME NICK " + repr(nick))
        self.send_process(
            'SugarNickName "' + QuoteTCL(nick) + '"\n')

        #logging.debug("started SimCity, pid " + repr(self._pid))

        ps = presenceservice.get_instance()

        for buddy in ps.get_buddies():
            self._buddy_appeared_cb(ps, buddy)

        ps.connect("buddy-appeared", self._buddy_appeared_cb)
        ps.connect("buddy-disappeared", self._buddy_disappeared_cb)


    def _stdout_thread_function(self, *args, **keys):
        logging.debug("_stdout_thread_function BEGIN " + repr(args) + " " + repr(keys))
        f = self._process.stdout
        fcntl.fcntl(f.fileno(), fcntl.F_SETFD, 0)
        while True:
            line = 'XXX'
            try:
                line = f.readline()
            except Exception, e:
                logging.debug("READLINE EXCEPTION " + str(e))
                break
            logging.debug("LINE: " + repr(line))
            line = line.strip()
            if not line:
                continue
            words = line.strip().split(' ')
            command = words[0]
            if command == 'PlaySound':
                logging.debug("PLAYSOUND " + " ".join(words[1:]))
                self.play_sound(words[1])
            else:
                pass # logging.debug(">>> " + line)
        logging.debug("_stdout_thread_function END")


    def play_sound(self, name):
        fileName = os.path.join(
            self._bundle_path,
            'res/sounds',
            name.lower() + '.wav')
        print "PLAY_SOUND " + fileName
        try:
            sound = pygame.mixer.Sound(fileName)
            sound.play()
        except Exception, e:
            print "Can't play sound: " + fileName + " " + str(e)
            pass


    def send_process(self, message):
        logging.debug("SEND_PROCESS " + message)
        self._process.stdin.write(message)


    def share(self):
        logging.debug("SHARE")
        Activity.share(self)
        self.send_process(
            'SugarShare\n')


    def quit_process(self):
        logging.debug("QUIT_PROCESS")
        self.send_process(
            'SugarQuit\n')
        time.sleep(10)


    def _plug_added_cb(self, sock):
        logging.debug("SimCity window opened")
        return False


    def _plug_removed_cb(self, sock):
        logging.debug("SimCity window closed")
        self.destroy()
        return False

   
    def _destroy_cb(self, window):
        logging.debug("SimCity activity destroyed %r" % window)
        self.quit_process()

       
    def _focus_in_cb(self, window, event):
        logging.debug("SimCity activated %r %r" % (window, event))
        self.send_process(
            'SugarActivate\n')


    def _focus_out_cb(self, window, event):
        logging.debug("SimCity deactivated %r %r" % (window, event))
        self.send_process(
            'SugarDeactivate\n')


    def _buddy_appeared_cb(self, ps, buddy):

        try:
            key = buddy.props.key or 
            nick = buddy.props.nick or 
            color = buddy.props.color or 
            address = buddy.props.ip4_address or 
        except AttributeError:
            key = buddy.get_name() or 
            nick = buddy.get_name() or 
            color = buddy.get_color() or 
            address = buddy.get_ip4_address() or 

        logging.debug("SIMCITYACTIVITY _BUDDY_APPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))

        logging.debug("Buddy appeared " + repr(buddy.props.nick))

        self.send_process(
            'SugarBuddyAdd "' +
            QuoteTCL(key) + '" "' +
            QuoteTCL(nick) + '" "' +
            QuoteTCL(color) + '" "' +
            QuoteTCL(address) + '"\n')

    def _buddy_disappeared_cb(self, ps, buddy):

        try:
            key = buddy.props.key or 
            nick = buddy.props.nick or 
            color = buddy.props.color or 
            address = buddy.props.ip4_address or 
        except AttributeError:
            key = buddy.get_name() or 
            nick = buddy.get_name() or 
            color = buddy.get_color() or 
            address = buddy.get_ip4_address() or 

        logging.debug("SIMCITYACTIVITY _BUDDY_DISAPPEARED_CB KEY " + repr(key) + " NICK " + repr(nick) + " COLOR " + repr(color) + " ADDRESS " + repr(address))

        logging.debug("Buddy disappeared " + repr(buddy.props.nick))

        self.send_process(
            'SugarBuddyDel "' +
            QuoteTCL(key) + '" "' +
            QuoteTCL(nick) + '" "' +
            QuoteTCL(color) + '" "' +
            QuoteTCL(address) + '"\n')

    def _sigchild_handler(self, signum, frame):
        logging.debug("got signal %i %r %r" % (signum, frame, self._process))
        sys.exit(0)


Don Hopkins did the work. I believe that Don stole some of it from the eToys glue code.

If you're going to stick it in documentation as an example, then I recommend using the Micropolis version (with the EA trademarks untimely ripp'd out), which should be in git within a week, under micropolis-acivity.

The SimCity project is written in C with TCL scripting.

This isn't a very Sugarized app, but it runs, goes in the donut, quits properly, and plays sounds. It also tries to be installable as a .xo file (see the src/Makefile -- this works for initial installs, but not for upgrades due to Sugar bugs). Sometimes a circle icon is left in the donut. It notices but ignores all the sharing stuff at the moment; we could barely figure out the UI syntax to share an app, let alone how to program it, nor what it would mean to the gameplay of SimCity. Sharing might mean that each person gets a piece of land, or that cities can sell resources like electricity.

See Also


This article is a stub. You can help the OLPC project by expanding it.