Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter.bender@gmail.com>2011-02-21 23:00:50 (GMT)
committer Walter Bender <walter.bender@gmail.com>2011-02-21 23:00:50 (GMT)
commitaa2d1d42f01f87b5a0a7e533bb6a93d3720b9d1a (patch)
tree7c6a4d820e001530082045875676f44e65280203
parente262536e85ce5289bd5eaafa157939ed8657fdab (diff)
parent860754f7e871617df9d101a51dc64a69b742a0ba (diff)
Merge git.sugarlabs.org:~walter/turtleart/collaboration-refactoring
Conflicts: NEWS TurtleArt/talogo.py
-rw-r--r--NEWS19
-rw-r--r--TurtleArt/tacanvas.py227
-rw-r--r--TurtleArt/tacollaboration.py282
-rw-r--r--TurtleArt/taconstants.py176
-rw-r--r--TurtleArt/taexporthtml.py7
-rw-r--r--TurtleArt/talogo.py198
-rw-r--r--TurtleArt/taturtle.py4
-rw-r--r--TurtleArt/tautils.py36
-rw-r--r--TurtleArt/tawindow.py247
-rw-r--r--TurtleArtActivity.py51
-rw-r--r--audio/__init__.py (copied from devices/__init__.py)0
-rw-r--r--audio/audiograb.py (renamed from TurtleArt/audiograb.py)8
-rw-r--r--audio/ringbuffer.py (renamed from TurtleArt/ringbuffer.py)0
-rw-r--r--camera/__init__.py (renamed from extra/__init__.py)0
-rw-r--r--camera/tacamera.py (copied from TurtleArt/tacamera.py)0
-rw-r--r--camera/v4l2.py (renamed from TurtleArt/v4l2.py)0
-rwxr-xr-xcollaboration/neighborhood.py15
-rw-r--r--collaboration/telepathyclient.py17
-rw-r--r--extra/plugin.py10
-rw-r--r--gnome_plugins/__init__.py (copied from devices/__init__.py)0
-rw-r--r--gnome_plugins/collaboration_plugin.py (renamed from extra/collaborationplugin.py)231
-rw-r--r--gnome_plugins/plugin.py (renamed from TurtleArt/tacamera.py)30
-rw-r--r--gnome_plugins/uploader_plugin.py (renamed from extra/upload.py)49
-rw-r--r--icons/blocksoff.svg45
-rw-r--r--icons/blockson.svg115
-rw-r--r--icons/extrasoff.svg72
-rw-r--r--icons/extrason.svg154
-rw-r--r--icons/mediaoff.svg46
-rw-r--r--icons/mediaon.svg46
-rw-r--r--icons/portfoliooff.svg92
-rw-r--r--icons/portfolioon.svg74
-rw-r--r--plugins/__init__.py (copied from devices/__init__.py)0
-rw-r--r--plugins/__init__.pycbin0 -> 143 bytes
-rw-r--r--plugins/audio_sensors_plugin.py272
-rw-r--r--plugins/camera_plugin.py190
-rw-r--r--plugins/camera_plugin.pycbin0 -> 5204 bytes
-rw-r--r--plugins/plugin.py56
-rw-r--r--plugins/plugin.pycbin0 -> 1014 bytes
-rw-r--r--plugins/rfid_plugin.py161
-rw-r--r--rfid/__init__.py (renamed from devices/__init__.py)0
-rw-r--r--rfid/device.py (renamed from devices/device.py)0
-rw-r--r--rfid/rfidrweusb.py (renamed from devices/rfidrweusb.py)0
-rw-r--r--rfid/rfidutils.py (renamed from TurtleArt/rfidutils.py)6
-rw-r--r--rfid/serial/__init__.py (renamed from devices/serial/__init__.py)0
-rw-r--r--rfid/serial/serialposix.py (renamed from devices/serial/serialposix.py)0
-rw-r--r--rfid/serial/serialutil.py (renamed from devices/serial/serialutil.py)0
-rw-r--r--rfid/tis2000.py (renamed from devices/tis2000.py)0
-rw-r--r--rfid/utils.py (renamed from devices/utils.py)0
-rw-r--r--samples/love-speaks-volumes.ta109
-rw-r--r--samples/spiralaterals.ta56
-rwxr-xr-xturtleart.py158
-rw-r--r--util/RtfParser.py (renamed from TurtleArt/RtfParser.py)0
-rw-r--r--util/configfile.py71
-rw-r--r--util/configwizard.py100
-rw-r--r--util/menubuilder.py16
55 files changed, 2492 insertions, 954 deletions
diff --git a/NEWS b/NEWS
index e317257..5c5c498 100644
--- a/NEWS
+++ b/NEWS
@@ -2,19 +2,28 @@
BUG FIXES
+* Added sharing to draw_text, fill_polygon, draw_pixbuf (#2461)
* Exposed see as an external method (#2542)
* Media type tests on file suffix use lower()
-* Uninitialized variables in Show block for numeric arguments (#2543)
-* Catch potential zero-divide in set_gray method (#2545)
-* Fixed regression with show Journal object thumbnails
+* Added support for localization to GNOME version (rgs)
+* Work around for situations where gst is not available
+* Fixed problem with displaying Journal preview images in portfolio
+* Restore overlay grids on clear
+* Fixed problem with help-string wrap width
ENHANCEMENTS
-* Code clean up by RGS
-* Added 'brightness' block for reading camera luminance level
+* Added a Media Palette for all media-related blocks
+* Added sharing between Gnome and Sugar versions (with Raul Gutierrez)
+* Added 'time' block for measuring elapsed time (in seconds)
* Added 'camera sees' block for reading average camera RGB value
+ (with help from Tony Forster and Guzman Trinidad)
* Added camera media block for grabbing images from the camera
* New psuedo-color.ta example (Tony Forster)
+* New love-speaks-volumes.ta example
+* New spiralaterals.ta example inspired by Spiralaterals activity
+* More complete translations in Spanish (es) and Italian (it)
+* Added plugin support for non-standard devices (camera, sensors, RFID)
105
diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py
index d4395a2..79bdd04 100644
--- a/TurtleArt/tacanvas.py
+++ b/TurtleArt/tacanvas.py
@@ -1,5 +1,5 @@
#Copyright (c) 2007-8, Playful Invention Company.
-#Copyright (c) 2008-10, Walter Bender
+#Copyright (c) 2008-11, 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
@@ -24,10 +24,11 @@ import gtk
from math import sin, cos, pi
import pango
import cairo
+import base64
from sprites import Sprite
from tasprite_factory import SVG
-from tautils import image_to_base64, data_to_string, round_int
+from tautils import image_to_base64, get_path, data_to_string, round_int
from taconstants import CANVAS_LAYER, BLACK, WHITE
import logging
@@ -43,6 +44,23 @@ def wrap100(n):
return n
+def calc_poly_bounds(poly_points):
+ """ Calculate the minx, miny, width, height of polygon """
+ minx = poly_points[0][0]
+ miny = poly_points[0][1]
+ maxx, maxy = minx, miny
+ for p in poly_points:
+ if p[0] < minx:
+ minx = p[0]
+ elif p[0] > maxx:
+ maxx = p[0]
+ if p[1] < miny:
+ miny = p[1]
+ elif p[1] > maxy:
+ maxy = p[1]
+ return(minx, miny, maxx - minx, maxy - miny)
+
+
def calc_shade(c, s, invert=False):
""" Convert a color to the current shade (lightness/darkness). """
# Assumes 16 bit input values
@@ -148,27 +166,24 @@ class TurtleGraphics:
self.fill = False
if len(self.poly_points) == 0:
return
- minx = self.poly_points[0][0]
- miny = self.poly_points[0][1]
- maxx = minx
- maxy = miny
- for p in self.poly_points:
- if p[0] < minx:
- minx = p[0]
- elif p[0] > maxx:
- maxx = p[0]
- if p[1] < miny:
- miny = p[1]
- elif p[1] > maxy:
- maxy = p[1]
- w = maxx - minx
- h = maxy - miny
- self.canvas.images[0].draw_polygon(self.gc, True, self.poly_points)
+ self.fill_polygon(self.poly_points)
+ if self.tw.sharing():
+ shared_poly_points = []
+ for p in self.poly_points:
+ shared_poly_points.append((self.screen_to_turtle_coordinates(
+ p[0], p[1])))
+ event = "F|%s" % (data_to_string([self._get_my_nick(),
+ shared_poly_points]))
+ self.tw.send_event(event)
+ self.poly_points = []
+
+ def fill_polygon(self, poly_points):
+ minx, miny, w, h = calc_poly_bounds(poly_points)
+ self.canvas.images[0].draw_polygon(self.gc, True, poly_points)
self.invalt(minx - self.pensize * self.tw.coord_scale / 2 - 3,
miny - self.pensize * self.tw.coord_scale / 2 - 3,
w + self.pensize * self.tw.coord_scale + 6,
h + self.pensize * self.tw.coord_scale + 6)
- self.poly_points = []
def clearscreen(self, share=True):
"""Clear the canvas and reset most graphics attributes to defaults."""
@@ -215,13 +230,14 @@ class TurtleGraphics:
self.move_turtle()
if self.tw.saving_svg and self.pendown:
self.tw.svg_string += self.svg.new_path(oldx,
- self.height / 2 - oldy)
+ self.invert_y_coordinate(oldy))
self.tw.svg_string += self.svg.line_to(self.xcor,
- self.height / 2 - self.ycor)
+ self.invert_y_coordinate(self.ycor))
self.tw.svg_string += "\"\n"
self.tw.svg_string += self.svg.style()
- event = "f|%s" % (data_to_string([self._get_my_nick(), int(n)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "f|%s" % (data_to_string([self._get_my_nick(), int(n)]))
+ self.tw.send_event(event)
def seth(self, n, share=True):
""" Set the turtle heading. """
@@ -232,8 +248,10 @@ class TurtleGraphics:
return
self.heading %= 360
self.turn_turtle()
- event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "r|%s" % (data_to_string([self._get_my_nick(),
+ round_int(self.heading)]))
+ self.tw.send_event(event)
def right(self, n, share=True):
""" Rotate turtle clockwise """
@@ -244,8 +262,10 @@ class TurtleGraphics:
return
self.heading %= 360
self.turn_turtle()
- event = "r|%s" % (data_to_string([self._get_my_nick(), round_int(self.heading)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "r|%s" % (data_to_string([self._get_my_nick(),
+ round_int(self.heading)]))
+ self.tw.send_event(event)
def arc(self, a, r, share=True):
""" Draw an arc """
@@ -260,8 +280,10 @@ class TurtleGraphics:
_logger.debug("bad value sent to %s" % (__name__))
return
self.move_turtle()
- event = "a|%s" % (data_to_string([self._get_my_nick(), [round_int(a), round_int(r)]]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "a|%s" % (data_to_string([self._get_my_nick(),
+ [round_int(a), round_int(r)]]))
+ self.tw.send_event(event)
def rarc(self, a, r):
""" draw a clockwise arc """
@@ -274,8 +296,7 @@ class TurtleGraphics:
oldx, oldy = self.xcor, self.ycor
cx = self.xcor + r * cos(self.heading * DEGTOR)
cy = self.ycor - r * sin(self.heading * DEGTOR)
- x = self.width / 2 + int(cx - r)
- y = self.height / 2 - int(cy + r)
+ x, y = self.turtle_to_screen_coordinates(int(cx - r), int(cy + r))
w = int(2 * r)
h = w
if self.pendown:
@@ -290,9 +311,10 @@ class TurtleGraphics:
self.ycor = cy + r * sin(self.heading * DEGTOR)
if self.tw.saving_svg and self.pendown:
self.tw.svg_string += self.svg.new_path(oldx,
- self.height / 2 - oldy)
+ self.invert_y_coordinate(oldx))
self.tw.svg_string += self.svg.arc_to(self.xcor,
- self.height / 2 - self.ycor, r, a, 0, s)
+ self.invert_y_coordinate(self.ycor),
+ r, a, 0, s)
self.tw.svg_string += "\"\n"
self.tw.svg_string += self.svg.style()
@@ -307,8 +329,7 @@ class TurtleGraphics:
oldx, oldy = self.xcor, self.ycor
cx = self.xcor - r * cos(self.heading * DEGTOR)
cy = self.ycor + r * sin(self.heading * DEGTOR)
- x = self.width / 2 + int(cx - r)
- y = self.height / 2 - int(cy + r)
+ x, y = self.turtle_to_screen_coordinates(int(cx - r), int(cy + r))
w = int(2 * r)
h = w
if self.pendown:
@@ -324,9 +345,9 @@ class TurtleGraphics:
self.ycor = cy - r * sin(self.heading * DEGTOR)
if self.tw.saving_svg and self.pendown:
self.tw.svg_string += self.svg.new_path(oldx,
- self.height / 2 - oldy)
+ self.invert_y_coordinate(oldy))
self.tw.svg_string += self.svg.arc_to(self.xcor,
- self.height / 2 - self.ycor,
+ self.invert_y_coordinate(self.ycor),
r, a, 0, s)
self.tw.svg_string += "\"\n"
self.tw.svg_string += self.svg.style()
@@ -347,8 +368,10 @@ class TurtleGraphics:
self.draw_line(oldx, oldy, self.xcor, self.ycor)
self.move_turtle()
- event = "x|%s" % (data_to_string([self._get_my_nick(), [round_int(x), round_int(y)]]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "x|%s" % (data_to_string([self._get_my_nick(),
+ [round_int(x), round_int(y)]]))
+ self.tw.send_event(event)
def setpensize(self, ps, share=True):
""" Set the pen size """
@@ -361,10 +384,12 @@ class TurtleGraphics:
return
self.tw.active_turtle.set_pen_size(ps)
self.gc.set_line_attributes(int(self.pensize * self.tw.coord_scale),
- gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER)
+ gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER)
self.svg.set_stroke_width(self.pensize)
- event = "w|%s" % (data_to_string([self._get_my_nick(), round_int(ps)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "w|%s" % (data_to_string([self._get_my_nick(),
+ round_int(ps)]))
+ self.tw.send_event(event)
def setcolor(self, c, share=True):
""" Set the pen color """
@@ -377,8 +402,10 @@ class TurtleGraphics:
self.tw.active_turtle.set_color(c)
self.set_fgcolor()
self.set_textcolor()
- event = "c|%s" % (data_to_string([self._get_my_nick(), round_int(c)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "c|%s" % (data_to_string([self._get_my_nick(),
+ round_int(c)]))
+ self.tw.send_event(event)
def setgray(self, g, share=True):
""" Set the gray level """
@@ -394,10 +421,12 @@ class TurtleGraphics:
self.set_fgcolor()
self.set_textcolor()
self.tw.active_turtle.set_gray(self.gray)
- event = "g|%s" % (data_to_string([self._get_my_nick(), round_int(self.gray)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "g|%s" % (data_to_string([self._get_my_nick(),
+ round_int(self.gray)]))
+ self.tw.send_event(event)
- def settextcolor(self, c):
+ def settextcolor(self, c): # depreciated
""" Set the text color """
try:
self.tcolor = c
@@ -423,8 +452,10 @@ class TurtleGraphics:
self.tw.active_turtle.set_shade(s)
self.set_fgcolor()
self.set_textcolor()
- event = "s|%s" % (data_to_string([self._get_my_nick(), round_int(s)]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "s|%s" % (data_to_string([self._get_my_nick(),
+ round_int(s)]))
+ self.tw.send_event(event)
def fillscreen(self, c, s):
""" Fill screen with color/shade and reset to defaults """
@@ -479,10 +510,11 @@ class TurtleGraphics:
""" Lower or raise the pen """
self.pendown = bool
self.tw.active_turtle.set_pen_state(bool)
- event = "p|%s" % (data_to_string([self._get_my_nick(), bool]))
- self._send_event(event, share)
+ if self.tw.sharing() and share:
+ event = "p|%s" % (data_to_string([self._get_my_nick(), bool]))
+ self.tw.send_event(event)
- def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path):
+ def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True):
""" Draw a pixbuf """
w *= self.tw.coord_scale
h *= self.tw.coord_scale
@@ -492,12 +524,31 @@ class TurtleGraphics:
if self.tw.running_sugar:
# In Sugar, we need to embed the images inside the SVG
self.tw.svg_string += self.svg.image(x - self.width / 2,
- y, w, h, path, image_to_base64(pixbuf, self.tw.activity))
+ y, w, h, path, image_to_base64(pixbuf,
+ get_path(self.tw.activity, 'instance')))
else:
+ # Outside of Sugar, we save a path
self.tw.svg_string += self.svg.image(x - self.width / 2,
y, w, h, path)
-
- def draw_text(self, label, x, y, size, w):
+ if self.tw.sharing() and share:
+ if self.tw.running_sugar:
+ tmp_path = get_path(self.tw.activity, 'instance')
+ else:
+ tmp_path = '/tmp'
+ data = image_to_base64(pixbuf, tmp_path)
+ height = pixbuf.get_height()
+ width = pixbuf.get_width()
+ x, y = self.screen_to_turtle_coordinates(x, y)
+ event = "P|%s" % (data_to_string([self._get_my_nick(),
+ [round_int(a), round_int(b),
+ round_int(x), round_int(y),
+ round_int(w), round_int(h),
+ round_int(width),
+ round_int(height),
+ data]]))
+ self.tw.send_event(event)
+
+ def draw_text(self, label, x, y, size, w, share=True):
""" Draw text """
w *= self.tw.coord_scale
self.gc.set_foreground(self.tw.textcolor)
@@ -532,27 +583,47 @@ class TurtleGraphics:
if self.tw.saving_svg and self.pendown:
self.tw.svg_string += self.svg.text(x - self.width / 2,
y + size, size, w, label)
+ if self.tw.sharing() and share:
+ event = "W|%s" % (data_to_string([self._get_my_nick(),
+ [label, round_int(x),
+ round_int(y), round_int(size),
+ round_int(w)]]))
+ self.tw.send_event(event)
+
+ def turtle_to_screen_coordinates(self, x, y):
+ """ The origin of turtle coordinates is the center of the screen """
+ return self.width / 2 + x, self.invert_y_coordinate(y)
+
+ def screen_to_turtle_coordinates(self, x, y):
+ """ The origin of the screen coordinates is the upper left corner """
+ return x - self.width / 2, self.invert_y_coordinate(y)
+
+ def invert_y_coordinate(self, y):
+ """ Positive y goes up in turtle coordinates, down in sceeen
+ coordinates """
+ return self.height / 2 - y
def draw_line(self, x1, y1, x2, y2):
""" Draw a line """
- x1, y1 = self.width / 2 + int(x1), self.height / 2 - int(y1)
- x2, y2 = self.width / 2 + int(x2), self.height / 2 - int(y2)
+ x1, y1 = self.turtle_to_screen_coordinates(x1, y1)
+ x2, y2 = self.turtle_to_screen_coordinates(x2, y2)
if x1 < x2:
- minx, maxx = x1, x2
+ minx, maxx = int(x1), int(x2)
else:
- minx, maxx = x2, x1
+ minx, maxx = int(x2), int(x1)
if y1 < y2:
- miny, maxy = y1, y2
+ miny, maxy = int(y1), int(y2)
else:
- miny, maxy = y2, y1
+ miny, maxy = int(y2), int(y1)
w, h = maxx - minx, maxy - miny
- self.canvas.images[0].draw_line(self.gc, x1, y1, x2, y2)
+ self.canvas.images[0].draw_line(self.gc, int(x1), int(y1), int(x2),
+ int(y2))
if self.fill and self.poly_points == []:
- self.poly_points.append((x1, y1))
+ self.poly_points.append((int(x1), int(y1)))
if self.fill:
- self.poly_points.append((x2, y2))
- self.invalt(minx - self.pensize * self.tw.coord_scale / 2 - 3,
- miny - self.pensize * self.tw.coord_scale / 2 - 3,
+ self.poly_points.append((int(x2), int(y2)))
+ self.invalt(minx - int(self.pensize * self.tw.coord_scale / 2) - 3,
+ miny - int(self.pensize * self.tw.coord_scale / 2) - 3,
w + self.pensize * self.tw.coord_scale + 6,
h + self.pensize * self.tw.coord_scale + 6)
@@ -562,8 +633,7 @@ class TurtleGraphics:
def move_turtle(self):
""" Move the turtle """
- x, y = self.width / 2 + int(self.xcor), \
- self.height / 2 - int(self.ycor)
+ x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor)
self.tw.active_turtle.move(
(int(self.cx + x - self.tw.active_turtle.spr.rect.width / 2),
int(self.cy + y - self.tw.active_turtle.spr.rect.height / 2)))
@@ -611,9 +681,9 @@ class TurtleGraphics:
def get_pixel(self):
""" Read the pixel at x, y """
if self.tw.interactive_mode:
- return self.canvas.get_pixel(
- (self.width / 2 + int(self.xcor),
- self.height / 2 - int(self.ycor)), 0, self.tw.color_mode)
+ x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor)
+ return self.canvas.get_pixel((int(x), int(y)), 0,
+ self.tw.color_mode)
else:
return(-1, -1, -1, -1)
@@ -628,10 +698,9 @@ class TurtleGraphics:
self.tw.active_turtle = self.tw.turtles.get_turtle(k, False)
self.tw.active_turtle.show()
tx, ty = self.tw.active_turtle.get_xy()
- self.xcor = -self.width / 2 + tx + \
- self.tw.active_turtle.spr.rect.width / 2
- self.ycor = self.height / 2 - ty - \
- self.tw.active_turtle.spr.rect.height / 2
+ self.xcor, self.ycor = self.screen_to_turtle_coordinates(tx, ty)
+ self.xcor += self.tw.active_turtle.spr.rect.width / 2
+ self.ycor -= self.tw.active_turtle.spr.rect.height / 2
self.heading = self.tw.active_turtle.get_heading()
self.setcolor(self.tw.active_turtle.get_color(), False)
self.setgray(self.tw.active_turtle.get_gray(), False)
@@ -651,11 +720,3 @@ class TurtleGraphics:
def _get_my_nick(self):
return self.tw.nick
-
- def _send_event(self, entry, share):
- if not share:
- return
-
- if self.tw.sharing():
- print "Sending: %s" % entry
- self.tw.send_event(entry)
diff --git a/TurtleArt/tacollaboration.py b/TurtleArt/tacollaboration.py
index 52164e0..d4b7529 100644
--- a/TurtleArt/tacollaboration.py
+++ b/TurtleArt/tacollaboration.py
@@ -1,9 +1,35 @@
+#Copyright (c) 2011, 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
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
from dbus.service import signal
from dbus.gobject_service import ExportedGObject
import logging
import telepathy
-from TurtleArt.tautils import data_to_string, data_from_string
+
+import gtk
+import base64
+
+from TurtleArt.tautils import data_to_string, data_from_string, get_path, \
+ base64_to_image
+from TurtleArt.taconstants import DEFAULT_TURTLE_COLORS
try:
from sugar import profile
@@ -19,17 +45,19 @@ 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._tw.send_event = self.send_event
self._activity = activity
+ self._setup_dispatch_table()
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)
+ self.initiating = None # sharing (True) or joining (False)
# Add my buddy object to the list
owner = self.pservice.get_owner()
@@ -40,6 +68,24 @@ class Collaboration():
self._activity.connect('shared', self._shared_cb)
self._activity.connect('joined', self._joined_cb)
+ def _setup_dispatch_table(self):
+ self._processing_methods = {
+ 't': self._turtle_request,
+ 'T': self._receive_turtle_dict,
+ 'f': self._move_forward,
+ 'a': self._move_in_arc,
+ 'r': self._rotate_turtle,
+ 'x': self._setxy,
+ 'W': self._draw_text,
+ 'c': self._set_pen_color,
+ 'g': self._set_pen_gray_level,
+ 's': self._set_pen_shade,
+ 'w': self._set_pen_width,
+ 'p': self._set_pen_state,
+ 'F': self._fill_polygon,
+ 'P': self._draw_pixbuf
+ }
+
def _shared_cb(self, activity):
self._shared_activity = self._activity._shared_activity
if self._shared_activity is None:
@@ -128,98 +174,25 @@ class Collaboration():
_logger.debug(event)
self.send_event(event)
- def event_received_cb(self, text):
+ def event_received_cb(self, event_message):
"""
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:
+ if len(event_message) == 0:
return
- # Save active Turtle
+
+ # 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)
+
+ try:
+ command, payload = event_message.split("|", 2)
+ self._processing_methods[command](payload)
+ except ValueError:
+ _logger.debug("could not split event message")
+
# Restore active Turtle
self._tw.canvas.set_turtle(self._tw.turtles.get_turtle_key(
save_active_turtle))
@@ -229,27 +202,156 @@ class Collaboration():
if hasattr(self, 'chattube') and self.chattube is not None:
self.chattube.SendText(entry)
+ def _turtle_request(self, payload):
+ if payload > 0:
+ [nick, colors] = data_from_string(payload)
+ 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:
+ event_payload = data_to_string(self.turtle_dictionary)
+ self.send_event("T|" + event_payload)
+
+ def _receive_turtle_dict(self, payload):
+ if self.waiting_for_turtles:
+ if len(payload) > 0:
+ self.turtle_dictionary = data_from_string(payload)
+ 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
+
+ def _draw_pixbuf(self, payload):
+ if len(payload) > 0:
+ [nick, [a, b, x, y, w, h, width, height, data]] =\
+ data_from_string(payload)
+ if nick != self._tw.nick:
+ if self._tw.running_sugar:
+ tmp_path = get_path(self._tw.activity, 'instance')
+ else:
+ tmp_path = '/tmp'
+ file_name = base64_to_image(data, tmp_path)
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name,
+ width, height)
+ x, y = self._tw.canvas.turtle_to_screen_coordinates(x, y)
+ self._tw.canvas.draw_pixbuf(pixbuf, a, b, x, y, w, h,
+ file_name, False)
+
+ def _move_forward(self, payload):
+ if len(payload) > 0:
+ [nick, x] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.forward(x, False)
+
+ def _move_in_arc(self, payload):
+ if len(payload) > 0:
+ [nick, [a, r]] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.arc(a, r, False)
+
+ def _rotate_turtle(self, payload):
+ if len(payload) > 0:
+ [nick, h] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.seth(h, False)
+
+ def _setxy(self, payload):
+ if len(payload) > 0:
+ [nick, [x, y]] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.setxy(x, y, False)
+
+ def _draw_text(self, payload):
+ if len(payload) > 0:
+ [nick, [label, x, y, size, w]] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.draw_text(label, x, y, size, w, False)
+
+ def _set_pen_color(self, payload):
+ if len(payload) > 0:
+ [nick, x] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.setcolor(x, False)
+
+ def _set_pen_gray_level(self, payload):
+ if len(payload) > 0:
+ [nick, x] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.setgray(x, False)
+
+ def _set_pen_shade(self, payload):
+ if len(payload) > 0:
+ [nick, x] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.setshade(x, False)
+
+ def _set_pen_width(self, payload):
+ if len(payload) > 0:
+ [nick, x] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.setpensize(x, False)
+
+ def _set_pen_state(self, payload):
+ if len(payload) > 0:
+ [nick, x] = data_from_string(payload)
+ if nick != self._tw.nick:
+ self._tw.canvas.set_turtle(nick)
+ self._tw.canvas.setpen(x, False)
+
+ def _fill_polygon(self, payload):
+ # Check to make sure that the poly_point array is passed properly
+ if len(payload) > 0:
+ [nick, poly_points] = data_from_string(payload)
+ shared_poly_points = []
+ for i in range(len(poly_points)):
+ shared_poly_points.append((
+ self._tw.canvas.turtle_to_screen_coordinates(
+ poly_points[i][0], poly_points[i][1])))
+ self._tw.canvas.fill_polygon(shared_poly_points)
+
def _get_dictionary(self):
- d = { self._get_nick(): self._get_colors()}
+ d = {self._get_nick(): self._get_colors()}
return d
def _get_nick(self):
return self._tw.nick
def _get_colors(self):
- if profile:
- colors = profile.get_color().to_string()
+ colors = None
+ if self._tw.running_sugar:
+ if profile.get_color() is not None:
+ colors = profile.get_color().to_string()
else:
colors = self._activity.get_colors()
+ if colors is None:
+ colors = '%s,%s' % (DEFAULT_TURTLE_COLORS[0],
+ DEFAULT_TURTLE_COLORS[1])
return colors
+
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.is_initiator = is_initiator # Are we sharing or joining activity?
self.stack_received_cb = stack_received_cb
self.stack = ''
diff --git a/TurtleArt/taconstants.py b/TurtleArt/taconstants.py
index 77ebefb..f5d97ad 100644
--- a/TurtleArt/taconstants.py
+++ b/TurtleArt/taconstants.py
@@ -113,35 +113,33 @@ TOP_LAYER = 1000
#
PALETTE_NAMES = ['turtle', 'pen', 'colors', 'numbers', 'flow', 'blocks',
- 'extras', 'sensor', 'portfolio', 'trash']
+ 'extras', 'sensor', 'media', 'portfolio', 'trash']
-PALETTES = [['clean', 'forward', 'back', 'show', 'left', 'right',
- 'seth', 'setxy2', 'heading', 'xcor', 'ycor', 'setscale',
- 'arc', 'scale', 'leftpos', 'toppos', 'rightpos',
- 'bottompos'],
+PALETTES = [['forward', 'back', 'clean', 'left', 'right',
+ 'arc', 'setxy2', 'seth', 'xcor', 'ycor', 'heading'],
['penup', 'pendown', 'setpensize', 'fillscreen', 'pensize',
- 'setcolor', 'setshade', 'setgray', 'color', 'shade',
- 'gray', 'startfill', 'stopfill'],
- ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple',
+ 'startfill', 'stopfill'],
+ ['setcolor', 'setshade', 'setgray', 'color', 'shade', 'gray',
+ 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple',
'white', 'black'],
['plus2', 'minus2', 'product2',
'division2', 'identity2', 'remainder2', 'sqrt', 'random',
'number', 'greater2', 'less2', 'equal2', 'not', 'and2', 'or2'],
['wait', 'forever', 'repeat', 'if', 'ifelse', 'while', 'until',
'hspace', 'vspace', 'stopstack'],
- ['start', 'hat1', 'stack1', 'hat', 'hat2', 'stack2', 'stack',
- 'storeinbox1', 'storeinbox2', 'string', 'box1', 'box2', 'box',
- 'storein'],
+ ['start', 'storeinbox1', 'storeinbox2', 'string', 'box1', 'box2',
+ 'box', 'storein', 'hat', 'hat1', 'hat2', 'stack', 'stack1',
+ 'stack2'],
['push', 'printheap', 'clearheap', 'pop', 'comment', 'print',
- 'myfunc1arg', 'userdefined',
- 'cartesian', 'width', 'height', 'polar', 'addturtle', 'reskin',
- 'sandwichtop_no_label', 'sandwichbottom'],
- ['kbinput', 'keyboard', 'readpixel', 'see',
- 'sound', 'volume', 'pitch'],
- ['journal', 'audio', 'video', 'description', 'hideblocks',
- 'showblocks', 'fullscreen', 'savepix', 'savesvg', 'mediawait',
- 'picturelist', 'picture1x1a', 'picture1x1', 'picture2x2',
- 'picture2x1', 'picture1x2'],
+ 'myfunc1arg', 'userdefined', 'cartesian', 'polar', 'addturtle',
+ 'reskin', 'sandwichtop_no_label', 'sandwichbottom'],
+ ['kbinput', 'keyboard', 'readpixel', 'see', 'time'],
+ ['journal', 'audio', 'video', 'description', 'string',
+ 'show', 'setscale', 'savepix', 'savesvg', 'scale', 'mediawait'],
+ ['hideblocks', 'showblocks', 'fullscreen', 'picturelist',
+ 'picture1x1a', 'picture1x1', 'picture2x2', 'picture2x1',
+ 'picture1x2', 'leftpos', 'bottompos', 'width', 'rightpos',
+ 'toppos', 'height'],
['empty', 'restoreall']]
#
@@ -152,7 +150,8 @@ COLORS = [["#00FF00", "#00A000"], ["#00FFFF", "#00A0A0"],
["#00FFFF", "#00A0A0"], ["#FF00FF", "#A000A0"],
["#FFC000", "#A08000"], ["#FFFF00", "#A0A000"],
["#FF0000", "#A00000"], ["#FF0000", "#A00000"],
- ["#0000FF", "#0000A0"], ["#FFFF00", "#A0A000"]]
+ ["#A0FF00", "#A0A000"], ["#0000FF", "#0000A0"],
+ ["#FFFF00", "#A0A000"]]
BOX_COLORS = {'red': ["#FF0000", "#A00000"],
'orange': ["#FFD000", "#AA8000"],
@@ -176,6 +175,7 @@ STANDARD_STROKE_WIDTH = 1.0
BLOCK_SCALE = 2.0
PALETTE_SCALE = 1.5
DEFAULT_TURTLE = 'Yertle'
+DEFAULT_TURTLE_COLORS = ['#008000', '#00A000']
HORIZONTAL_PALETTE = 0
VERTICAL_PALETTE = 1
BLACK = -9999
@@ -190,10 +190,6 @@ DEFAULT_SCALE = 33
XO1 = 'xo1'
XO15 = 'xo1.5'
UNKNOWN = 'unknown'
-SENSOR_AC_NO_BIAS = 'external'
-SENSOR_AC_BIAS = 'sound'
-SENSOR_DC_NO_BIAS = 'voltage'
-SENSOR_DC_BIAS = 'resistance'
#
# Block-style definitions
@@ -205,15 +201,15 @@ BASIC_STYLE = []
BASIC_STYLE_EXTENDED_VERTICAL = ['clean', 'penup', 'pendown', 'stack1',
'stack2', 'hideblocks', 'showblocks', 'clearheap', 'printheap', 'kbinput',
'fullscreen', 'cartesian', 'polar', 'startfill', 'mediawait',
- 'stopfill', 'readpixel', 'readcamera', 'vspace']
+ 'stopfill', 'readpixel', 'vspace']
INVISIBLE = ['sandwichcollapsed']
BASIC_STYLE_EXTENDED = ['picturelist', 'picture1x1', 'picture2x2',
'picture2x1', 'picture1x2', 'picture1x1a']
-BASIC_STYLE_1ARG = ['forward', 'back', 'left', 'right', 'seth', 'show', 'image',
+BASIC_STYLE_1ARG = ['forward', 'back', 'left', 'right', 'seth', 'show',
'setscale', 'setpensize', 'setcolor', 'setshade', 'print', 'showaligned',
'settextsize', 'settextcolor', 'print', 'wait', 'storeinbox1', 'savepix',
'storeinbox2', 'wait', 'stack', 'push', 'nop', 'addturtle', 'comment',
- 'savesvg', 'setgray', 'skin', 'reskin']
+ 'image', 'savesvg', 'setgray', 'skin', 'reskin']
BASIC_STYLE_VAR_ARG = ['userdefined', 'userdefined2args', 'userdefined3args']
BULLET_STYLE = ['templatelist', 'list']
BASIC_STYLE_2ARG = ['arc', 'setxy', 'setxy2', 'fillscreen', 'storein', 'write']
@@ -222,9 +218,8 @@ BOX_STYLE = ['number', 'xcor', 'ycor', 'heading', 'pensize', 'color', 'shade',
'toppos', 'rightpos', 'bottompos', 'width', 'height', 'pop', 'keyboard',
'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'white',
'black', 'titlex', 'titley', 'leftx', 'topy', 'rightx', 'bottomy',
- 'sound', 'volume', 'pitch', 'voltage', 'resistance', 'gray', 'see', 'rfid',
- 'luminance']
-BOX_STYLE_MEDIA = ['description', 'audio', 'journal', 'video', 'camera']
+ 'gray', 'see', 'time']
+BOX_STYLE_MEDIA = ['description', 'audio', 'journal', 'video']
NUMBER_STYLE = ['plus2', 'product2', 'myfunc']
NUMBER_STYLE_VAR_ARG = ['myfunc1arg', 'myfunc2arg', 'myfunc3arg']
NUMBER_STYLE_BLOCK = ['random']
@@ -278,7 +273,7 @@ OLD_DOCK = ['and', 'or', 'plus', 'minus', 'division', 'product', 'remainder']
# Blocks that contain media
#
CONTENT_BLOCKS = ['number', 'string', 'description', 'audio', 'video',
- 'journal', 'camera']
+ 'journal']
#
# These blocks get a special skin
@@ -291,11 +286,11 @@ PYTHON_SKIN = ['nop', 'userdefined', 'userdefined2args', 'userdefined3args']
#
# These blocks hold constants
#
-CONSTANTS = {'leftpos':None, 'toppos':None, 'rightpos':None, 'bottompos':None,
- 'width':None, 'height':None, 'red':0, 'orange':10, 'yellow':20,
- 'green':40, 'cyan':50, 'blue':70, 'purple':90, 'titlex':None,
- 'titley':None, 'leftx':None, 'topy':None, 'rightx':None,
- 'bottomy':None}
+CONSTANTS = {'leftpos': None, 'toppos': None, 'rightpos': None,
+ 'bottompos': None, 'width': None, 'height': None, 'red': 0,
+ 'orange': 10, 'yellow': 20, 'green': 40, 'cyan': 50, 'blue': 70,
+ 'purple': 90, 'titlex': None, 'titley': None, 'leftx': None,
+ 'topy': None, 'rightx': None, 'bottomy': None}
#
# Block-name dictionary used for labels
@@ -314,7 +309,6 @@ BLOCK_NAMES = {
'box': [_('box')],
'box1': [_('box 1')],
'box2': [_('box 2')],
- 'camera': [' '],
'cartesian': [_('Cartesian')],
'clean': [_(' clean ')],
'clearheap': [_('empty heap')],
@@ -354,7 +348,6 @@ BLOCK_NAMES = {
'leftx': [_('picture left')],
'less2': ['<'],
'list': ['list'],
- 'luminance': [_('brightness')],
'mediawait': [_('media wait')],
'minus2': ['–'],
'myfunc': [_('Python'), 'f(x)', 'x'],
@@ -388,7 +381,6 @@ BLOCK_NAMES = {
'purple': [_('purple')],
'push': [_('push')],
'random': [_('random'), _('min'), _('max')],
- 'readcamera': [_('read camera')],
'readpixel': [_('read pixel')],
'red': [_('red')],
'remainder2': [_('mod')],
@@ -397,7 +389,6 @@ BLOCK_NAMES = {
'resistance': [_('resistance')],
'restore': [_('restore last')],
'restoreall': [_('restore all')],
- 'rfid': [_('RFID')],
'right': [_('right')],
'rightpos': [_('right')],
'rightx': [_('picture right')],
@@ -447,6 +438,7 @@ BLOCK_NAMES = {
'template2x2': [' '],
'templatelist': [' '],
'textsize': [_('text size')],
+ 'time': [_('time')],
'titlex': [_('title x')],
'titley': [_('title y')],
'toppos': [_('top')],
@@ -520,7 +512,6 @@ PRIMITIVES = {
'leftx': 'leftx',
'less2': 'less?',
'list': 'bulletlist',
- 'luminance': 'luminance',
'mediawait': 'mediawait',
'minus2': 'minus',
'myfunc': 'myfunction',
@@ -534,7 +525,6 @@ PRIMITIVES = {
'pendown': 'pendown',
'pensize': 'pensize',
'penup': 'penup',
- 'pitch': 'pitch',
'plus2': 'plus',
'polar': 'polar',
'pop': 'pop',
@@ -545,12 +535,9 @@ PRIMITIVES = {
'push': 'push',
'random': 'random',
'red': 'red',
- 'readcamera': 'readcamera',
'readpixel': 'readpixel',
'remainder2': 'mod',
'repeat': 'repeat',
- 'resistance': 'resistance',
- 'rfid': 'rfid',
'right': 'right',
'rightpos': 'rpos',
'rightx': 'rightx',
@@ -579,7 +566,6 @@ PRIMITIVES = {
'showblocks': 'showblocks',
'showaligned': 'showaligned',
'skin': 'skin',
- 'sound': 'sound',
'sqrt': 'sqrt',
'stack': 'stack',
'stack1': 'stack1',
@@ -598,6 +584,7 @@ PRIMITIVES = {
'template2x2': 't2x2',
'templatelist': 'bullet',
'textsize': 'textsize',
+ 'time': 'time',
'titlex': 'titlex',
'titley': 'titley',
'toppos': 'tpos',
@@ -605,8 +592,6 @@ PRIMITIVES = {
'userdefined': 'userdefined',
'userdefined2args': 'userdefined2',
'userdefined3args': 'userdefined3',
- 'voltage': 'voltage',
- 'volume': 'volume',
'vspace': 'nop',
'wait': 'wait',
'while2': 'while',
@@ -627,7 +612,6 @@ DEFAULTS = {
'audio': [None],
'back': [100],
'box': [_('my box')],
- 'camera': ['CAMERA'],
'comment': [_('comment')],
'description': [None],
'fillscreen': [60, 80],
@@ -697,6 +681,9 @@ STRING_OR_NUMBER_ARGS = ['plus2', 'equal2', 'less2', 'greater2', 'box',
CONTENT_ARGS = ['show', 'showaligned', 'push', 'storein', 'storeinbox1',
'storeinbox2']
+PREFIX_DICTIONARY = {'journal': '#smedia_', 'description': '#sdescr_',
+ 'audio': '#saudio_', 'video': '#svideo_'}
+
#
# Status blocks
#
@@ -758,8 +745,9 @@ TEMPLATES = {'t1x1': (0.5, 0.5, 0.0625, 0.125, 1.05, 0),
# Names for blocks without names for popup help
#
SPECIAL_NAMES = {
+ 'and2': _('and'),
'audio': _('audio'),
- 'camera': _('camera'),
+ 'description': _('description'),
'division2': _('divide'),
'equal2': _('equal'),
'greater2': _('greater than'),
@@ -769,11 +757,16 @@ SPECIAL_NAMES = {
'ifelse': _('if then else'),
'journal': _('journal'),
'less2': _('less than'),
+ 'or2': _('or'),
'minus2': _('minus'),
'nop': _('Python code'),
'number': _('number'),
'plus2': _('plus'),
'product2': _('multiply'),
+ 'repeat': _('repeat'),
+ 'sandwichtop_no_label': _('top of a collapsible stack'),
+ 'sandwichbottom': _('bottom of a collapsible stack'),
+ 'sensors': _('sensors'),
'sqrt': _('square root'),
'template1x1': _('presentation 1x1'),
'template1x1a': _('presentation 1x1'),
@@ -782,6 +775,9 @@ SPECIAL_NAMES = {
'template2x2': _('presentation 2x2'),
'templatelist': _('presentation bulleted list'),
'textsize': _('text size'),
+ 'userdefined': _('Python block'),
+ 'userdefined2args': _('Python block'),
+ 'userdefined3args': _('Python block'),
'video': _('video'),
'vspace': _('vertical space')}
@@ -799,11 +795,11 @@ HELP_STRINGS = {
'box1': _("Variable 1 (numeric value)"),
'box2': _("Variable 2 (numeric value)"),
'box': _("named variable (numeric value)"),
- 'camera': _('camera output'),
'cartesian': _("displays Cartesian coordinates"),
'clean': _("clears the screen and reset the turtle"),
'clearheap': _("emptys FILO (first-in-last-out heap)"),
- 'color': _("holds current pen color (can be used in place of a number block)"),
+ 'color': _(
+ "holds current pen color (can be used in place of a number block)"),
'colors': _("Palette of pen colors"),
'comment': _("places a comment in your code"),
'debugoff': _("Debug"),
@@ -818,7 +814,8 @@ HELP_STRINGS = {
'forever': _("loops forever"),
'forward': _("moves turtle forward"),
'fullscreen': _("hides the Sugar toolbars"),
- 'gray': _("holds current gray level (can be used in place of a number block)"),
+ 'gray': _(
+ "holds current gray level (can be used in place of a number block)"),
'greater2': _("logical greater-than operator"),
'hat1': _("top of Action 1 stack"),
'hat2': _("top of Action 2 stack"),
@@ -830,14 +827,15 @@ HELP_STRINGS = {
'hspace': _("jogs stack right"),
'identity2': _("identity operator used for extending blocks"),
'ifelse': _("if-then-else operator that uses boolean operators from Numbers palette"),
- 'if': _("if-then operator that uses boolean operators from Numbers palette"),
+ 'if': _(
+ "if-then operator that uses boolean operators from Numbers palette"),
'journal': _("Sugar Journal media object"),
'kbinput': _("query for keyboard input (results stored in keyboard block)"),
'keyboard': _("holds results of query-keyboard block"),
'leftpos': _("xcor of left of screen"),
'left': _("turns turtle counterclockwise (angle in degrees)"),
'less2': _("logical less-than operator"),
- 'luminance': _("light level detected by camera"),
+ 'media': _("Palette of media objects"),
'mediawait': _("wait for current video or audio to complete"),
'minus2': _("subtracts bottom numeric input from top numeric input"),
'myfunc': _("a programmable block: used to add advanced math equations, e.g., sin(x)"),
@@ -849,19 +847,21 @@ HELP_STRINGS = {
'not': _("logical NOT operator"),
'numbers': _("Palette of numeric operators"),
'number': _("used as numeric input in mathematic operators"),
- 'or': _("logical OR operator"),
+ 'or2': _("logical OR operator"),
'orientation': _("changes the orientation of the palette of blocks"),
'pendown': _("Turtle will draw when moved."),
'pen': _("Palette of pen commands"),
- 'pensize': _("holds current pen size (can be used in place of a number block)"),
+ 'pensize': _(
+ "holds current pen size (can be used in place of a number block)"),
'penup': _("Turtle will not draw when moved."),
- 'picture1x1': _("presentation template: select Journal object (with description)"),
- 'picture1x1a': _("presentation template: select Journal object (no description)"),
+ 'picture1x1': _(
+ "presentation template: select Journal object (with description)"),
+ 'picture1x1a': _(
+ "presentation template: select Journal object (no description)"),
'picture1x2': _("presentation template: select two Journal objects"),
'picture2x1': _("presentation template: select two Journal objects"),
'picture2x2': _("presentation template: select four Journal objects"),
'picturelist': _("presentation template: list of bullets"),
- 'pitch': _('microphone input pitch'),
'plus2': _("adds two alphanumeric inputs"),
'polar': _("displays polar coordinates"),
'pop': _("pops value off FILO (first-in last-out heap)"),
@@ -871,7 +871,6 @@ HELP_STRINGS = {
'product2': _("multiplies two numeric inputs"),
'push': _("pushes value onto FILO (first-in last-out heap)"),
'random': _("returns random number between minimum (top) and maximum (bottom) values"),
- 'readcamera': _("Average RGB color from camera is pushed to the stack"),
'readpixel': _("RGB color under the turtle is pushed to the stack"),
'remainder2': _("modular (remainder) operator"),
'repeat': _("loops specified number of times"),
@@ -879,12 +878,12 @@ HELP_STRINGS = {
'reskin': _("put a custom 'shell' on the turtle"),
'restore': _("restores most recent blocks from trash"),
'restoreall': _("restore all blocks from trash"),
- 'rfid': _("RFID"),
'rightpos': _("xcor of right of screen"),
'right': _("turns turtle clockwise (angle in degrees)"),
'run-fastoff': _("Run"),
'run-slowoff': _("Step"),
- 'sandwichbottom': _("bottom block in a collapsibe stack: click to collapse"),
+ 'sandwichbottom': _(
+ "bottom block in a collapsibe stack: click to collapse"),
'sandwichcollapsed': _("bottom block in a collapsed stack: click to open"),
'sandwichtop': _("top of a collapsible stack"),
'sandwichtop_no_label': _("top of a collapsed stack"),
@@ -897,7 +896,8 @@ HELP_STRINGS = {
'sensor': _("Palette of sensor blocks"),
'setcolor': _("sets color of the line drawn by the turtle"),
'setgray': _("sets gray level of the line drawn by the turtle"),
- 'seth': _("sets the heading of the turtle (0 is towards the top of the screen.)"),
+ 'seth': _(
+ "sets the heading of the turtle (0 is towards the top of the screen.)"),
'setpensize': _("sets size of the line drawn by the turtle"),
'setscale': _("sets the scale of media"),
'setshade': _("sets shade of the line drawn by the turtle"),
@@ -909,7 +909,6 @@ HELP_STRINGS = {
'show': _("draws text or show media from the Journal"),
'showblocks': _("restores hidden blocks"),
'skin': _("put a custom 'shell' on the turtle"),
- 'sound': _("raw microphone input signal"),
'sqrt': _("calculates square root"),
'stack1': _("invokes Action 1 stack"),
'stack2': _("invokes Action 2 stack"),
@@ -923,24 +922,31 @@ HELP_STRINGS = {
'storeinbox2': _("stores numeric value in Variable 2"),
'storein': _("stores numeric value in named variable"),
'string': _("string value"),
- 'template1x1': _("presentation template: select Journal object (with description)"),
- 'template1x1a': _("presentation template: select Journal object (no description)"),
+ 'template1x1': _(
+ "presentation template: select Journal object (with description)"),
+ 'template1x1a': _(
+ "presentation template: select Journal object (no description)"),
'template1x2': _("presentation template: select two Journal objects"),
'template2x1': _("presentation template: select two Journal objects"),
'template2x2': _("presentation template: select four Journal objects"),
'templatelist': _("presentation template: list of bullets"),
- 'textcolor': _("holds current text color (can be used in place of a number block)"),
- 'textsize': _("holds current text size (can be used in place of a number block)"),
+ 'textcolor': _(
+ "holds current text color (can be used in place of a number block)"),
+ 'textsize': _(
+ "holds current text size (can be used in place of a number block)"),
+ 'time': _("elapsed time (in seconds) since program started"),
'toppos': _("ycor of top of screen"),
'trash': _("Trashcan"),
'turtle': _("Palette of turtle commands"),
'until': _("do-until-True operator that uses boolean operators from Numbers palette"),
- 'userdefined': _("runs code found in the tamyblock.py module found in the Journal"),
- 'userdefined2args': _("runs code found in the tamyblock.py module found in the Journal"),
- 'userdefined3args': _("runs code found in the tamyblock.py module found in the Journal"),
+ 'userdefined': _(
+ "runs code found in the tamyblock.py module found in the Journal"),
+ 'userdefined2args': _(
+ "runs code found in the tamyblock.py module found in the Journal"),
+ 'userdefined3args': _(
+ "runs code found in the tamyblock.py module found in the Journal"),
'video': _("Sugar Journal video object"),
'voltage': _("sensor voltage"),
- 'volume': _("microphone input volume"),
'vspace': _("jogs stack down"),
'wait': _("pauses program execution a specified number of seconds"),
'while': _("do-while-True operator that uses boolean operators from Numbers palette"),
@@ -953,17 +959,17 @@ HELP_STRINGS = {
#
DEAD_KEYS = ['grave', 'acute', 'circumflex', 'tilde', 'diaeresis', 'abovering']
-DEAD_DICTS = [{'A':192, 'E':200, 'I':204, 'O':210, 'U':217, 'a':224, 'e':232,
- 'i':236, 'o':242, 'u':249},
- {'A':193, 'E':201, 'I':205, 'O':211, 'U':218, 'a':225, 'e':233,
- 'i':237, 'o':243, 'u':250},
- {'A':194, 'E':202, 'I':206, 'O':212, 'U':219, 'a':226, 'e':234,
- 'i':238, 'o':244, 'u':251},
- {'A':195, 'O':211, 'N':209, 'U':360, 'a':227, 'o':245, 'n':241,
- 'u':361},
- {'A':196, 'E':203, 'I':207, 'O':211, 'U':218, 'a':228, 'e':235,
- 'i':239, 'o':245, 'u':252},
- {'A':197, 'a':229}]
+DEAD_DICTS = [{'A': 192, 'E': 200, 'I': 204, 'O': 210, 'U': 217, 'a': 224,
+ 'e': 232, 'i': 236, 'o': 242, 'u': 249},
+ {'A': 193, 'E': 201, 'I': 205, 'O': 211, 'U': 218, 'a': 225,
+ 'e': 233, 'i': 237, 'o': 243, 'u': 250},
+ {'A': 194, 'E': 202, 'I': 206, 'O': 212, 'U': 219, 'a': 226,
+ 'e': 234, 'i': 238, 'o': 244, 'u': 251},
+ {'A': 195, 'O': 211, 'N': 209, 'U': 360, 'a': 227, 'o': 245,
+ 'n': 241, 'u': 361},
+ {'A': 196, 'E': 203, 'I': 207, 'O': 211, 'U': 218, 'a': 228,
+ 'e': 235, 'i': 239, 'o': 245, 'u': 252},
+ {'A': 197, 'a': 229}]
NOISE_KEYS = ['Shift_L', 'Shift_R', 'Control_L', 'Caps_Lock', 'Pause',
'Alt_L', 'Alt_R', 'KP_Enter', 'ISO_Level3_Shift', 'KP_Divide',
'Escape', 'Return', 'KP_Page_Up', 'Up', 'Down', 'Menu',
diff --git a/TurtleArt/taexporthtml.py b/TurtleArt/taexporthtml.py
index 09042f8..2dac7e6 100644
--- a/TurtleArt/taexporthtml.py
+++ b/TurtleArt/taexporthtml.py
@@ -22,7 +22,7 @@ import pygtk
pygtk.require('2.0')
import gtk
import os.path
-from tautils import data_to_string, save_picture, image_to_base64
+from tautils import data_to_string, save_picture, image_to_base64, get_path
from gettext import gettext as _
from cgi import escape
@@ -90,7 +90,8 @@ def save_html(self, tw, embed_flag=True):
tmp = imgdata
else:
pixbuf = gtk.gdk.pixbuf_new_from_file(p)
- imgdata = image_to_base64(pixbuf, tw.activity)
+ imgdata = image_to_base64(pixbuf,
+ get_path(tw.activity, 'instance'))
tmp = self.html_glue['img2'][0]
tmp += imgdata
tmp += self.html_glue['img2'][1]
@@ -110,7 +111,7 @@ def save_html(self, tw, embed_flag=True):
else:
if self.embed_images == True:
imgdata = image_to_base64(save_picture(self.tw.canvas),
- tw.activity)
+ get_path(tw.activity, 'instance'))
else:
imgdata = os.path.join(self.tw.load_save_folder, 'image')
self.tw.save_as_image(imgdata)
diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py
index e3576fa..d3b214c 100644
--- a/TurtleArt/talogo.py
+++ b/TurtleArt/talogo.py
@@ -25,12 +25,8 @@ import gtk
from time import time
from math import sqrt
-from numpy import append
-from numpy.fft import rfft
from random import uniform
from operator import isNumberType
-from fcntl import ioctl
-
import os.path
from UserDict import UserDict
@@ -40,32 +36,26 @@ try:
except ImportError:
pass
-from taconstants import TAB_LAYER, BLACK, WHITE, \
- DEFAULT_SCALE, ICON_SIZE, BLOCK_NAMES, CONSTANTS, SENSOR_DC_NO_BIAS, \
- SENSOR_DC_BIAS, XO1, XO15
-from tagplay import play_audio_from_file, play_movie_from_file, stop_media, \
- media_playing
-from tacamera import Camera
-import v4l2
+from taconstants import TAB_LAYER, BLACK, WHITE, DEFAULT_SCALE, ICON_SIZE, \
+ BLOCK_NAMES, CONSTANTS, PREFIX_DICTIONARY
from tajail import myfunc, myfunc_import
from tautils import get_pixbuf_from_journal, convert, data_from_file, \
text_media_type, round_int, chr_to_ord, strtype, get_path
-from RtfParser import RtfTextOnly
-
-from ringbuffer import RingBuffer1d
+from util.RtfParser import RtfTextOnly
from gettext import gettext as _
VALUE_BLOCKS = ['box1', 'box2', 'color', 'shade', 'gray', 'scale', 'pensize',
- 'heading', 'xcor', 'ycor', 'pop', 'see', 'keyboard',
- 'sound', 'volume', 'pitch', 'resistance', 'voltage',
- 'luminance']
+ 'heading', 'xcor', 'ycor', 'pop', 'time', 'keyboard', 'see']
+MEDIA_BLOCKS_DICTIONARY = {} # new media blocks get added here
+PLUGIN_DICTIONARY = {} # new block primitives get added here
import logging
_logger = logging.getLogger('turtleart-activity')
+<<<<<<< HEAD
def find_device():
""" Search for RFID devices. Return a device instance or None. """
device = None
@@ -83,6 +73,8 @@ def find_device():
return device
+=======
+>>>>>>> 860754f7e871617df9d101a51dc64a69b742a0ba
class noKeyError(UserDict):
__missing__ = lambda x, y: 0
@@ -290,6 +282,7 @@ def _identity(x):
return(x)
+<<<<<<< HEAD
def _avg(array, abs_value=False):
""" Calc. the average value of an array """
if len(array) == 0:
@@ -319,6 +312,8 @@ def stop_logo(tw):
tw.active_turtle.show()
+=======
+>>>>>>> 860754f7e871617df9d101a51dc64a69b742a0ba
def _just_stop():
""" yield False to stop stack """
yield False
@@ -337,6 +332,7 @@ class LogoCode:
self.tw = tw
self.oblist = {}
+ # TODO: remove plugin blocks
DEFPRIM = {
'(': [1, lambda self, x: self._prim_opar(x)],
'and': [2, lambda self, x, y: _and(x, y)],
@@ -386,7 +382,6 @@ class LogoCode:
'leftx': [0, lambda self: CONSTANTS['leftx']],
'lpos': [0, lambda self: CONSTANTS['leftpos']],
'less?': [2, lambda self, x, y: _less(x, y)],
- 'luminance': [0, lambda self: self._read_camera(True)],
'mediawait': [0, self._media_wait, True],
'minus': [2, lambda self, x, y: _minus(x, y)],
'mod': [2, lambda self, x, y: _mod(x, y)],
@@ -404,7 +399,6 @@ class LogoCode:
'pendown': [0, lambda self: self.tw.canvas.setpen(True)],
'pensize': [0, lambda self: self.tw.canvas.pensize],
'penup': [0, lambda self: self.tw.canvas.setpen(False)],
- 'pitch': [0, lambda self: self._get_pitch()],
'plus': [2, lambda self, x, y: _plus(x, y)],
'polar': [0, lambda self: self.tw.set_polar(True)],
'pop': [0, lambda self: self._prim_pop()],
@@ -414,12 +408,9 @@ class LogoCode:
'purple': [0, lambda self: CONSTANTS['purple']],
'push': [1, lambda self, x: self._prim_push(x)],
'random': [2, lambda self, x, y: _random(x, y)],
- 'readcamera': [0, lambda self: self._read_camera()],
'readpixel': [0, lambda self: self._read_pixel()],
'red': [0, lambda self: CONSTANTS['red']],
'repeat': [2, self._prim_repeat, True],
- 'resistance': [0, lambda self: self._get_resistance()],
- 'rfid': [0, lambda self: self.tw.rfid_idn],
'right': [1, lambda self, x: self._prim_right(x)],
'rightx': [0, lambda self: CONSTANTS['rightx']],
'rpos': [0, lambda self: CONSTANTS['rightpos']],
@@ -450,7 +441,6 @@ class LogoCode:
'showaligned': [1, lambda self, x: self._show(x, False)],
'showblocks': [0, lambda self: self.tw.showblocks()],
'skin': [1, lambda self, x: self._reskin(x)],
- 'sound': [0, lambda self: self._get_sound()],
'sqrt': [1, lambda self, x: _sqrt(x)],
'stack1': [0, self._prim_stack1, True],
'stack': [1, self._prim_stack, True],
@@ -470,6 +460,7 @@ class LogoCode:
x, y, z, a, b)],
'textcolor': [0, lambda self: self.tw.canvas.textcolor],
'textsize': [0, lambda self: self.tw.textsize],
+ 'time': [0, lambda self: self._elapsed_time()],
'titlex': [0, lambda self: CONSTANTS['titlex']],
'titley': [0, lambda self: CONSTANTS['titley']],
'topy': [0, lambda self: CONSTANTS['topy']],
@@ -480,8 +471,6 @@ class LogoCode:
'userdefined3': [3, lambda self, x, y,
z: self._prim_myblock([x, y, z])],
'video': [1, lambda self, x: self._play_video(x)],
- 'voltage': [0, lambda self: self._get_voltage()],
- 'volume': [0, lambda self: self._get_volume()],
'vres': [0, lambda self: CONSTANTS['height']],
'wait': [1, self._prim_wait, True],
'white': [0, lambda self: WHITE],
@@ -520,9 +509,9 @@ class LogoCode:
self.trace = 0
self.update_values = False
self.gplay = None
- self.ag = None
self.filepath = None
self.dsobject = None
+ self._start_time = None
# Scale factors for depreciated portfolio blocks
self.title_height = int((self.tw.canvas.height / 20) * self.tw.scale)
@@ -531,6 +520,7 @@ class LogoCode:
self.scale = DEFAULT_SCALE
+<<<<<<< HEAD
self.max_samples = 1500
self.input_step = 1
@@ -550,6 +540,18 @@ class LogoCode:
else:
self.imagepath = '/tmp/turtlepic.png'
self.camera = Camera(self.imagepath)
+=======
+ def stop_logo(self):
+ """ Stop logo is called from the Stop button on the toolbar """
+ self.tw.step_time = 0
+ self.step = _just_stop()
+ for plugin in self.tw._plugins:
+ plugin.stop()
+ if self.tw.gst_available:
+ from tagplay import stop_media
+ stop_media(self)
+ self.tw.active_turtle.show()
+>>>>>>> 860754f7e871617df9d101a51dc64a69b742a0ba
def _def_prim(self, name, args, fcn, rprim=False):
""" Define the primitives associated with the blocks """
@@ -574,7 +576,6 @@ class LogoCode:
self.tw.saving_svg = False
self.find_value_blocks()
- self._update_audio_mode()
if self.trace > 0:
self.update_values = True
else:
@@ -601,6 +602,7 @@ class LogoCode:
code = self._blocks_to_code(blk)
if run_flag:
_logger.debug("running code: %s" % (code))
+ self._start_time = time()
self._setup_cmd(code)
if not self.tw.hide:
self.tw.display_coordinates()
@@ -625,35 +627,22 @@ class LogoCode:
code.append(float(blk.values[0]))
except ValueError:
code.append(float(ord(blk.values[0][0])))
- elif blk.name == 'string' or blk.name == 'title':
+ elif blk.name == 'string' or \
+ blk.name == 'title': # depreciated block
if type(blk.values[0]) == float or type(blk.values[0]) == int:
if int(blk.values[0]) == blk.values[0]:
blk.values[0] = int(blk.values[0])
code.append('#s' + str(blk.values[0]))
else:
code.append('#s' + blk.values[0])
- elif blk.name == 'journal':
- if blk.values[0] is not None:
- code.append('#smedia_' + str(blk.values[0]))
- else:
- code.append('#smedia_None')
- elif blk.name == 'description':
- if blk.values[0] is not None:
- code.append('#sdescr_' + str(blk.values[0]))
- else:
- code.append('#sdescr_None')
- elif blk.name == 'audio':
+ elif blk.name in PREFIX_DICTIONARY:
if blk.values[0] is not None:
- code.append('#saudio_' + str(blk.values[0]))
+ code.append(PREFIX_DICTIONARY[blk.name] + \
+ str(blk.values[0]))
else:
- code.append('#saudio_None')
- elif blk.name == 'video':
- if blk.values[0] is not None:
- code.append('#svideo_' + str(blk.values[0]))
- else:
- code.append('#svideo_None')
- elif blk.name == 'camera':
- code.append('#smedia_CAMERA')
+ code.append(PREFIX_DICTIONARY[blk.name] + 'None')
+ elif blk.name in MEDIA_BLOCKS_DICTIONARY:
+ code.append('#smedia_' + blk.name.upper())
else:
return ['%nothing%']
else:
@@ -900,12 +889,16 @@ class LogoCode:
def prim_clear(self):
""" Clear screen """
- stop_media(self)
+ if self.tw.gst_available:
+ from tagplay import stop_media
+ stop_media(self)
self.tw.canvas.clearscreen()
self.scale = DEFAULT_SCALE
- self.tw.set_polar(False)
- self.tw.set_cartesian(False)
+ # Note: users find this "feature" confusing
+ # self.tw.set_polar(False)
+ # self.tw.set_cartesian(False)
self.hidden_turtle = None
+ self._start_time = time()
for name in VALUE_BLOCKS:
self.update_label_value(name)
@@ -953,27 +946,27 @@ class LogoCode:
y = myfunc(f, x)
if str(y) == 'nan':
_logger.debug("python function returned nan")
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror("#notanumber")
else:
return y
except ZeroDivisionError:
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror("#zerodivide")
except ValueError, e:
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror('#' + str(e))
except SyntaxError, e:
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror('#' + str(e))
except NameError, e:
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror('#' + str(e))
except OverflowError:
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror("#overflowerror")
except TypeError:
- stop_logo(self.tw)
+ self.stop_logo()
raise logoerror("#notanumber")
def _prim_forever(self, blklist):
@@ -1106,7 +1099,8 @@ class LogoCode:
if flag and (self.tw.hide or self.tw.step_time == 0):
return
if type(n) == str or type(n) == unicode:
- if n[0:6] == 'media_' and n[6:] != 'CAMERA':
+ if n[0:6] == 'media_' and \
+ n[6:].lower not in MEDIA_BLOCKS_DICTIONARY:
try:
if self.tw.running_sugar:
try:
@@ -1134,9 +1128,8 @@ class LogoCode:
else:
try:
self.keyboard = {'Escape': 27, 'space': 32, ' ': 32,
- 'Return': 13, \
- 'KP_Up': 2, 'KP_Down': 4, 'KP_Left': 1, \
- 'KP_Right': 3}[self.tw.keypress]
+ 'Return': 13, 'KP_Up': 2, 'KP_Down': 4,
+ 'KP_Left': 1, 'KP_Right': 3}[self.tw.keypress]
except KeyError:
self.keyboard = 0
self.update_label_value('keyboard', self.keyboard)
@@ -1149,19 +1142,6 @@ class LogoCode:
self.value_blocks[name] = self.tw.block_list.get_similar_blocks(
'block', name)
- def _update_audio_mode(self):
- """ If there are sensor blocks, set the appropriate audio mode """
- for name in ['sound', 'volume', 'pitch']:
- if len(self.value_blocks[name]) > 0:
- self.tw.audiograb.set_sensor_type()
- return
- if len(self.value_blocks['resistance']) > 0:
- self.tw.audiograb.set_sensor_type(SENSOR_DC_BIAS)
- return
- elif len(self.value_blocks['voltage']) > 0:
- self.tw.audiograb.set_sensor_type(SENSOR_DC_NO_BIAS)
- return
-
def update_label_value(self, name, value=None):
""" Update the label of value blocks to reflect current value """
if self.tw.hide or not self.tw.interactive_mode or \
@@ -1188,10 +1168,12 @@ class LogoCode:
self.update_label_value(name, value)
def _prim_right(self, value):
+ """ Turtle rotates clockwise """
self.tw.canvas.right(float(value))
self.update_label_value('heading', self.tw.canvas.heading)
def _prim_move(self, cmd, value1, value2=None, pendown=True):
+ """ Turtle moves by method specified in value1 """
if value2 is None:
cmd(value1)
else:
@@ -1204,6 +1186,7 @@ class LogoCode:
self.see()
def _prim_arc(self, cmd, value1, value2):
+ """ Turtle draws an arc of degree, radius """
cmd(float(value1), float(value2))
self.update_label_value('xcor',
self.tw.canvas.xcor / self.tw.coord_scale)
@@ -1336,11 +1319,9 @@ class LogoCode:
elif string[0:6] in ['media_', 'descr_', 'audio_', 'video_']:
self.filepath = None
self.dsobject = None
- if string[6:] == 'CAMERA':
- if self.tw.camera_available:
- self.camera.save_camera_input_to_file()
- self.camera.stop_camera_input()
- self.filepath = self.imagepath
+ print string[6:], MEDIA_BLOCKS_DICTIONARY
+ if string[6:].lower() in MEDIA_BLOCKS_DICTIONARY:
+ MEDIA_BLOCKS_DICTIONARY[string[6:].lower()]()
elif os.path.exists(string[6:]): # is it a path?
self.filepath = string[6:]
elif self.tw.running_sugar: # is it a datastore object?
@@ -1377,8 +1358,7 @@ class LogoCode:
if self.dsobject is not None:
self.dsobject.destroy()
else: # assume it is text to display
- x = self._x()
- y = self._y()
+ x, y = self._x(), self._y()
if center:
y -= self.tw.canvas.textsize
self.tw.canvas.draw_text(string, x, y,
@@ -1387,8 +1367,7 @@ class LogoCode:
self.tw.canvas.width - x)
elif type(string) == float or type(string) == int:
string = round_int(string)
- x = self._x()
- y = self._y()
+ x, y = self._x(), self._y()
if center:
y -= self.tw.canvas.textsize
self.tw.canvas.draw_text(string, x, y,
@@ -1401,23 +1380,23 @@ class LogoCode:
if filepath is not None:
self.filepath = filepath
pixbuf = None
- w = self._w()
- h = self._h()
+ w, h = self._w(), self._h()
if w < 1 or h < 1:
return
- if self.filepath is not None and self.filepath != '':
+ if self.dsobject is not None:
+ try:
+ pixbuf = get_pixbuf_from_journal(self.dsobject, w, h)
+ except:
+ _logger.debug("Couldn't open dsobject %s" % (self.dsobject))
+ if pixbuf is None and \
+ self.filepath is not None and \
+ self.filepath != '':
try:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self.filepath,
w, h)
except:
self.tw.showlabel('nojournal', self.filepath)
_logger.debug("Couldn't open filepath %s" % (self.filepath))
- elif self.dsobject is not None:
- try:
- pixbuf = get_pixbuf_from_journal(self.dsobject, w, h)
- except:
- self.tw.showlabel('nojournal', self.dsobject)
- _logger.debug("Couldn't open dsobject %s" % (self.dsobject))
if pixbuf is not None:
if center:
self.tw.canvas.draw_pixbuf(pixbuf, 0, 0,
@@ -1448,8 +1427,12 @@ class LogoCode:
f.close()
except IOError:
self.tw.showlabel('nojournal', self.filepath)
+<<<<<<< HEAD
_logger.debug("Couldn't open filepath %s" % \
(self.filepath))
+=======
+ _logger.debug("Couldn't open %s" % (self.filepath))
+>>>>>>> 860754f7e871617df9d101a51dc64a69b742a0ba
else:
if description is not None:
text = str(description)
@@ -1461,23 +1444,35 @@ class LogoCode:
def _media_wait(self):
""" Wait for media to stop playing """
- while(media_playing(self)):
- yield True
+ if self.tw.gst_available:
+ from tagplay import media_playing
+ while(media_playing(self)):
+ yield True
self._ireturn()
yield True
def _play_sound(self):
""" Sound file from Journal """
- play_audio_from_file(self, self.filepath)
+ if self.tw.gst_available:
+ from tagplay import play_audio_from_file
+ play_audio_from_file(self, self.filepath)
def _play_video(self):
""" Movie file from Journal """
- w = self._w()
- h = self._h()
+ w, h = self._w(), self._h()
if w < 1 or h < 1:
return
- play_movie_from_file(self, self.filepath, self._x(), self._y(),
- self._w(), self._h())
+ if self.tw.gst_available:
+ from tagplay import play_movie_from_file
+ play_movie_from_file(self, self.filepath, self._x(), self._y(),
+ w, h)
+
+ def _elapsed_time(self):
+ """ Number of seconds since program execution has started or
+ clean (prim_clear) block encountered """
+ elapsed_time = int(time() - self._start_time)
+ self.update_label_value('time', elapsed_time)
+ return elapsed_time
def see(self):
""" Read r, g, b from the canvas and return a corresponding palette
@@ -1493,6 +1488,7 @@ class LogoCode:
self.heap.append(b)
self.heap.append(g)
self.heap.append(r)
+<<<<<<< HEAD
def _read_camera(self, luminance_only=False):
""" Read average pixel from camera and push b, g, r to the stack """
@@ -1635,6 +1631,8 @@ class LogoCode:
else:
return 0
+=======
+>>>>>>> 860754f7e871617df9d101a51dc64a69b742a0ba
# Depreciated block methods
def _show_template1x1(self, title, media):
diff --git a/TurtleArt/taturtle.py b/TurtleArt/taturtle.py
index a7a3205..f309eef 100644
--- a/TurtleArt/taturtle.py
+++ b/TurtleArt/taturtle.py
@@ -19,7 +19,7 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
-from taconstants import TURTLE_LAYER
+from taconstants import TURTLE_LAYER, DEFAULT_TURTLE_COLORS
from tasprite_factory import SVG, svg_str_to_pixbuf
from tacanvas import wrap100, color_table
from sprites import Sprite
@@ -149,7 +149,7 @@ class Turtle:
self.shapes = generate_turtle_pixbufs(self.colors)
else:
if turtles is not None:
- self.colors = ['#008000', '#00A000']
+ self.colors = DEFAULT_TURTLE_COLORS
self.shapes = turtles.get_pixbufs()
def set_shapes(self, shapes):
diff --git a/TurtleArt/tautils.py b/TurtleArt/tautils.py
index c66b322..521637e 100644
--- a/TurtleArt/tautils.py
+++ b/TurtleArt/tautils.py
@@ -285,18 +285,30 @@ def get_path(activity, subpath):
"org.laptop.TurtleArtActivity", subpath))
-def image_to_base64(pixbuf, activity):
- """ Convert an image to base64 """
- _file_name = os.path.join(get_path(activity, 'instance'), 'imagetmp.png')
+def image_to_base64(pixbuf, path_name):
+ """ Convert an image to base64-encoded data """
+ file_name = os.path.join(path_name, 'imagetmp.png')
if pixbuf != None:
- pixbuf.save(_file_name, "png")
- _base64 = os.path.join(get_path(activity, 'instance'), 'base64tmp')
- _cmd = "base64 <" + _file_name + " >" + _base64
- subprocess.check_call(_cmd, shell=True)
- _file_handle = open(_base64, 'r')
- _data = _file_handle.read()
- _file_handle.close()
- return _data
+ pixbuf.save(file_name, "png")
+ base64 = os.path.join(path_name, 'base64tmp')
+ cmd = "base64 <" + file_name + " >" + base64
+ subprocess.check_call(cmd, shell=True)
+ file_handle = open(base64, 'r')
+ data = file_handle.read()
+ file_handle.close()
+ return data
+
+
+def base64_to_image(data, path_name):
+ """ Convert base64-encoded data to an image """
+ base64 = os.path.join(path_name, 'base64tmp')
+ file_handle = open(base64, 'w')
+ file_handle.write(data)
+ file_handle.close()
+ file_name = os.path.join(path_name, 'imagetmp.png')
+ cmd = "base64 -d <" + base64 + ">" + file_name
+ subprocess.check_call(cmd, shell=True)
+ return file_name
def movie_media_type(name):
@@ -318,7 +330,7 @@ def image_media_type(name):
def text_media_type(name):
""" Is it text media? """
- return name.lower().endswith(('.txt', '.py', '.lg', '.rtf', '.ta'))
+ return name.lower().endswith(('.txt', '.py', '.lg', '.rtf'))
def round_int(num):
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index ad830d9..f12a753 100644
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
#Copyright (c) 2007, Playful Invention Company
-#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) 2008-11, Walter Bender
+#Copyright (c) 2009-11 Raúl Gutiérrez Segalés
#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
#Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,7 +26,14 @@ import pygtk
pygtk.require('2.0')
import gtk
import gobject
-import gst
+
+try:
+ import gst
+ GST_AVAILABLE = True
+except ImportError:
+ # Turtle Art should not fail if gst is not available
+ GST_AVAILABLE = False
+
import os
import os.path
import dbus
@@ -60,7 +66,7 @@ from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \
NUMBER_STYLE_PORCH, NUMBER_STYLE_BLOCK, \
NUMBER_STYLE_VAR_ARG, CONSTANTS, XO1, XO15, UNKNOWN, \
BASIC_STYLE_VAR_ARG
-from talogo import LogoCode, stop_logo
+from talogo import LogoCode
from tacanvas import TurtleGraphics
from tablock import Blocks, Block
from taturtle import Turtles, Turtle
@@ -76,18 +82,11 @@ from tautils import magnitude, get_load_name, get_save_name, data_from_file, \
dock_dx_dy, data_to_string, journal_check, chooser, \
get_hardware
from tasprite_factory import SVG, svg_str_to_pixbuf, svg_from_file
-from tagplay import stop_media
from sprites import Sprites, Sprite
-from audiograb import AudioGrab_Unknown, AudioGrab_XO1, AudioGrab_XO15
-from rfidutils import strhex2bin, strbin2dec, find_device
from dbus.mainloop.glib import DBusGMainLoop
-HAL_SERVICE = 'org.freedesktop.Hal'
-HAL_MGR_PATH = '/org/freedesktop/Hal/Manager'
-HAL_MGR_IFACE = 'org.freedesktop.Hal.Manager'
-HAL_DEV_IFACE = 'org.freedesktop.Hal.Device'
-REGEXP_SERUSB = '\/org\/freedesktop\/Hal\/devices\/usb_device['\
- 'a-z,A-Z,0-9,_]*serial_usb_[0-9]'
+if GST_AVAILABLE:
+ from tagplay import stop_media
import logging
_logger = logging.getLogger('turtleart-activity')
@@ -96,13 +95,17 @@ _logger = logging.getLogger('turtleart-activity')
class TurtleArtWindow():
""" TurtleArt Window class abstraction """
timeout_tag = [0]
+ _INSTALL_PATH = '/usr/share/turtleart'
+ _ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart'
+ _PLUGIN_SUBPATH = 'plugins'
def __init__(self, win, path, parent=None, mycolors=None, mynick=None):
self._loaded_project = ''
self.win = None
self._sharing = False
self.parent = parent
- self.send_event = None # method to send events over the network
+ self.send_event = None # method to send events over the network
+ self.gst_available = GST_AVAILABLE
if type(win) == gtk.DrawingArea:
self.interactive_mode = True
self.window = win
@@ -147,7 +150,11 @@ class TurtleArtWindow():
self.mouse_x = 0
self.mouse_y = 0
- locale.setlocale(locale.LC_NUMERIC, '')
+ # if self.running_sugar:
+ try:
+ locale.setlocale(locale.LC_NUMERIC, '')
+ except locale.Error:
+ _logger.debug('unsupported locale')
self.decimal_point = locale.localeconv()['decimal_point']
if self.decimal_point == '' or self.decimal_point is None:
self.decimal_point = '.'
@@ -256,95 +263,91 @@ class TurtleArtWindow():
self._setup_misc()
self._show_toolbar_palette(0, False)
- # setup sound/sensor grab
- if self.hw in [XO1, XO15]:
- PALETTES[PALETTE_NAMES.index('sensor')].append('resistance')
- PALETTES[PALETTE_NAMES.index('sensor')].append('voltage')
- self.audio_started = False
-
- self.camera_available = False
- v4l2src = gst.element_factory_make('v4l2src')
- if v4l2src.props.device_name is not None:
- PALETTES[PALETTE_NAMES.index('sensor')].append('readcamera')
- PALETTES[PALETTE_NAMES.index('sensor')].append('luminance')
- PALETTES[PALETTE_NAMES.index('sensor')].append('camera')
- self.camera_available = True
+ self._plugins = []
+ self._init_plugins()
self.lc = LogoCode(self)
- self.saved_pictures = []
+ self._setup_plugins()
+ self.saved_pictures = []
self.block_operation = ''
- """
- The following code will initialize a USB RFID reader. Please note that
- in order to make this initialization function work, it is necessary to
- set the permission for the ttyUSB device to 0666. You can do this by
- adding a rule to /etc/udev/rules.d
-
- As root (using sudo or su), copy the following text into a new file in
- /etc/udev/rules.d/94-ttyUSB-rules
-
- KERNEL=="ttyUSB[0-9]",MODE="0666"
-
- You only have to do this once.
- """
-
- self.rfid_connected = False
- self.rfid_device = find_device()
- self.rfid_idn = ''
-
- if self.rfid_device is not None:
- _logger.info("RFID device found")
- self.rfid_connected = self.rfid_device.do_connect()
- if self.rfid_connected:
- self.rfid_device.connect("tag-read", self._tag_read_cb)
- self.rfid_device.connect("disconnected", self._disconnected_cb)
-
- loop = DBusGMainLoop()
- bus = dbus.SystemBus(mainloop=loop)
- hmgr_iface = dbus.Interface(bus.get_object(HAL_SERVICE,
- HAL_MGR_PATH), HAL_MGR_IFACE)
-
- hmgr_iface.connect_to_signal('DeviceAdded', self._device_added_cb)
-
- PALETTES[PALETTE_NAMES.index('sensor')].append('rfid')
-
- def _device_added_cb(self, path):
- """
- Called from hal connection when a new device is plugged.
- """
- if not self.rfid_connected:
- self.rfid_device = find_device()
- _logger.debug("DEVICE_ADDED: %s"%self.rfid_device)
- if self.rfid_device is not None:
- _logger.debug("DEVICE_ADDED: RFID device is not None!")
- self.rfid_connected = self._device.do_connect()
- if self.rfid_connected:
- _logger.debug("DEVICE_ADDED: Connected!")
- self.rfid_device.connect("tag-read", self._tag_read_cb)
- self.rfid_device.connect("disconnected", self._disconnected_cb)
-
- def _disconnected_cb(self, device, text):
- """
- Called when the device is disconnected.
- """
- self.rfid_connected = False
- self.rfid_device = None
-
- def _tag_read_cb(self, device, tagid):
- """
- Callback for "tag-read" signal. Receives the read tag id.
- """
- idbin = strhex2bin(tagid)
- self.rfid_idn = strbin2dec(idbin[26:64])
- while self.rfid_idn.__len__() < 9:
- self.rfid_idn = '0' + self.rfid_idn
- print tagid, idbin, self.rfid_idn
-
- def new_buffer(self, buf):
- """ Append a new buffer to the ringbuffer """
- self.lc.ringbuffer.append(buf)
- return True
+ def _get_plugin_home(self):
+ """ Look in current directory first, then usual places """
+ path = os.path.join(os.getcwd(), self._PLUGIN_SUBPATH)
+ if os.path.exists(path):
+ return path
+ path = os.path.expanduser(os.path.join('~', 'Activities',
+ 'TurtleBlocks.activity',
+ self._PLUGIN_SUBPATH))
+ if os.path.exists(path):
+ return path
+ path = os.path.expanduser(os.path.join('~', 'Activities',
+ 'TurtleArt.activity',
+ self._PLUGIN_SUBPATH))
+ if os.path.exists(path):
+ return path
+ path = os.path.join(self._INSTALL_PATH, self._PLUGIN_SUBPATH)
+ if os.path.exists(path):
+ return path
+ path = os.path.join(self._ALTERNATE_INSTALL_PATH,
+ self._PLUGIN_SUBPATH)
+ if os.path.exists(path):
+ return path
+ return None
+
+ def _get_plugin_candidates(self, path):
+ """ Look for plugin files in plugin directory. """
+ plugin_files = []
+ if path is not None:
+ candidates = os.listdir(path)
+ for c in candidates:
+ if c[-10:] == '_plugin.py' and c[0] != '#' and c[0] != '.':
+ plugin_files.append(c.split('.')[0])
+ return plugin_files
+
+ def _init_plugins(self):
+ """ Try importing plugin files from the plugin directory. """
+ for pluginfile in self._get_plugin_candidates(self._get_plugin_home()):
+ pluginclass = pluginfile.capitalize()
+ f = "def f(self): from plugins.%s import %s; return %s(self)" \
+ % (pluginfile, pluginclass, pluginclass)
+ plugins = {}
+ try:
+ exec f in globals(), plugins
+ self._plugins.append(plugins.values()[0](self))
+ except ImportError:
+ print 'failed to import %s' % (pluginclass)
+
+ def _setup_plugins(self):
+ """ Initial setup -- call just once. """
+ for plugin in self._plugins:
+ plugin.setup()
+
+ def _start_plugins(self):
+ """ Start is called everytime we execute blocks. """
+ for plugin in self._plugins:
+ plugin.start()
+
+ def _stop_plugins(self):
+ """ Stop is called whenever we stop execution. """
+ for plugin in self._plugins:
+ plugin.stop()
+
+ def background_plugins(self):
+ """ Background is called when we are pushed to the background. """
+ for plugin in self._plugins:
+ plugin.goto_background()
+
+ def foreground_plugins(self):
+ """ Foreground is called when we are return from the background. """
+ for plugin in self._plugins:
+ plugin.return_to_foreground()
+
+ def _quit_plugins(self):
+ """ Quit is called upon program exit. """
+ for plugin in self._plugins:
+ plugin.quit()
def _setup_events(self):
""" Register the events we listen to. """
@@ -420,29 +423,13 @@ class TurtleArtWindow():
self.lc.prim_clear()
self.display_coordinates()
- def _start_audiograb(self):
- """ Start grabbing audio if there is an audio block in use """
- if len(self.block_list.get_similar_blocks('block',
- ['volume', 'sound', 'pitch', 'resistance', 'voltage'])) > 0:
- if self.audio_started:
- self.audiograb.resume_grabbing()
- else:
- if self.hw == XO15:
- self.audiograb = AudioGrab_XO15(self.new_buffer, self)
- elif self.hw == XO1:
- self.audiograb = AudioGrab_XO1(self.new_buffer, self)
- else:
- self.audiograb = AudioGrab_Unknown(self.new_buffer, self)
- self.audiograb.start_grabbing()
- self.audio_started = True
-
def run_button(self, time):
""" Run turtle! """
if self.running_sugar:
self.activity.recenter()
if self.interactive_mode:
- self._start_audiograb()
+ self._start_plugins()
# Look for a 'start' block
for blk in self.just_blocks():
@@ -462,9 +449,8 @@ class TurtleArtWindow():
def stop_button(self):
""" Stop button """
- stop_logo(self)
- if self.audio_started:
- self.audiograb.pause_grabbing()
+ self.lc.stop_logo()
+ self._stop_plugins()
def set_userdefined(self, blk=None):
""" Change icon for user-defined blocks after loading Python code. """
@@ -1585,6 +1571,7 @@ class TurtleArtWindow():
blk.spr.labels[0] += CURSOR
elif blk.name in BOX_STYLE_MEDIA and blk.name != 'camera':
+ # TODO: isolate reference to camera
self._import_from_journal(self.selected_blk)
if blk.name == 'journal' and self.running_sugar:
self._load_description_block(blk)
@@ -1628,7 +1615,7 @@ class TurtleArtWindow():
dy = 20
blk.expand_in_y(dy)
else:
- self._start_audiograb()
+ self._start_plugins()
self._run_stack(blk)
return
@@ -1691,10 +1678,10 @@ class TurtleArtWindow():
elif blk.name in PYTHON_SKIN:
self._import_py()
else:
- self._start_audiograb()
+ self._start_plugins()
self._run_stack(blk)
- elif blk.name in ['sandwichtop_no_arm_no_label',
+ elif blk.name in ['sandwichtop_no_arm_no_label',
'sandwichtop_no_arm']:
restore_stack(blk)
@@ -1706,7 +1693,7 @@ class TurtleArtWindow():
collapse_stack(top)
else:
- self._start_audiograb()
+ self._start_plugins()
self._run_stack(blk)
def _expand_boolean(self, blk, blk2, dy):
@@ -1790,7 +1777,6 @@ class TurtleArtWindow():
""" Run a stack of blocks. """
if blk is None:
return
- self.lc.ag = None
top = find_top_block(blk)
self.lc.run_blocks(top, self.just_blocks(), True)
if self.interactive_mode:
@@ -1996,9 +1982,9 @@ class TurtleArtWindow():
if keyname == "p":
self.hideshow_button()
elif keyname == 'q':
- if self.audio_started:
- self.audiograb.stop_grabbing()
- stop_media(self.lc)
+ self._plugins_quit()
+ if self.gst_available:
+ stop_media(self.lc)
exit()
elif keyname == 'g':
self._align_to_grid()
@@ -2356,7 +2342,7 @@ class TurtleArtWindow():
def new_project(self):
""" Start a new project """
- stop_logo(self)
+ self.lc.stop_logo()
self._loaded_project = ""
# Put current project in the trash.
while len(self.just_blocks()) > 0:
@@ -2474,7 +2460,7 @@ class TurtleArtWindow():
if self.running_sugar:
try:
dsobject = datastore.get(value)
- except: # Should be IOError, but dbus error is raised
+ except: # Should be IOError, but dbus error is raised
dsobject = None
_logger.debug("couldn't get dsobject %s" % value)
if dsobject is not None:
@@ -2515,6 +2501,7 @@ class TurtleArtWindow():
else:
self._block_skin('pythonoff', blk)
elif btype in BOX_STYLE_MEDIA and blk.spr is not None:
+ # TODO: isolate reference to camera
if len(blk.values) == 0 or blk.values[0] == 'None' or \
blk.values[0] is None or btype == 'camera':
self._block_skin(btype + 'off', blk)
diff --git a/TurtleArtActivity.py b/TurtleArtActivity.py
index 98968a8..aa8d5bd 100644
--- a/TurtleArtActivity.py
+++ b/TurtleArtActivity.py
@@ -43,7 +43,8 @@ from gettext import gettext as _
import os.path
import tarfile
-from TurtleArt.taconstants import PALETTE_NAMES, OVERLAY_LAYER, HELP_STRINGS
+from TurtleArt.taconstants import PALETTE_NAMES, OVERLAY_LAYER, HELP_STRINGS, \
+ ICON_SIZE
from TurtleArt.taexporthtml import save_html
from TurtleArt.taexportlogo import save_logo
from TurtleArt.tautils import data_to_file, data_to_string, data_from_string, \
@@ -164,7 +165,7 @@ class TurtleArtActivity(activity.Activity):
def do_load_ta_project_cb(self, button):
""" Load a project from the Journal """
- chooser(self, SERVICE, self._load_ta_project)
+ chooser(self, 'org.laptop.TurtleArtActivity', self._load_ta_project)
def _load_ta_project(self, dsobject):
""" Load a ta project from the datastore """
@@ -258,7 +259,7 @@ class TurtleArtActivity(activity.Activity):
def do_hideshow_cb(self, button):
""" Toggle visibility. """
self.tw.hideshow_button()
- if self.tw.hide == True: # we just hid the blocks
+ if self.tw.hide == True: # we just hid the blocks
self.blocks_button.set_icon("hideshowon")
self.blocks_button.set_tooltip(_('Show blocks'))
else:
@@ -428,10 +429,10 @@ class TurtleArtActivity(activity.Activity):
def __visibility_notify_cb(self, window, event):
""" Callback method for when the activity's visibility changes. """
if event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED:
- self.tw.lc.ag = None
+ self.tw.background_plugins()
elif event.state in \
[gtk.gdk.VISIBILITY_UNOBSCURED, gtk.gdk.VISIBILITY_PARTIAL]:
- pass
+ self.tw.foreground_plugins()
def update_title_cb(self, widget, event, toolbox):
""" Update the title. """
@@ -531,14 +532,16 @@ class TurtleArtActivity(activity.Activity):
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 = self._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 = self._add_button('load-from-journal',
_("Import project from the Journal"), self.do_load_ta_project_cb,
journal_toolbar_button)
self._add_separator(journal_toolbar)
- self.load_python = self._add_button('pippy-openoff', _("Load Python block"),
+ self.load_python = self._add_button('pippy-openoff', _(
+ "Load Python block"),
self.do_load_python_cb,
journal_toolbar_button)
self.samples_button = self._add_button("ta-open", _('Load example'),
@@ -547,8 +550,8 @@ class TurtleArtActivity(activity.Activity):
edit_toolbar_button, '<Ctrl>c')
paste = self._add_button('edit-paste', _('Paste'), self._paste_cb,
edit_toolbar_button, '<Ctrl>v')
- fullscreen_button = self._add_button('view-fullscreen', _("Fullscreen"),
- self.do_fullscreen_cb,
+ fullscreen_button = self._add_button('view-fullscreen',
+ _("Fullscreen"), self.do_fullscreen_cb,
view_toolbar_button, '<Alt>Return')
cartesian_button = self._add_button('view-Cartesian',
_("Cartesian coordinates"),
@@ -566,11 +569,11 @@ class TurtleArtActivity(activity.Activity):
view_toolbar_button)
self.resize_up_button = self._add_button('resize+', _("Grow blocks"),
self.do_grow_blocks_cb, view_toolbar_button)
- self.resize_down_button = self._add_button('resize-', _("Shrink blocks"),
- self.do_shrink_blocks_cb, view_toolbar_button)
+ self.resize_down_button = self._add_button('resize-',
+ _("Shrink blocks"), self.do_shrink_blocks_cb, view_toolbar_button)
self.hover_help_label = self._add_label(
_("Move the cursor over the orange palette for help."),
- help_toolbar)
+ help_toolbar, gtk.gdk.screen_width() - 2 * ICON_SIZE)
# The palette toolbar is only used with 0.86+
if self.new_sugar_system:
@@ -607,7 +610,8 @@ 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 = self._add_button("paletteoff", _('Hide palette'),
+ self.palette_button = self._add_button("paletteoff", _(
+ 'Hide palette'),
self.do_palette_cb, toolbar, _('<Ctrl>p'))
self.blocks_button = self._add_button("hideshowoff", _('Hide blocks'),
self.do_hideshow_cb, toolbar, _('<Ctrl>b'))
@@ -616,14 +620,14 @@ class TurtleArtActivity(activity.Activity):
""" Creates the turtle buttons for both toolbar types"""
self.eraser_button = self._add_button("eraseron", _('Clean'),
self.do_eraser_cb, toolbar, _('<Ctrl>e'))
- self.run_button = self._add_button("run-fastoff", _('Run'), self.do_run_cb,
- toolbar, _('<Ctrl>r'))
+ self.run_button = self._add_button("run-fastoff", _('Run'),
+ self.do_run_cb, toolbar, _('<Ctrl>r'))
self.step_button = self._add_button("run-slowoff", _('Step'),
self.do_step_cb, toolbar, _('<Ctrl>w'))
self.debug_button = self._add_button("debugoff", _('Debug'),
self.do_debug_cb, toolbar, _('<Ctrl>d'))
- self.stop_turtle_button = self._add_button("stopitoff", _('Stop turtle'),
- self.do_stop_cb, toolbar, _('<Ctrl>s'))
+ self.stop_turtle_button = self._add_button("stopitoff",
+ _('Stop turtle'), self.do_stop_cb, toolbar, _('<Ctrl>s'))
def _setup_scrolled_window(self):
""" Create a scrolled window to contain the turtle canvas. """
@@ -681,7 +685,7 @@ class TurtleArtActivity(activity.Activity):
if self._jobject and self._jobject.file_path:
self.read_file(self._jobject.file_path)
- else: # if new, load a start brick onto the canvas
+ else: # if new, load a start brick onto the canvas
self.tw.load_start()
def _setup_sharing(self):
@@ -717,7 +721,7 @@ class TurtleArtActivity(activity.Activity):
# If run_it is True, we want to create a new project
tar_fd.extractall(tmpdir)
self.tw.load_files(os.path.join(tmpdir, 'ta_code.ta'), \
- run_it) # create a new project flag
+ run_it) # create a new project flag
finally:
shutil.rmtree(tmpdir)
tar_fd.close()
@@ -777,10 +781,12 @@ class TurtleArtActivity(activity.Activity):
self.tw.paste_offset)
self.tw.paste_offset += 20
- def _add_label(self, string, toolbar):
+ def _add_label(self, string, toolbar, width=None):
""" add a label to a toolbar """
label = gtk.Label(string)
label.set_line_wrap(True)
+ if width is not None:
+ label.set_size_request(width, -1)
label.show()
toolitem = gtk.ToolItem()
toolitem.add(label)
@@ -796,7 +802,8 @@ class TurtleArtActivity(activity.Activity):
toolbar.insert(separator, -1)
separator.show()
- def _add_button(self, name, tooltip, callback, toolbar, accelerator=None, arg=None):
+ def _add_button(self, name, tooltip, callback, toolbar, accelerator=None,
+ arg=None):
""" add a button to a toolbar """
button = ToolButton(name)
button.set_tooltip(tooltip)
@@ -810,7 +817,7 @@ class TurtleArtActivity(activity.Activity):
except AttributeError:
pass
button.show()
- if hasattr(toolbar, 'insert'): # the main toolbar
+ if hasattr(toolbar, 'insert'): # the main toolbar
toolbar.insert(button, -1)
else: # or a secondary toolbar
toolbar.props.page.insert(button, -1)
diff --git a/devices/__init__.py b/audio/__init__.py
index e69de29..e69de29 100644
--- a/devices/__init__.py
+++ b/audio/__init__.py
diff --git a/TurtleArt/audiograb.py b/audio/audiograb.py
index 3ecdc11..1240be7 100644
--- a/TurtleArt/audiograb.py
+++ b/audio/audiograb.py
@@ -21,6 +21,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+SENSOR_AC_NO_BIAS = 'external'
+SENSOR_AC_BIAS = 'sound'
+SENSOR_DC_NO_BIAS = 'voltage'
+SENSOR_DC_BIAS = 'resistance'
+
import pygst
import gst
import gst.interfaces
@@ -49,8 +54,7 @@ _logger = logging.getLogger('TurtleArt')
_logger.setLevel(logging.DEBUG)
logging.basicConfig()
-from taconstants import SENSOR_AC_NO_BIAS, SENSOR_AC_BIAS, SENSOR_DC_NO_BIAS, \
- SENSOR_DC_BIAS, XO1
+from TurtleArt.taconstants import XO1
class AudioGrab:
diff --git a/TurtleArt/ringbuffer.py b/audio/ringbuffer.py
index 2afb5c9..2afb5c9 100644
--- a/TurtleArt/ringbuffer.py
+++ b/audio/ringbuffer.py
diff --git a/extra/__init__.py b/camera/__init__.py
index e69de29..e69de29 100644
--- a/extra/__init__.py
+++ b/camera/__init__.py
diff --git a/TurtleArt/tacamera.py b/camera/tacamera.py
index 2177288..2177288 100644
--- a/TurtleArt/tacamera.py
+++ b/camera/tacamera.py
diff --git a/TurtleArt/v4l2.py b/camera/v4l2.py
index 9c052fd..9c052fd 100644
--- a/TurtleArt/v4l2.py
+++ b/camera/v4l2.py
diff --git a/collaboration/neighborhood.py b/collaboration/neighborhood.py
index b587bc8..192b66f 100755
--- a/collaboration/neighborhood.py
+++ b/collaboration/neighborhood.py
@@ -1,8 +1,21 @@
#!/usr/bin/python
+# Copyright (C) 2007, Red Hat, Inc.
+# Copyright (C) 2010-11 Collabora Ltd. <http://www.collabora.co.uk/>
#
-# neighborhood.py
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
from functools import partial
from hashlib import sha1
diff --git a/collaboration/telepathyclient.py b/collaboration/telepathyclient.py
index 5491530..f3e8578 100644
--- a/collaboration/telepathyclient.py
+++ b/collaboration/telepathyclient.py
@@ -18,11 +18,15 @@ import logging
import dbus
from dbus import PROPERTIES_IFACE
-from telepathy.interfaces import CLIENT, \
- CLIENT_APPROVER, \
- CLIENT_HANDLER, \
- CLIENT_INTERFACE_REQUESTS
-from telepathy.server import DBusProperties
+try:
+ from telepathy.interfaces import CLIENT, \
+ CLIENT_APPROVER, \
+ CLIENT_HANDLER, \
+ CLIENT_INTERFACE_REQUESTS
+ from telepathy.server import DBusProperties
+ TELEPATHY_AVAILABLE = True
+except ImportError:
+ TELEPATHY_AVAILABLE = False
import dispatch
@@ -34,7 +38,10 @@ _instance = None
class TelepathyClient(dbus.service.Object, DBusProperties):
+
def __init__(self):
+ if not TELEPATHY_AVAILABLE:
+ return None
self._interfaces = set([CLIENT, CLIENT_HANDLER,
CLIENT_INTERFACE_REQUESTS, PROPERTIES_IFACE,
CLIENT_APPROVER])
diff --git a/extra/plugin.py b/extra/plugin.py
deleted file mode 100644
index fe95f95..0000000
--- a/extra/plugin.py
+++ /dev/null
@@ -1,10 +0,0 @@
-
-import gobject
-
-class Plugin(gobject.GObject):
- def __init__(self):
- gobject.GObject.__init__(self)
-
- def get_menu(self):
- raise RuntimeError("You need to define get_menu for your plugin.")
-
diff --git a/devices/__init__.py b/gnome_plugins/__init__.py
index e69de29..e69de29 100644
--- a/devices/__init__.py
+++ b/gnome_plugins/__init__.py
diff --git a/extra/collaborationplugin.py b/gnome_plugins/collaboration_plugin.py
index 56fcae5..54abdc8 100644
--- a/extra/collaborationplugin.py
+++ b/gnome_plugins/collaboration_plugin.py
@@ -1,3 +1,24 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 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
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
import sys
sys.path.append("..")
@@ -6,23 +27,29 @@ import dbus
from gettext import gettext as _
import gobject
import gtk
+
from plugin import Plugin
+
from util.menubuilder import MenuBuilder
from util.configfile import ConfigFile
from util.configwizard import ConfigWizard
+
import telepathy
from collaboration.neighborhood import get_neighborhood
from collaboration.connectionmanager import get_connection_manager
from collaboration.activity import Activity
from collaboration import telepathyclient
from collaboration.tubeconn import TubeConnection
-import traceback
+
from TurtleArt.tacollaboration import Collaboration
+import traceback
+
CONNECTION_INTERFACE_ACTIVITY_PROPERTIES = \
'org.laptop.Telepathy.ActivityProperties'
-class CollaborationPlugin(Plugin):
+
+class Collaboration_plugin(Plugin):
__gsignals__ = {
'joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
@@ -31,56 +58,75 @@ class CollaborationPlugin(Plugin):
()),
}
- # Using activity here is kinda misleading cause the
- # obj we are receiving is not a Sugar activity.
- def __init__(self, activity, config_file_path):
+ def __init__(self, parent):
Plugin.__init__(self)
- self._activity = activity
+ self._parent = parent
self._neighborhood = None
- self._title = "My Turtle Art session"
+ self._title = _('My Turtle Art session')
self._bundle_id = "org.laptop.TurtleArt"
- self._activity_id = "1234567" # This could be hashed from the file path (if resuming)
+ # This could be hashed from the file path (if resuming)
+ self._activity_id = "1234567"
self._nick = ""
+ self._setup_has_been_called = False
+
+ def _setup_config_file(self, config_file_path):
self._config_file_path = config_file_path
self._collaboration_config_values = ConfigFile(self._config_file_path)
- valid_config_values = {
- "nick" : { "type" : "text"},
- "account_id" : { "type" : "text"},
- "password" : { "type" : "text"},
- "server" : { "type" : "text"},
- "port" : { "type" : "integer"},
- "register": { "type" : "boolean"},
- "turtle_color" : { "type" : "text"},
- "colors" : { "type" : "text"}
+ self._valid_config_values = {
+ 'nick': {'type': 'text'},
+ 'account_id': {'type': 'text'},
+ 'password': {'type': 'text'},
+ 'server': {'type': 'text'},
+ 'port': {'type': 'integer'},
+ 'register': {'type': 'boolean'},
+ 'colors': {'type': 'text'}
}
- self._collaboration_config_values.set_valid_keys(valid_config_values)
- self._collaboration_config_values.connect("configuration-loaded", self._connect_to_neighborhood)
- self._collaboration_config_values.connect("configuration-saved", self._connect_to_neighborhood)
+
+ def _connect_cb(self, button):
+ """ Enable connection """
+ self._collaboration_config_values.set_valid_keys(
+ self._valid_config_values)
+ self._collaboration_config_values.connect(
+ 'configuration-loaded', self._connect_to_neighborhood)
+ self._collaboration_config_values.connect(
+ 'configuration-saved', self._connect_to_neighborhood)
self._collaboration_config_values.load()
+ self.setup()
def setup(self):
self._collaboration = Collaboration(self.tw, self)
self._collaboration.setup()
+ # Do we know if we were successful?
+ self._setup_has_been_called = True
+ # TODO:
+ # use set_sensitive to enable Share and Configuration menuitems
def set_tw(self, turtleart_window):
self.tw = turtleart_window
self.tw.nick = self._get_nick()
+ self._setup_config_file(self._parent.get_config_home())
def get_menu(self):
menu = gtk.Menu()
+ MenuBuilder.make_menu_item(menu, _('Enable collaboration'),
+ self._connect_cb)
+
self._activities_submenu = gtk.Menu()
- activities_menu = MenuBuilder.make_sub_menu(self._activities_submenu, _('Activities'))
+ activities_menu = MenuBuilder.make_sub_menu(self._activities_submenu,
+ _('Activities'))
menu.append(activities_menu)
self._buddies_submenu = gtk.Menu()
- buddies_menu = MenuBuilder.make_sub_menu(self._buddies_submenu, _('Buddies'))
+ buddies_menu = MenuBuilder.make_sub_menu(self._buddies_submenu,
+ _('Buddies'))
menu.append(buddies_menu)
-
+
MenuBuilder.make_menu_item(menu, _('Share'), self._share_cb)
- MenuBuilder.make_menu_item(menu, _('Configuration'), self._config_neighborhood_cb)
-
+ MenuBuilder.make_menu_item(menu, _('Configuration'),
+ self._config_neighborhood_cb)
+
neighborhood_menu = MenuBuilder.make_sub_menu(menu, _('Neighborhood'))
return neighborhood_menu
@@ -99,40 +145,42 @@ class CollaborationPlugin(Plugin):
def _get_title(self):
return self._title
-
- def _get_turtle_color(self):
- return self._turtle_color
def _connect_to_neighborhood(self, config_file_obj):
if self._neighborhood is not None:
return
- print "_connect_to_neighborhood has been called"
-
params = {}
- params["nickname"] = self._collaboration_config_values.get("nick")
- params["account_id"] = self._collaboration_config_values.get("account_id")
- params["server"] = self._collaboration_config_values.get("server")
- params["port"] = self._collaboration_config_values.get("port")
- params["password"] = self._collaboration_config_values.get("password")
- params["register"] = self._collaboration_config_values.get("register")
- if params["server"] == "":
- raise RuntimeError("Invalid server address")
-
- self._nick = self._collaboration_config_values.get("nick")
- self._colors = self._collaboration_config_values.get("colors")
- self._turtle_color = self._collaboration_config_values.get("turtle_color")
+ params['nickname'] = self._collaboration_config_values.get('nick')
+ params['account_id'] = self._collaboration_config_values.get(
+ 'account_id')
+ params['server'] = self._collaboration_config_values.get('server')
+ params['port'] = self._collaboration_config_values.get('port')
+ params['password'] = self._collaboration_config_values.get('password')
+ params['register'] = self._collaboration_config_values.get('register')
+ if params['server'] == '':
+ raise RuntimeError('Invalid server address')
+
+ self._nick = self._collaboration_config_values.get('nick')
+ # Tell the parent activity that the nick may have changed
+ self._parent.nick_changed(self._nick)
+
+ self._colors = self._collaboration_config_values.get('colors')
+ # Tell the parent activity that the colors may have changed
+ self._parent.color_changed(self._colors)
self._activities = {}
self._buddies = {}
- print "connecting to the neighborhood"
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-
+
self._client_handler = telepathyclient.get_instance()
+ if self._client_handler == None:
+ raise RuntimeError('Telepathy client unavailable')
self._neighborhood = get_neighborhood(params)
self._neighborhood.connect('activity-added', self._activity_added_cb)
- self._neighborhood.connect('activity-removed', self._activity_removed_cb)
+ self._neighborhood.connect('activity-removed',
+ self._activity_removed_cb)
self._neighborhood.connect('buddy-added', self._buddy_added_cb)
self._neighborhood.connect('buddy-removed', self._buddy_removed_cb)
@@ -147,7 +195,7 @@ class CollaborationPlugin(Plugin):
try:
self._activities.pop(activity_model.props.name)
except:
- print "Failed to remove activity %s" % activity_model.props.name
+ print 'Failed to remove activity %s' % activity_model.props.name
self._recreate_available_activities_menu()
@@ -162,7 +210,7 @@ class CollaborationPlugin(Plugin):
print "Couldn't remove buddy %s" % buddy.get_key()
self._recreate_available_buddies_menu()
- # TODO: we should have a list of available actions over
+ # TODO: we should have a list of available actions over
# a given buddy. I.e.: a) chat with him b) make friend
# c) invite to current activity
#
@@ -173,26 +221,28 @@ class CollaborationPlugin(Plugin):
for buddy in self._buddies.values():
key = buddy.get_key()
if key is None:
- key = ""
- n = buddy.get_nick() + "|" + key[0:15]
- MenuBuilder.make_menu_item(self._buddies_submenu, n, self._buddy_actions_cb, buddy)
+ key = ''
+ n = buddy.get_nick() + '|' + key[0:15]
+ MenuBuilder.make_menu_item(self._buddies_submenu, n,
+ self._buddy_actions_cb, buddy)
def _buddy_actions_cb(self, widget, buddy):
- print "do something with %s" % buddy.get_nick()
+ print 'do something with %s' % buddy.get_nick()
- # TODO
- # - we need an extra menu branch with a) 'Join' button b) List of buddies
+ # TODO:
+ # we need an extra menu branch with a) 'Join' button b) List of buddies
def _recreate_available_activities_menu(self):
for child in self._activities_submenu.get_children():
self._activities_submenu.remove(child)
for activity in self._activities.values():
n = activity.props.name
- MenuBuilder.make_menu_item(self._activities_submenu, n, self._join_activity_cb, activity)
+ MenuBuilder.make_menu_item(self._activities_submenu, n,
+ self._join_activity_cb, activity)
def _join_activity_cb(self, widget, activity):
- print "Lets try to join..."
-
+ print 'Lets try to join...'
+
connection_manager = get_connection_manager()
account_path, connection = \
connection_manager.get_preferred_connection()
@@ -201,17 +251,17 @@ class CollaborationPlugin(Plugin):
return
properties = {}
- properties["id"] = activity.activity_id
- properties["color"] = activity.get_color()
- print "room handle according to activity %s" % activity.room_handle
- properties["private"] = True
+ properties['id'] = activity.activity_id
+ properties['color'] = activity.get_color()
+ print 'room handle according to activity %s' % activity.room_handle
+ properties['private'] = True
try:
room_handle = connection.GetActivity(activity.activity_id,
- dbus_interface=CONNECTION_INTERFACE_ACTIVITY_PROPERTIES)
- print("room_handle = %s" % str(room_handle))
- self._joined_activity = Activity(account_path, connection, room_handle,
- properties=properties)
+ dbus_interface=CONNECTION_INTERFACE_ACTIVITY_PROPERTIES)
+ print('room_handle = %s' % str(room_handle))
+ self._joined_activity = Activity(
+ account_path, connection, room_handle, properties=properties)
# FIXME: this should be unified, no need to keep 2 references
self._shared_activity = self._joined_activity
except:
@@ -224,32 +274,42 @@ class CollaborationPlugin(Plugin):
_join_id = self._joined_activity.connect('joined', self.__joined_cb)
self._joined_activity.join()
- def __joined_cb(self,activity, success, err):
+ def __joined_cb(self, activity, success, err):
print "We've joined an activity"
self.emit('joined')
def _config_neighborhood_cb(self, widget):
+ if not self._setup_has_been_called:
+ return
config_w = ConfigWizard(self._config_file_path)
config_items = [
- {"item_label" : _("Nickname"), "item_type" : "text", "item_name" : "nick" },
- { "item_label" : _("Account ID"), "item_type" : "text", "item_name" : "account_id" },
- { "item_label" : _("Server"), "item_type" : "text", "item_name" : "server" },
- { "item_label" : _("Port"), "item_type" : "text", "item_name" : "port" },
- { "item_label" : _("Password"), "item_type" : "text", "item_name" : "password" },
- { "item_label" : _("Register"), "item_type" : "boolean", "item_name" : "register" },
- { "item_label" : _("Colors"), "item_type" : "text", "item_name" : "colors" },
- { "item_label" : _("Turtle Color"), "item_type" : "text", "item_name" : "turtle_color" }
+ {'item_label': _('Nickname'), 'item_type': 'text',
+ 'item_name': 'nick'},
+ {'item_label': _('Account ID'), 'item_type': 'text',
+ 'item_name': 'account_id'},
+ {'item_label': _('Server'), 'item_type': 'text',
+ 'item_name': 'server'},
+ {'item_label': _('Port'), 'item_type': 'text',
+ 'item_name': 'port'},
+ {'item_label': _('Password'), 'item_type': 'text',
+ 'item_name': 'password'},
+ {'item_label': _('Register'), 'item_type': 'boolean',
+ 'item_name': 'register'},
+ {'item_label': _('Colors'), 'item_type': 'text',
+ 'item_name': 'colors'}
]
config_w.set_config_items(config_items)
config_w.set_config_file_obj(self._collaboration_config_values)
config_w.show()
def _share_cb(self, button):
+ if not self._setup_has_been_called:
+ return
properties = {}
properties['id'] = self._get_activity_id()
properties['type'] = self._get_bundle_id()
properties['name'] = self._get_title()
- properties['color'] = self._get_turtle_color()
+ properties['color'] = self.get_colors()
properties['private'] = False
connection_manager = get_connection_manager()
@@ -261,18 +321,19 @@ class CollaborationPlugin(Plugin):
return
try:
- self._activity._shared_activity = Activity(account_path, connection,
- properties=properties)
+ self._parent._shared_activity = Activity(account_path,
+ connection,
+ properties=properties)
# FIXME: this should be unified, no need to keep 2 references
- self._shared_activity = self._activity._shared_activity
+ self._shared_activity = self._parent._shared_activity
except:
traceback.print_exc(file=sys.stdout)
- if self._activity._shared_activity.props.joined:
+ if self._parent._shared_parent.props.joined:
raise RuntimeError('Activity %s is already shared.' %
- self._activity._get_activity_id())
+ self._parent._get_activity_id())
- self._activity._shared_activity.share(self.__share_activity_cb,
+ self._parent._shared_parent.share(self.__share_activity_cb,
self.__share_activity_error_cb)
def __share_activity_cb(self, activity):
@@ -280,10 +341,8 @@ class CollaborationPlugin(Plugin):
self.emit('shared')
def __share_activity_error_cb(self, activity, error):
- """Notify with GObject event of unsuccessful sharing of activity
- """
- print "%s got error: %s" % (activity, error)
-
-if __name__ == "__main__":
- print "testing collaboration"
+ """Notify with GObject event of unsuccessful sharing of activity"""
+ print '%s got error: %s' % (activity, error)
+if __name__ == '__main__':
+ print 'testing collaboration'
diff --git a/TurtleArt/tacamera.py b/gnome_plugins/plugin.py
index 2177288..590c9bc 100644
--- a/TurtleArt/tacamera.py
+++ b/gnome_plugins/plugin.py
@@ -1,6 +1,6 @@
-# -*- coding: utf-8 -*-
-#Copyright (c) 2010, Walter Bender
-#Copyright (c) 2010, Tony Forster
+#!/usr/bin/env python
+#Copyright (c) 2011 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
@@ -20,23 +20,15 @@
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#THE SOFTWARE.
-import gst, time
+import gobject
-GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc']
-class Camera():
- """ A class for representing the video camera """
+class Plugin(gobject.GObject):
+ def __init__(self):
+ gobject.GObject.__init__(self)
- def __init__(self, imagepath):
- GST_PIPE.append('filesink location=%s' % imagepath)
- self.pipe = gst.parse_launch('!'.join(GST_PIPE))
- self.bus = self.pipe.get_bus()
-
- def save_camera_input_to_file(self):
- """ Grab a frame from the camera """
- self.pipe.set_state(gst.STATE_PLAYING)
- self.bus.poll(gst.MESSAGE_EOS, -1)
-
- def stop_camera_input(self):
- self.pipe.set_state(gst.STATE_NULL)
+ def get_menu(self):
+ raise RuntimeError("You need to define get__menu for your plugin.")
+ def set_tw(self, turtle_window=None):
+ raise RuntimeError("You need to define set_tw for your plugin.")
diff --git a/extra/upload.py b/gnome_plugins/uploader_plugin.py
index e39d099..06224fa 100644
--- a/extra/upload.py
+++ b/gnome_plugins/uploader_plugin.py
@@ -1,3 +1,26 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 Walter Bender
+#Copyright (c) 2010 Jamie Boisture
+#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
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
#!/usr/bin/python
try:
@@ -8,14 +31,21 @@ except ImportError, e:
print "Import Error: %s. Project upload is disabled." % (e)
_UPLOAD_AVAILABLE = False
+import os
import gtk
+
+from plugin import Plugin
+from util.menubuilder import MenuBuilder
+
from gettext import gettext as _
-class Uploader():
+
+class Uploader_plugin(Plugin):
MAX_FILE_SIZE = 950000
UPLOAD_SERVER = 'http://turtleartsite.appspot.com'
- def __init__(self, upload_server = None, max_file_size = 0):
+ def __init__(self, parent, upload_server=None, max_file_size=None):
+ self._parent = parent
self.uploading = False
if upload_server is None:
@@ -23,14 +53,23 @@ class Uploader():
if max_file_size is None:
self._max_file_size = self.MAX_FILE_SIZE
+ else:
+ self._max_file_size = max_file_size
def set_tw(self, turtleart_window):
self.tw = turtleart_window
+ def get_menu(self):
+ menu = gtk.Menu()
+ MenuBuilder.make_menu_item(menu, _('Upload to Web'),
+ self.do_upload_to_web)
+ upload_menu = MenuBuilder.make_sub_menu(menu, _('Upload'))
+ return upload_menu
+
def enabled(self):
return _UPLOAD_AVAILABLE
- def do_upload_to_web(self, widget = None):
+ def do_upload_to_web(self, widget=None):
if self.uploading:
return
@@ -127,14 +166,14 @@ http://turtleartsite.sugarlabs.org to upload your project.'))
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:
+ 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)
+ smaller_file.save(imagefile, quality=100)
c = pycurl.Curl()
c.setopt(c.POST, 1)
diff --git a/icons/blocksoff.svg b/icons/blocksoff.svg
index f0e10b3..e7db290 100644
--- a/icons/blocksoff.svg
+++ b/icons/blocksoff.svg
@@ -2,13 +2,29 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0"
width="55"
height="55"
id="svg2">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
<rect
width="55"
height="55"
@@ -16,12 +32,23 @@
y="0"
id="rect3269"
style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
- <path
- d="m 31.1663,14.065103 c 5.3328,0 5.3328,0 5.3328,0 0,0 1.377128,0.828768 1.83315,1.3332 0.465777,0.515223 1.16655,1.9998 1.16655,1.9998 l 0,6.332701 c 0,0 -0.738943,1.231094 -1.16655,1.6665 -0.476472,0.485161 -1.83315,1.3332 -1.83315,1.3332 l -5.6661,0 0,0 0,1.333199 -6.666,0 0,-1.333199 -5.6661,0 c 0,0 -1.356677,-0.848039 -1.83315,-1.3332 -0.427607,-0.435406 -1.16655,-1.6665 -1.16655,-1.6665 l 0,-6.332701 c 0,0 0.700773,-1.484577 1.16655,-1.9998 0.456022,-0.504432 1.83315,-1.3332 1.83315,-1.3332 l 5.3328,0 0,1.6665 7.3326,0 0,-1.6665 z"
- id="path9"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- d="m 31.1663,26.936297 c 5.3328,0 5.3328,0 5.3328,0 0,0 1.377128,0.828767 1.83315,1.333199 0.465777,0.515223 1.16655,1.9998 1.16655,1.9998 l 0,6.332701 c 0,0 -0.738943,1.231095 -1.16655,1.666499 -0.476472,0.485162 -1.83315,1.333201 -1.83315,1.333201 l -5.6661,0 0,0 0,1.3332 -6.666,0 0,-1.3332 -5.6661,0 c 0,0 -1.356677,-0.848039 -1.83315,-1.333201 -0.427607,-0.435404 -1.16655,-1.666499 -1.16655,-1.666499 l 0,-6.332701 c 0,0 0.700773,-1.484577 1.16655,-1.9998 0.456022,-0.504432 1.83315,-1.333199 1.83315,-1.333199 l 5.3328,0 0,1.666499 7.3326,0 0,-1.666499 z"
- id="path2559"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ id="g3789">
+ <path
+ d="m 15.719636,31.331478 0.114372,5.261133 11.437247,6.290486 L 27.5,29.387146"
+ id="path2463"
+ style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 27.728744,29.501519 0.05719,13.381578 11.723178,-7.548583 0,-4.689272 -5.947368,3.545548"
+ id="path2465"
+ style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.001012,27.328441 5.604251,-5.146761 -5.832996,-2.973684 12.123482,-7.091093 5.718623,3.316801 5.489879,-3.088056 12.123482,7.319838 -5.832996,2.401822 5.489878,5.032388 -11.322874,7.205466 -5.947369,-5.261134 -6.290485,5.261134 -11.322875,-6.976721 z"
+ id="path2459"
+ style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="M 16.40587,21.952935 27.156883,15.204959 38.937247,22.067308 27.614372,28.815283 16.40587,21.952935 z"
+ id="path2461"
+ style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
</svg>
diff --git a/icons/blockson.svg b/icons/blockson.svg
index df19fdb..4435cc2 100644
--- a/icons/blockson.svg
+++ b/icons/blockson.svg
@@ -2,89 +2,29 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0"
width="55"
height="55"
id="svg2">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs
- id="defs5">
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient2431"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,32.193732)" />
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient2428"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,45.064925)" />
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient3172"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient3166">
- <stop
- id="stop3168"
- style="stop-color:#ffffff;stop-opacity:1"
- offset="0" />
- <stop
- id="stop3170"
- style="stop-color:#ffff00;stop-opacity:1"
- offset="1" />
- </linearGradient>
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient2557"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.3333,0,0,0.3333,9.2560985,9.9123239)" />
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient2561"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.3333,0,0,0.3333,8.962951,22.783517)" />
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient2461"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,32.193732)" />
- <linearGradient
- x1="0"
- y1="22"
- x2="74"
- y2="22"
- id="linearGradient2463"
- xlink:href="#linearGradient3166"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,45.064925)" />
- </defs>
+ id="defs4" />
<rect
width="55"
height="55"
@@ -94,15 +34,22 @@
id="rect2839"
style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
<g
- transform="translate(-11.863578,-18.461929)"
- id="g2457">
+ id="g3788">
<path
- d="m 43.029878,32.527032 c 5.3328,0 5.3328,0 5.3328,0 0,0 1.377128,0.828768 1.83315,1.3332 0.465777,0.515223 1.16655,1.9998 1.16655,1.9998 l 0,6.332701 c 0,0 -0.738943,1.231094 -1.16655,1.6665 -0.476472,0.485161 -1.83315,1.3332 -1.83315,1.3332 l -5.6661,0 0,0 0,1.333199 -6.666,0 0,-1.333199 -5.6661,0 c 0,0 -1.356677,-0.848039 -1.83315,-1.3332 -0.427607,-0.435406 -1.16655,-1.6665 -1.16655,-1.6665 l 0,-6.332701 c 0,0 0.700773,-1.484577 1.16655,-1.9998 0.456022,-0.504432 1.83315,-1.3332 1.83315,-1.3332 l 5.3328,0 0,1.6665 7.3326,0 0,-1.6665 z"
- id="path9"
- style="fill:url(#linearGradient2461);fill-opacity:1;stroke:#c0a000;stroke-width:0.66659999;stroke-opacity:1" />
+ d="m 15.719636,31.331478 0.114372,5.261133 11.437247,6.290486 L 27.5,29.387146"
+ id="path2463"
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#804000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
- d="m 43.029878,45.398226 c 5.3328,0 5.3328,0 5.3328,0 0,0 1.377128,0.828767 1.83315,1.333199 0.465777,0.515223 1.16655,1.9998 1.16655,1.9998 l 0,6.332701 c 0,0 -0.738943,1.231095 -1.16655,1.666499 -0.476472,0.485162 -1.83315,1.333201 -1.83315,1.333201 l -5.6661,0 0,0 0,1.3332 -6.666,0 0,-1.3332 -5.6661,0 c 0,0 -1.356677,-0.848039 -1.83315,-1.333201 -0.427607,-0.435404 -1.16655,-1.666499 -1.16655,-1.666499 l 0,-6.332701 c 0,0 0.700773,-1.484577 1.16655,-1.9998 0.456022,-0.504432 1.83315,-1.333199 1.83315,-1.333199 l 5.3328,0 0,1.666499 7.3326,0 0,-1.666499 z"
- id="path2559"
- style="fill:url(#linearGradient2463);fill-opacity:1;stroke:#c0a000;stroke-width:0.66659999;stroke-opacity:1" />
+ d="m 27.728744,29.501519 0.05719,13.381578 11.723178,-7.548583 0,-4.689272 -5.947368,3.545548"
+ id="path2465"
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#804000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="m 10.001012,27.328441 5.604251,-5.146761 -5.832996,-2.973684 12.123482,-7.091093 5.718623,3.316801 5.489879,-3.088056 12.123482,7.319838 -5.832996,2.401822 5.489878,5.032388 -11.322874,7.205466 -5.947369,-5.261134 -6.290485,5.261134 -11.322875,-6.976721 z"
+ id="path2459"
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#804000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ d="M 16.40587,21.952935 27.156883,15.204959 38.937247,22.067308 27.614372,28.815283 16.40587,21.952935 z"
+ id="path2461"
+ style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#804000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</svg>
diff --git a/icons/extrasoff.svg b/icons/extrasoff.svg
index 47547fc..7975a9d 100644
--- a/icons/extrasoff.svg
+++ b/icons/extrasoff.svg
@@ -2,14 +2,29 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0"
width="55"
height="55"
id="svg2">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs
- id="defs4" />
+ id="defs8" />
<rect
width="55"
height="55"
@@ -18,24 +33,43 @@
id="rect3269"
style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
<g
- transform="translate(51.861336,-0.235324)"
- id="g3247"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="g3">
+ <polygon
+ points="20.555,2.531 26.891,12.363 17.238,16.369 14.659,4.975 "
+ id="polygon5"
+ style="fill:#ffffff" />
+ <polygon
+ points="52.477,20.55 42.646,26.88 38.649,17.228 50.04,14.654 "
+ id="polygon7"
+ style="fill:#ffffff" />
+ <polygon
+ points="34.453,52.471 28.117,42.645 37.775,38.645 40.349,50.029 "
+ id="polygon9"
+ style="fill:#ffffff" />
+ <polygon
+ points="40.295,4.882 37.824,16.315 28.171,12.309 34.394,2.439 "
+ id="polygon11"
+ style="fill:#ffffff" />
+ <polygon
+ points="50.055,40.258 38.628,37.791 42.623,28.139 52.493,34.365 "
+ id="polygon13"
+ style="fill:#ffffff" />
+ <polygon
+ points="4.953,40.234 16.385,37.76 12.391,28.105 2.515,34.34 "
+ id="polygon15"
+ style="fill:#ffffff" />
+ <polygon
+ points="2.493,20.539 12.319,26.875 16.32,17.216 4.936,14.643 "
+ id="polygon17"
+ style="fill:#ffffff" />
+ <polygon
+ points="20.609,52.461 26.939,42.623 17.287,38.629 14.719,50.018 "
+ id="polygon19"
+ style="fill:#ffffff" />
<path
- d="M -36.1417,31.566802 l 0.114372,5.261133 11.437247,6.290486 0.228745,-13.495951"
- id="path2463"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- d="M -24.132592,29.736843 l 0.05719,13.381578 11.723178,-7.548583 0,-4.689272 -5.947368,3.545548"
- id="path2465"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- d="M -41.860324,27.563765 l 5.604251,-5.146761 -5.832996,-2.973684 12.123482,-7.091093 5.718623,3.316801 5.489879,-3.088056 12.1234818,7.319838 -5.8329958,2.401822 5.4898784,5.032388 -11.3228744,7.205466 -5.947369,-5.261134 -6.290485,5.261134 -11.322875,-6.976721 z"
- id="path2459"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- d="M -35.455466,22.188259 l 10.751013,-6.747976 11.780364,6.862349 -11.322875,6.747975 -11.208502,-6.862348 z"
- id="path2461"
- style="fill:none;stroke:#ffffff;stroke-width:2.29999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ d="m 39.925,22.352 c 2.845,6.863 -0.412,14.728 -7.274,17.574 -6.867,2.85 -14.734,-0.418 -17.578,-7.281 -2.84,-6.862 0.418,-14.733 7.279,-17.572 6.862,-2.845 14.734,0.417 17.573,7.279 z"
+ id="path21"
+ style="fill:none;stroke:#ffffff;stroke-width:11.69439983" />
</g>
</svg>
diff --git a/icons/extrason.svg b/icons/extrason.svg
index 3d2cd85..7ee08bf 100644
--- a/icons/extrason.svg
+++ b/icons/extrason.svg
@@ -2,14 +2,104 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0"
width="55"
height="55"
id="svg2">
+ <metadata
+ id="metadata20">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs
- id="defs4" />
+ id="defs5">
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2431"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,32.193732)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2428"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,45.064925)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient3172"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="linearGradient3166">
+ <stop
+ id="stop3168"
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0" />
+ <stop
+ id="stop3170"
+ style="stop-color:#ffff00;stop-opacity:1"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2557"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,9.2560985,9.9123239)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2561"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,8.962951,22.783517)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2461"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,32.193732)" />
+ <linearGradient
+ x1="0"
+ y1="22"
+ x2="74"
+ y2="22"
+ id="linearGradient2463"
+ xlink:href="#linearGradient3166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.3333,0,0,0.3333,27.031478,45.064925)" />
+ </defs>
<rect
width="55"
height="55"
@@ -19,24 +109,50 @@
id="rect2839"
style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
<g
- transform="translate(51.861336,-0.235324)"
- id="g3247"
- style="stroke:#800000;stroke-opacity:1">
+ id="g3799">
+ <polygon
+ points="20.555,2.531 26.891,12.363 17.238,16.369 14.659,4.975 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon5"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="52.477,20.55 42.646,26.88 38.649,17.228 50.04,14.654 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon7"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="34.453,52.471 28.117,42.645 37.775,38.645 40.349,50.029 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon9"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="40.295,4.882 37.824,16.315 28.171,12.309 34.394,2.439 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon11"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="50.055,40.258 38.628,37.791 42.623,28.139 52.493,34.365 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon13"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="4.953,40.234 16.385,37.76 12.391,28.105 2.515,34.34 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon15"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="2.493,20.539 12.319,26.875 16.32,17.216 4.936,14.643 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon17"
+ style="fill:#ff0000;fill-opacity:1" />
+ <polygon
+ points="20.609,52.461 26.939,42.623 17.287,38.629 14.719,50.018 "
+ transform="matrix(0.64,0,0,0.63959066,9.90448,9.9400384)"
+ id="polygon19"
+ style="fill:#ff0000;fill-opacity:1" />
<path
- d="M -36.1417,31.566802 l 0.114372,5.261133 11.437247,6.290486 0.228745,-13.495951"
- id="path2463"
- style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#800000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="M -24.132592,29.736843 l 0.05719,13.381578 11.723178,-7.548583 0,-4.689272 -5.947368,3.545548"
- id="path2465"
- style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#800000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="M -41.860324,27.563765 l 5.604251,-5.146761 -5.832996,-2.973684 12.123482,-7.091093 5.718623,3.316801 5.489879,-3.088056 12.1234818,7.319838 -5.8329958,2.401822 5.4898784,5.032388 -11.3228744,7.205466 -5.947369,-5.261134 -6.290485,5.261134 -11.322875,-6.976721 z"
- id="path2459"
- style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#800000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="M -35.455466,22.188259 l 10.751013,-6.747976 11.780364,6.862349 -11.322875,6.747975 -11.208502,-6.862348 z"
- id="path2461"
- style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#800000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ d="m 35.45648,24.236169 c 1.8208,4.389511 -0.26368,9.419891 -4.65536,11.240166 -4.39488,1.822833 -9.42976,-0.267349 -11.24992,-4.65686 -1.8176,-4.388871 0.26752,-9.423089 4.65856,-11.238887 4.39168,-1.819635 9.42976,0.26671 11.24672,4.655581 z"
+ id="path21"
+ style="fill:none;stroke:#ff0000;stroke-width:7.48202181;stroke-opacity:1" />
</g>
</svg>
diff --git a/icons/mediaoff.svg b/icons/mediaoff.svg
new file mode 100644
index 0000000..b2f460a
--- /dev/null
+++ b/icons/mediaoff.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+` <g
+ transform="matrix(0.67,0,0,0.67,9.075,9.075)"
+ id="g2855">
+ <path
+ d="m 46.7405,44.669 c 0,2.511 -1.528,4.331 -4.332,4.331 l -29.457,0 0,-43 29.458,0 c 2.15,0 4.332,2.154 4.332,4.33 l -0.001,34.339 0,0 z"
+ id="path2458"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ x1="22.2155"
+ x2="22.2155"
+ y1="6.1209998"
+ y2="48.881001"
+ id="line2460" />
+ <path
+ d="m 8.2585,14.464 c 0,0 2.084,0.695 4.17,0.695 2.086,0 4.173,-0.695 4.173,-0.695"
+ id="path2462"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 8.2585,28.021 c 0,0 1.912,0.695 4.345,0.695 2.433,0 3.999,-0.695 3.999,-0.695"
+ id="path2464"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 8.2585,41.232 c 0,0 1.736,0.695 4.518,0.695 2.781,0 3.825,-0.695 3.825,-0.695"
+ id="path2466"
+ style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/icons/mediaon.svg b/icons/mediaon.svg
new file mode 100644
index 0000000..27dd516
--- /dev/null
+++ b/icons/mediaon.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.0"
+ width="55"
+ height="55"
+ id="svg2">
+ <rect
+ width="55"
+ height="55"
+ rx="0"
+ x="0"
+ y="0"
+ id="rect2839"
+ style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ <path
+ d="m 40.391135,39.00323 c 0,1.68237 -1.02376,2.90177 -2.90244,2.90177 l -19.73619,0 0,-28.81 19.73686,0 c 1.4405,0 2.90244,1.44318 2.90244,2.9011 l -6.7e-4,23.00713 0,0 z"
+ id="path2458"
+ style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ id="line2460"
+ y2="41.825272"
+ y1="13.17607"
+ x2="23.959385"
+ x1="23.959385"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 14.608195,18.76588 c 0,0 1.39628,0.46565 2.7939,0.46565 1.39762,0 2.79591,-0.46565 2.79591,-0.46565"
+ id="path2462"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 14.608195,27.84907 c 0,0 1.28104,0.46565 2.91115,0.46565 1.63011,0 2.67933,-0.46565 2.67933,-0.46565"
+ id="path2464"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <path
+ d="m 14.608195,36.70044 c 0,0 1.16312,0.46565 3.02706,0.46565 1.86327,0 2.56275,-0.46565 2.56275,-0.46565"
+ id="path2466"
+ style="fill:none;stroke:#000000;stroke-width:2.34500003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+</svg>
diff --git a/icons/portfoliooff.svg b/icons/portfoliooff.svg
index b2f460a..d404bab 100644
--- a/icons/portfoliooff.svg
+++ b/icons/portfoliooff.svg
@@ -2,45 +2,71 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0"
width="55"
height="55"
- id="svg2">
- <rect
- width="55"
- height="55"
- x="0"
- y="0"
- id="rect3269"
- style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+ id="svg2"><metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+</metadata>
+<defs
+ id="defs12" />
+<rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+
+
` <g
- transform="matrix(0.67,0,0,0.67,9.075,9.075)"
- id="g2855">
- <path
- d="m 46.7405,44.669 c 0,2.511 -1.528,4.331 -4.332,4.331 l -29.457,0 0,-43 29.458,0 c 2.15,0 4.332,2.154 4.332,4.33 l -0.001,34.339 0,0 z"
- id="path2458"
- style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ transform="matrix(0.8501594,0,0,0.8501594,4.1206165,2.9055499)"
+ id="g3041">
+ <rect
+ width="40"
+ height="30"
+ x="7.5"
+ y="5.8274999"
+ id="rect2996"
+ style="fill:none;stroke:#ffffff;stroke-width:2.34500003;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ transform="translate(-1.0846148,0)"
+ id="g3793">
+ <line
+ id="line2460"
+ y2="52.31316"
+ y1="36.358479"
+ x2="23.419287"
+ x1="27.69433"
+ style="fill:none;stroke:#ffffff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
<line
- style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- x1="22.2155"
- x2="22.2155"
- y1="6.1209998"
- y2="48.881001"
- id="line2460" />
- <path
- d="m 8.2585,14.464 c 0,0 2.084,0.695 4.17,0.695 2.086,0 4.173,-0.695 4.173,-0.695"
- id="path2462"
- style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
- <path
- d="m 8.2585,28.021 c 0,0 1.912,0.695 4.345,0.695 2.433,0 3.999,-0.695 3.999,-0.695"
- id="path2464"
- style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
- <path
- d="m 8.2585,41.232 c 0,0 1.736,0.695 4.518,0.695 2.781,0 3.825,-0.695 3.825,-0.695"
- id="path2466"
- style="fill:none;stroke:#ffffff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ id="line2460-2"
+ y2="52.313156"
+ y1="36.358479"
+ x2="33.749943"
+ x1="29.474899"
+ style="fill:none;stroke:#ffffff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
</g>
+ <line
+ style="fill:none;stroke:#ffffff;stroke-width:2.66399026;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ x1="9.0131855"
+ x2="45.986813"
+ y1="8.168005"
+ y2="8.168005"
+ id="line2460-0" />
+</g>
</svg>
diff --git a/icons/portfolioon.svg b/icons/portfolioon.svg
index fa4ddf6..e174ee2 100644
--- a/icons/portfolioon.svg
+++ b/icons/portfolioon.svg
@@ -2,6 +2,9 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
@@ -9,6 +12,18 @@
width="55"
height="55"
id="svg2">
+ <metadata
+ id="metadata3983">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs
id="defs5">
<linearGradient
@@ -229,30 +244,41 @@
id="rect2839"
style="fill:#ffd200;fill-opacity:1;fill-rule:evenodd;stroke:none" />
<g
- transform="matrix(0.67,0,0,0.67,9.075,9.075)"
- id="g2856">
- <path
- d="m 46.7405,44.669 c 0,2.511 -1.528,4.331 -4.332,4.331 l -29.457,0 0,-43 29.458,0 c 2.15,0 4.332,2.154 4.332,4.33 l -0.001,34.339 0,0 z"
- id="path2458"
- style="fill:#ffffff;stroke:#0000ff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ transform="matrix(0.8501594,0,0,0.8501594,4.1206165,2.9055499)"
+ id="g3816"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-opacity:1">
+ <rect
+ width="40"
+ height="30"
+ x="7.5"
+ y="5.8274999"
+ id="rect2996"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:2.34500003;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <g
+ transform="translate(-1.0846148,0)"
+ id="g3793"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-opacity:1">
+ <line
+ id="line2460-5"
+ y2="52.31316"
+ y1="36.358479"
+ x2="23.419287"
+ x1="27.69433"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ <line
+ id="line2460-2"
+ y2="52.313156"
+ y1="36.358479"
+ x2="33.749943"
+ x1="29.474899"
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:1.78056884;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ </g>
<line
- style="fill:none;stroke:#0000ff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
- x1="22.2155"
- x2="22.2155"
- y1="6.1209998"
- y2="48.881001"
- id="line2460" />
- <path
- d="m 8.2585,14.464 c 0,0 2.084,0.695 4.17,0.695 2.086,0 4.173,-0.695 4.173,-0.695"
- id="path2462"
- style="fill:none;stroke:#0000ff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
- <path
- d="m 8.2585,28.021 c 0,0 1.912,0.695 4.345,0.695 2.433,0 3.999,-0.695 3.999,-0.695"
- id="path2464"
- style="fill:none;stroke:#0000ff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
- <path
- d="m 8.2585,41.232 c 0,0 1.736,0.695 4.518,0.695 2.781,0 3.825,-0.695 3.825,-0.695"
- id="path2466"
- style="fill:none;stroke:#0000ff;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
+ style="fill:#f9f9f9;stroke:#0000ff;stroke-width:2.66399026;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+ x1="9.0131855"
+ x2="45.986813"
+ y1="8.168005"
+ y2="8.168005"
+ id="line2460-0" />
</g>
</svg>
diff --git a/devices/__init__.py b/plugins/__init__.py
index e69de29..e69de29 100644
--- a/devices/__init__.py
+++ b/plugins/__init__.py
diff --git a/plugins/__init__.pyc b/plugins/__init__.pyc
new file mode 100644
index 0000000..ce06608
--- /dev/null
+++ b/plugins/__init__.pyc
Binary files differ
diff --git a/plugins/audio_sensors_plugin.py b/plugins/audio_sensors_plugin.py
new file mode 100644
index 0000000..d8908c8
--- /dev/null
+++ b/plugins/audio_sensors_plugin.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 Walter Bender
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+import gst
+try:
+ from numpy import append
+ from numpy.fft import rfft
+ PITCH_AVAILABLE = True
+except:
+ PITCH_AVAILABLE = False
+import gtk
+
+from gettext import gettext as _
+
+from plugin import Plugin
+
+from audio.audiograb import AudioGrab_Unknown, AudioGrab_XO1, AudioGrab_XO15, \
+ SENSOR_DC_NO_BIAS, SENSOR_DC_BIAS
+
+from audio.ringbuffer import RingBuffer1d
+
+from TurtleArt.taconstants import PALETTES, PALETTE_NAMES, BOX_STYLE_MEDIA, \
+ CONTENT_BLOCKS, BLOCK_NAMES, DEFAULTS, SPECIAL_NAMES, HELP_STRINGS, \
+ BOX_STYLE, PRIMITIVES, XO1, XO15
+from TurtleArt.talogo import VALUE_BLOCKS, PLUGIN_DICTIONARY
+from TurtleArt.tautils import get_path
+
+import logging
+_logger = logging.getLogger('turtleart-activity audio sensors plugin')
+
+
+def _avg(array, abs_value=False):
+ """ Calc. the average value of an array """
+ if len(array) == 0:
+ return 0
+ array_sum = 0
+ if abs_value:
+ for a in array:
+ array_sum += abs(a)
+ else:
+ for a in array:
+ array_sum += a
+ return float(array_sum) / len(array)
+
+
+class Audio_sensors_plugin(Plugin):
+
+ def __init__(self, parent):
+ self._parent = parent
+ self.hw = self._parent.hw
+ self._status = True # TODO: test for audio device
+
+ def setup(self):
+ # set up audio-sensor-specific blocks
+ if not self._status:
+ return
+
+ self.max_samples = 1500
+ self.input_step = 1
+
+ self.ringbuffer = RingBuffer1d(self.max_samples, dtype='int16')
+ if self.hw == XO1:
+ self.voltage_gain = 0.00002225
+ self.voltage_bias = 1.140
+ elif self.hw == XO15:
+ self.voltage_gain = -0.0001471
+ self.voltage_bias = 1.695
+
+ PALETTES[PALETTE_NAMES.index('sensor')].append('sound')
+ BOX_STYLE.append('sound')
+ BLOCK_NAMES['sound'] = [_('sound')]
+ HELP_STRINGS['sound'] = _('raw microphone input signal')
+ VALUE_BLOCKS.append('sound')
+ PRIMITIVES['sound'] = 'sound'
+ PLUGIN_DICTIONARY['sound'] = self.prim_sound
+ self._parent.lc._def_prim('sound', 0,
+ lambda self: PLUGIN_DICTIONARY['sound']())
+ PALETTES[PALETTE_NAMES.index('sensor')].append('volume')
+ BOX_STYLE.append('volume')
+ BLOCK_NAMES['volume'] = [_('volume')]
+ HELP_STRINGS['volume'] = _('microphone input volume')
+ VALUE_BLOCKS.append('volume')
+ PRIMITIVES['volume'] = 'volume'
+ PLUGIN_DICTIONARY['volume'] = self.prim_volume
+ self._parent.lc._def_prim('volume', 0,
+ lambda self: PLUGIN_DICTIONARY['volume']())
+ PALETTES[PALETTE_NAMES.index('sensor')].append('pitch')
+ BOX_STYLE.append('pitch')
+ BLOCK_NAMES['pitch'] = [_('pitch')]
+ HELP_STRINGS['pitch'] = _('microphone input pitch')
+ VALUE_BLOCKS.append('pitch')
+ PRIMITIVES['pitch'] = 'pitch'
+ PLUGIN_DICTIONARY['pitch'] = self.prim_pitch
+ self._parent.lc._def_prim('pitch', 0,
+ lambda self: PLUGIN_DICTIONARY['pitch']())
+
+ if self.hw in [XO1, XO15]:
+ PALETTES[PALETTE_NAMES.index('sensor')].append('resistance')
+ BOX_STYLE.append('resistance')
+ BLOCK_NAMES['resistance'] = [_('resistance')]
+ HELP_STRINGS['resistance'] = _('sensor input resistance')
+ VALUE_BLOCKS.append('resistance')
+ PRIMITIVES['resistance'] = 'resistance'
+ PLUGIN_DICTIONARY['resistance'] = self.prim_resistance
+ self._parent.lc._def_prim('resistance', 0,
+ lambda self: PLUGIN_DICTIONARY['resistance']())
+
+ PALETTES[PALETTE_NAMES.index('sensor')].append('voltage')
+ BOX_STYLE.append('voltage')
+ BLOCK_NAMES['voltage'] = [_('voltage')]
+ HELP_STRINGS['voltage'] = _('sensor voltage')
+ VALUE_BLOCKS.append('voltage')
+ PRIMITIVES['voltage'] = 'voltage'
+ PLUGIN_DICTIONARY['voltage'] = self.prim_voltage
+ self._parent.lc._def_prim('voltage', 0,
+ lambda self: PLUGIN_DICTIONARY['voltage']())
+ self.audio_started = False
+
+ def start(self):
+ # This gets called by the start button
+ if not self._status:
+ return
+ """ Start grabbing audio if there is an audio block in use """
+ if len(self._parent.block_list.get_similar_blocks('block',
+ ['volume', 'sound', 'pitch', 'resistance', 'voltage'])) > 0:
+ if self.audio_started:
+ self.audiograb.resume_grabbing()
+ else:
+ if self.hw == XO15:
+ self.audiograb = AudioGrab_XO15(self.new_buffer, self)
+ elif self.hw == XO1:
+ self.audiograb = AudioGrab_XO1(self.new_buffer, self)
+ else:
+ self.audiograb = AudioGrab_Unknown(self.new_buffer, self)
+ self.audiograb.start_grabbing()
+ self.audio_started = True
+ self._update_audio_mode()
+
+ def new_buffer(self, buf):
+ """ Append a new buffer to the ringbuffer """
+ self.ringbuffer.append(buf)
+ return True
+
+ def _update_audio_mode(self):
+ """ If there are sensor blocks, set the appropriate audio mode """
+ if not hasattr(self._parent.lc, 'value_blocks'):
+ return
+ for name in ['sound', 'volume', 'pitch']:
+ if name in self._parent.lc.value_blocks:
+ if len(self._parent.lc.value_blocks[name]) > 0:
+ self.audiograb.set_sensor_type()
+ return
+ if 'resistance' in self._parent.lc.value_blocks:
+ if len(self._parent.lc.value_blocks['resistance']) > 0:
+ self.audiograb.set_sensor_type(SENSOR_DC_BIAS)
+ return
+ if 'voltage' in self._parent.lc.value_blocks:
+ if len(self._parent.lc.value_blocks['voltage']) > 0:
+ self.audiograb.set_sensor_type(SENSOR_DC_NO_BIAS)
+ return
+
+ def stop(self):
+ # This gets called by the stop button
+ if self._status:
+ if self.audio_started:
+ self.audiograb.pause_grabbing()
+
+ def goto_background(self):
+ # This gets called when your process is sent to the background
+ pass
+
+ def return_to_foreground(self):
+ # This gets called when your process returns from the background
+ pass
+
+ def quit(self):
+ # This gets called by the quit button
+ self.stop()
+
+ def _status_report(self):
+ print 'Reporting audio sensor status: %s' % (str(self._status))
+ return self._status
+
+ # Block primitives used in talogo
+
+ def prim_volume(self):
+ """ return mic in value """
+ #TODO: Adjust gain for different HW
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ volume = float(_avg(buf, abs_value=True))
+ self._parent.lc.update_label_value('volume', volume)
+ return volume
+ else:
+ return 0
+
+ def prim_sound(self):
+ """ return raw mic in value """
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ sound = float(buf[0])
+ self._parent.lc.update_label_value('sound', sound)
+ return sound
+ else:
+ return 0
+
+ def prim_pitch(self):
+ """ return index of max value in fft of mic in values """
+ if not PITCH_AVAILABLE:
+ return 0
+ buf = []
+ for i in range(4):
+ buf = append(buf, self.ringbuffer.read(None, self.input_step))
+ if len(buf) > 0:
+ r = []
+ for j in rfft(buf):
+ r.append(abs(j))
+ # Convert output to Hertz
+ pitch = r.index(max(r)) * 48000 / len(buf)
+ self._parent.lc.update_label_value('pitch', pitch)
+ return pitch
+ else:
+ return 0
+
+ def prim_resistance(self):
+ """ return resistance sensor value """
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ # See <http://bugs.sugarlabs.org/ticket/552#comment:7>
+ # TODO: test this calibration on XO 1.5
+ if self.hw == XO1:
+ resistance = 2.718 ** ((float(_avg(buf)) * 0.000045788) + \
+ 8.0531)
+ else:
+ avg_buf = float(_avg(buf))
+ if avg_buf > 0:
+ resistance = (420000000 / avg_buf) - 13500
+ else:
+ resistance = 420000000
+ self._parent.lc.update_label_value('resistance', resistance)
+ return resistance
+ else:
+ return 0
+
+ def prim_voltage(self):
+ """ return voltage sensor value """
+ buf = self.ringbuffer.read(None, self.input_step)
+ if len(buf) > 0:
+ # See <http://bugs.sugarlabs.org/ticket/552#comment:7>
+ voltage = float(_avg(buf)) * self.voltage_gain + self.voltage_bias
+ self._parent.lc.update_label_value('voltage', voltage)
+ return voltage
+ else:
+ return 0
diff --git a/plugins/camera_plugin.py b/plugins/camera_plugin.py
new file mode 100644
index 0000000..3061d39
--- /dev/null
+++ b/plugins/camera_plugin.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 Walter Bender
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+import gst
+import gtk
+from fcntl import ioctl
+
+from gettext import gettext as _
+
+from camera.tacamera import Camera
+from camera.v4l2 import v4l2_control, V4L2_CID_AUTOGAIN, VIDIOC_G_CTRL, \
+ VIDIOC_S_CTRL
+
+from plugin import Plugin
+from TurtleArt.taconstants import PALETTES, PALETTE_NAMES, BOX_STYLE_MEDIA, \
+ CONTENT_BLOCKS, BLOCK_NAMES, DEFAULTS, SPECIAL_NAMES, HELP_STRINGS, \
+ BOX_STYLE, PRIMITIVES
+from TurtleArt.talogo import VALUE_BLOCKS, MEDIA_BLOCKS_DICTIONARY, \
+ PLUGIN_DICTIONARY
+from TurtleArt.tautils import get_path
+
+import logging
+_logger = logging.getLogger('turtleart-activity camera plugin')
+
+
+class Camera_plugin(Plugin):
+
+ def __init__(self, parent):
+ self._parent = parent
+ self._status = False
+
+ v4l2src = gst.element_factory_make('v4l2src')
+ if v4l2src.props.device_name is not None:
+
+ if self._parent.running_sugar:
+ self._imagepath = get_path(self._parent.activity,
+ 'data/turtlepic.png')
+ else:
+ self._imagepath = '/tmp/turtlepic.png'
+ self._camera = Camera(self._imagepath)
+
+ self._status = True
+
+ def setup(self):
+ # set up camera-specific blocks
+ if self._status:
+ PALETTES[PALETTE_NAMES.index('sensor')].append('luminance')
+ BOX_STYLE.append('luminance')
+ BLOCK_NAMES['luminance'] = [_('brightness')]
+ HELP_STRINGS['luminance'] = _("light level detected by camera")
+ VALUE_BLOCKS.append('luminance')
+ PRIMITIVES['luminance'] = 'luminance'
+ PLUGIN_DICTIONARY['luminance'] = self.prim_read_camera
+ self._parent.lc._def_prim('luminance', 0,
+ lambda self: PLUGIN_DICTIONARY['luminance'](True))
+
+ # Depreciated block
+ BOX_STYLE.append('readcamera')
+ BLOCK_NAMES['readcamera'] = [_('read camera')]
+ HELP_STRINGS['readcamera'] = \
+ _("Average RGB color from camera is pushed to the stack")
+ VALUE_BLOCKS.append('readcamera')
+ PRIMITIVES['readcamera'] = 'readcamera'
+ PLUGIN_DICTIONARY['readcamera'] = self.prim_read_camera
+ self._parent.lc._def_prim('readcamera', 0,
+ lambda self: PLUGIN_DICTIONARY['readcamera'](True))
+
+ PALETTES[PALETTE_NAMES.index('sensor')].append('camera')
+ BOX_STYLE_MEDIA.append('camera')
+ CONTENT_BLOCKS.append('camera')
+ BLOCK_NAMES['camera'] = [' ']
+ DEFAULTS['camera'] = ['CAMERA']
+ SPECIAL_NAMES['camera'] = _('camera')
+ HELP_STRINGS['camera'] = _('camera output')
+ MEDIA_BLOCKS_DICTIONARY['camera'] = self.prim_take_picture
+
+ def start(self):
+ # This gets called by the start button
+ pass
+
+ def stop(self):
+ # This gets called by the stop button
+ if self._status:
+ self._camera.stop_camera_input()
+
+ def goto_background(self):
+ # This gets called when your process is sent to the background
+ pass
+
+ def return_to_foreground(self):
+ # This gets called when your process returns from the background
+ pass
+
+ def quit(self):
+ # This gets called by the quit button
+ pass
+
+ def _status_report(self):
+ print 'Reporting camera status: %s' % (str(self._status))
+ return self._status
+
+ # Block primitives used in talogo
+
+ def prim_take_picture(self):
+ if self._status:
+ ''' method called by media block '''
+ self._camera.save_camera_input_to_file()
+ self._camera.stop_camera_input()
+ self._parent.lc.filepath = self._imagepath
+
+ def prim_read_camera(self, luminance_only=False):
+ """ Read average pixel from camera and push b, g, r to the stack """
+ pixbuf = None
+ array = None
+ w, h = self._parent.lc._w(), self._parent.lc._h()
+ if w > 0 and h > 0 and self._status:
+ try:
+ self._video_capture_device = open('/dev/video0', 'rw')
+ except:
+ self._video_capture_device = None
+ _logger.debug('video capture device not available')
+
+ if self._video_capture_device is not None:
+ self._ag_control = v4l2_control(V4L2_CID_AUTOGAIN)
+ try:
+ ioctl(self._video_capture_device, VIDIOC_G_CTRL,
+ self._ag_control)
+ self._ag_control.value = 0 # disable AUTOGAIN
+ ioctl(self._video_capture_device, VIDIOC_S_CTRL,
+ self._ag_control)
+ except:
+ _logger.debug('AUTOGAIN control not available')
+ pass
+
+ if self._video_capture_device is not None:
+ self._video_capture_device.close()
+
+ self._camera.save_camera_input_to_file()
+ self._camera.stop_camera_input()
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(self._imagepath, w, h)
+ try:
+ array = pixbuf.get_pixels()
+ except:
+ array = None
+
+ if array is not None:
+ length = len(array) / 3
+ r, g, b, i = 0, 0, 0, 0
+ for j in range(length):
+ r += ord(array[i])
+ i += 1
+ g += ord(array[i])
+ i += 1
+ b += ord(array[i])
+ i += 1
+ if luminance_only:
+ lum = int((r * 0.3 + g * 0.6 + b * 0.1) / length)
+ self._parent.lc.update_label_value('luminance', lum)
+ return lum
+ else:
+ self._parent.lc.heap.append(int((b / length)))
+ self._parent.lc.heap.append(int((g / length)))
+ self._parent.lc.heap.append(int((r / length)))
+ else:
+ if luminance_only:
+ return -1
+ else:
+ self._parent.lc.heap.append(-1)
+ self._parent.lc.heap.append(-1)
+ self._parent.lc.heap.append(-1)
+
+
diff --git a/plugins/camera_plugin.pyc b/plugins/camera_plugin.pyc
new file mode 100644
index 0000000..a557a12
--- /dev/null
+++ b/plugins/camera_plugin.pyc
Binary files differ
diff --git a/plugins/plugin.py b/plugins/plugin.py
new file mode 100644
index 0000000..0fe836b
--- /dev/null
+++ b/plugins/plugin.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#Copyright (c) 2011 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
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+import gobject
+
+
+class Plugin(gobject.GObject):
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ def setup(self):
+ """ Setup is called once, when the Turtle Window is created. """
+ raise RuntimeError("You need to define setup for your plugin.")
+
+ def start(self):
+ """ start is called when run button is pressed. """
+ raise RuntimeError("You need to define start for your plugin.")
+
+ def stop(self):
+ """ stop is called when stop button is pressed. """
+ raise RuntimeError("You need to define stop for your plugin.")
+
+ def goto_background(self):
+ """ goto_background is called when the activity is sent to the
+ background. """
+ raise RuntimeError(
+ "You need to define goto_background for your plugin.")
+
+ def return_to_foreground(self):
+ """ return_to_foreground is called when the activity returns to
+ the foreground. """
+ raise RuntimeError(
+ "You need to define return_to_foreground for your plugin.")
+
+ def quit(self):
+ """ cleanup is called when the activity is exiting. """
+ raise RuntimeError("You need to define quit for your plugin.")
diff --git a/plugins/plugin.pyc b/plugins/plugin.pyc
new file mode 100644
index 0000000..cf6e6fd
--- /dev/null
+++ b/plugins/plugin.pyc
Binary files differ
diff --git a/plugins/rfid_plugin.py b/plugins/rfid_plugin.py
new file mode 100644
index 0000000..e0cfafc
--- /dev/null
+++ b/plugins/rfid_plugin.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#Copyright (C) 2010 Emiliano Pastorino <epastorino@plan.ceibal.edu.uy>
+#Copyright (c) 2011 Walter Bender
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+import os
+
+from gettext import gettext as _
+
+from rfid.rfidutils import strhex2bin, strbin2dec, find_device
+
+from plugin import Plugin
+from TurtleArt.taconstants import PALETTES, PALETTE_NAMES, BLOCK_NAMES, \
+ HELP_STRINGS, BOX_STYLE
+from TurtleArt.talogo import VALUE_BLOCKS, PLUGIN_DICTIONARY
+
+import logging
+_logger = logging.getLogger('turtleart-activity RFID plugin')
+
+HAL_SERVICE = 'org.freedesktop.Hal'
+HAL_MGR_PATH = '/org/freedesktop/Hal/Manager'
+HAL_MGR_IFACE = 'org.freedesktop.Hal.Manager'
+HAL_DEV_IFACE = 'org.freedesktop.Hal.Device'
+REGEXP_SERUSB = '\/org\/freedesktop\/Hal\/devices\/usb_device['\
+ 'a-z,A-Z,0-9,_]*serial_usb_[0-9]'
+
+
+class Rfid_plugin(Plugin):
+
+ def __init__(self, parent):
+ self._parent = parent
+ self._status = False
+
+ """
+ The following code will initialize a USB RFID reader. Please note that
+ in order to make this initialization function work, it is necessary to
+ set the permission for the ttyUSB device to 0666. You can do this by
+ adding a rule to /etc/udev/rules.d
+
+ As root (using sudo or su), copy the following text into a new file in
+ /etc/udev/rules.d/94-ttyUSB-rules
+
+ KERNEL=="ttyUSB[0-9]",MODE="0666"
+
+ You only have to do this once.
+ """
+
+ self.rfid_connected = False
+ self.rfid_device = find_device()
+ self.rfid_idn = ''
+
+ if self.rfid_device is not None:
+ _logger.info("RFID device found")
+ self.rfid_connected = self.rfid_device.do_connect()
+ if self.rfid_connected:
+ self.rfid_device.connect("tag-read", self._tag_read_cb)
+ self.rfid_device.connect("disconnected", self._disconnected_cb)
+
+ loop = DBusGMainLoop()
+ bus = dbus.SystemBus(mainloop=loop)
+ hmgr_iface = dbus.Interface(bus.get_object(HAL_SERVICE,
+ HAL_MGR_PATH), HAL_MGR_IFACE)
+
+ hmgr_iface.connect_to_signal('DeviceAdded', self._device_added_cb)
+
+ self._status = True
+
+ def setup(self):
+ # set up camera-specific blocks
+ if self._status:
+ PALETTES[PALETTE_NAMES.index('sensor')].append('rfid')
+ BOX_STYLE.append('rfid')
+ BLOCK_NAMES['rfid'] = [_('RFID')]
+ HELP_STRINGS['rfid'] = _("read value from RFID device")
+ PRIMITIVES['rfid'] = 'rfid'
+ VALUE_BLOCKS.append('rfid')
+ PLUGIN_DICTIONARY['rfid'] = self.prim_read_rfid
+ self._parent.lc._def_prim('rfid', 0,
+ lambda self: PLUGIN_DICTIONARY['rfid']())
+
+
+ def start(self):
+ # This gets called by the start button
+ if self._status:
+ pass
+
+ def stop(self):
+ # This gets called by the stop button
+ if self._status:
+ pass
+
+ def goto_background(self):
+ # This gets called when your process is sent to the background
+ pass
+
+ def return_to_foreground(self):
+ # This gets called when your process returns from the background
+ pass
+
+ def quit(self):
+ # This gets called by the quit button
+ pass
+
+ def _status_report(self):
+ print 'Reporting RFID status: %s' % (str(self._status))
+ return self._status
+
+ def _device_added_cb(self, path):
+ """
+ Called from hal connection when a new device is plugged.
+ """
+ if not self.rfid_connected:
+ self.rfid_device = find_device()
+ _logger.debug("DEVICE_ADDED: %s" % self.rfid_device)
+ if self.rfid_device is not None:
+ _logger.debug("DEVICE_ADDED: RFID device is not None!")
+ self.rfid_connected = self._device.do_connect()
+ if self.rfid_connected:
+ _logger.debug("DEVICE_ADDED: Connected!")
+ self.rfid_device.connect("tag-read", self._tag_read_cb)
+ self.rfid_device.connect("disconnected", self._disconnected_cb)
+
+ def _disconnected_cb(self, device, text):
+ """
+ Called when the device is disconnected.
+ """
+ self.rfid_connected = False
+ self.rfid_device = None
+
+ def _tag_read_cb(self, device, tagid):
+ """
+ Callback for "tag-read" signal. Receives the read tag id.
+ """
+ idbin = strhex2bin(tagid)
+ self.rfid_idn = strbin2dec(idbin[26:64])
+ while self.rfid_idn.__len__() < 9:
+ self.rfid_idn = '0' + self.rfid_idn
+ print tagid, idbin, self.rfid_idn
+
+ # Block primitives used in talogo
+
+ def prim_read_rfid(self):
+ if self._status:
+ return self.rfid_idn
diff --git a/devices/__init__.py b/rfid/__init__.py
index e69de29..e69de29 100644
--- a/devices/__init__.py
+++ b/rfid/__init__.py
diff --git a/devices/device.py b/rfid/device.py
index 04a82b2..04a82b2 100644
--- a/devices/device.py
+++ b/rfid/device.py
diff --git a/devices/rfidrweusb.py b/rfid/rfidrweusb.py
index bd12631..bd12631 100644
--- a/devices/rfidrweusb.py
+++ b/rfid/rfidrweusb.py
diff --git a/TurtleArt/rfidutils.py b/rfid/rfidutils.py
index f2c74b4..be79dd5 100644
--- a/TurtleArt/rfidutils.py
+++ b/rfid/rfidutils.py
@@ -23,10 +23,10 @@ def find_device():
Return a device instance or None.
"""
device = None
- for i in os.listdir(os.path.join('.', 'devices')):
- if not os.path.isdir(os.path.join('.', 'devices', i)):
+ for i in os.listdir(os.path.join('.', 'rfid')):
+ if not os.path.isdir(os.path.join('.', 'rfid', i)):
try:
- _tempmod = __import__('devices.%s'%i.split('.')[0], globals(),
+ _tempmod = __import__('rfid.%s'%i.split('.')[0], globals(),
locals(), ['RFIDReader'], -1)
devtemp = _tempmod.RFIDReader()
if devtemp.get_present() == True:
diff --git a/devices/serial/__init__.py b/rfid/serial/__init__.py
index 681ad5c..681ad5c 100644
--- a/devices/serial/__init__.py
+++ b/rfid/serial/__init__.py
diff --git a/devices/serial/serialposix.py b/rfid/serial/serialposix.py
index 174e2f7..174e2f7 100644
--- a/devices/serial/serialposix.py
+++ b/rfid/serial/serialposix.py
diff --git a/devices/serial/serialutil.py b/rfid/serial/serialutil.py
index fd466f2..fd466f2 100644
--- a/devices/serial/serialutil.py
+++ b/rfid/serial/serialutil.py
diff --git a/devices/tis2000.py b/rfid/tis2000.py
index 91d1991..91d1991 100644
--- a/devices/tis2000.py
+++ b/rfid/tis2000.py
diff --git a/devices/utils.py b/rfid/utils.py
index 94e5540..94e5540 100644
--- a/devices/utils.py
+++ b/rfid/utils.py
diff --git a/samples/love-speaks-volumes.ta b/samples/love-speaks-volumes.ta
new file mode 100644
index 0000000..30e0cb3
--- /dev/null
+++ b/samples/love-speaks-volumes.ta
@@ -0,0 +1,109 @@
+[[0, ["start", 2.0], 760, 100, [null, 55]],
+[1, "hat1", 780, 260, [null, 106]],
+[2, "hat2", 120, 200, [null, 80]],
+[3, "setcolor", 1164, 992, [51, 4, 16]],
+[4, ["number", 0], 1242, 992, [3, null]],
+[5, "stack1", 1164, 698, [17, 62]],
+[6, "volume", 1233, 572, [23, null]],
+[7, "forever", 1100, 344, [102, 29, 103]],
+[8, "penup", 780, 378, [67, 101]],
+[9, "pendown", 780, 462, [101, 70]],
+[10, "penup", 780, 546, [70, 75]],
+[11, "pendown", 780, 714, [78, 107]],
+[12, "clean", 900, 344, [104, 13]],
+[13, ["fillscreen", 0], 900, 386, [12, 15, 14, 35]],
+[14, ["number", 80], 986, 428, [13, null]],
+[15, "white", 986, 386, [13, null]],
+[16, "stack1", 1164, 1034, [3, 42]],
+[17, "setcolor", 1164, 656, [64, 18, 5]],
+[18, "white", 1242, 656, [17, null]],
+[19, ["storein", 0], 1164, 446, [29, 20, 27, 23]],
+[20, ["string", "b"], 1233, 446, [19, null]],
+[21, ["storein", 0], 900, 554, [35, 22, 41, 38]],
+[22, ["string", "b"], 969, 554, [21, null]],
+[23, ["storein", 0], 1164, 530, [19, 24, 6, 64]],
+[24, ["string", "a"], 1233, 530, [23, null]],
+[25, "box", 1283, 908, [63, 26, null]],
+[26, ["string", "a"], 1338, 908, [25, null]],
+[27, "box", 1233, 488, [19, 28, null]],
+[28, ["string", "a"], 1288, 488, [27, null]],
+[29, ["storein", 0], 1164, 362, [7, 30, 31, 19]],
+[30, ["string", "c"], 1233, 362, [29, null]],
+[31, "box", 1233, 404, [29, 32, null]],
+[32, ["string", "b"], 1288, 404, [31, null]],
+[33, "box", 1283, 614, [64, 34, null]],
+[34, ["string", "c"], 1338, 614, [33, null]],
+[35, ["storein", 0], 900, 470, [13, 36, 37, 21]],
+[36, ["string", "a"], 969, 470, [35, null]],
+[37, ["number", 0], 969, 512, [35, null]],
+[38, ["storein", 0], 900, 638, [21, 39, 40, 105]],
+[39, ["string", "c"], 969, 638, [38, null]],
+[40, ["number", 0], 969, 680, [38, null]],
+[41, ["number", 0], 969, 596, [21, null]],
+[42, "wait", 1164, 1076, [16, 43, null]],
+[43, ["number", 0.1], 1222, 1076, [42, null]],
+[44, "setshade", 1164, 782, [62, 45, 46]],
+[45, ["number", 75], 1250, 782, [44, null]],
+[46, "setcolor", 1164, 824, [44, 47, 50]],
+[47, ["number", 0], 1242, 824, [46, null]],
+[48, "box", 1283, 740, [62, 49, null]],
+[49, ["string", "b"], 1338, 740, [48, null]],
+[50, "stack1", 1164, 866, [46, 63]],
+[51, "setshade", 1164, 950, [63, 52, 3]],
+[52, ["number", 50], 1250, 950, [51, null]],
+[53, "hat", 900, 260, [null, 54, 104]],
+[54, ["string", "setup"], 958, 268, [53, null]],
+[55, "stack", 760, 142, [0, 56, 59]],
+[56, ["string", "setup"], 818, 142, [55, null]],
+[57, "hat", 1100, 260, [null, 58, 102]],
+[58, ["string", "loop"], 1158, 268, [57, null]],
+[59, "stack", 760, 184, [55, 60, null]],
+[60, ["string", "loop"], 818, 184, [59, null]],
+[61, "box1", 953, 336, [71, null]],
+[62, "storeinbox1", 1164, 740, [5, 48, 44]],
+[63, "storeinbox1", 1164, 908, [50, 25, 51]],
+[64, "storeinbox1", 1164, 614, [23, 33, 17]],
+[65, "forward", 120, 326, [80, 82, 99]],
+[66, "box2", 178, 284, [80, null]],
+[67, "storeinbox2", 780, 336, [106, 71, 8]],
+[68, "box2", 246, 326, [82, null]],
+[69, "box2", 906, 420, [73, null]],
+[70, "stack2", 780, 504, [9, 10]],
+[71, ["division2", 0], 899, 336, [67, 61, 72]],
+[72, ["number", 2], 977, 378, [71, null]],
+[73, ["division2", 0], 852, 420, [101, 69, 74]],
+[74, ["number", 2], 930, 462, [73, null]],
+[75, ["setxy2", 0], 780, 588, [10, 76, 77, 78]],
+[76, ["number", 0], 838, 588, [75, null]],
+[77, ["number", 0], 838, 630, [75, null]],
+[78, "seth", 780, 672, [75, 79, 11]],
+[79, ["number", 0], 882, 672, [78, null]],
+[80, ["arc", 0], 120, 242, [2, 81, 66, 65]],
+[81, ["number", 225], 178, 242, [80, null]],
+[82, ["product2", 0], 192, 326, [65, 68, 85]],
+[83, "sqrt", 300, 410, [85, 84]],
+[84, ["number", 2], 354, 410, [83, null]],
+[85, ["product2", 0], 246, 368, [82, 86, 83]],
+[86, ["number", 1.7], 300, 368, [85, null]],
+[87, ["arc", 0], 120, 616, [100, 88, 89, null]],
+[88, ["number", 225], 178, 616, [87, null]],
+[89, "box2", 178, 658, [87, null]],
+[90, "right", 120, 450, [99, 91, 92]],
+[91, ["number", 90], 178, 450, [90, null]],
+[92, "forward", 120, 492, [90, 93, 100]],
+[93, ["product2", 0], 192, 492, [92, 94, 95]],
+[94, "box2", 246, 492, [93, null]],
+[95, ["product2", 0], 246, 534, [93, 96, 97]],
+[96, ["number", 1.7], 300, 534, [95, null]],
+[97, "sqrt", 300, 576, [95, 98]],
+[98, ["number", 2], 354, 576, [97, null]],
+[99, ["vspace", 20], 120, 368, [65, 90]],
+[100, ["vspace", 20], 120, 534, [92, 87]],
+[101, "forward", 780, 420, [8, 73, 9]],
+[102, "sandwichtop_no_arm_no_label", 1082, 310, [57, 7]],
+[103, ["sandwichcollapsed", 1], 1100, 344, [7, null]],
+[104, "sandwichtop_no_arm_no_label", 882, 310, [53, 12]],
+[105, ["sandwichcollapsed", 1], 900, 344, [38, null]],
+[106, "sandwichtop_no_arm_no_label", 762, 302, [1, 67]],
+[107, ["sandwichcollapsed", 1], 780, 336, [11, null]],
+[-1, ["turtle", "Yertle"], 0, 0, 0.0, 0.0, 50.0, 5]] \ No newline at end of file
diff --git a/samples/spiralaterals.ta b/samples/spiralaterals.ta
new file mode 100644
index 0000000..dcde280
--- /dev/null
+++ b/samples/spiralaterals.ta
@@ -0,0 +1,56 @@
+[[0, ["start", 2.0], 183, 0, [null, 6]],
+[1, "hat1", 490, 0, [null, 18]],
+[2, "stack1", 313, 706, [31, 32]],
+[3, "forward", 719, 42, [9, 11, 12]],
+[4, "right", 719, 126, [12, 5, null]],
+[5, ["number", 90], 793, 126, [4, null]],
+[6, "storeinbox1", 183, 42, [0, 7, 53]],
+[7, ["number", 20], 317, 42, [6, null]],
+[8, "box1", 853, 42, [11, null]],
+[9, "hat2", 719, 0, [null, 3]],
+[10, "pop", 853, 84, [11, null]],
+[11, ["product2", 0], 799, 42, [3, 8, 10]],
+[12, ["vspace", 0], 719, 84, [3, 4]],
+[13, "stack2", 490, 168, [19, 22]],
+[14, "stack2", 490, 84, [18, 19]],
+[15, "stack2", 490, 252, [22, 21]],
+[16, "stack2", 490, 336, [21, 20]],
+[17, "stack2", 490, 420, [20, null]],
+[18, "push", 490, 42, [1, 23, 14]],
+[19, "push", 490, 126, [14, 24, 13]],
+[20, "push", 490, 378, [16, 25, 17]],
+[21, "push", 490, 294, [15, 26, 16]],
+[22, "push", 490, 210, [13, 27, 15]],
+[23, ["number", 1], 565, 42, [18, null]],
+[24, ["number", 1], 565, 126, [19, null]],
+[25, ["number", 2], 565, 378, [20, null]],
+[26, ["number", 3], 565, 294, [21, null]],
+[27, ["number", 1], 565, 210, [22, null]],
+[28, "repeat", 248, 604, [30, 29, 31, null]],
+[29, ["number", 4], 299, 604, [28, null]],
+[30, ["vspace", 0], 248, 562, [43, 28]],
+[31, "startfill", 313, 664, [28, 2]],
+[32, "stopfill", 313, 748, [2, null]],
+[33, "repeat", 183, 168, [53, 34, 48, null]],
+[34, ["number", 400], 234, 168, [33, null]],
+[35, ["vspace", 0], 248, 436, [36, 52]],
+[36, ["setxy2", 20], 248, 312, [47, 37, 38, 35]],
+[37, ["random", 0], 312, 312, [36, 39, 41, null]],
+[38, ["random", 0], 312, 394, [36, 40, 42, null]],
+[39, "leftpos", 372, 312, [37, null]],
+[40, "bottompos", 372, 394, [38, null]],
+[41, "rightpos", 372, 354, [37, null]],
+[42, "toppos", 372, 436, [38, null]],
+[43, "setcolor", 248, 520, [52, 44, 30]],
+[44, ["random", 0], 333, 520, [43, 45, 46, null]],
+[45, ["number", 0], 393, 520, [44, null]],
+[46, ["number", 100], 393, 562, [44, null]],
+[47, "penup", 248, 270, [48, 36]],
+[48, "seth", 248, 228, [33, 49, 47]],
+[49, ["random", 0], 345, 228, [48, 50, 51, null]],
+[50, ["number", 0], 405, 228, [49, null]],
+[51, ["number", 90], 405, 270, [49, null]],
+[52, "pendown", 248, 478, [35, 43]],
+[53, ["fillscreen", 0], 183, 84, [6, 55, 54, 33]],
+[54, ["number", 80], 322, 126, [53, null]],
+[55, "black", 322, 84, [53, null]]]
diff --git a/turtleart.py b/turtleart.py
index b8ffa08..d9a205b 100755
--- a/turtleart.py
+++ b/turtleart.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
#Copyright (c) 2007-8, Playful Invention Company
-#Copyright (c) 2008-10, Walter Bender
+#Copyright (c) 2008-11, 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
@@ -42,17 +43,21 @@ except ImportError, e:
argv = sys.argv[:] # Workaround for import behavior of gst in tagplay
sys.argv[1:] = [] # Execution of import gst cannot see '--help' or '-h'
-from gettext import gettext as _
+import gettext
-from TurtleArt.taconstants import OVERLAY_LAYER
+gettext.bindtextdomain('org.laptop.TurtleArtActivity', 'locale')
+gettext.textdomain('org.laptop.TurtleArtActivity')
+_ = gettext.gettext
+
+from TurtleArt.taconstants import OVERLAY_LAYER, DEFAULT_TURTLE_COLORS
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
-from extra.collaborationplugin import CollaborationPlugin
+
from util.menubuilder import MenuBuilder
+
class TurtleMain():
""" Launch Turtle Art from outside of Sugar """
@@ -64,11 +69,13 @@ class TurtleMain():
_INSTALL_PATH = '/usr/share/turtleart'
_ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart'
_ICON_SUBPATH = 'images/turtle.png'
+ _GNOME_PLUGIN_SUBPATH = 'gnome_plugins'
def __init__(self):
self._init_vars()
self._parse_command_line()
self._ensure_sugar_paths()
+ self._plugins = []
if self.output_png:
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8,
@@ -85,17 +92,60 @@ class TurtleMain():
self._run_plugins()
self._start_gtk()
+ def get_config_home(self):
+ return CONFIG_HOME
+
+ def _get_gnome_plugin_home(self):
+ """ Look in current directory first, then usual places """
+ path = os.path.join(os.getcwd(), self._GNOME_PLUGIN_SUBPATH)
+ if os.path.exists(path):
+ return path
+ path = os.path.expanduser(os.path.join('~', 'Activities',
+ 'TurtleBlocks.activity',
+ self._GNOME_PLUGIN_SUBPATH))
+ if os.path.exists(path):
+ return path
+ path = os.path.expanduser(os.path.join('~', 'Activities',
+ 'TurtleArt.activity',
+ self._GNOME_PLUGIN_SUBPATH))
+ if os.path.exists(path):
+ return path
+ path = os.path.join(self._INSTALL_PATH, self._GNOME_PLUGIN_SUBPATH)
+ if os.path.exists(path):
+ return path
+ path = os.path.join(self._ALTERNATE_INSTALL_PATH,
+ self._GNOME_PLUGIN_SUBPATH)
+ if os.path.exists(path):
+ return path
+ return None
+
+ def _get_plugin_candidates(self, path):
+ """ Look for plugin files in plugin directory. """
+ plugin_files = []
+ if path is not None:
+ candidates = os.listdir(path)
+ for c in candidates:
+ if c[-10:] == '_plugin.py' and c[0] != '#' and c[0] != '.':
+ plugin_files.append(c.split('.')[0])
+ return plugin_files
+
def _init_plugins(self):
- config_file_path = os.path.join(CONFIG_HOME, 'turtleartrc.collab')
- self._collab_plugin = CollaborationPlugin(self, config_file_path)
- self._uploader = Uploader()
+ for p in self._get_plugin_candidates(self._get_gnome_plugin_home()):
+ P = p.capitalize()
+ f = "def f(self): from gnome_plugins.%s import %s; return %s(self)" \
+ % (p, P, P)
+ plugin = {}
+ try:
+ exec f in globals(), plugin
+ self._plugins.append(plugin.values()[0](self))
+ except ImportError:
+ print 'failed to import %s' % (P)
def _run_plugins(self):
- self._uploader.set_tw(self.tw)
- self._collab_plugin.set_tw(self.tw)
- self._collab_plugin.setup()
+ for p in self._plugins:
+ p.set_tw(self.tw)
- def _mkdir_p(path):
+ def _mkdir_p(self, path):
'''Create a directory in a fashion similar to `mkdir -p`'''
try:
os.makedirs(path)
@@ -136,15 +186,16 @@ class TurtleMain():
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)
+ self.tw = TurtleArtWindow(self.canvas,
+ self._ALTERNATE_INSTALL_PATH)
else:
self.tw = TurtleArtWindow(self.canvas, os.path.abspath('.'))
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.
+ """ 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.ta_file = None
@@ -182,10 +233,8 @@ class TurtleMain():
if not os.path.exists(self.ta_file):
assert False, ('%s: %s' % (self.ta_file, _('File not found')))
- """
- make sure Sugar paths are present
- """
def _ensure_sugar_paths(self):
+ """ Make sure Sugar paths are present. """
tapath = os.path.join(os.environ['HOME'], '.sugar', 'default',
'org.laptop.TurtleArtActivity')
map(self._makepath, (os.path.join(tapath, 'data/'),
@@ -212,10 +261,16 @@ class TurtleMain():
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())
+ try:
+ self.x = int(data_file.readline())
+ self.y = int(data_file.readline())
+ self.width = int(data_file.readline())
+ self.height = int(data_file.readline())
+ except ValueError:
+ self.x = 50
+ self.y = 50
+ self.width = 800
+ self.height = 550
def _setup_gtk(self):
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
@@ -223,7 +278,8 @@ class TurtleMain():
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)):
+ 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:
@@ -261,23 +317,25 @@ class TurtleMain():
MenuBuilder.make_menu_item(menu, _('New'), self._do_new_cb)
MenuBuilder.make_menu_item(menu, _('Open'), self._do_open_cb)
MenuBuilder.make_menu_item(menu, _('Save'), self._do_save_cb)
- MenuBuilder.make_menu_item(menu, _('Save As'), self._do_save_as_cb)
- MenuBuilder.make_menu_item(menu, _('Save as image'), self._do_save_picture_cb)
- MenuBuilder.make_menu_item(menu, _('Save as HTML'), self._do_save_html_cb)
- MenuBuilder.make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb)
- if self._uploader.enabled():
- MenuBuilder.make_menu_item(menu, _('Upload to Web'),
- self._uploader.do_upload_to_web)
+ MenuBuilder.make_menu_item(menu, _('Save as'), self._do_save_as_cb)
+ MenuBuilder.make_menu_item(menu, _('Save as image'),
+ self._do_save_picture_cb)
+ MenuBuilder.make_menu_item(menu, _('Save as HTML'),
+ self._do_save_html_cb)
+ MenuBuilder.make_menu_item(menu, _('Save as Logo'),
+ self._do_save_logo_cb)
MenuBuilder.make_menu_item(menu, _('Quit'), self.destroy)
activity_menu = MenuBuilder.make_sub_menu(menu, _('File'))
menu = gtk.Menu()
MenuBuilder.make_menu_item(menu, _('Cartesian coordinates'),
self._do_cartesian_cb)
- MenuBuilder.make_menu_item(menu, _('Polar coordinates'), self._do_polar_cb)
+ MenuBuilder.make_menu_item(menu, _('Polar coordinates'),
+ self._do_polar_cb)
MenuBuilder.make_menu_item(menu, _('Rescale coordinates'),
self._do_rescale_cb)
- MenuBuilder.make_menu_item(menu, _('Grow blocks'), self._do_resize_cb, 1.5)
+ MenuBuilder.make_menu_item(menu, _('Grow blocks'),
+ self._do_resize_cb, 1.5)
MenuBuilder.make_menu_item(menu, _('Shrink blocks'),
self._do_resize_cb, 0.667)
MenuBuilder.make_menu_item(menu, _('Reset block size'),
@@ -290,9 +348,12 @@ class TurtleMain():
edit_menu = MenuBuilder.make_sub_menu(menu, _('Edit'))
menu = gtk.Menu()
- MenuBuilder.make_menu_item(menu, _('Show palette'), self._do_palette_cb)
- MenuBuilder.make_menu_item(menu, _('Hide palette'), self._do_hide_palette_cb)
- MenuBuilder.make_menu_item(menu, _('Show/hide blocks'), self._do_hideshow_cb)
+ MenuBuilder.make_menu_item(menu, _('Show palette'),
+ self._do_palette_cb)
+ MenuBuilder.make_menu_item(menu, _('Hide palette'),
+ self._do_hide_palette_cb)
+ MenuBuilder.make_menu_item(menu, _('Show/hide blocks'),
+ self._do_hideshow_cb)
tool_menu = MenuBuilder.make_sub_menu(menu, _('Tools'))
menu = gtk.Menu()
@@ -303,15 +364,16 @@ class TurtleMain():
MenuBuilder.make_menu_item(menu, _('Stop'), self._do_stop_cb)
turtle_menu = MenuBuilder.make_sub_menu(menu, _('Turtle'))
- collaboration_menu = self._collab_plugin.get_menu()
-
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)
- menu_bar.append(collaboration_menu)
+
+ # Add menus for plugins
+ for p in self._plugins:
+ menu_bar.append(p.get_menu())
return menu_bar
def _quit_ta(self, widget=None, e=None):
@@ -329,8 +391,8 @@ class TurtleMain():
""" Dialog for save project """
dlg = gtk.MessageDialog(parent=None, type=gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_OK_CANCEL,
- message_format= \
- _('You have unsaved work. Would you like to save before quitting?'))
+ message_format=_(
+ 'You have unsaved work. Would you like to save before quitting?'))
dlg.set_title(_('Save project?'))
dlg.set_property('skip-taskbar-hint', False)
@@ -529,5 +591,21 @@ class TurtleMain():
""" Callback for destroy event. """
gtk.main_quit()
+ def nick_changed(self, nick):
+ """ TODO: Rename default turtle in dictionary """
+ pass
+
+ def color_changed(self, colors):
+ """ Reskin turtle with collaboration colors """
+ turtle = self.tw.turtles.get_turtle(self.tw.default_turtle_name)
+ try:
+ turtle.colors = colors.split(',')
+ except:
+ turtle.colors = DEFAULT_TURTLE_COLORS
+ turtle.custom_shapes = True # Force regeneration of shapes
+ turtle.reset_shapes()
+ turtle.show()
+
+
if __name__ == "__main__":
TurtleMain()
diff --git a/TurtleArt/RtfParser.py b/util/RtfParser.py
index 9a141a4..9a141a4 100644
--- a/TurtleArt/RtfParser.py
+++ b/util/RtfParser.py
diff --git a/util/configfile.py b/util/configfile.py
index 5df8f59..adabb34 100644
--- a/util/configfile.py
+++ b/util/configfile.py
@@ -1,10 +1,24 @@
#!/usr/bin/env python
#
# Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
import gobject
+
class ConfigFile(gobject.GObject):
"""Load/save a simple (key = value) config file"""
@@ -15,7 +29,7 @@ class ConfigFile(gobject.GObject):
()),
}
- def __init__(self, config_file_path, valid_keys = {}):
+ def __init__(self, config_file_path, valid_keys={}):
gobject.GObject.__init__(self)
self._config_file_path = config_file_path
@@ -29,24 +43,24 @@ class ConfigFile(gobject.GObject):
def is_loaded(self):
return self._is_loaded
- def get(self, key, empty_if_not_loaded = False):
- if not self._valid_keys.has_key(key):
+ def get(self, key, empty_if_not_loaded=False):
+ if not key in self._valid_keys:
raise RuntimeError("Unknown config value %s" % key)
- if self._config_hash.has_key(key):
+ if key in self._config_hash:
value = self._config_hash[key]
else:
if self._valid_keys[key]["type"] == "text":
value = ""
- elif self._valid_keys[key]["type"] == "boolean":
+ elif self._valid_keys[key]["type"] == "boolean":
value = False
- elif self._valid_keys[key]["type"] == "integer":
- value = 0
+ elif self._valid_keys[key]["type"] == "integer":
+ value = 0
return value
def set(self, key, value):
- if not self._valid_keys.has_key(key):
+ if not key in self._valid_keys:
raise RuntimeError("Unknown config value %s" % key)
self._config_hash[key] = value
@@ -58,10 +72,10 @@ class ConfigFile(gobject.GObject):
config_file.close()
for line in lines:
line = line.strip()
- k,v = line.split('=')
+ k, v = line.split('=')
k = k.strip(' ')
v = v.strip(' ')
- if not self._valid_keys.has_key(k):
+ if not k in self._valid_keys:
raise RuntimeError("Unknown config value %s" % k)
value_type = self._valid_keys[k]["type"]
if value_type == "text":
@@ -73,11 +87,11 @@ class ConfigFile(gobject.GObject):
self._config_hash[k] = value
self._is_loaded = True
self.emit('configuration-loaded')
- except Exception,e:
+ except Exception, e:
print e
return self._is_loaded
-
+
def save(self):
config_file = open(self._config_file_path, 'w')
for k in self._config_hash.keys():
@@ -94,14 +108,15 @@ class ConfigFile(gobject.GObject):
l = "%s = %s\n" % (k, v)
print l
+
def test_save_load(test_config_file):
keys = {}
- keys["nick"] = { "type" : "text" }
- keys["account_id"] = { "type" : "text" }
- keys["server"] = { "type" : "text" }
- keys["port"] = { "type" : "text" }
- keys["password"] = { "type" : "text" }
- keys["register"] = { "type" : "text" }
+ keys["nick"] = {"type": "text"}
+ keys["account_id"] = {"type": "text"}
+ keys["server"] = {"type": "text"}
+ keys["port"] = {"type": "text"}
+ keys["password"] = {"type": "text"}
+ keys["register"] = {"type": "text"}
c = ConfigFile(test_config_file)
c.set_valid_keys(keys)
@@ -113,28 +128,31 @@ def test_save_load(test_config_file):
c.set("register", True)
c.save()
-
+
c = ConfigFile(test_config_file)
c.set_valid_keys(keys)
c.load()
c.dump_keys()
+
def _configuration_saved_cb(config_file_obj):
print "_configuration_saved_cb called"
config_file_obj.dump_keys()
+
def _configuration_loaded_cb(config_file_obj):
print "_configuration_loaded_cb called"
config_file_obj.dump_keys()
+
def test_signals(test_config_file):
keys = {}
- keys["nick"] = { "type" : "text" }
- keys["account_id"] = { "type" : "text" }
- keys["server"] = { "type" : "text" }
- keys["port"] = { "type" : "text" }
- keys["password"] = { "type" : "text" }
- keys["register"] = { "type" : "text" }
+ keys["nick"] = {"type": "text"}
+ keys["account_id"] = {"type": "text"}
+ keys["server"] = {"type": "text"}
+ keys["port"] = {"type": "text"}
+ keys["password"] = {"type": "text"}
+ keys["register"] = {"type": "text"}
c = ConfigFile(test_config_file)
c.connect('configuration-saved', _configuration_saved_cb)
@@ -147,12 +165,13 @@ def test_signals(test_config_file):
c.set("register", True)
c.save()
-
+
c = ConfigFile(test_config_file)
c.connect('configuration-loaded', _configuration_loaded_cb)
c.set_valid_keys(keys)
c.load()
+
if __name__ == "__main__":
test_save_load("/tmp/configfile.0001")
test_signals("/tmp/configfile.0002")
diff --git a/util/configwizard.py b/util/configwizard.py
index 7716362..6c66bd1 100644
--- a/util/configwizard.py
+++ b/util/configwizard.py
@@ -1,25 +1,41 @@
#!/usr/bin/python
+# Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
from configfile import ConfigFile
-import gtk
+import gtk
+
class ConfigWizard():
"""Simple configuration wizard window."""
-
+
def __init__(self, config_file_path):
self._config_items = []
self._config_entries = {}
self._config_file_path = config_file_path
- self._config_file_obj = None
+ self._config_file_obj = None
"""
- [ { item_label, item_type, item_name, item_with_value } , ... ]
+ [ {item_label, item_type, item_name, item_with_value} , ... ]
"""
def set_config_items(self, items):
self._config_items = items
keys = {}
for i in self._config_items:
- keys[i["item_name"]] = { "type" : i["item_type"] }
+ keys[i["item_name"]] = {"type": i["item_type"]}
self._valid_keys = keys
def set_config_file_obj(self, obj):
@@ -28,7 +44,7 @@ class ConfigWizard():
def get_config_file_obj(self, obj):
return self._config_file_obj
- def show(self, read_from_disc = False):
+ def show(self, read_from_disc=False):
if read_from_disc:
self._config_file_obj = ConfigFile(self._config_file_path)
@@ -47,8 +63,8 @@ class ConfigWizard():
row = 1
for i in self._config_items:
hbox = self._create_param(i)
- table.attach(hbox, 0, 1, row, row+1, xpadding=5, ypadding=2)
- row = row +1
+ table.attach(hbox, 0, 1, row, row + 1, xpadding=5, ypadding=2)
+ row = row + 1
hbox = gtk.HBox()
save_button = gtk.Button('Save')
@@ -59,7 +75,7 @@ class ConfigWizard():
cancel_button.set_size_request(50, 15)
cancel_button.connect('pressed', self._close_config_cb)
hbox.add(cancel_button)
- table.attach(hbox, 0, 1, row, row+1, xpadding=5, ypadding=2)
+ table.attach(hbox, 0, 1, row, row + 1, xpadding=5, ypadding=2)
self._config_popup.show_all()
@@ -89,11 +105,12 @@ class ConfigWizard():
self._config_file_obj.save()
"""
- { item_label, item_type, item_name, item_with_value }
+ {item_label, item_type, item_name, item_with_value}
"""
def _create_param(self, opts):
param_name = opts["item_name"]
- with_value = opts["item_with_value"] if opts.has_key("item_with_value") else True
+ with_value = opts["item_with_value"] if "item_with_value" in opts \
+ else True
hbox = gtk.HBox()
if opts["item_type"] == "text":
entry = gtk.Entry()
@@ -117,14 +134,15 @@ class ConfigWizard():
def _close_config_cb(self, widget, event=None):
self._config_popup.hide()
+
def test_wizard_from_config_file_obj(test_config_file):
keys = {}
- keys["nick"] = { "type" : "text" }
- keys["account_id"] = { "type" : "text" }
- keys["server"] = { "type" : "text" }
- keys["port"] = { "type" : "text" }
- keys["password"] = { "type" : "text" }
- keys["register"] = { "type" : "text" }
+ keys["nick"] = {"type": "text"}
+ keys["account_id"] = {"type": "text"}
+ keys["server"] = {"type": "text"}
+ keys["port"] = {"type": "text"}
+ keys["password"] = {"type": "text"}
+ keys["register"] = {"type": "text"}
c = ConfigFile(test_config_file)
c.set_valid_keys(keys)
@@ -136,32 +154,36 @@ def test_wizard_from_config_file_obj(test_config_file):
c.set("register", True)
c.save()
-
+
c = ConfigFile(test_config_file)
c.set_valid_keys(keys)
c.load()
config_w = ConfigWizard(test_config_file)
config_items = [
- {"item_label" : "Nickname", "item_type" : "text", "item_name" : "nick" },
- { "item_label" : "Account ID", "item_type" : "text", "item_name" : "account_id" },
- { "item_label" : "Server", "item_type" : "text", "item_name" : "server" },
- { "item_label" : "Port", "item_type" : "text", "item_name" : "port" },
- { "item_label" : "Password", "item_type" : "text", "item_name" : "password" },
- { "item_label" : "Register", "item_type" : "text", "item_name" : "register" }
+ {"item_label": "Nickname", "item_type": "text", "item_name": "nick"},
+ {"item_label": "Account ID", "item_type": "text",
+ "item_name": "account_id"},
+ {"item_label": "Server", "item_type": "text", "item_name": "server"},
+ {"item_label": "Port", "item_type": "text", "item_name": "port"},
+ {"item_label": "Password", "item_type": "text",
+ "item_name": "password"},
+ {"item_label": "Register", "item_type": "text",
+ "item_name": "register"}
]
config_w.set_config_items(config_items)
config_w.set_config_file_obj(c)
config_w.show()
+
def test_wizard_from_config_file_path(test_config_file):
keys = {}
- keys["nick"] = { "type" : "text" }
- keys["account_id"] = { "type" : "text" }
- keys["server"] = { "type" : "text" }
- keys["port"] = { "type" : "text" }
- keys["password"] = { "type" : "text" }
- keys["register"] = { "type" : "text" }
+ keys["nick"] = {"type": "text"}
+ keys["account_id"] = {"type": "text"}
+ keys["server"] = {"type": "text"}
+ keys["port"] = {"type": "text"}
+ keys["password"] = {"type": "text"}
+ keys["register"] = {"type": "text"}
c = ConfigFile(test_config_file)
c.set_valid_keys(keys)
@@ -173,19 +195,23 @@ def test_wizard_from_config_file_path(test_config_file):
c.set("register", True)
c.save()
-
+
config_w = ConfigWizard(test_config_file)
config_items = [
- {"item_label" : "Nickname", "item_type" : "text", "item_name" : "nick" },
- { "item_label" : "Account ID", "item_type" : "text", "item_name" : "account_id" },
- { "item_label" : "Server", "item_type" : "text", "item_name" : "server" },
- { "item_label" : "Port", "item_type" : "text", "item_name" : "port" },
- { "item_label" : "Password", "item_type" : "text", "item_name" : "password" },
- { "item_label" : "Register", "item_type" : "text", "item_name" : "register" }
+ {"item_label": "Nickname", "item_type": "text", "item_name": "nick"},
+ {"item_label": "Account ID", "item_type": "text",
+ "item_name": "account_id"},
+ {"item_label": "Server", "item_type": "text", "item_name": "server"},
+ {"item_label": "Port", "item_type": "text", "item_name": "port"},
+ {"item_label": "Password", "item_type": "text",
+ "item_name": "password"},
+ {"item_label": "Register", "item_type": "text",
+ "item_name": "register"}
]
config_w.set_config_items(config_items)
config_w.show(True)
+
if __name__ == "__main__":
#test_wizard_from_config_file_obj("/tmp/configwizard.test.0001")
test_wizard_from_config_file_path("/tmp/configwizard.test.0002")
diff --git a/util/menubuilder.py b/util/menubuilder.py
index 302d510..4ee4550 100644
--- a/util/menubuilder.py
+++ b/util/menubuilder.py
@@ -1,7 +1,23 @@
#!/usr/bin/python
+# Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
import gtk
+
class MenuBuilder():
@classmethod
def make_sub_menu(cls, menu, name):