Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter@sugarlabs.org>2011-01-26 01:04:50 (GMT)
committer Walter Bender <walter@sugarlabs.org>2011-01-26 01:04:50 (GMT)
commit4ad1d9c535a0183a628c92edc6709150f1879cdc (patch)
tree1e6564ffad646f628fc3cb98452c36c5d64ac964
parentaaee4ed0879f85ab754bba8bcee5ad4330106a68 (diff)
parent25b970f077bd0cae6b38a6dcb57652f4631c8c55 (diff)
Merge git://git.collabora.co.uk/git/user/rgs/turtleart/
-rw-r--r--TurtleArt/collaboration.py251
-rwxr-xr-xTurtleArt/tacanvas.py66
-rwxr-xr-xTurtleArt/tawindow.py6
-rw-r--r--TurtleArtActivity.py384
-rw-r--r--extra/__init__.py0
-rw-r--r--extra/upload.py170
-rwxr-xr-xturtleart.py558
7 files changed, 753 insertions, 682 deletions
diff --git a/TurtleArt/collaboration.py b/TurtleArt/collaboration.py
new file mode 100644
index 0000000..21cae17
--- /dev/null
+++ b/TurtleArt/collaboration.py
@@ -0,0 +1,251 @@
+
+from dbus.service import signal
+from dbus.gobject_service import ExportedGObject
+import logging
+import telepathy
+from sugar import profile
+from sugar.presence import presenceservice
+from sugar.presence.tubeconn import TubeConnection
+
+SERVICE = 'org.laptop.TurtleArtActivity'
+IFACE = SERVICE
+PATH = '/org/laptop/TurtleArtActivity'
+_logger = logging.getLogger('turtleart-activity')
+
+class Collaboration():
+ def __init__(self, tw, activity):
+ """ A simplistic sharing model: the sharer is the master """
+ self._tw = tw
+ self._activity = activity
+
+ def setup(self):
+ # TODO: hand off role of master is sharer leaves
+ self.pservice = presenceservice.get_instance()
+ self.initiating = None # sharing (True) or joining (False)
+
+ # Add my buddy object to the list
+ owner = self.pservice.get_owner()
+ self.owner = owner
+ self.tw.buddies.append(self.owner)
+ self._share = ""
+
+ self._activity.connect('shared', self._shared_cb)
+ self._activity.connect('joined', self._joined_cb)
+
+ def _shared_cb(self, activity):
+ self._shared_activity = self._activity._shared_activity
+ if self._shared_activity is None:
+ _logger.error("Failed to share or join activity ... \
+ _shared_activity is null in _shared_cb()")
+ return
+
+ self.initiating = True
+ self.waiting_for_turtles = False
+ self.turtle_dictionary = self._get_dictionary()
+
+ _logger.debug('I am sharing...')
+
+ self.conn = self._shared_activity.telepathy_conn
+ self.tubes_chan = self._shared_activity.telepathy_tubes_chan
+ self.text_chan = self._shared_activity.telepathy_text_chan
+
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
+ 'NewTube', self._new_tube_cb)
+
+ _logger.debug('This is my activity: making a tube...')
+
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ SERVICE, {})
+
+ def _joined_cb(self, activity):
+ self._shared_activity = self._activity._shared_activity
+ if self._shared_activity is None:
+ _logger.error("Failed to share or join activity ... \
+ _shared_activity is null in _shared_cb()")
+ return
+
+ self.initiating = False
+ self.conn = self._shared_activity.telepathy_conn
+ self.tubes_chan = self._shared_activity.telepathy_tubes_chan
+ self.text_chan = self._shared_activity.telepathy_text_chan
+
+ # call back for "NewTube" signal
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
+ 'NewTube', self._new_tube_cb)
+
+ _logger.debug('I am joining an activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ # Joiner should request current state from sharer.
+ self.waiting_for_turtles = True
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ _logger.error('ListTubes() failed: %s', e)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ """ Create a new tube. """
+ _logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', id, initiator, type, service,
+ params, state)
+
+ if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[ \
+ telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+
+ tube_conn = TubeConnection(self.conn,
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \
+ group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
+
+ # We'll use a chat tube to send serialized stacks back and forth.
+ self.chattube = ChatTube(tube_conn, self.initiating, \
+ self.event_received_cb)
+
+ # Now that we have the tube, we can ask for the turtle dictionary.
+ if self.waiting_for_turtles:
+ _logger.debug("Sending a request for the turtle dictionary")
+ # we need to send our own nick and colors
+ colors = self._get_colors()
+ event = "t|" + data_to_string([self._get_nick(), colors])
+ _logger.debug(event)
+ self.send_event(event)
+
+ def event_received_cb(self, text):
+ """
+ Events are sent as a tuple, nick|cmd, where nick is a turle name
+ and cmd is a turtle event. Everyone gets the turtle dictionary from
+ the sharer and watches for 't' events, which indicate that a new
+ turtle has joined.
+ """
+ if len(text) == 0:
+ return
+ # Save active Turtle
+ save_active_turtle = self.tw.active_turtle
+ e = text.split("|", 2)
+ text = e[1]
+ if e[0] == 't': # request for turtle dictionary
+ if text > 0:
+ [nick, colors] = data_from_string(text)
+ if nick != self.tw.nick:
+ # There may not be a turtle dictionary.
+ if hasattr(self, "turtle_dictionary"):
+ self.turtle_dictionary[nick] = colors
+ else:
+ self.turtle_dictionary = {nick: colors}
+ # Add new turtle for the joiner.
+ self.tw.canvas.set_turtle(nick, colors)
+ # Sharer should send turtle dictionary.
+ if self.initiating:
+ text = data_to_string(self.turtle_dictionary)
+ self.send_event("T|" + text)
+ elif e[0] == 'T': # Receiving the turtle dictionary.
+ if self.waiting_for_turtles:
+ if len(text) > 0:
+ self.turtle_dictionary = data_from_string(text)
+ for nick in self.turtle_dictionary:
+ if nick != self.tw.nick:
+ colors = self.turtle_dictionary[nick]
+ # add new turtle for the joiner
+ self.tw.canvas.set_turtle(nick, colors)
+ self.waiting_for_turtles = False
+ elif e[0] == 'f': # move a turtle forward
+ if len(text) > 0:
+ [nick, x] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.forward(x, False)
+ elif e[0] == 'a': # move a turtle in an arc
+ if len(text) > 0:
+ [nick, [a, r]] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.arc(a, r, False)
+ elif e[0] == 'r': # rotate turtle
+ if len(text) > 0:
+ [nick, h] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.seth(h, False)
+ elif e[0] == 'x': # set turtle xy position
+ if len(text) > 0:
+ [nick, [x, y]] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.setxy(x, y, False)
+ elif e[0] == 'c': # set turtle pen color
+ if len(text) > 0:
+ [nick, x] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.setcolor(x, False)
+ elif e[0] == 'g': # set turtle pen gray level
+ if len(text) > 0:
+ [nick, x] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.setgray(x, False)
+ elif e[0] == 's': # set turtle pen shade
+ if len(text) > 0:
+ [nick, x] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.setshade(x, False)
+ elif e[0] == 'w': # set turtle pen width
+ if len(text) > 0:
+ [nick, x] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.setpensize(x, False)
+ elif e[0] == 'p': # set turtle pen state
+ if len(text) > 0:
+ [nick, x] = data_from_string(text)
+ if nick != self.tw.nick:
+ self.tw.canvas.set_turtle(nick)
+ self.tw.canvas.setpen(x, False)
+ # Restore active Turtle
+ self.tw.canvas.set_turtle(self.tw.turtles.get_turtle_key(
+ save_active_turtle))
+
+ def send_event(self, entry):
+ """ Send event through the tube. """
+ if hasattr(self, 'chattube') and self.chattube is not None:
+ self.chattube.SendText(entry)
+
+ def _get_dictionary(self):
+ d = { self._get_nick(): self._get_colors()}
+ return d
+
+ def _get_nick(self):
+ return self.tw.nick
+
+ def _get_colors(self):
+ return profile.get_color().to_string()
+
+class ChatTube(ExportedGObject):
+
+ def __init__(self, tube, is_initiator, stack_received_cb):
+ """Class for setting up tube for sharing."""
+ super(ChatTube, self).__init__(tube, PATH)
+ self.tube = tube
+ self.is_initiator = is_initiator # Are we sharing or joining activity?
+ self.stack_received_cb = stack_received_cb
+ self.stack = ''
+
+ self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \
+ path=PATH, sender_keyword='sender')
+
+ def send_stack_cb(self, text, sender=None):
+ if sender == self.tube.get_unique_name():
+ return
+ self.stack = text
+ self.stack_received_cb(text)
+
+ @signal(dbus_interface=IFACE, signature='s')
+ def SendText(self, text):
+ self.stack = text
diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py
index ed71c4b..45c389c 100755
--- a/TurtleArt/tacanvas.py
+++ b/TurtleArt/tacanvas.py
@@ -1,5 +1,6 @@
#Copyright (c) 2007-8, Playful Invention Company.
#Copyright (c) 2008-10, Walter Bender
+#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
@@ -219,9 +220,8 @@ class TurtleGraphics:
self.height / 2 - self.ycor)
self.tw.svg_string += "\"\n"
self.tw.svg_string += self.svg.style()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("f|%s" % \
- (data_to_string([self.tw.nick, int(n)])))
+ event = "f|%s" % (data_to_string([self._get_my_nick(), int(n)]))
+ self._send_event(event, share)
def seth(self, n, share=True):
""" Set the turtle heading. """
@@ -232,9 +232,8 @@ class TurtleGraphics:
return
self.heading %= 360
self.turn_turtle()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("r|%s" % \
- (data_to_string([self.tw.nick, round_int(self.heading)])))
+ event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)]))
+ self._send_event(event, share)
def right(self, n, share=True):
""" Rotate turtle clockwise """
@@ -245,9 +244,8 @@ class TurtleGraphics:
return
self.heading %= 360
self.turn_turtle()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("r|%s" % \
- (data_to_string([self.tw.nick, round_int(self.heading)])))
+ event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)]))
+ self._send_event(event, share)
def arc(self, a, r, share=True):
""" Draw an arc """
@@ -262,9 +260,8 @@ class TurtleGraphics:
_logger.debug("bad value sent to %s" % (__name__))
return
self.move_turtle()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("a|%s" % \
- (data_to_string([self.tw.nick, [round_int(a), round_int(r)]])))
+ event = "a|%s" % (data_to_string([self._get_my_nick(), [round_int(a), round_int(r)]]))
+ self._send_event(event, share)
def rarc(self, a, r):
""" draw a clockwise arc """
@@ -350,9 +347,8 @@ class TurtleGraphics:
self.draw_line(oldx, oldy, self.xcor, self.ycor)
self.move_turtle()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("x|%s" % \
- (data_to_string([self.tw.nick, [round_int(x), round_int(y)]])))
+ event = "x|%s" % (data_to_string([self._get_my_nick(), [round_int(x), round_int(y)]]))
+ self._send_event(event, share)
def setpensize(self, ps, share=True):
""" Set the pen size """
@@ -367,9 +363,8 @@ class TurtleGraphics:
self.gc.set_line_attributes(int(self.pensize * self.tw.coord_scale),
gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER)
self.svg.set_stroke_width(self.pensize)
- if self.tw.sharing() and share:
- self.tw.activity.send_event("w|%s" % \
- (data_to_string([self.tw.nick, round_int(ps)])))
+ event = "w|%s" % (data_to_string([self._get_my_nick(), round_int(ps)]))
+ self._send_event(event, share)
def setcolor(self, c, share=True):
""" Set the pen color """
@@ -382,9 +377,8 @@ class TurtleGraphics:
self.tw.active_turtle.set_color(c)
self.set_fgcolor()
self.set_textcolor()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("c|%s" % \
- (data_to_string([self.tw.nick, round_int(c)])))
+ event = "c|%s" % (data_to_string([self._get_my_nick(), round_int(c)]))
+ self._send_event(event, share)
def setgray(self, g, share=True):
""" Set the gray level """
@@ -400,9 +394,8 @@ class TurtleGraphics:
self.set_fgcolor()
self.set_textcolor()
self.tw.active_turtle.set_gray(self.gray)
- if self.tw.sharing() and share:
- self.tw.activity.send_event("g|%s" % \
- (data_to_string([self.tw.nick, round_int(self.gray)])))
+ event = "g|%s" % (data_to_string([self._get_my_nick(), round_int(self.gray)]))
+ self._send_event(event, share)
def settextcolor(self, c):
""" Set the text color """
@@ -430,9 +423,8 @@ class TurtleGraphics:
self.tw.active_turtle.set_shade(s)
self.set_fgcolor()
self.set_textcolor()
- if self.tw.sharing() and share:
- self.tw.activity.send_event("s|%s" % \
- (data_to_string([self.tw.nick, round_int(s)])))
+ event = "s|%s" % (data_to_string([self._get_my_nick(), round_int(s)]))
+ self._send_event(event, share)
def fillscreen(self, c, s):
""" Fill screen with color/shade and reset to defaults """
@@ -487,9 +479,8 @@ class TurtleGraphics:
""" Lower or raise the pen """
self.pendown = bool
self.tw.active_turtle.set_pen_state(bool)
- if self.tw.sharing() and share:
- self.tw.activity.send_event("p|%s" % \
- (data_to_string([self.tw.nick, bool])))
+ event = "p|%s" % (data_to_string([self._get_my_nick(), bool]))
+ self._send_event(event, share)
def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path):
""" Draw a pixbuf """
@@ -657,3 +648,18 @@ class TurtleGraphics:
self.svg.background("#%02x%02x%02x" % \
(self.bgrgb[0], self.bgrgb[1], self.bgrgb[2])),
self.tw.svg_string, self.svg.footer())
+
+ def _get_my_nick(self):
+ return self.tw.nick
+
+ def _send_event(self, entry, share):
+ if not share:
+ return
+
+ if self.tw.sharing():
+ if self.tw.activity:
+ print "Sending: %s" % entry
+ self.tw.activity.send_event(entry)
+ elif self.tw.send_event:
+ print "Sending: %s" % entry
+ self.tw.send_event(entry)
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index ba5b5cf..f9c0d4b 100755
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -3,6 +3,7 @@
#Copyright (c) 2008-10, Walter Bender
#Copyright (c) 2009-10 Raúl Gutiérrez Segalés
#Copyright (C) 2010 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy>
+#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
@@ -100,6 +101,7 @@ class TurtleArtWindow():
self._loaded_project = ''
self.win = None
self.parent = parent
+ self.send_event = None # method to send events over the network
if type(win) == gtk.DrawingArea:
self.interactive_mode = True
self.window = win
@@ -400,6 +402,10 @@ class TurtleArtWindow():
if self.running_sugar and hasattr(self.activity, 'chattube') and\
self.activity.chattube is not None:
return True
+
+ if self.send_event:
+ return True
+
return False
def is_project_empty(self):
diff --git a/TurtleArtActivity.py b/TurtleArtActivity.py
index da471cb..a32e56b 100644
--- a/TurtleArtActivity.py
+++ b/TurtleArtActivity.py
@@ -38,12 +38,6 @@ except ImportError:
from sugar.graphics.toolbutton import ToolButton
from sugar.datastore import datastore
-import telepathy
-from dbus.service import signal
-from dbus.gobject_service import ExportedGObject
-from sugar.presence import presenceservice
-from sugar.presence.tubeconn import TubeConnection
-
from sugar import profile
from gettext import gettext as _
import os.path
@@ -55,52 +49,7 @@ from TurtleArt.taexportlogo import save_logo
from TurtleArt.tautils import data_to_file, data_to_string, data_from_string, \
get_path, chooser
from TurtleArt.tawindow import TurtleArtWindow
-
-SERVICE = 'org.laptop.TurtleArtActivity'
-IFACE = SERVICE
-PATH = '/org/laptop/TurtleArtActivity'
-
-
-def _add_label(string, toolbar):
- """ add a label to a toolbar """
- label = gtk.Label(string)
- label.set_line_wrap(True)
- label.show()
- toolitem = gtk.ToolItem()
- toolitem.add(label)
- toolbar.insert(toolitem, -1)
- toolitem.show()
- return label
-
-
-def _add_separator(toolbar, expand=False):
- """ add a separator to a toolbar """
- separator = gtk.SeparatorToolItem()
- separator.props.draw = True
- separator.set_expand(expand)
- toolbar.insert(separator, -1)
- separator.show()
-
-
-def _add_button(name, tooltip, callback, toolbar, accelerator=None, arg=None):
- """ add a button to a toolbar """
- button = ToolButton(name)
- button.set_tooltip(tooltip)
- if arg is None:
- button.connect('clicked', callback)
- else:
- button.connect('clicked', callback, arg)
- if accelerator is not None:
- try:
- button.props.accelerator = accelerator
- except AttributeError:
- pass
- button.show()
- if hasattr(toolbar, 'insert'): # the main toolbar
- toolbar.insert(button, -1)
- else: # or a secondary toolbar
- toolbar.props.page.insert(button, -1)
- return button
+from TurtleArt.collaboration import Collaboration
class TurtleArtActivity(activity.Activity):
@@ -476,196 +425,6 @@ class TurtleArtActivity(activity.Activity):
tmpfile = None
return tmpfile
- # Sharing-related callbacks
-
- def _shared_cb(self, activity):
- """ Either set up initial share... """
- if self._shared_activity is None:
- _logger.error("Failed to share or join activity ... \
- _shared_activity is null in _shared_cb()")
- return
-
- self.initiating = True
- self.waiting_for_turtles = False
- self.turtle_dictionary = \
- {profile.get_nick_name(): profile.get_color().to_string()}
- _logger.debug('I am sharing...')
-
- self.conn = self._shared_activity.telepathy_conn
- self.tubes_chan = self._shared_activity.telepathy_tubes_chan
- self.text_chan = self._shared_activity.telepathy_text_chan
-
- # call back for "NewTube" signal
- self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
- 'NewTube', self._new_tube_cb)
-
- _logger.debug('This is my activity: making a tube...')
- id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
- SERVICE, {})
-
- def _joined_cb(self, activity):
- """ ...or join an exisiting share. """
- if self._shared_activity is None:
- _logger.error("Failed to share or join activity ... \
- _shared_activity is null in _shared_cb()")
- return
-
- self.initiating = False
- self.conn = self._shared_activity.telepathy_conn
- self.tubes_chan = self._shared_activity.telepathy_tubes_chan
- self.text_chan = self._shared_activity.telepathy_text_chan
-
- # call back for "NewTube" signal
- self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
- 'NewTube', self._new_tube_cb)
-
- _logger.debug('I am joining an activity: waiting for a tube...')
- self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
- reply_handler=self._list_tubes_reply_cb,
- error_handler=self._list_tubes_error_cb)
-
- # Joiner should request current state from sharer.
- self.waiting_for_turtles = True
-
- def _list_tubes_reply_cb(self, tubes):
- for tube_info in tubes:
- self._new_tube_cb(*tube_info)
-
- def _list_tubes_error_cb(self, e):
- _logger.error('ListTubes() failed: %s', e)
-
- def _new_tube_cb(self, id, initiator, type, service, params, state):
- """ Create a new tube. """
- _logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
- 'params=%r state=%d', id, initiator, type, service,
- params, state)
-
- if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
- if state == telepathy.TUBE_STATE_LOCAL_PENDING:
- self.tubes_chan[ \
- telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
-
- tube_conn = TubeConnection(self.conn,
- self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \
- group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
-
- # We'll use a chat tube to send serialized stacks back and forth.
- self.chattube = ChatTube(tube_conn, self.initiating, \
- self.event_received_cb)
-
- # Now that we have the tube, we can ask for the turtle dictionary.
- if self.waiting_for_turtles:
- _logger.debug("Sending a request for the turtle dictionary")
- # we need to send our own nick and colors
- colors = profile.get_color().to_string()
- _logger.debug("t|" + data_to_string([self.tw.nick, colors]))
- self.send_event("t|%s" % \
- (data_to_string([self.tw.nick, colors])))
-
- def event_received_cb(self, text):
- """ Handle the receiving of events in share """
- # _logger.debug(text)
-
- """
- Events are sent as a tuple, nick|cmd, where nick is a turle name
- and cmd is a turtle event. Everyone gets the turtle dictionary from
- the sharer and watches for 't' events, which indicate that a new
- turtle has joined.
- """
- if len(text) == 0:
- return
- # Save active Turtle
- save_active_turtle = self.tw.active_turtle
- e = text.split("|", 2)
- text = e[1]
- if e[0] == 't': # request for turtle dictionary
- if text > 0:
- [nick, colors] = data_from_string(text)
- if nick != self.tw.nick:
- # There may not be a turtle dictionary.
- if hasattr(self, "turtle_dictionary"):
- self.turtle_dictionary[nick] = colors
- else:
- self.turtle_dictionary = {nick: colors}
- # Add new turtle for the joiner.
- self.tw.canvas.set_turtle(nick, colors)
- # Sharer should send turtle dictionary.
- if self.initiating:
- text = data_to_string(self.turtle_dictionary)
- self.send_event("T|" + text)
- elif e[0] == 'T': # Receiving the turtle dictionary.
- if self.waiting_for_turtles:
- if len(text) > 0:
- self.turtle_dictionary = data_from_string(text)
- for nick in self.turtle_dictionary:
- if nick != self.tw.nick:
- colors = self.turtle_dictionary[nick]
- # add new turtle for the joiner
- self.tw.canvas.set_turtle(nick, colors)
- self.waiting_for_turtles = False
- elif e[0] == 'f': # move a turtle forward
- if len(text) > 0:
- [nick, x] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.forward(x, False)
- elif e[0] == 'a': # move a turtle in an arc
- if len(text) > 0:
- [nick, [a, r]] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.arc(a, r, False)
- elif e[0] == 'r': # rotate turtle
- if len(text) > 0:
- [nick, h] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.seth(h, False)
- elif e[0] == 'x': # set turtle xy position
- if len(text) > 0:
- [nick, [x, y]] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.setxy(x, y, False)
- elif e[0] == 'c': # set turtle pen color
- if len(text) > 0:
- [nick, x] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.setcolor(x, False)
- elif e[0] == 'g': # set turtle pen gray level
- if len(text) > 0:
- [nick, x] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.setgray(x, False)
- elif e[0] == 's': # set turtle pen shade
- if len(text) > 0:
- [nick, x] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.setshade(x, False)
- elif e[0] == 'w': # set turtle pen width
- if len(text) > 0:
- [nick, x] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.setpensize(x, False)
- elif e[0] == 'p': # set turtle pen state
- if len(text) > 0:
- [nick, x] = data_from_string(text)
- if nick != self.tw.nick:
- self.tw.canvas.set_turtle(nick)
- self.tw.canvas.setpen(x, False)
- # Restore active Turtle
- self.tw.canvas.set_turtle(self.tw.turtles.get_turtle_key(
- save_active_turtle))
-
- def send_event(self, entry):
- """ Send event through the tube. """
- if hasattr(self, 'chattube') and self.chattube is not None:
- self.chattube.SendText(entry)
-
def __visibility_notify_cb(self, window, event):
""" Callback method for when the activity's visibility changes. """
if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:
@@ -725,11 +484,11 @@ class TurtleArtActivity(activity.Activity):
help_toolbar_button.show()
toolbox.toolbar.insert(help_toolbar_button, -1)
- _add_separator(toolbox.toolbar)
+ self._add_separator(toolbox.toolbar)
self._make_project_buttons(toolbox.toolbar)
- _add_separator(toolbox.toolbar, True)
+ self._add_separator(toolbox.toolbar, True)
stop_button = StopButton(self)
stop_button.props.accelerator = '<Ctrl>Q'
@@ -759,57 +518,57 @@ class TurtleArtActivity(activity.Activity):
self._make_palette_buttons(project_toolbar, palette_button=True)
- _add_separator(project_toolbar)
+ self._add_separator(project_toolbar)
self._make_project_buttons(project_toolbar)
- self.keep_button = _add_button('filesaveoff', _("Save snapshot"),
+ self.keep_button = self._add_button('filesaveoff', _("Save snapshot"),
self.do_keep_cb,
journal_toolbar_button)
- self.save_as_html = _add_button('htmloff', _("Save as HTML"),
+ self.save_as_html = self._add_button('htmloff', _("Save as HTML"),
self.do_save_as_html_cb,
journal_toolbar_button)
- self.save_as_logo = _add_button('logo-saveoff', _("Save as Logo"),
+ self.save_as_logo = self._add_button('logo-saveoff', _("Save as Logo"),
self.do_save_as_logo_cb,
journal_toolbar_button)
- self.save_as_image = _add_button('image-saveoff', _("Save as image"),
+ self.save_as_image = self._add_button('image-saveoff', _("Save as image"),
self.do_save_as_image_cb,
journal_toolbar_button)
- self.load_ta_project = _add_button('load-from-journal',
+ self.load_ta_project = self._add_button('load-from-journal',
_("Import project from the Journal"), self.do_load_ta_project_cb,
journal_toolbar_button)
- _add_separator(journal_toolbar)
- self.load_python = _add_button('pippy-openoff', _("Load Python block"),
+ self._add_separator(journal_toolbar)
+ self.load_python = self._add_button('pippy-openoff', _("Load Python block"),
self.do_load_python_cb,
journal_toolbar_button)
- self.samples_button = _add_button("ta-open", _('Load example'),
+ self.samples_button = self._add_button("ta-open", _('Load example'),
self.do_samples_cb, journal_toolbar_button)
- copy = _add_button('edit-copy', _('Copy'), self._copy_cb,
+ copy = self._add_button('edit-copy', _('Copy'), self._copy_cb,
edit_toolbar_button, '<Ctrl>c')
- paste = _add_button('edit-paste', _('Paste'), self._paste_cb,
+ paste = self._add_button('edit-paste', _('Paste'), self._paste_cb,
edit_toolbar_button, '<Ctrl>v')
- fullscreen_button = _add_button('view-fullscreen', _("Fullscreen"),
+ fullscreen_button = self._add_button('view-fullscreen', _("Fullscreen"),
self.do_fullscreen_cb,
view_toolbar_button, '<Alt>Return')
- cartesian_button = _add_button('view-Cartesian',
+ cartesian_button = self._add_button('view-Cartesian',
_("Cartesian coordinates"),
self.do_cartesian_cb,
view_toolbar_button)
- polar_button = _add_button('view-polar', _("Polar coordinates"),
+ polar_button = self._add_button('view-polar', _("Polar coordinates"),
self.do_polar_cb, view_toolbar_button)
- _add_separator(view_toolbar)
- self.coordinates_label = _add_label(
+ self._add_separator(view_toolbar)
+ self.coordinates_label = self._add_label(
_("xcor") + " = 0 " + _("ycor") + " = 0 " + _("heading") + " = 0",
view_toolbar)
- _add_separator(view_toolbar, True)
- self.rescale_button = _add_button('expand-coordinates',
+ self._add_separator(view_toolbar, True)
+ self.rescale_button = self._add_button('expand-coordinates',
_("Rescale coordinates up"), self.do_rescale_cb,
view_toolbar_button)
- self.resize_up_button = _add_button('resize+', _("Grow blocks"),
+ self.resize_up_button = self._add_button('resize+', _("Grow blocks"),
self.do_grow_blocks_cb, view_toolbar_button)
- self.resize_down_button = _add_button('resize-', _("Shrink blocks"),
+ self.resize_down_button = self._add_button('resize-', _("Shrink blocks"),
self.do_shrink_blocks_cb, view_toolbar_button)
- self.hover_help_label = _add_label(
+ self.hover_help_label = self._add_label(
_("Move the cursor over the orange palette for help."),
help_toolbar)
@@ -821,10 +580,10 @@ class TurtleArtActivity(activity.Activity):
suffix = 'off'
else:
suffix = 'on'
- self.palette_buttons.append(_add_button(name + suffix,
+ self.palette_buttons.append(self._add_button(name + suffix,
HELP_STRINGS[name], self.do_palette_buttons_cb,
palette_toolbar_button, None, i))
- _add_separator(palette_toolbar, True)
+ self._add_separator(palette_toolbar, True)
self._make_palette_buttons(palette_toolbar_button)
@@ -848,22 +607,22 @@ class TurtleArtActivity(activity.Activity):
def _make_palette_buttons(self, toolbar, palette_button=False):
""" Creates the palette and block buttons for both toolbar types"""
if palette_button: # old-style toolbars need this button
- self.palette_button = _add_button("paletteoff", _('Hide palette'),
+ self.palette_button = self._add_button("paletteoff", _('Hide palette'),
self.do_palette_cb, toolbar, _('<Ctrl>p'))
- self.blocks_button = _add_button("hideshowoff", _('Hide blocks'),
+ self.blocks_button = self._add_button("hideshowoff", _('Hide blocks'),
self.do_hideshow_cb, toolbar, _('<Ctrl>b'))
def _make_project_buttons(self, toolbar):
""" Creates the turtle buttons for both toolbar types"""
- self.eraser_button = _add_button("eraseron", _('Clean'),
+ self.eraser_button = self._add_button("eraseron", _('Clean'),
self.do_eraser_cb, toolbar, _('<Ctrl>e'))
- self.run_button = _add_button("run-fastoff", _('Run'), self.do_run_cb,
+ self.run_button = self._add_button("run-fastoff", _('Run'), self.do_run_cb,
toolbar, _('<Ctrl>r'))
- self.step_button = _add_button("run-slowoff", _('Step'),
+ self.step_button = self._add_button("run-slowoff", _('Step'),
self.do_step_cb, toolbar, _('<Ctrl>w'))
- self.debug_button = _add_button("debugoff", _('Debug'),
+ self.debug_button = self._add_button("debugoff", _('Debug'),
self.do_debug_cb, toolbar, _('<Ctrl>d'))
- self.stop_turtle_button = _add_button("stopitoff", _('Stop turtle'),
+ self.stop_turtle_button = self._add_button("stopitoff", _('Stop turtle'),
self.do_stop_cb, toolbar, _('<Ctrl>s'))
def _setup_scrolled_window(self):
@@ -926,20 +685,8 @@ class TurtleArtActivity(activity.Activity):
self.tw.load_start()
def _setup_sharing(self):
- """ A simplistic sharing model: the sharer is the master """
- # TODO: hand off role of master is sharer leaves
- # Get the Presence Service
- self.pservice = presenceservice.get_instance()
- self.initiating = None # sharing (True) or joining (False)
-
- # Add my buddy object to the list
- owner = self.pservice.get_owner()
- self.owner = owner
- self.tw.buddies.append(self.owner)
- self._share = ""
-
- self.connect('shared', self._shared_cb)
- self.connect('joined', self._joined_cb)
+ self._collaboration = Collaboration(self.tw)
+ self._collaboration.setup()
def _setup_visibility_handler(self):
""" Notify when the visibility state changes """
@@ -1030,26 +777,41 @@ class TurtleArtActivity(activity.Activity):
self.tw.paste_offset)
self.tw.paste_offset += 20
-
-class ChatTube(ExportedGObject):
-
- def __init__(self, tube, is_initiator, stack_received_cb):
- """Class for setting up tube for sharing."""
- super(ChatTube, self).__init__(tube, PATH)
- self.tube = tube
- self.is_initiator = is_initiator # Are we sharing or joining activity?
- self.stack_received_cb = stack_received_cb
- self.stack = ''
-
- self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \
- path=PATH, sender_keyword='sender')
-
- def send_stack_cb(self, text, sender=None):
- if sender == self.tube.get_unique_name():
- return
- self.stack = text
- self.stack_received_cb(text)
-
- @signal(dbus_interface=IFACE, signature='s')
- def SendText(self, text):
- self.stack = text
+ def _add_label(self, string, toolbar):
+ """ add a label to a toolbar """
+ label = gtk.Label(string)
+ label.set_line_wrap(True)
+ label.show()
+ toolitem = gtk.ToolItem()
+ toolitem.add(label)
+ toolbar.insert(toolitem, -1)
+ toolitem.show()
+ return label
+
+ def _add_separator(toolbar, expand=False):
+ """ add a separator to a toolbar """
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = True
+ separator.set_expand(expand)
+ toolbar.insert(separator, -1)
+ separator.show()
+
+ def _add_button(name, tooltip, callback, toolbar, accelerator=None, arg=None):
+ """ add a button to a toolbar """
+ button = ToolButton(name)
+ button.set_tooltip(tooltip)
+ if arg is None:
+ button.connect('clicked', callback)
+ else:
+ button.connect('clicked', callback, arg)
+ if accelerator is not None:
+ try:
+ button.props.accelerator = accelerator
+ except AttributeError:
+ pass
+ button.show()
+ if hasattr(toolbar, 'insert'): # the main toolbar
+ toolbar.insert(button, -1)
+ else: # or a secondary toolbar
+ toolbar.props.page.insert(button, -1)
+ return button
diff --git a/extra/__init__.py b/extra/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extra/__init__.py
diff --git a/extra/upload.py b/extra/upload.py
new file mode 100644
index 0000000..e39d099
--- /dev/null
+++ b/extra/upload.py
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+
+try:
+ import pycurl
+ import xmlrpclib
+ _UPLOAD_AVAILABLE = True
+except ImportError, e:
+ print "Import Error: %s. Project upload is disabled." % (e)
+ _UPLOAD_AVAILABLE = False
+
+import gtk
+from gettext import gettext as _
+
+class Uploader():
+ MAX_FILE_SIZE = 950000
+ UPLOAD_SERVER = 'http://turtleartsite.appspot.com'
+
+ def __init__(self, upload_server = None, max_file_size = 0):
+ self.uploading = False
+
+ if upload_server is None:
+ self._upload_server = self.UPLOAD_SERVER
+
+ if max_file_size is None:
+ self._max_file_size = self.MAX_FILE_SIZE
+
+ def set_tw(self, turtleart_window):
+ self.tw = turtleart_window
+
+ def enabled(self):
+ return _UPLOAD_AVAILABLE
+
+ def do_upload_to_web(self, widget = None):
+ if self.uploading:
+ return
+
+ self.uploading = False
+ self.pop_up = gtk.Window()
+ self.pop_up.set_default_size(600, 400)
+ self.pop_up.connect('delete_event', self._stop_uploading)
+ table = gtk.Table(8, 1, False)
+ self.pop_up.add(table)
+
+ login_label = gtk.Label(_('You must have an account at \
+http://turtleartsite.sugarlabs.org to upload your project.'))
+ table.attach(login_label, 0, 1, 0, 1)
+ self.login_message = gtk.Label('')
+ table.attach(self.login_message, 0, 1, 1, 2)
+
+ self.Hbox1 = gtk.HBox()
+ table.attach(self.Hbox1, 0, 1, 2, 3, xpadding=5, ypadding=3)
+ self.username_entry = gtk.Entry()
+ username_label = gtk.Label(_('Username:') + ' ')
+ username_label.set_size_request(150, 25)
+ username_label.set_alignment(1.0, 0.5)
+ self.username_entry.set_size_request(450, 25)
+ self.Hbox1.add(username_label)
+ self.Hbox1.add(self.username_entry)
+
+ self.Hbox2 = gtk.HBox()
+ table.attach(self.Hbox2, 0, 1, 3, 4, xpadding=5, ypadding=3)
+ self.password_entry = gtk.Entry()
+ password_label = gtk.Label(_('Password:') + ' ')
+ self.password_entry.set_visibility(False)
+ password_label.set_size_request(150, 25)
+ password_label.set_alignment(1.0, 0.5)
+ self.password_entry.set_size_request(450, 25)
+ self.Hbox2.add(password_label)
+ self.Hbox2.add(self.password_entry)
+
+ self.Hbox3 = gtk.HBox()
+ table.attach(self.Hbox3, 0, 1, 4, 5, xpadding=5, ypadding=3)
+ self.title_entry = gtk.Entry()
+ title_label = gtk.Label(_('Title:') + ' ')
+ title_label.set_size_request(150, 25)
+ title_label.set_alignment(1.0, 0.5)
+ self.title_entry.set_size_request(450, 25)
+ self.Hbox3.add(title_label)
+ self.Hbox3.add(self.title_entry)
+
+ self.Hbox4 = gtk.HBox()
+ table.attach(self.Hbox4, 0, 1, 5, 6, xpadding=5, ypadding=3)
+ self.description_entry = gtk.TextView()
+ description_label = gtk.Label(_('Description:') + ' ')
+ description_label.set_size_request(150, 25)
+ description_label.set_alignment(1.0, 0.5)
+ self.description_entry.set_wrap_mode(gtk.WRAP_WORD)
+ self.description_entry.set_size_request(450, 50)
+ self.Hbox4.add(description_label)
+ self.Hbox4.add(self.description_entry)
+
+ self.Hbox5 = gtk.HBox()
+ table.attach(self.Hbox5, 0, 1, 6, 7, xpadding=5, ypadding=3)
+ self.submit_button = gtk.Button(_('Submit to Web'))
+ self.submit_button.set_size_request(300, 25)
+ self.submit_button.connect('pressed', self._do_remote_logon)
+ self.Hbox5.add(self.submit_button)
+ self.cancel_button = gtk.Button(_('Cancel'))
+ self.cancel_button.set_size_request(300, 25)
+ self.cancel_button.connect('pressed', self._stop_uploading)
+ self.Hbox5.add(self.cancel_button)
+
+ self.pop_up.show_all()
+
+ def _stop_uploading(self, widget, event=None):
+ """ Hide the popup when the upload is complte """
+ self.uploading = False
+ self.pop_up.hide()
+
+ def _do_remote_logon(self, widget):
+ """ Log into the upload server """
+ username = self.username_entry.get_text()
+ password = self.password_entry.get_text()
+ server = xmlrpclib.ServerProxy(self._upload_server + '/call/xmlrpc')
+ logged_in = server.login_remote(username, password)
+ if logged_in:
+ upload_key = logged_in
+ self._do_submit_to_web(upload_key)
+ else:
+ self.login_message.set_text(_('Login failed'))
+
+ def _do_submit_to_web(self, key):
+ """ Submit project to the server """
+ title = self.title_entry.get_text()
+ description = self.description_entry.get_buffer().get_text(
+ *self.description_entry.get_buffer().get_bounds())
+ tafile, imagefile = self.tw.save_for_upload(title)
+
+ # Set a maximum file size for image to be uploaded.
+ if int(os.path.getsize(imagefile)) > self.max_file_size:
+ import Image
+ while int(os.path.getsize(imagefile)) > self._max_file_size:
+ big_file = Image.open(imagefile)
+ smaller_file = big_file.resize(int(0.9 * big_file.size[0]),
+ int(0.9 * big_file.size[1]),
+ Image.ANTIALIAS)
+ smaller_file.save(imagefile, quality = 100)
+
+ c = pycurl.Curl()
+ c.setopt(c.POST, 1)
+ c.setopt(c.FOLLOWLOCATION, 1)
+ c.setopt(c.URL, self._upload_server + '/upload')
+ c.setopt(c.HTTPHEADER, ["Expect:"])
+ c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, tafile)),
+ ('newimage', (c.FORM_FILE, imagefile)),
+ ('small_image', (c.FORM_FILE, imagefile)),
+ ('title', title),
+ ('description', description),
+ ('upload_key', key), ('_formname',
+ 'image_create')])
+ c.perform()
+ error_code = c.getinfo(c.HTTP_CODE)
+ c.close
+ os.remove(imagefile)
+ os.remove(tafile)
+ if error_code == 400:
+ self.login_message.set_text(_('Failed to upload!'))
+ else:
+ self.pop_up.hide()
+ self.uploading = False
+
+if __name__ == "__main__":
+ # TODO: create test data...
+ u = Uploader(None)
+ if u.enabled():
+ print "Uploader is enabled... trying to upload"
+ u.do_upload_to_web()
+ gtk.main()
+ else:
+ print "Uploader is not enabled... exiting"
diff --git a/turtleart.py b/turtleart.py
index d3a0f70..996e94a 100755
--- a/turtleart.py
+++ b/turtleart.py
@@ -32,14 +32,6 @@ import cStringIO
import errno
try:
- import pycurl
- import xmlrpclib
- _UPLOAD_AVAILABLE = True
-except ImportError, e:
- print "Import Error: %s. Project upload is disabled." % (e)
- _UPLOAD_AVAILABLE = False
-
-try:
# Try to use XDG Base Directory standard for config files
import xdg.BaseDirectory
CONFIG_HOME = os.path.join(xdg.BaseDirectory.xdg_config_home, 'turtleart')
@@ -57,86 +49,128 @@ from TurtleArt.tautils import data_to_string, data_from_string, get_save_name
from TurtleArt.tawindow import TurtleArtWindow
from TurtleArt.taexporthtml import save_html
from TurtleArt.taexportlogo import save_logo
+from extra.upload import Uploader
+
-_HELP_MSG = 'turtleart.py: ' + _('usage is') + """
+class TurtleMain():
+ """ Launch Turtle Art from outside of Sugar """
+
+ _HELP_MSG = 'turtleart.py: ' + _('usage is') + """
\tturtleart.py
\tturtleart.py project.ta
\tturtleart.py --output_png project.ta
\tturtleart.py -o project"""
+ _INSTALL_PATH = '/usr/share/turtleart'
+ _ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart'
+ _ICON_SUBPATH = 'images/turtle.png'
-_MAX_FILE_SIZE = 950000
-
-_INSTALL_PATH = '/usr/share/turtleart'
-_ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart'
-_ICON_SUBPATH = 'images/turtle.png'
-_UPLOAD_SERVER = 'http://turtleartsite.appspot.com'
-
+ def __init__(self):
+ self._init_vars()
+ self._parse_command_line()
+ self._ensure_sugar_paths()
-def mkdir_p(path):
- '''Create a directory in a fashion similar to `mkdir -p`'''
- try:
- os.makedirs(path)
- except OSError as exc:
- if exc.errno == errno.EEXIST:
- pass
+ if self.output_png:
+ pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
+ gtk.gdk.screen_width(),
+ gtk.gdk.screen_height())
+ self.canvas, mask = pixbuf.render_pixmap_and_mask()
+ self._build_window()
+ self._draw_and_quit()
else:
- raise
-
-
-def _make_sub_menu(menu, name):
- """ add a new submenu to the toolbar """
- sub_menu = gtk.MenuItem(name)
- sub_menu.show()
- sub_menu.set_submenu(menu)
- return sub_menu
-
-
-def _make_menu_item(menu, tooltip, callback, arg=None):
- """ add a new item to the submenu """
- menu_items = gtk.MenuItem(tooltip)
- menu.append(menu_items)
- if arg is None:
- menu_items.connect('activate', callback)
- else:
- menu_items.connect('activate', callback, arg)
- menu_items.show()
-
-
-def makepath(path):
- """ Make a path if it doesn't previously exist """
- from os import makedirs
- from os.path import normpath, dirname, exists
-
- dpath = normpath(dirname(path))
- if not exists(dpath):
- makedirs(dpath)
+ self._read_initial_pos()
+ self._setup_gtk()
+ self._build_window()
+ self._uploader.set_tw(self.tw)
+ self._start_gtk()
+
+ def _mkdir_p(path):
+ '''Create a directory in a fashion similar to `mkdir -p`'''
+ try:
+ os.makedirs(path)
+ except OSError as exc:
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise
+
+ def _make_sub_menu(self, menu, name):
+ """ add a new submenu to the toolbar """
+ sub_menu = gtk.MenuItem(name)
+ sub_menu.show()
+ sub_menu.set_submenu(menu)
+ return sub_menu
+
+
+ def _make_menu_item(self, menu, tooltip, callback, arg=None):
+ """ add a new item to the submenu """
+ menu_items = gtk.MenuItem(tooltip)
+ menu.append(menu_items)
+ if arg is None:
+ menu_items.connect('activate', callback)
+ else:
+ menu_items.connect('activate', callback, arg)
+ menu_items.show()
+
+ def _makepath(self, path):
+ """ Make a path if it doesn't previously exist """
+ from os import makedirs
+ from os.path import normpath, dirname, exists
+
+ dpath = normpath(dirname(path))
+ if not exists(dpath):
+ makedirs(dpath)
+
+ def _start_gtk(self):
+ self.win.connect('configure_event', self.tw.update_overlay_position)
+ self.tw.win = self.win
+ if self.ta_file is None:
+ self.tw.load_start()
+ else:
+ print self.ta_file
+ self.tw.load_start(self.ta_file)
+ self.tw.lc.trace = 0
+ self.tw.run_button(0)
+ gtk.main()
+ def _draw_and_quit(self):
+ self.tw.load_start(self.ta_file)
+ self.tw.lc.trace = 0
+ self.tw.run_button(0)
+ self.tw.save_as_image(self.ta_file, self.canvas)
-class TurtleMain():
- """ Launch Turtle Art from outside of Sugar """
+ def _build_window(self):
+ if os.path.exists(self._INSTALL_PATH):
+ self.tw = TurtleArtWindow(self.canvas, self._INSTALL_PATH)
+ elif os.path.exists(self._ALTERNATE_INSTALL_PATH):
+ self.tw = TurtleArtWindow(self.canvas, self._ALTERNATE_INSTALL_PATH)
+ else:
+ self.tw = TurtleArtWindow(self.canvas, os.path.abspath('.'))
- def __init__(self):
- """ Parse command-line options and initialize class """
+ self.tw.save_folder = os.path.expanduser('~')
+ def _init_vars(self):
# If we are invoked to start a project from Gnome, we should make
# sure our current directory is TA's source dir.
os.chdir(os.path.dirname(__file__))
+ self._uploader = Uploader()
self.ta_file = None
self.output_png = False
- self.uploading = False
+ self.i = 0 # FIXME: use a better name for this variable
+ self.scale = 2.0
+ self.tw = None
- # Parse command line
+ def _parse_command_line(self):
try:
opts, args = getopt.getopt(argv[1:], 'ho',
['help', 'output_png'])
except getopt.GetoptError, err:
print str(err)
- print _HELP_MSG
+ print self._HELP_MSG
sys.exit(2)
for o, a in opts:
if o in ('-h', '--help'):
- print _HELP_MSG
+ print self._HELP_MSG
sys.exit()
if o in ('-o', '--output_png'):
self.output_png = True
@@ -146,7 +180,7 @@ class TurtleMain():
self.ta_file = args[0]
if len(args) > 1 or self.output_png and self.ta_file is None:
- print _HELP_MSG
+ print self._HELP_MSG
sys.exit()
if self.ta_file is not None:
@@ -155,163 +189,134 @@ class TurtleMain():
if not os.path.exists(self.ta_file):
assert False, ('%s: %s' % (self.ta_file, _('File not found')))
- self.i = 0
- self.scale = 2.0
- self.tw = None
-
- # make sure Sugar paths are present
+ """
+ make sure Sugar paths are present
+ """
+ def _ensure_sugar_paths(self):
tapath = os.path.join(os.environ['HOME'], '.sugar', 'default',
'org.laptop.TurtleArtActivity')
- map(makepath, (os.path.join(tapath, 'data/'),
+ map(self._makepath, (os.path.join(tapath, 'data/'),
os.path.join(tapath, 'instance/')))
- if self.output_png:
- pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
- gtk.gdk.screen_width(),
- gtk.gdk.screen_height())
- canvas, mask = pixbuf.render_pixmap_and_mask()
+ def _read_initial_pos(self):
+ try:
+ data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), 'r')
+ except IOError:
+ # Opening the config file failed
+ # We'll assume it needs to be created
+ try:
+ self._mkdir_p(CONFIG_HOME)
+ data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'),
+ 'a+')
+ except IOError, e:
+ # We can't write to the configuration file, use
+ # a faux file that will persist for the length of
+ # the session.
+ print _('Configuration directory not writable: %s') % (e)
+ data_file = cStringIO.StringIO()
+ data_file.write(str(50) + '\n')
+ data_file.write(str(50) + '\n')
+ data_file.write(str(800) + '\n')
+ data_file.write(str(550) + '\n')
+ data_file.seek(0)
+ self.x = int(data_file.readline())
+ self.y = int(data_file.readline())
+ self.width = int(data_file.readline())
+ self.height = int(data_file.readline())
+
+ def _setup_gtk(self):
+ win = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ win.set_default_size(self.width, self.height)
+ win.move(self.x, self.y)
+ win.maximize()
+ win.set_title(_('Turtle Art'))
+ if os.path.exists(os.path.join(self._INSTALL_PATH, self._ICON_SUBPATH)):
+ win.set_icon_from_file(os.path.join(self._INSTALL_PATH,
+ self._ICON_SUBPATH))
else:
- win = gtk.Window(gtk.WINDOW_TOPLEVEL)
-
try:
- data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), 'r')
+ win.set_icon_from_file(self._ICON_SUBPATH)
except IOError:
- # Opening the config file failed
- # We'll assume it needs to be created
- try:
- mkdir_p(CONFIG_HOME)
- data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'),
- 'a+')
- except IOError, e:
- # We can't write to the configuration file, use
- # a faux file that will persist for the length of
- # the session.
- print _('Configuration directory not writable: %s') % (e)
- data_file = cStringIO.StringIO()
- data_file.write(str(50) + '\n')
- data_file.write(str(50) + '\n')
- data_file.write(str(800) + '\n')
- data_file.write(str(550) + '\n')
- data_file.seek(0)
- self.x = int(data_file.readline())
- self.y = int(data_file.readline())
- self.width = int(data_file.readline())
- self.height = int(data_file.readline())
-
- win.set_default_size(self.width, self.height)
- win.move(self.x, self.y)
- win.maximize()
- win.set_title(_('Turtle Art'))
- if os.path.exists(os.path.join(_INSTALL_PATH, _ICON_SUBPATH)):
- win.set_icon_from_file(os.path.join(_INSTALL_PATH,
- _ICON_SUBPATH))
- else:
- try:
- win.set_icon_from_file(_ICON_SUBPATH)
- except IOError:
- pass
- win.connect('delete_event', self._quit_ta)
-
- menu = gtk.Menu()
- _make_menu_item(menu, _('New'), self._do_new_cb)
- _make_menu_item(menu, _('Open'), self._do_open_cb)
- _make_menu_item(menu, _('Save'), self._do_save_cb)
- _make_menu_item(menu, _('Save As'), self._do_save_as_cb)
- _make_menu_item(menu, _('Save as image'), self._do_save_picture_cb)
- _make_menu_item(menu, _('Save as HTML'), self._do_save_html_cb)
- _make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb)
- if _UPLOAD_AVAILABLE:
- _make_menu_item(menu, _('Upload to Web'),
- self._do_upload_to_web)
- _make_menu_item(menu, _('Quit'), self.destroy)
- activity_menu = _make_sub_menu(menu, _('File'))
-
- menu = gtk.Menu()
- _make_menu_item(menu, _('Cartesian coordinates'),
- self._do_cartesian_cb)
- _make_menu_item(menu, _('Polar coordinates'), self._do_polar_cb)
- _make_menu_item(menu, _('Rescale coordinates'),
- self._do_rescale_cb)
- _make_menu_item(menu, _('Grow blocks'), self._do_resize_cb, 1.5)
- _make_menu_item(menu, _('Shrink blocks'),
- self._do_resize_cb, 0.667)
- _make_menu_item(menu, _('Reset block size'),
- self._do_resize_cb, -1)
- view_menu = _make_sub_menu(menu, _('View'))
-
- menu = gtk.Menu()
- _make_menu_item(menu, _('Copy'), self._do_copy_cb)
- _make_menu_item(menu, _('Paste'), self._do_paste_cb)
- edit_menu = _make_sub_menu(menu, _('Edit'))
-
- menu = gtk.Menu()
- _make_menu_item(menu, _('Show palette'), self._do_palette_cb)
- _make_menu_item(menu, _('Hide palette'), self._do_hide_palette_cb)
- _make_menu_item(menu, _('Show/hide blocks'), self._do_hideshow_cb)
- tool_menu = _make_sub_menu(menu, _('Tools'))
-
- menu = gtk.Menu()
- _make_menu_item(menu, _('Clean'), self._do_eraser_cb)
- _make_menu_item(menu, _('Run'), self._do_run_cb)
- _make_menu_item(menu, _('Step'), self._do_step_cb)
- _make_menu_item(menu, _('Debug'), self._do_trace_cb)
- _make_menu_item(menu, _('Stop'), self._do_stop_cb)
- turtle_menu = _make_sub_menu(menu, _('Turtle'))
-
- vbox = gtk.VBox(False, 0)
- win.add(vbox)
- vbox.show()
-
- menu_bar = gtk.MenuBar()
- vbox.pack_start(menu_bar, False, False, 2)
- menu_bar.show()
-
- sw = gtk.ScrolledWindow()
- sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- sw.show()
- canvas = gtk.DrawingArea()
- width = gtk.gdk.screen_width() * 2
- height = gtk.gdk.screen_height() * 2
- canvas.set_size_request(width, height)
- sw.add_with_viewport(canvas)
- canvas.show()
- vbox.pack_end(sw, True, True)
-
- menu_bar.append(activity_menu)
- menu_bar.append(edit_menu)
- menu_bar.append(view_menu)
- menu_bar.append(tool_menu)
- menu_bar.append(turtle_menu)
-
- win.show_all()
-
- if os.path.exists(_INSTALL_PATH):
- self.tw = TurtleArtWindow(canvas, _INSTALL_PATH)
- elif os.path.exists(_ALTERNATE_INSTALL_PATH):
- self.tw = TurtleArtWindow(canvas, _ALTERNATE_INSTALL_PATH)
- else:
- self.tw = TurtleArtWindow(canvas, os.path.abspath('.'))
-
- self.tw.save_folder = os.path.expanduser('~')
-
- if not self.output_png:
- win.connect('configure_event', self.tw.update_overlay_position)
- self.tw.win = win
- if self.ta_file is None:
- self.tw.load_start()
- else:
- print self.ta_file
- self.tw.load_start(self.ta_file)
- self.tw.lc.trace = 0
- self.tw.run_button(0)
-
- gtk.main()
-
- else:
- self.tw.load_start(self.ta_file)
- self.tw.lc.trace = 0
- self.tw.run_button(0)
- self.tw.save_as_image(self.ta_file, canvas)
+ pass
+ win.connect('delete_event', self._quit_ta)
+
+ vbox = gtk.VBox(False, 0)
+ win.add(vbox)
+ vbox.show()
+
+ menu_bar = self._get_menu_bar()
+ vbox.pack_start(menu_bar, False, False, 2)
+ menu_bar.show()
+
+ sw = gtk.ScrolledWindow()
+ sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ sw.show()
+ canvas = gtk.DrawingArea()
+ width = gtk.gdk.screen_width() * 2
+ height = gtk.gdk.screen_height() * 2
+ canvas.set_size_request(width, height)
+ sw.add_with_viewport(canvas)
+ canvas.show()
+ vbox.pack_end(sw, True, True)
+
+ win.show_all()
+ self.win = win
+ self.canvas = canvas
+
+ def _get_menu_bar(self):
+ menu = gtk.Menu()
+ self._make_menu_item(menu, _('New'), self._do_new_cb)
+ self._make_menu_item(menu, _('Open'), self._do_open_cb)
+ self._make_menu_item(menu, _('Save'), self._do_save_cb)
+ self._make_menu_item(menu, _('Save As'), self._do_save_as_cb)
+ self._make_menu_item(menu, _('Save as image'), self._do_save_picture_cb)
+ self._make_menu_item(menu, _('Save as HTML'), self._do_save_html_cb)
+ self._make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb)
+ if self._uploader.enabled():
+ self._make_menu_item(menu, _('Upload to Web'),
+ self._uploader.do_upload_to_web)
+ self._make_menu_item(menu, _('Quit'), self.destroy)
+ activity_menu = self._make_sub_menu(menu, _('File'))
+
+ menu = gtk.Menu()
+ self._make_menu_item(menu, _('Cartesian coordinates'),
+ self._do_cartesian_cb)
+ self._make_menu_item(menu, _('Polar coordinates'), self._do_polar_cb)
+ self._make_menu_item(menu, _('Rescale coordinates'),
+ self._do_rescale_cb)
+ self._make_menu_item(menu, _('Grow blocks'), self._do_resize_cb, 1.5)
+ self._make_menu_item(menu, _('Shrink blocks'),
+ self._do_resize_cb, 0.667)
+ self._make_menu_item(menu, _('Reset block size'),
+ self._do_resize_cb, -1)
+ view_menu = self._make_sub_menu(menu, _('View'))
+
+ menu = gtk.Menu()
+ self._make_menu_item(menu, _('Copy'), self._do_copy_cb)
+ self._make_menu_item(menu, _('Paste'), self._do_paste_cb)
+ edit_menu = self._make_sub_menu(menu, _('Edit'))
+
+ menu = gtk.Menu()
+ self._make_menu_item(menu, _('Show palette'), self._do_palette_cb)
+ self._make_menu_item(menu, _('Hide palette'), self._do_hide_palette_cb)
+ self._make_menu_item(menu, _('Show/hide blocks'), self._do_hideshow_cb)
+ tool_menu = self._make_sub_menu(menu, _('Tools'))
+
+ menu = gtk.Menu()
+ self._make_menu_item(menu, _('Clean'), self._do_eraser_cb)
+ self._make_menu_item(menu, _('Run'), self._do_run_cb)
+ self._make_menu_item(menu, _('Step'), self._do_step_cb)
+ self._make_menu_item(menu, _('Debug'), self._do_trace_cb)
+ self._make_menu_item(menu, _('Stop'), self._do_stop_cb)
+ turtle_menu = self._make_sub_menu(menu, _('Turtle'))
+
+ menu_bar = gtk.MenuBar()
+ menu_bar.append(activity_menu)
+ menu_bar.append(edit_menu)
+ menu_bar.append(view_menu)
+ menu_bar.append(tool_menu)
+ menu_bar.append(turtle_menu)
+ return menu_bar
def _quit_ta(self, widget=None, e=None):
""" Save changes on exit """
@@ -528,134 +533,5 @@ class TurtleMain():
""" Callback for destroy event. """
gtk.main_quit()
- def _do_upload_to_web(self, widget):
- """ Create dialog for uploading current project to the Web """
- if not self.uploading:
- self.uploading = True
- self.pop_up = gtk.Window()
- self.pop_up.set_default_size(600, 400)
- self.pop_up.connect('delete_event', self._stop_uploading)
- table = gtk.Table(8, 1, False)
- self.pop_up.add(table)
-
- login_label = gtk.Label(_('You must have an account at \
-http://turtleartsite.sugarlabs.org to upload your project.'))
- table.attach(login_label, 0, 1, 0, 1)
- self.login_message = gtk.Label('')
- table.attach(self.login_message, 0, 1, 1, 2)
-
- self.Hbox1 = gtk.HBox()
- table.attach(self.Hbox1, 0, 1, 2, 3, xpadding=5, ypadding=3)
- self.username_entry = gtk.Entry()
- username_label = gtk.Label(_('Username:') + ' ')
- username_label.set_size_request(150, 25)
- username_label.set_alignment(1.0, 0.5)
- self.username_entry.set_size_request(450, 25)
- self.Hbox1.add(username_label)
- self.Hbox1.add(self.username_entry)
-
- self.Hbox2 = gtk.HBox()
- table.attach(self.Hbox2, 0, 1, 3, 4, xpadding=5, ypadding=3)
- self.password_entry = gtk.Entry()
- password_label = gtk.Label(_('Password:') + ' ')
- self.password_entry.set_visibility(False)
- password_label.set_size_request(150, 25)
- password_label.set_alignment(1.0, 0.5)
- self.password_entry.set_size_request(450, 25)
- self.Hbox2.add(password_label)
- self.Hbox2.add(self.password_entry)
-
- self.Hbox3 = gtk.HBox()
- table.attach(self.Hbox3, 0, 1, 4, 5, xpadding=5, ypadding=3)
- self.title_entry = gtk.Entry()
- title_label = gtk.Label(_('Title:') + ' ')
- title_label.set_size_request(150, 25)
- title_label.set_alignment(1.0, 0.5)
- self.title_entry.set_size_request(450, 25)
- self.Hbox3.add(title_label)
- self.Hbox3.add(self.title_entry)
-
- self.Hbox4 = gtk.HBox()
- table.attach(self.Hbox4, 0, 1, 5, 6, xpadding=5, ypadding=3)
- self.description_entry = gtk.TextView()
- description_label = gtk.Label(_('Description:') + ' ')
- description_label.set_size_request(150, 25)
- description_label.set_alignment(1.0, 0.5)
- self.description_entry.set_wrap_mode(gtk.WRAP_WORD)
- self.description_entry.set_size_request(450, 50)
- self.Hbox4.add(description_label)
- self.Hbox4.add(self.description_entry)
-
- self.Hbox5 = gtk.HBox()
- table.attach(self.Hbox5, 0, 1, 6, 7, xpadding=5, ypadding=3)
- self.submit_button = gtk.Button(_('Submit to Web'))
- self.submit_button.set_size_request(300, 25)
- self.submit_button.connect('pressed', self._do_remote_logon)
- self.Hbox5.add(self.submit_button)
- self.cancel_button = gtk.Button(_('Cancel'))
- self.cancel_button.set_size_request(300, 25)
- self.cancel_button.connect('pressed', self._stop_uploading)
- self.Hbox5.add(self.cancel_button)
-
- self.pop_up.show_all()
-
- def _stop_uploading(self, widget, event=None):
- """ Hide the popup when the upload is complte """
- self.uploading = False
- self.pop_up.hide()
-
- def _do_remote_logon(self, widget):
- """ Log into the upload server """
- username = self.username_entry.get_text()
- password = self.password_entry.get_text()
- server = xmlrpclib.ServerProxy(_UPLOAD_SERVER + '/call/xmlrpc')
- logged_in = server.login_remote(username, password)
- if logged_in:
- upload_key = logged_in
- self._do_submit_to_web(upload_key)
- else:
- self.login_message.set_text(_('Login failed'))
-
- def _do_submit_to_web(self, key):
- """ Submit project to the server """
- title = self.title_entry.get_text()
- description = self.description_entry.get_buffer().get_text(
- *self.description_entry.get_buffer().get_bounds())
- tafile, imagefile = self.tw.save_for_upload(title)
-
- # Set a maximum file size for image to be uploaded.
- if int(os.path.getsize(imagefile)) > _MAX_FILE_SIZE:
- import Image
- while int(os.path.getsize(imagefile)) > _MAX_FILE_SIZE:
- big_file = Image.open(imagefile)
- smaller_file = big_file.resize(int(0.9 * big_file.size[0]),
- int(0.9 * big_file.size[1]),
- Image.ANTIALIAS)
- smaller_file.save(imagefile, quality = 100)
-
- c = pycurl.Curl()
- c.setopt(c.POST, 1)
- c.setopt(c.FOLLOWLOCATION, 1)
- c.setopt(c.URL, _UPLOAD_SERVER + '/upload')
- c.setopt(c.HTTPHEADER, ["Expect:"])
- c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, tafile)),
- ('newimage', (c.FORM_FILE, imagefile)),
- ('small_image', (c.FORM_FILE, imagefile)),
- ('title', title),
- ('description', description),
- ('upload_key', key), ('_formname',
- 'image_create')])
- c.perform()
- error_code = c.getinfo(c.HTTP_CODE)
- c.close
- os.remove(imagefile)
- os.remove(tafile)
- if error_code == 400:
- self.login_message.set_text(_('Failed to upload!'))
- else:
- self.pop_up.hide()
- self.uploading = False
-
-
if __name__ == "__main__":
TurtleMain()