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>2012-02-18 22:41:14 (GMT)
committer Walter Bender <walter.bender@gmail.com>2012-02-18 22:41:14 (GMT)
commitd67a8ae233995c513b1971dbe16ab249f3a2d7b1 (patch)
tree8378c8a9d580c759645cebe648a5e6ecb30b68a9
parent7a9d0865db3aebb04211c0f269c45ffd971d9fd4 (diff)
sync up with Turtle Blocks v136
-rw-r--r--TurtleArt/sprites.py29
-rw-r--r--TurtleArt/tabasics.py64
-rw-r--r--TurtleArt/tablock.py47
-rw-r--r--TurtleArt/tacanvas.py13
-rw-r--r--TurtleArt/taconstants.py40
-rw-r--r--TurtleArt/taexporthtml.py24
-rw-r--r--TurtleArt/taexportlogo.py2
-rw-r--r--TurtleArt/talogo.py259
-rw-r--r--TurtleArt/tapalette.py23
-rwxr-xr-xTurtleArt/tasprite_factory.py58
-rw-r--r--TurtleArt/taturtle.py39
-rw-r--r--TurtleArt/tautils.py468
-rw-r--r--TurtleArt/tawindow.py342
13 files changed, 948 insertions, 460 deletions
diff --git a/TurtleArt/sprites.py b/TurtleArt/sprites.py
index baae94f..fdf0105 100644
--- a/TurtleArt/sprites.py
+++ b/TurtleArt/sprites.py
@@ -120,6 +120,11 @@ class Sprites:
else:
self.list.insert(i, spr)
+ def find_in_list(self, spr):
+ if spr in self.list:
+ return True
+ return False
+
def remove_from_list(self, spr):
''' Remove a sprite from the list. '''
if spr in self.list:
@@ -187,7 +192,8 @@ class Sprite:
self._dy.append(0)
self._dx[i] = dx
self._dy[i] = dy
- if isinstance(image, gtk.gdk.Pixbuf):
+ if isinstance(image, gtk.gdk.Pixbuf) or \
+ isinstance(image, cairo.ImageSurface):
w = image.get_width()
h = image.get_height()
else:
@@ -200,14 +206,17 @@ class Sprite:
self.rect.width = w + dx
if h + dy > self.rect.height:
self.rect.height = h + dy
- surface = cairo.ImageSurface(
- cairo.FORMAT_ARGB32, self.rect.width, self.rect.height)
- context = cairo.Context(surface)
- context = gtk.gdk.CairoContext(context)
- context.set_source_pixbuf(image, 0, 0)
- context.rectangle(0, 0, self.rect.width, self.rect.height)
- context.fill()
- self.cached_surfaces[i] = surface
+ if isinstance(image, cairo.ImageSurface):
+ self.cached_surfaces[i] = image
+ else: # Convert to Cairo surface
+ surface = cairo.ImageSurface(
+ cairo.FORMAT_ARGB32, self.rect.width, self.rect.height)
+ context = cairo.Context(surface)
+ context = gtk.gdk.CairoContext(context)
+ context.set_source_pixbuf(image, 0, 0)
+ context.rectangle(0, 0, self.rect.width, self.rect.height)
+ context.fill()
+ self.cached_surfaces[i] = surface
def move(self, pos):
''' Move to new (x, y) position '''
@@ -352,7 +361,7 @@ class Sprite:
return False
if y > self.rect.y + self.rect.height:
return False
- return True
+ return self._sprites.find_in_list(self)
def draw_label(self, cr):
''' Draw the label based on its attributes '''
diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py
index d99c51a..60b8d91 100644
--- a/TurtleArt/tabasics.py
+++ b/TurtleArt/tabasics.py
@@ -67,7 +67,7 @@ from gettext import gettext as _
from tapalette import make_palette, define_logo_function
from talogo import primitive_dictionary, logoerror
from tautils import convert, chr_to_ord, round_int, strtype
-from taconstants import BLACK, WHITE, CONSTANTS
+from taconstants import BLACK, WHITE, CONSTANTS, XO30
def _num_type(x):
""" Is x a number type? """
@@ -356,9 +356,14 @@ start fill block)'))
def _color_palette(self):
""" The basic Turtle Art color palette """
- palette = make_palette('colors',
- colors=["#00FFFF", "#00A0A0"],
- help_string=_('Palette of pen colors'))
+ if self.tw.hw == XO30:
+ palette = make_palette('pen',
+ colors=["#00FFFF", "#00A0A0"],
+ help_string=_('Palette of pen colors'))
+ else:
+ palette = make_palette('colors',
+ colors=["#00FFFF", "#00A0A0"],
+ help_string=_('Palette of pen colors'))
palette.add_block('setcolor',
style='basic-style-1arg',
@@ -661,7 +666,11 @@ operators'))
logo_command='greater?',
help_string=_('logical greater-than operator'))
self.tw.lc.def_prim(
- 'greater?', 2, lambda self, x, y: primitive_dictionary['more'](x, y))
+ 'greater?', 2,
+ lambda self, x, y: primitive_dictionary['more'](x, y))
+
+ if self.tw.canvas.width > 1024:
+ self._make_constant(palette, 'true', _('True'), 1)
primitive_dictionary['less'] = self._prim_less
palette.add_block('less2',
@@ -675,6 +684,9 @@ operators'))
self.tw.lc.def_prim(
'less?', 2, lambda self, x, y: primitive_dictionary['less'](x, y))
+ if self.tw.canvas.width > 1024:
+ self._make_constant(palette, 'false', _('False'), 0)
+
primitive_dictionary['equal'] = self._prim_equal
palette.add_block('equal2',
hidden=True,
@@ -765,7 +777,7 @@ number of seconds'))
palette.add_block('if',
hidden=True,
style='flow-style-boolean',
- label=[' ', _('if'), _('then')],
+ label=[_('if'), ' ', _('then')],
prim_name='if',
default=[None, None, 'vspace'],
special_name=_('if then'),
@@ -778,7 +790,7 @@ operators from Numbers palette'))
palette.add_block('ifelse',
hidden=True,
style='flow-style-else',
- label=[' ', _('if'), _('then else')],
+ label=[_('if'), ' ', _('then else')],
prim_name='ifelse',
default=[None, 'vspace', None, 'vspace'],
logo_command='ifelse',
@@ -788,7 +800,6 @@ boolean operators from Numbers palette'))
self.tw.lc.def_prim('ifelse', 3, primitive_dictionary['ifelse'], True)
palette.add_block('hspace',
- hidden=True,
style='flow-style-tail',
label=' ',
prim_name='nop',
@@ -857,6 +868,7 @@ buttons'))
'box2', None, x))
palette.add_block('string',
+ hidden=True,
style='box-style',
label=_('text'),
default=_('text'),
@@ -883,6 +895,7 @@ buttons'))
primitive_dictionary['box'] = self._prim_box
palette.add_block('box',
+ hidden=True,
style='number-style-1strarg',
label=_('box'),
prim_name='box',
@@ -1142,6 +1155,14 @@ variable'))
def _prim_careful_divide(self, x, y):
""" Raise error on divide by zero """
+ if type(x) == list and _num_type(y):
+ z = []
+ for i in range(len(x)):
+ try:
+ z.append(x[i] / y)
+ except ZeroDivisionError:
+ raise logoerror("#zerodivide")
+ return z
try:
return x / y
except ZeroDivisionError:
@@ -1158,6 +1179,11 @@ variable'))
def _prim_equal(self, x, y):
""" Numeric and logical equal """
+ if type(x) == list and type(y) == list:
+ for i in range(len(x)):
+ if x[i] != y[i]:
+ return False
+ return True
try:
return float(x) == float(y)
except ValueError:
@@ -1198,6 +1224,11 @@ variable'))
""" Add numbers, concat strings """
if _num_type(x) and _num_type(y):
return(x + y)
+ elif type(x) == list and type(y) == list:
+ z = []
+ for i in range(len(x)):
+ z.append(x[i] + y[i])
+ return(z)
else:
if _num_type(x):
xx = str(round_int(x))
@@ -1213,6 +1244,11 @@ variable'))
""" Numerical subtraction """
if _num_type(x) and _num_type(y):
return(x - y)
+ elif type(x) == list and type(y) == list:
+ z = []
+ for i in range(len(x)):
+ z.append(x[i] - y[i])
+ return(z)
try:
return self._string_to_num(x) - self._string_to_num(y)
except TypeError:
@@ -1222,6 +1258,16 @@ variable'))
""" Numerical multiplication """
if _num_type(x) and _num_type(y):
return(x * y)
+ elif type(x) == list and _num_type(y):
+ z = []
+ for i in range(len(x)):
+ z.append(x[i] * y)
+ return(z)
+ elif type(y) == list and _num_type(x):
+ z = []
+ for i in range(len(y)):
+ z.append(y[i] * x)
+ return(z)
try:
return self._string_to_num(x) * self._string_to_num(y)
except TypeError:
@@ -1282,6 +1328,8 @@ variable'))
return(x)
if type(x) is ord:
return(int(x))
+ if type(x) is list:
+ raise logoerror("#syntaxerror")
xx = convert(x.replace(self.tw.decimal_point, '.'), float)
if type(xx) is float:
return xx
diff --git a/TurtleArt/tablock.py b/TurtleArt/tablock.py
index 03ddc05..b809930 100644
--- a/TurtleArt/tablock.py
+++ b/TurtleArt/tablock.py
@@ -128,6 +128,7 @@ class Block:
self.shapes = [None, None]
self.name = name
self.colors = colors
+ self._custom_colors = False
self.scale = scale
self.docks = None
self.connections = None
@@ -141,6 +142,7 @@ class Block:
self._ei = 0
self._font_size = [6.0, 4.5]
self._image = None
+ self._visible = True
self.block_methods = {
'basic-style': self._make_basic_style,
@@ -172,7 +174,6 @@ class Block:
'flow-style-tail': self._make_flow_style_tail,
'flow-style-1arg': self._make_flow_style_1arg,
'flow-style-boolean': self._make_flow_style_boolean,
- 'flow-style-while': self._make_flow_style_while,
'flow-style-else': self._make_flow_style_else,
'collapsible-top': [self._make_collapsible_style_top, True, True],
'collapsible-top-no-arm': [self._make_collapsible_style_top,
@@ -211,6 +212,18 @@ class Block:
self.block_list.append_to_list(self)
+ def get_visibility(self):
+ ''' Should block be visible on the palette? '''
+ return self._visible
+
+ def set_visibility(self, state):
+ ''' Should block be visible? '''
+ self._visible = state
+ if self._visible:
+ self.spr.restore()
+ else:
+ self.spr.hide()
+
def expandable(self):
""" Can this block be expanded? """
if self.name in EXPANDABLE:
@@ -282,6 +295,11 @@ class Block:
self.refresh()
self.spr.inval()
+ def set_colors(self, colors):
+ self.colors = colors[:]
+ self._custom_colors = True
+ self.refresh()
+
def refresh(self):
if self.spr is None:
return
@@ -445,6 +463,15 @@ class Block:
elif self.name in block_styles['number-style-porch']:
self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
True, 'right', 'bottom', i)
+
+ elif self.name in block_styles['flow-style-boolean'] or \
+ self.name in block_styles['flow-style-else']:
+ self.spr.set_label_attributes(int(self._font_size[0] + 0.5),
+ True, 'left', 'middle', 0)
+ self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
+ True, 'right', 'top', 1)
+ self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
+ True, 'right', 'bottom', 2)
elif i == 1: # top
self.spr.set_label_attributes(int(self._font_size[1] + 0.5),
True, 'right', 'top', i)
@@ -479,6 +506,9 @@ class Block:
self.block_methods['basic-style'](svg)
def _set_colors(self, svg):
+ if self._custom_colors:
+ self.svg.set_colors(self.colors)
+ return
if self.name in BOX_COLORS:
self.colors = BOX_COLORS[self.name]
elif self.name in special_block_colors:
@@ -800,21 +830,6 @@ class Block:
['flow', False, self.svg.docks[3][0],
self.svg.docks[3][1], ']']]
- def _make_flow_style_while(self, svg):
- self.svg.expand(self.dx + self.ex, self.ey)
- self.svg.set_slot(True)
- self.svg.set_tab(True)
- self.svg.set_boolean(True)
- self._make_block_graphics(svg, self.svg.basic_flow)
- self.docks = [['flow', True, self.svg.docks[0][0],
- self.svg.docks[0][1]],
- ['bool', False, self.svg.docks[1][0],
- self.svg.docks[1][1], '['],
- ['flow', False, self.svg.docks[2][0],
- self.svg.docks[2][1], ']['],
- ['flow', False, self.svg.docks[3][0],
- self.svg.docks[3][1], ']']]
-
def _make_flow_style_else(self, svg):
self.svg.expand(self.dx + self.ex, self.ey)
self.svg.set_slot(True)
diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py
index 43e5ad2..bf22248 100644
--- a/TurtleArt/tacanvas.py
+++ b/TurtleArt/tacanvas.py
@@ -22,6 +22,7 @@
import gtk
from math import sin, cos, atan, pi, sqrt
+import os
import pango
import cairo
import pangocairo
@@ -552,23 +553,24 @@ class TurtleGraphics:
if self.tw.saving_svg:
if self.tw.running_sugar:
# In Sugar, we embed the images inside the SVG
- tmp_file = os.path.join(get_path(tw.activity, 'instance'),
+ tmp_file = os.path.join(get_path(self.tw.activity, 'instance'),
'tmpfile.png')
pixbuf.save(tmp_file, 'png', {'quality': '100'})
self.tw.svg_string += self.svg.image(
- x - self.width / 2, y, w, h, path,
+ x, y, w, h, path,
image_to_base64(tmp_file,
get_path(self.tw.activity, 'instance')))
+ os.remove(tmp_file)
else:
# In GNOME, we embed a path
- self.tw.svg_string += self.svg.image(x - self.width / 2,
- y, w, h, path)
+ self.tw.svg_string += self.svg.image(
+ x - self.width / 2, y, w, h, path)
if self.tw.sharing() and share:
if self.tw.running_sugar:
tmp_path = get_path(self.tw.activity, 'instance')
else:
tmp_path = '/tmp'
- tmp_file = os.path.join(get_path(tw.activity, 'instance'),
+ tmp_file = os.path.join(get_path(self.tw.activity, 'instance'),
'tmpfile.png')
pixbuf.save(tmp_file, 'png', {'quality': '100'})
data = image_to_base64(tmp_file, tmp_path)
@@ -583,6 +585,7 @@ class TurtleGraphics:
round_int(height),
data]]))
self.tw.send_event(event)
+ os.remove(tmp_file)
def draw_text(self, label, x, y, size, w, share=True):
''' Draw text '''
diff --git a/TurtleArt/taconstants.py b/TurtleArt/taconstants.py
index 4dd5f50..74291fc 100644
--- a/TurtleArt/taconstants.py
+++ b/TurtleArt/taconstants.py
@@ -25,11 +25,12 @@ from gettext import gettext as _
# Sprite layers
#
-OVERLAY_LAYER = 525
-TURTLE_LAYER = 550
+OVERLAY_LAYER = 400
+TURTLE_LAYER = 500
BLOCK_LAYER = 600
CATEGORY_LAYER = 700
-TAB_LAYER = 710
+TAB_LAYER = 800
+PROTO_LAYER = 801
STATUS_LAYER = 900
TOP_LAYER = 1000
@@ -57,6 +58,8 @@ BLOCK_SCALE = [0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0]
PALETTE_SCALE = 1.5
DEFAULT_TURTLE = 'Yertle'
DEFAULT_TURTLE_COLORS = ['#008000', '#00A000']
+PALETTE_COLOR = "#FFD000"
+TOOLBAR_COLOR = "#282828"
HORIZONTAL_PALETTE = 0
VERTICAL_PALETTE = 1
BLACK = -9999
@@ -71,6 +74,7 @@ DEFAULT_SCALE = 33
XO1 = 'xo1'
XO15 = 'xo1.5'
XO175 = 'xo1.75'
+XO30 = 'xo3.0'
UNKNOWN = 'unknown'
CONSTANTS = {'leftpos': None, 'toppos': None, 'rightpos': None,
@@ -225,30 +229,14 @@ VOICES = {'af': 'afrikaans', 'cy': 'welsh-test', 'el': 'greek',
# Macros (groups of blocks)
#
MACROS = {
- 'until':
- [[0, 'forever', 0, 0, [None, 2, 1]],
- [1, 'vspace', 0, 0, [0, None]],
- [2, 'ifelse', 0, 0, [0, None, 3, None, None]],
- [3, 'vspace', 0, 0, [2, 4]],
- [4, 'stopstack', 0, 0, [3, None]]],
- 'while':
- [[0, 'forever', 0, 0, [None, 2, 1]],
- [1, 'vspace', 0, 0, [0, None]],
- [2, 'ifelse', 0, 0, [0, None, 3, 4, None]],
- [3, 'vspace', 0, 0, [2, None]],
- [4, 'stopstack', 0, 0, [2, None]]],
'kbinput':
- [[0, 'forever', 0, 0, [None, 1, None]],
- [1, 'kbinput', 0, 0, [0, 2]],
- [2, 'vspace', 0, 0, [1, 3]],
- [3, 'if', 0, 0, [2, 4, 7, 8]],
- [4, 'greater2', 0, 0, [3, 5, 6, None]],
- [5, 'keyboard', 0, 0, [4, None]],
- [6, ['number', '0'], 0, 0, [4, None]],
- [7, 'stopstack', 0, 0, [3, None]],
- [8, 'vspace', 0, 0, [3, 9]],
- [9, 'wait', 0, 0, [8, 10, None]],
- [10, ['number', '1'], 0, 0, [9, None]]],
+ [[0, 'until', 0, 0, [None, 1, 4, None]],
+ [1, 'greater2', 0, 0, [0, 2, 3, None]],
+ [2, 'keyboard', 0, 0, [1, None]],
+ [3, ['number', '0'], 0, 0, [1, None]],
+ [4, 'wait', 0, 0, [0, 5, 6]],
+ [5, ['number', '0.1'], 0, 0, [4, None]],
+ [6, 'kbinput', 0, 0, [4, None]]],
'picturelist':
[[0, 'sandwichtop_no_label', 0, 0, [None, 1]],
[1, 'penup', 0, 0, [0, 2]],
diff --git a/TurtleArt/taexporthtml.py b/TurtleArt/taexporthtml.py
index 90dcc4d..0e6a2f1 100644
--- a/TurtleArt/taexporthtml.py
+++ b/TurtleArt/taexporthtml.py
@@ -69,8 +69,8 @@ def save_html(self, tw, embed_flag=True):
HTML_GLUE['img'] = ('<img width="400" height="300" alt=' + \
'"Image" src="data:image/png;base64,\n',
'"/>\n')
- HTML_GLUE['img2'] = ('<img alt="Image" src="data:image/png;"' + \
- '"base64,\n"', '"/>\n')
+ HTML_GLUE['img2'] = ('<img alt="Image" src="data:image/png;' + \
+ 'base64,\n', '"/>\n')
"""
If there are saved_pictures, put them into a .html; otherwise,
@@ -78,25 +78,33 @@ def save_html(self, tw, embed_flag=True):
"""
htmlcode = ''
if len(tw.saved_pictures) > 0:
- for i, image_file in enumerate(tw.saved_pictures):
+ # saved_picture list is a collection of tuples of either the
+ # image_file or the containing dsobject and an SVG flag
+ for i, (image, svg_flag) in enumerate(tw.saved_pictures):
htmlcode += HTML_GLUE['slide'][0] + str(i)
htmlcode += HTML_GLUE['slide'][1] + \
HTML_GLUE['div'][0] + \
HTML_GLUE['h1'][0]
+ if tw.running_sugar:
+ from sugar.datastore import datastore
+ dobject = datastore.get(image) # dsobject.object_id
+ image_file = dobject.file_path
+ else:
+ image_file = image
if embed_flag:
f = open(image_file, 'r')
imgdata = f.read()
f.close()
- if image_file.endswith(('.svg')):
+ if svg_flag:
tmp = imgdata
else:
- imgdata = image_to_base64(image_file,
- get_path(tw.activity, 'instance'))
+ imgdata = image_to_base64(
+ image_file, get_path(tw.activity, 'instance'))
tmp = HTML_GLUE['img2'][0]
tmp += imgdata
tmp += HTML_GLUE['img2'][1]
else:
- if image_file.endswith(('.svg')):
+ if svg_flag:
f = open(image_file, 'r')
imgdata = f.read()
f.close()
@@ -135,7 +143,7 @@ def save_html(self, tw, embed_flag=True):
style = HTML_GLUE['style'][0] + \
HTML_GLUE['style'][1]
if len(tw.saved_pictures) > 0:
- if tw.saved_pictures[0].endswith(('.svg')):
+ if tw.saved_pictures[0][1]:
header = HTML_GLUE['html_svg'][0]
style = COMMENT
diff --git a/TurtleArt/taexportlogo.py b/TurtleArt/taexportlogo.py
index 127689b..0d0660e 100644
--- a/TurtleArt/taexportlogo.py
+++ b/TurtleArt/taexportlogo.py
@@ -266,7 +266,7 @@ def _walk_stack(tw, blk_in_stack):
top = find_top_block(blk_in_stack)
if blk_in_stack == top:
- psuedocode = tw.lc.run_blocks(top, tw.block_list.list, False)
+ psuedocode = tw.lc.run_blocks(top, tw.just_blocks(), False)
return psuedocode
else:
return []
diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py
index 0e28cf0..a2901eb 100644
--- a/TurtleArt/talogo.py
+++ b/TurtleArt/talogo.py
@@ -67,6 +67,19 @@ class logoerror(Exception):
def __str__(self):
return repr(self.value)
+class HiddenBlock:
+
+ def __init__(self, name, value=None):
+ self.name = name
+ self.values = []
+ if value is not None:
+ self.values.append(value)
+ self.primitive = None
+ else:
+ self.primitive = name
+ self.connections = []
+ self.docks = []
+
# Utility functions
def _just_stop():
@@ -122,6 +135,7 @@ class LogoCode:
self.update_values = False
self.gplay = None
self.filepath = None
+ self.pixbuf = None
self.dsobject = None
self.start_time = None
@@ -162,6 +176,11 @@ class LogoCode:
self.stacks['stack2'] = None
self.tw.saving_svg = False
+ # Save state in case there is a hidden macro expansion
+ self.save_blocks = None
+ self.save_blk = blk
+ self.save_while_blks = []
+
if self.trace > 0:
self.update_values = True
else:
@@ -169,13 +188,23 @@ class LogoCode:
for b in blocks:
b.unhighlight()
+
+ for b in blocks:
+ # Hidden macro expansions
+ if b.name in ['while', 'until', 'forever']:
+ action_blk, new_blocks = self._expand_forever(b, blk, blocks)
+ blocks = new_blocks[:]
+ if b == blk:
+ blk = action_blk
+
+ for b in blocks:
if b.name == 'hat1':
code = self._blocks_to_code(b)
self.stacks['stack1'] = self._readline(code)
- if b.name == 'hat2':
+ elif b.name == 'hat2':
code = self._blocks_to_code(b)
self.stacks['stack2'] = self._readline(code)
- if b.name == 'hat':
+ elif b.name == 'hat':
if b.connections is not None and len(b.connections) > 1 and \
b.connections[1] is not None:
code = self._blocks_to_code(b)
@@ -190,6 +219,19 @@ class LogoCode:
self.stacks['stack3' + str(x)] = self._readline(code)
code = self._blocks_to_code(blk)
+
+ if self.save_blocks is not None:
+ # Undo any hidden macro expansion
+ blocks = self.save_blocks[:]
+ blk = self.save_blk
+ for b in self.save_while_blks:
+ if b[1] is not None:
+ b[0].connections[0].connections[b[1]] = b[0]
+ if b[2] is not None:
+ b[0].connections[-1].connections[b[2]] = b[0]
+ if b[3] is not None:
+ b[0].connections[-2].connections[b[3]] = b[0]
+
if run_flag:
# debug_output("running code: %s" % (code), self.tw.running_sugar)
self.start_time = time()
@@ -210,7 +252,12 @@ class LogoCode:
if blk.name == 'savesvg':
self.tw.saving_svg = True
if blk.primitive is not None: # make a tuple (prim, blk)
- code.append((blk.primitive, self.tw.block_list.list.index(blk)))
+ # special case: expand 'while' and 'until' primitives
+ try:
+ code.append((blk.primitive,
+ self.tw.block_list.list.index(blk)))
+ except ValueError:
+ code.append(blk.primitive) # Hidden block
elif len(blk.values) > 0: # Extract the value from content blocks.
if blk.name == 'number':
try:
@@ -264,6 +311,7 @@ class LogoCode:
The block associated with the command is stored as the second element
in a tuple, e.g., (#forward, 16)
"""
+ # debug_output(line, self.tw.running_sugar)
res = []
while line:
token = line.pop(0)
@@ -430,7 +478,12 @@ class LogoCode:
while (_millisecond() - starttime) < 120:
try:
if self.step is not None:
- self.step.next()
+ try:
+ self.step.next()
+ except ValueError:
+ debug_output('generator already executing',
+ self.tw.running_sugar)
+ return False
else:
return False
except StopIteration:
@@ -494,11 +547,11 @@ class LogoCode:
from tagplay import stop_media
# stop_media(self) # TODO: gplay variable
self.tw.canvas.clearscreen()
- self.tw.lc.scale = DEFAULT_SCALE
- self.tw.lc.hidden_turtle = None
- self.tw.lc.start_time = time()
+ self.scale = DEFAULT_SCALE
+ self.hidden_turtle = None
+ self.start_time = time()
for name in value_blocks:
- self.tw.lc.update_label_value(name)
+ self.update_label_value(name)
def int(self, n):
""" Raise an error if n doesn't convert to int. """
@@ -562,50 +615,55 @@ class LogoCode:
return int((self.tw.canvas.height * self.scale) / 100.)
def insert_image(self, center=False, filepath=None, resize=True,
- offset=False):
+ offset=False, pixbuf=False):
""" Image only (at current x, y) """
if filepath is not None:
self.filepath = filepath
- pixbuf = None
+ if not pixbuf:
+ self.pixbuf = None
w, h = self.wpercent(), self.hpercent()
if w < 1 or h < 1:
return
- if self.dsobject is not None:
+ if pixbuf: # We may have to rescale the picture
+ if w != self.pixbuf.get_width() or h != self.pixbuf.get_height():
+ self.pixbuf = self.pixbuf.scale_simple(
+ w, h, gtk.gdk.INTERP_BILINEAR)
+ elif self.dsobject is not None:
try:
- pixbuf = get_pixbuf_from_journal(self.dsobject, w, h)
+ self.pixbuf = get_pixbuf_from_journal(self.dsobject, w, h)
except:
debug_output("Couldn't open dsobject %s" % (self.dsobject),
self.tw.running_sugar)
- if pixbuf is None and \
+ if self.pixbuf is None and \
self.filepath is not None and \
self.filepath != '':
try:
if not resize:
- pixbuf = gtk.gdk.pixbuf_new_from_file(self.filepath)
- w = pixbuf.get_width()
- h = pixbuf.get_height()
+ self.pixbuf = gtk.gdk.pixbuf_new_from_file(self.filepath)
+ w = self.pixbuf.get_width()
+ h = self.pixbuf.get_height()
else:
- pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
+ self.pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
self.filepath, w, h)
except:
self.tw.showlabel('nojournal', self.filepath)
debug_output("Couldn't open filepath %s" % (self.filepath),
self.tw.running_sugar)
- if pixbuf is not None:
+ if self.pixbuf is not None:
x = self.tw.canvas.xcor
y = self.tw.canvas.ycor
w *= self.tw.coord_scale
h *= self.tw.coord_scale
if center:
- self.tw.canvas.draw_pixbuf(pixbuf, 0, 0,
+ self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0,
self.x2tx() - int(w / 2),
self.y2ty() - int(h / 2), w, h,
self.filepath)
elif offset:
- self.tw.canvas.draw_pixbuf(pixbuf, 0, 0, self.x2tx(),
+ self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0, self.x2tx(),
self.y2ty() - h, w, h, self.filepath)
else:
- self.tw.canvas.draw_pixbuf(pixbuf, 0, 0,
+ self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0,
self.x2tx(),
self.y2ty(),
w, h, self.filepath)
@@ -665,3 +723,162 @@ class LogoCode:
from tagplay import play_movie_from_file
play_movie_from_file(self, self.filepath, self.x2tx(), self.y2ty(),
w, h)
+
+ def _expand_forever(self, b, blk, blocks):
+ """ Expand a while or until block into: forever, ifelse, stopstack
+ Expand a forever block to run in a separate stack """
+
+ # TODO: create a less brittle way of doing this; having to
+ # manage the connections and flows locally means we may run
+ # into trouble if any of these block types (forever, while,
+ # until. ifelse, stopstack, or stack) is changed in tablock.py
+ if b.name == 'while':
+ while_blk = True
+ else:
+ while_blk = False
+ if b.name == 'until':
+ until_blk = True
+ else:
+ until_blk = False
+
+ # We'll restore the original blocks when we are finished
+ if self.save_blocks is None:
+ self.save_blocks = blocks[:]
+
+ # Create an action block that will jump to the new stack
+ action_name = '#s_forever %d' % (len(self.save_while_blks) + 1)
+ action_blk = HiddenBlock('stack')
+ action_label_blk = HiddenBlock('string', value=action_name)
+
+ # Create the blocks we'll put in the new stack
+ forever_blk = HiddenBlock('forever')
+ if while_blk or until_blk:
+ ifelse_blk = HiddenBlock('ifelse')
+ stopstack_blk = HiddenBlock('stopstack')
+ inflow = None
+ whileflow = None
+ outflow = None
+ boolflow = None
+ if b.connections is not None:
+ inflow = b.connections[0]
+ if while_blk or until_blk:
+ boolflow = b.connections[1]
+ whileflow = b.connections[-2]
+ outflow = b.connections[-1]
+
+ # Create action block(s) to run the code inside the forever loop
+ if until_blk and whileflow is not None: # run until flow at least once
+ action_flow_name = '#s_flow %d' % (len(self.save_while_blks) + 1)
+ action_first = HiddenBlock('stack')
+ first_label_blk = HiddenBlock('string', value=action_flow_name)
+
+ # Assign new connections and build the docks
+ if inflow is not None:
+ i = inflow.connections.index(b)
+ if until_blk and whileflow is not None:
+ inflow.connections[i] = action_first
+ else:
+ inflow.connections[i] = action_blk
+ else:
+ i = None
+ if outflow is not None:
+ j = outflow.connections.index(b)
+ outflow.connections[j] = action_blk
+ else:
+ j = None
+
+ if until_blk and whileflow is not None:
+ action_first.connections.append(inflow)
+ action_first.docks.append(['flow', True, 0, 0])
+ action_first.connections.append(first_label_blk)
+ action_first.docks.append(['number', False, 0, 0])
+ action_first.connections.append(action_blk)
+ action_first.docks.append(['flow', False, 0, 0])
+ first_label_blk.connections.append(action_first)
+ first_label_blk.docks.append(['number', True, 0, 0])
+ action_blk.connections.append(action_first)
+ else:
+ action_blk.connections.append(inflow)
+ action_blk.docks.append(['flow', True, 0, 0])
+ action_blk.connections.append(action_label_blk)
+ action_blk.docks.append(['number', False, 0, 0])
+ action_blk.connections.append(outflow)
+ action_blk.docks.append(['flow', False, 0, 0])
+ action_label_blk.connections.append(action_blk)
+ action_label_blk.docks.append(['number', True, 0, 0])
+
+ forever_blk.connections.append(None)
+ forever_blk.docks.append(['flow', True, 0, 0])
+ if while_blk or until_blk:
+ forever_blk.connections.append(ifelse_blk)
+ else:
+ forever_blk.connections.append(whileflow)
+ forever_blk.docks.append(['flow', False, 0, 0, '['])
+ forever_blk.connections.append(outflow)
+ forever_blk.docks.append(['flow', False, 0, 0, ']'])
+ if while_blk or until_blk:
+ ifelse_blk.connections.append(forever_blk)
+ ifelse_blk.docks.append(['flow', True, 0, 0])
+ ifelse_blk.connections.append(boolflow)
+ ifelse_blk.docks.append(['bool', False, 0, 0])
+ if while_blk:
+ ifelse_blk.connections.append(whileflow)
+ ifelse_blk.connections.append(stopstack_blk)
+ else: # until
+ ifelse_blk.connections.append(stopstack_blk)
+ ifelse_blk.connections.append(whileflow)
+ ifelse_blk.docks.append(['flow', False, 0, 0, '['])
+ ifelse_blk.docks.append(['flow', False, 0, 0, ']['])
+ ifelse_blk.connections.append(None)
+ ifelse_blk.docks.append(['flow', False, 0, 0, ']'])
+ stopstack_blk.connections.append(ifelse_blk)
+ stopstack_blk.docks.append(['flow', False, 0, 0])
+
+ if whileflow is not None:
+ if while_blk or until_blk:
+ whileflow.connections[0] = ifelse_blk
+ else:
+ whileflow.connections[0] = forever_blk
+
+ # Create a separate stacks for the forever loop and the whileflow
+ code = self._blocks_to_code(forever_blk)
+ self.stacks['stack3' + str(action_name)] = self._readline(code)
+ if until_blk and whileflow is not None:
+ # Create a stack from the whileflow to be called from
+ # action_first, but then reconnect it to the ifelse block
+ c = whileflow.connections[0]
+ whileflow.connections[0] = None
+ code = self._blocks_to_code(whileflow)
+ self.stacks['stack3' + str(action_flow_name)] = self._readline(code)
+ whileflow.connections[0] = c
+
+ # Save the connections so we can restore them later
+ if whileflow is not None:
+ self.save_while_blks.append([b, i, j, 0])
+ else:
+ self.save_while_blks.append([b, i, j, None])
+
+ # Insert the new blocks into the stack
+ i = blocks.index(b)
+ if i == 0:
+ blocks_left = []
+ else:
+ blocks_left = blocks[0:i]
+ if i == len(blocks) - 1:
+ blocks_right = []
+ else:
+ blocks_right = blocks[i + 1:]
+ blocks = blocks_left[:]
+ if until_blk and whileflow is not None:
+ blocks.append(action_first)
+ blocks.append(action_blk)
+ blocks.append(forever_blk)
+ if while_blk or until_blk:
+ blocks.append(ifelse_blk)
+ blocks.append(stopstack_blk)
+ blocks.extend(blocks_right)
+
+ if until_blk and whileflow is not None:
+ return action_first, blocks
+ else:
+ return action_blk, blocks
diff --git a/TurtleArt/tapalette.py b/TurtleArt/tapalette.py
index 61fcb14..0bdf3d6 100644
--- a/TurtleArt/tapalette.py
+++ b/TurtleArt/tapalette.py
@@ -31,6 +31,7 @@ logo_functions = {}
special_names = {} # Names for blocks without names for popup help
content_blocks = ['number', 'string', 'description', 'audio', 'video',
'journal']
+hidden_proto_blocks = [] # proto blocks that are (at least initially) hidden
value_blocks = [] # blocks whose labels are updated get added here
special_block_colors = {}
block_styles = {'basic-style': [],
@@ -155,8 +156,9 @@ class Palette():
block.set_colors(colors)
block.set_value_block(value_block)
block.set_content_block(content_block)
- if not hidden:
- block.set_palette(self._name)
+ block.set_palette(self._name)
+ if hidden:
+ block.set_hidden()
block.add_block()
@@ -179,10 +181,13 @@ def palette_name_to_index(palette_name):
else:
return None
+
def define_logo_function(key, value):
- ''' Add a logo function to the table. '''
+ ''' Add a logo function to the table (not necessarily associated
+ with a block, e.g., color lookup tables) '''
logo_functions[key] = value
+
class Block():
""" a class for defining new block primitives """
@@ -199,12 +204,18 @@ class Block():
self._value_block = False
self._content_block = False
self._colors = None
+ self._hidden = False
def add_block(self, position=None):
if self._name is None:
debug_output('You must specify a name for your block')
return
+ # FIXME: Does the block already exist? A block can live on
+ # multiple palettes, but it can only have one set of
+ # atttributes. So if this is a redefinition, remove it from
+ # all lists except palettes before regeneration.
+
if self._style is None:
debug_output('You must specify a style for your block')
return
@@ -253,6 +264,12 @@ class Block():
if self._colors is not None:
special_block_colors[self._name] = self._colors
+ if self._hidden:
+ hidden_proto_blocks.append(self._name)
+
+ def set_hidden(self):
+ self._hidden = True
+
def set_colors(self, colors=None):
self._colors = colors
diff --git a/TurtleArt/tasprite_factory.py b/TurtleArt/tasprite_factory.py
index d05902d..963c37c 100755
--- a/TurtleArt/tasprite_factory.py
+++ b/TurtleArt/tasprite_factory.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-#Copyright (c) 2009,10 Walter Bender
+#Copyright (c) 2009-12 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
@@ -25,7 +25,8 @@ pygtk.require('2.0')
import gtk
import os
-from taconstants import HIT_RED, HIT_GREEN, HIDE_WHITE, SHOW_WHITE
+from taconstants import HIT_RED, HIT_GREEN, HIDE_WHITE, SHOW_WHITE, \
+ PALETTE_COLOR, TOOLBAR_COLOR
class SVG:
@@ -90,6 +91,8 @@ class SVG:
"""
def basic_block(self):
+ ''' The most common block type: used for 0, 1, 2, or 3
+ argument commands, stacks, et al. '''
self.reset_min_max()
(x, y) = self._calculate_x_y()
self.margins[2] = 0
@@ -136,6 +139,8 @@ class SVG:
return self.header() + svg
def invisible(self):
+ ''' A block that is invisible but still has connectors: used
+ when collapsing stacks. '''
self.reset_min_max()
(x, y) = self._calculate_x_y()
self.margins[2] = 0
@@ -155,6 +160,7 @@ class SVG:
return self.header() + self.footer()
def basic_flow(self):
+ ''' A flow block includes an arm that holds a branch in the flow '''
self.reset_min_max()
(x, y) = self._calculate_x_y()
self.margins[2] = 0
@@ -183,6 +189,7 @@ class SVG:
svg += self._rline_to(self._radius * 3 + self._slot_x * 2, 0)
else:
svg += self._rline_to(self._radius + self._slot_x, 0)
+ save_y = self._y
hh = self._x
svg += self._corner(1, 1)
svg += self._rline_to(-self._radius, 0)
@@ -211,9 +218,14 @@ class SVG:
if self._show is True:
svg += self._show_dot()
svg += self.footer()
+ if self._bool is True: # move secondary labels to arm
+ self.margins[2] = self._radius * 1.5 * self._scale
+ self.margins[3] = (self._max_y - save_y - self._radius + \
+ self._stroke_width) * self._scale
return self.header() + svg
def portfolio(self):
+ ''' Deprecated block '''
self.reset_min_max()
(x, y) = self._calculate_x_y()
self.margins[0] = int(x + 2 * self._stroke_width + 0.5)
@@ -255,6 +267,7 @@ class SVG:
return self.header() + svg
def basic_box(self):
+ ''' Basic argument style used for numbers, text, media '''
self.reset_min_max()
self.set_outie(True)
x = self._stroke_width / 2.0 + self._innie_x1 + self._innie_x2
@@ -277,6 +290,7 @@ class SVG:
return self.header() + svg
def boolean_and_or(self):
+ ''' Booleans are in a class of their own '''
self.reset_min_max()
svg = self._start_boolean(self._stroke_width / 2.0,
self._radius * 5.5 + self._stroke_width / \
@@ -317,6 +331,7 @@ class SVG:
return self.header() + svg
def boolean_not(self):
+ ''' Booleans are in a class of their own '''
self.reset_min_max()
svg = self._start_boolean(self._stroke_width / 2.0, self._radius * \
2.0 + self._stroke_width / 2.0)
@@ -339,6 +354,7 @@ class SVG:
return self.header() + svg
def boolean_compare(self):
+ ''' Booleans are in a class of their own '''
self.reset_min_max()
yoffset = self._radius * 2 + 2 * self._innie_y2 + \
self._innie_spacer + self._stroke_width / 2.0 + \
@@ -377,7 +393,34 @@ class SVG:
self.margins[2] = int(self._stroke_width * self._scale)
return self.header() + svg
+ def triangle_up(self, colors):
+ ''' A triangle that points up '''
+ self.reset_min_max()
+ self._fill, self._stroke = colors[0], colors[1]
+ self._width, self._height = 55 * self._scale, 55 * self._scale
+ svg = self.new_path(5, 50)
+ svg += self._rline_to(22.5, -45)
+ svg += self._rline_to(22.5, 45)
+ svg += self._close_path()
+ svg += self.style()
+ svg += self.footer()
+ return self.header() + svg
+
+ def triangle_down(self, colors):
+ ''' A triangle that points down '''
+ self.reset_min_max()
+ self._fill, self._stroke = colors[0], colors[1]
+ self._width, self._height = 55 * self._scale, 55 * self._scale
+ svg = self.new_path(5, 5)
+ svg += self._rline_to(22.5, 45)
+ svg += self._rline_to(22.5, -45)
+ svg += self._close_path()
+ svg += self.style()
+ svg += self.footer()
+ return self.header() + svg
+
def turtle(self, colors):
+ ''' Turtles are just another block '''
self.reset_min_max()
self._fill, self._stroke = colors[0], colors[1]
@@ -459,9 +502,10 @@ class SVG:
return self.header() + svg
def palette(self, width, height):
+ ''' Just a rectangle with a hide button. '''
self.reset_min_max()
self._width, self._height = width, height
- self._fill, self._stroke = "#FFD000", "none"
+ self._fill, self._stroke = PALETTE_COLOR, "none"
svg = self._rect(width, height, 0, 0)
self._hide_x = (width - self._radius * 1.5) / 2
self._hide_y = (height - self._radius * 1.5) / 2
@@ -470,14 +514,17 @@ class SVG:
return self.header() + svg
def toolbar(self, width, height):
+ ''' Just a rectangle '''
self.reset_min_max()
self._width, self._height = width, height
- self._fill, self._stroke = "#282828", "none"
+ self._fill, self._stroke = TOOLBAR_COLOR, "none"
svg = self._rect(width, height, 0, 0)
svg += self.footer()
return self.header() + svg
def sandwich_top(self, innie_flag=True):
+ ''' Special block for the top of a collapsible stack; includes
+ an 'arm" that extends down the left side of a stack '''
self.reset_min_max()
x = self._stroke_width / 2.0
y = self._stroke_width / 2.0 + self._radius
@@ -516,6 +563,9 @@ class SVG:
return self.header() + svg
def sandwich_bottom(self):
+ ''' Special block for the bottom of a collapsible stack;
+ includes a connection to the 'arm" that extends down the left
+ side of a stack '''
self.reset_min_max()
x = self._stroke_width / 2.0
y = self._stroke_width / 2.0
diff --git a/TurtleArt/taturtle.py b/TurtleArt/taturtle.py
index c7bb768..c13c494 100644
--- a/TurtleArt/taturtle.py
+++ b/TurtleArt/taturtle.py
@@ -20,8 +20,10 @@
#THE SOFTWARE.
from random import uniform
-from math import sin, cos, pi
+from math import sin, cos, pi, sqrt
from gettext import gettext as _
+import gtk
+import cairo
from taconstants import TURTLE_LAYER, DEFAULT_TURTLE_COLORS
from tasprite_factory import SVG, svg_str_to_pixbuf
@@ -176,25 +178,38 @@ class Turtle:
self.shapes = generate_turtle_pixbufs(self.colors)
self.set_heading(self.heading)
- def set_shapes(self, shapes):
+ def set_shapes(self, shapes, i=0):
""" Reskin the turtle """
n = len(shapes)
- if n == SHAPES:
+ if n == 1 and i > 0: # set shape[i]
+ if i < len(self.shapes):
+ self.shapes[i] = shapes[0]
+ elif n == SHAPES: # all shapes have been precomputed
self.shapes = shapes[:]
- else:
+ else: # rotate shapes
if n != 1:
debug_output("%d images passed to set_shapes: ignoring" % (n),
self.tw.running_sugar)
- images = [shapes[0]]
- if self.heading == 0:
- for i in range(3):
- images.append(images[i].rotate_simple(270))
+ if self.heading == 0: # rotate the shapes
+ images = []
+ w, h = shapes[0].get_width(), shapes[0].get_height()
+ nw = nh = int(sqrt(w * w + h * h))
for i in range(SHAPES):
- j = (i + 4) % SHAPES
- self.shapes[j] = images[int(j / 9)]
- else:
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh)
+ context = cairo.Context(surface)
+ context = gtk.gdk.CairoContext(context)
+ context.translate(nw / 2., nh / 2.)
+ context.rotate(i * 10 * pi / 180.)
+ context.translate(-nw / 2., -nh / 2.)
+ context.set_source_pixbuf(shapes[0], (nw - w) / 2.,
+ (nh - h) / 2.)
+ context.rectangle(0, 0, nw, nh)
+ context.fill()
+ images.append(surface)
+ self.shapes = images[:]
+ else: # associate shape with image at current heading
j = int(self.heading + 5) % 360 / (360 / SHAPES)
- self.shapes[j] = images[0]
+ self.shapes[j] = shapes[0]
self.custom_shapes = True
self.show()
diff --git a/TurtleArt/tautils.py b/TurtleArt/tautils.py
index a402563..97b396d 100644
--- a/TurtleArt/tautils.py
+++ b/TurtleArt/tautils.py
@@ -23,7 +23,7 @@ import gtk
import cairo
import pickle
import subprocess
-import os.path
+import os
from gettext import gettext as _
try:
@@ -42,14 +42,17 @@ except (ImportError, AttributeError):
from StringIO import StringIO
from taconstants import COLLAPSIBLE, HIT_HIDE, HIT_SHOW, XO1, XO15, XO175, \
- UNKNOWN
+ XO30, UNKNOWN
+
+SANDWICHES = ['sandwichtop', 'sandwichtop_no_label', 'sandwichtop_no_arm',
+ 'sandwichtop_no_arm_no_label']
import logging
_logger = logging.getLogger('turtleart-activity')
def debug_output(message_string, running_sugar=False):
- """ unified debugging output """
+ ''' unified debugging output '''
if running_sugar:
_logger.debug(message_string)
else:
@@ -57,7 +60,7 @@ def debug_output(message_string, running_sugar=False):
def error_output(message_string, running_sugar=False):
- """ unified debugging output """
+ ''' unified debugging output '''
if running_sugar:
_logger.error(message_string)
else:
@@ -92,7 +95,7 @@ def convert(x, fn, try_ord=True):
def chr_to_ord(x):
- """ Try to comvert a string to an ord """
+ ''' Try to comvert a string to an ord '''
if strtype(x) and len(x) == 1:
try:
return ord(x[0]), True
@@ -102,7 +105,7 @@ def chr_to_ord(x):
def strtype(x):
- """ Is x a string type? """
+ ''' Is x a string type? '''
if type(x) == str:
return True
if type(x) == unicode:
@@ -111,15 +114,15 @@ def strtype(x):
def magnitude(pos):
- """ Calculate the magnitude of the distance between to blocks. """
+ ''' Calculate the magnitude of the distance between to blocks. '''
x, y = pos
return x * x + y * y
def json_load(text):
- """ Load JSON data using what ever resources are available. """
+ ''' Load JSON data using what ever resources are available. '''
if OLD_SUGAR_SYSTEM is True:
- _listdata = json.read(text)
+ listdata = json.read(text)
else:
# Strip out leading and trailing whitespace, nulls, and newlines
clean_text = text.lstrip()
@@ -132,137 +135,139 @@ def json_load(text):
while left_count > right_count:
clean_text += ']'
right_count = clean_text.count(']')
- _io = StringIO(clean_text)
+ io = StringIO(clean_text)
try:
- _listdata = jload(_io)
+ listdata = jload(io)
except ValueError:
# Assume that text is ascii list
- _listdata = text.split()
- for i, value in enumerate(_listdata):
- _listdata[i] = convert(value, float)
+ listdata = text.split()
+ for i, value in enumerate(listdata):
+ listdata[i] = convert(value, float)
# json converts tuples to lists, so we need to convert back,
- return _tuplify(_listdata)
+ return _tuplify(listdata)
def _tuplify(tup):
- """ Convert to tuples """
+ ''' Convert to tuples '''
if type(tup) is not list:
return tup
return tuple(map(_tuplify, tup))
def get_id(connection):
- """ Get a connection block ID. """
+ ''' Get a connection block ID. '''
if connection is not None and hasattr(connection, 'id'):
return connection.id
return None
def json_dump(data):
- """ Save data using available JSON tools. """
+ ''' Save data using available JSON tools. '''
if OLD_SUGAR_SYSTEM is True:
return json.write(data)
else:
- _io = StringIO()
- jdump(data, _io)
- return _io.getvalue()
+ io = StringIO()
+ jdump(data, io)
+ return io.getvalue()
def get_load_name(suffix, load_save_folder):
- """ Open a load file dialog. """
- _dialog = gtk.FileChooserDialog(_('Load...'), None,
+ ''' Open a load file dialog. '''
+ dialog = gtk.FileChooserDialog(
+ _('Load...'), None,
gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
- _dialog.set_default_response(gtk.RESPONSE_OK)
- return do_dialog(_dialog, suffix, load_save_folder)
+ dialog.set_default_response(gtk.RESPONSE_OK)
+ return do_dialog(dialog, suffix, load_save_folder)
def get_save_name(suffix, load_save_folder, save_file_name):
- """ Open a save file dialog. """
- _dialog = gtk.FileChooserDialog(_('Save...'), None,
+ ''' Open a save file dialog. '''
+ dialog = gtk.FileChooserDialog(
+ _('Save...'), None,
gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK))
- _dialog.set_default_response(gtk.RESPONSE_OK)
+ dialog.set_default_response(gtk.RESPONSE_OK)
if save_file_name is not None:
- _dialog.set_current_name(save_file_name + suffix)
- return do_dialog(_dialog, suffix, load_save_folder)
+ dialog.set_current_name(save_file_name + suffix)
+ return do_dialog(dialog, suffix, load_save_folder)
def chooser(parent_window, filter, action):
- """ Choose an object from the datastore and take some action """
+ ''' Choose an object from the datastore and take some action '''
from sugar.graphics.objectchooser import ObjectChooser
- _chooser = None
+ chooser = None
try:
- _chooser = ObjectChooser(parent=parent_window, what_filter=filter)
+ chooser = ObjectChooser(parent=parent_window, what_filter=filter)
except TypeError:
- _chooser = ObjectChooser(None, parent_window,
+ chooser = ObjectChooser(None, parent_window,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
- if _chooser is not None:
+ if chooser is not None:
try:
- result = _chooser.run()
+ result = chooser.run()
if result == gtk.RESPONSE_ACCEPT:
- dsobject = _chooser.get_selected_object()
+ dsobject = chooser.get_selected_object()
action(dsobject)
dsobject.destroy()
finally:
- _chooser.destroy()
- del _chooser
+ chooser.destroy()
+ del chooser
def data_from_file(ta_file):
- """ Open the .ta file, ignoring any .png file that might be present. """
- file_handle = open(ta_file, "r")
+ ''' Open the .ta file, ignoring any .png file that might be present. '''
+ file_handle = open(ta_file, 'r')
#
# We try to maintain read-compatibility with all versions of Turtle Art.
# Try pickle first; then different versions of json.
#
try:
- _data = pickle.load(file_handle)
+ data = pickle.load(file_handle)
except:
# Rewind necessary because of failed pickle.load attempt
file_handle.seek(0)
- _text = file_handle.read()
- _data = data_from_string(_text)
+ text = file_handle.read()
+ data = data_from_string(text)
file_handle.close()
- return _data
+ return data
def data_from_string(text):
- """ JSON load data from a string. """
+ ''' JSON load data from a string. '''
return json_load(text.replace(']],\n', ']], '))
def data_to_file(data, ta_file):
- """ Write data to a file. """
- file_handle = file(ta_file, "w")
+ ''' Write data to a file. '''
+ file_handle = file(ta_file, 'w')
file_handle.write(data_to_string(data))
file_handle.close()
def data_to_string(data):
- """ JSON dump a string. """
+ ''' JSON dump a string. '''
return json_dump(data).replace(']], ', ']],\n')
def do_dialog(dialog, suffix, load_save_folder):
- """ Open a file dialog. """
- _result = None
+ ''' Open a file dialog. '''
+ result = None
file_filter = gtk.FileFilter()
file_filter.add_pattern('*' + suffix)
- file_filter.set_name("Turtle Art")
+ file_filter.set_name('Turtle Art')
dialog.add_filter(file_filter)
dialog.set_current_folder(load_save_folder)
- _response = dialog.run()
- if _response == gtk.RESPONSE_OK:
- _result = dialog.get_filename()
+ response = dialog.run()
+ if response == gtk.RESPONSE_OK:
+ result = dialog.get_filename()
load_save_folder = dialog.get_current_folder()
dialog.destroy()
- return _result, load_save_folder
+ return result, load_save_folder
def save_picture(canvas, file_name):
- """ Save the canvas to a file """
+ ''' Save the canvas to a file '''
x_surface = canvas.canvas.get_target()
img_surface = cairo.ImageSurface(cairo.FORMAT_RGB24,
canvas.width, canvas.height)
@@ -273,6 +278,7 @@ def save_picture(canvas, file_name):
def get_canvas_data(canvas):
+ ''' Get pixel data from the turtle canvas '''
x_surface = canvas.canvas.get_target()
img_surface = cairo.ImageSurface(cairo.FORMAT_RGB24,
canvas.width, canvas.height)
@@ -282,116 +288,117 @@ def get_canvas_data(canvas):
return img_surface.get_data()
-
def save_svg(string, file_name):
- """ Write a string to a file. """
- file_handle = file(file_name, "w")
+ ''' Write a string to a file. '''
+ file_handle = file(file_name, 'w')
file_handle.write(string)
file_handle.close()
def get_pixbuf_from_journal(dsobject, w, h):
- """ Load a pixbuf from a Journal object. """
+ ''' Load a pixbuf from a Journal object. '''
try:
- _pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(dsobject.file_path,
- int(w), int(h))
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(dsobject.file_path,
+ int(w), int(h))
except:
try:
- _pixbufloader = \
+ pixbufloader = \
gtk.gdk.pixbuf_loader_new_with_mime_type('image/png')
- _pixbufloader.set_size(min(300, int(w)), min(225, int(h)))
- _pixbufloader.write(dsobject.metadata['preview'])
- _pixbufloader.close()
- _pixbuf = _pixbufloader.get_pixbuf()
+ pixbufloader.set_size(min(300, int(w)), min(225, int(h)))
+ pixbufloader.write(dsobject.metadata['preview'])
+ pixbufloader.close()
+ pixbuf = pixbufloader.get_pixbuf()
except:
- _pixbuf = None
- return _pixbuf
+ pixbuf = None
+ return pixbuf
def get_path(activity, subpath):
- """ Find a Rainbow-approved place for temporary files. """
+ ''' Find a Rainbow-approved place for temporary files. '''
try:
return(os.path.join(activity.get_activity_root(), subpath))
except:
# Early versions of Sugar didn't support get_activity_root()
- return(os.path.join(os.environ['HOME'], ".sugar/default",
- "org.laptop.TurtleArtActivity", subpath))
+ return(os.path.join(os.environ['HOME'], '.sugar/default',
+ 'org.laptop.TurtleArtActivity', subpath))
def image_to_base64(image_path, tmp_path):
- """ Convert an image to base64-encoded data """
+ ''' Convert an image to base64-encoded data '''
base64 = os.path.join(tmp_path, 'base64tmp')
- cmd = "base64 <" + image_path + " >" + base64
+ cmd = 'base64 <' + image_path + ' >' + base64
subprocess.check_call(cmd, shell=True)
file_handle = open(base64, 'r')
data = file_handle.read()
file_handle.close()
+ os.remove(base64)
return data
def base64_to_image(data, path_name):
- """ Convert base64-encoded data to an image """
+ ''' 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
+ cmd = 'base64 -d <' + base64 + '>' + file_name
subprocess.check_call(cmd, shell=True)
return file_name
def movie_media_type(name):
- """ Is it movie media? """
+ ''' Is it movie media? '''
return name.lower().endswith(('.ogv', '.vob', '.mp4', '.wmv', '.mov',
'.mpeg', '.ogg', '.webm'))
def audio_media_type(name):
- """ Is it audio media? """
+ ''' Is it audio media? '''
return name.lower().endswith(('.oga', '.m4a'))
def image_media_type(name):
- """ Is it image media? """
+ ''' Is it image media? '''
return name.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.tiff',
'.tif', '.svg'))
def text_media_type(name):
- """ Is it text media? """
+ ''' Is it text media? '''
return name.lower().endswith(('.txt', '.py', '.lg', '.rtf'))
def round_int(num):
- """ Remove trailing decimal places if number is an int """
+ ''' Remove trailing decimal places if number is an int '''
try:
float(num)
except TypeError:
- raise pythonerror("#syntaxerror")
+ raise pythonerror('#syntaxerror')
if int(float(num)) == num:
return int(num)
else:
if float(num) < 0:
- _nn = int((float(num) - 0.005) * 100) / 100.
+ nn = int((float(num) - 0.005) * 100) / 100.
else:
- _nn = int((float(num) + 0.005) * 100) / 100.
- if int(float(_nn)) == _nn:
- return int(_nn)
- return _nn
+ nn = int((float(num) + 0.005) * 100) / 100.
+ if int(float(nn)) == nn:
+ return int(nn)
+ return nn
def calc_image_size(spr):
- """ Calculate the maximum size for placing an image onto a sprite. """
+ ''' Calculate the maximum size for placing an image onto a sprite. '''
return int(max(spr.label_safe_width(), 1)), \
int(max(spr.label_safe_height(), 1))
+
# Collapsible stacks live between 'sandwichtop' and 'sandwichbottom' blocks
def reset_stack_arm(top):
- """ When we undock, retract the 'arm' that extends from 'sandwichtop'. """
+ ''' When we undock, retract the 'arm' that extends from 'sandwichtop'. '''
if top is not None and top.name in ['sandwichtop', 'sandwichtop_no_label']:
if top.ey > 0:
top.reset_y()
@@ -400,109 +407,103 @@ def reset_stack_arm(top):
def grow_stack_arm(top):
- """ When we dock, grow an 'arm' from 'sandwichtop'. """
+ ''' When we dock, grow an 'arm' from 'sandwichtop'. '''
if top is not None and top.name in ['sandwichtop', 'sandwichtop_no_label']:
- _bot = find_sandwich_bottom(top)
- if _bot is None:
+ bot = find_sandwich_bottom(top)
+ if bot is None:
return
if top.ey > 0:
top.reset_y()
- _ty = top.spr.get_xy()[1]
- _th = top.spr.get_dimensions()[1]
- _by = _bot.spr.get_xy()[1]
- _dy = _by - (_ty + _th)
- if _dy > 0:
- top.expand_in_y(_dy / top.scale)
+ ty = top.spr.get_xy()[1]
+ th = top.spr.get_dimensions()[1]
+ by = bot.spr.get_xy()[1]
+ dy = by - (ty + th)
+ if dy > 0:
+ top.expand_in_y(dy / top.scale)
top.svg.set_hide(True)
top.refresh()
def find_sandwich_top(blk):
- """ Find the sandwich top above this block. """
+ ''' Find the sandwich top above this block. '''
# Always follow the main branch of a flow: the first connection.
- _blk = blk.connections[0]
- while _blk is not None:
- if _blk.name in COLLAPSIBLE:
+ cblk = blk.connections[0]
+ while cblk is not None:
+ if cblk.name in COLLAPSIBLE:
return None
- if _blk.name in ['repeat', 'if', 'ifelse', 'forever', 'while']:
- if blk != _blk.connections[len(_blk.connections) - 1]:
+ if cblk.name in ['repeat', 'if', 'ifelse', 'forever', 'while']:
+ if blk != cblk.connections[len(cblk.connections) - 1]:
return None
- if _blk.name in ['sandwichtop', 'sandwichtop_no_label',
- 'sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
- return _blk
- blk = _blk
- _blk = _blk.connections[0]
+ if cblk.name in SANDWICHES:
+ return cblk
+ blk = cblk
+ cblk = cblk.connections[0]
return None
def find_sandwich_bottom(blk):
- """ Find the sandwich bottom below this block. """
+ ''' Find the sandwich bottom below this block. '''
# Always follow the main branch of a flow: the last connection.
- _blk = blk.connections[len(blk.connections) - 1]
- while _blk is not None:
- if _blk.name in ['sandwichtop', 'sandwichtop_no_label',
- 'sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
+ cblk = blk.connections[len(blk.connections) - 1]
+ while cblk is not None:
+ if cblk.name in SANDWICHES:
return None
- if _blk.name in COLLAPSIBLE:
- return _blk
- _blk = _blk.connections[len(_blk.connections) - 1]
+ if cblk.name in COLLAPSIBLE:
+ return cblk
+ cblk = cblk.connections[len(cblk.connections) - 1]
return None
def find_sandwich_top_below(blk):
- """ Find the sandwich top below this block. """
- if blk.name in ['sandwichtop', 'sandwichtop_no_label',
- 'sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
+ ''' Find the sandwich top below this block. '''
+ if blk.name in SANDWICHES:
return blk
# Always follow the main branch of a flow: the last connection.
- _blk = blk.connections[len(blk.connections) - 1]
- while _blk is not None:
- if _blk.name in ['sandwichtop', 'sandwichtop_no_label',
- 'sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
- return _blk
- _blk = _blk.connections[len(_blk.connections) - 1]
+ cblk = blk.connections[len(blk.connections) - 1]
+ while cblk is not None:
+ if cblk.name in SANDWICHES:
+ return cblk
+ cblk = cblk.connections[len(cblk.connections) - 1]
return None
def restore_stack(top):
- """ Restore the blocks between the sandwich top and sandwich bottom. """
- _group = find_group(top.connections[len(top.connections) - 1])
- _hit_bottom = False
- _bot = find_sandwich_bottom(top)
- for _blk in _group:
- if not _hit_bottom and _blk == _bot:
- _hit_bottom = True
- if len(_blk.values) == 0:
- _blk.values.append(0)
+ ''' Restore the blocks between the sandwich top and sandwich bottom. '''
+ group = find_group(top.connections[len(top.connections) - 1])
+ hit_bottom = False
+ bot = find_sandwich_bottom(top)
+ for gblk in group:
+ if not hit_bottom and gblk == bot:
+ hit_bottom = True
+ if len(gblk.values) == 0:
+ gblk.values.append(0)
else:
- _blk.values[0] = 0
- _olddx = _blk.docks[1][2]
- _olddy = _blk.docks[1][3]
+ gblk.values[0] = 0
+ olddx = gblk.docks[1][2]
+ olddy = gblk.docks[1][3]
# Replace 'sandwichcollapsed' shape with 'sandwichbottom' shape
- _blk.name = 'sandwichbottom'
- _blk.spr.set_label(' ', 1)
- _blk.svg.set_show(False)
- _blk.svg.set_hide(True)
- _blk.refresh()
+ gblk.name = 'sandwichbottom'
+ gblk.spr.set_label(' ', 1)
+ gblk.svg.set_show(False)
+ gblk.svg.set_hide(True)
+ gblk.refresh()
# Redock to previous block in group
- _you = _blk.connections[0]
- (_yx, _yy) = _you.spr.get_xy()
- _yd = _you.docks[len(_you.docks) - 1]
- (_bx, _by) = _blk.spr.get_xy()
- _dx = _yx + _yd[2] - _blk.docks[0][2] - _bx
- _dy = _yy + _yd[3] - _blk.docks[0][3] - _by
- _blk.spr.move_relative((_dx, _dy))
+ you = gblk.connections[0]
+ (yx, yy) = you.spr.get_xy()
+ yd = you.docks[len(you.docks) - 1]
+ (bx, by) = gblk.spr.get_xy()
+ dx = yx + yd[2] - gblk.docks[0][2] - bx
+ dy = yy + yd[3] - gblk.docks[0][3] - by
+ gblk.spr.move_relative((dx, dy))
# Since the shapes have changed, the dock positions have too.
- _newdx = _blk.docks[1][2]
- _newdy = _blk.docks[1][3]
- _dx += _newdx - _olddx
- _dy += _newdy - _olddy
+ dx += gblk.docks[1][2] - olddx
+ dy += gblk.docks[1][3] - olddy
else:
- if not _hit_bottom:
- _blk.spr.restore()
- _blk.status = None
+ if not hit_bottom:
+ gblk.spr.restore()
+ gblk.status = None
else:
- _blk.spr.move_relative((_dx, _dy))
+ gblk.spr.move_relative((dx, dy))
# Add 'sandwichtop' arm
if top.name == 'sandwichtop_no_arm':
top.name = 'sandwichtop'
@@ -515,7 +516,7 @@ def restore_stack(top):
def uncollapse_forks(top, looping=False):
- """ From the top, find and restore any collapsible stacks on forks. """
+ ''' From the top, find and restore any collapsible stacks on forks. '''
if top == None:
return
if looping and top.name in ['sandwichtop_no_arm',
@@ -524,68 +525,66 @@ def uncollapse_forks(top, looping=False):
return
if len(top.connections) == 0:
return
- _blk = top.connections[len(top.connections) - 1]
- while _blk is not None:
- if _blk.name in COLLAPSIBLE:
+ cblk = top.connections[len(top.connections) - 1]
+ while cblk is not None:
+ if cblk.name in COLLAPSIBLE:
return
- if _blk.name in ['sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
- restore_stack(_blk)
+ if cblk.name in ['sandwichtop_no_arm', 'sandwichtop_no_arm_no_label']:
+ restore_stack(cblk)
return
# Follow a fork
- if _blk.name in ['repeat', 'if', 'ifelse', 'forever', 'while',
+ if cblk.name in ['repeat', 'if', 'ifelse', 'forever', 'while',
'until']:
top = find_sandwich_top_below(
- _blk.connections[len(_blk.connections) - 2])
+ cblk.connections[len(cblk.connections) - 2])
if top is not None:
uncollapse_forks(top, True)
- if _blk.name == 'ifelse':
+ if cblk.name == 'ifelse':
top = find_sandwich_top_below(
- _blk.connections[len(_blk.connections) - 3])
+ cblk.connections[len(cblk.connections) - 3])
if top is not None:
uncollapse_forks(top, True)
- _blk = _blk.connections[len(_blk.connections) - 1]
+ cblk = cblk.connections[len(cblk.connections) - 1]
return
def collapse_stack(top):
- """ Hide all the blocks between the sandwich top and sandwich bottom. """
+ ''' Hide all the blocks between the sandwich top and sandwich bottom. '''
# First uncollapse any nested stacks
if top == None or top.spr == None:
return
uncollapse_forks(top)
- _hit_bottom = False
- _bot = find_sandwich_bottom(top)
- _group = find_group(top.connections[len(top.connections) - 1])
- for _blk in _group:
- if not _hit_bottom and _blk == _bot:
- _hit_bottom = True
+ hit_bottom = False
+ bot = find_sandwich_bottom(top)
+ group = find_group(top.connections[len(top.connections) - 1])
+ for gblk in group:
+ if not hit_bottom and gblk == bot:
+ hit_bottom = True
# Replace 'sandwichbottom' with invisible 'sandwichcollapsed'
- if len(_blk.values) == 0:
- _blk.values.append(1)
+ if len(gblk.values) == 0:
+ gblk.values.append(1)
else:
- _blk.values[0] = 1
- _olddx = _blk.docks[1][2]
- _olddy = _blk.docks[1][3]
- _blk.name = 'sandwichcollapsed'
- _blk.resize()
- _you = find_sandwich_top(_blk)
- (_yx, _yy) = _you.spr.get_xy()
- _yd = _you.docks[len(_you.docks) - 1]
- (_bx, _by) = _blk.spr.get_xy()
- _dx = _yx + _yd[2] - _blk.docks[0][2] - _bx
- _dy = _yy + _yd[3] - _blk.docks[0][3] - _by
- _blk.spr.move_relative((_dx, _dy))
+ gblk.values[0] = 1
+ olddx = gblk.docks[1][2]
+ olddy = gblk.docks[1][3]
+ gblk.name = 'sandwichcollapsed'
+ gblk.resize()
+ you = find_sandwich_top(gblk)
+ (yx, yy) = you.spr.get_xy()
+ yd = you.docks[len(you.docks) - 1]
+ (bx, by) = gblk.spr.get_xy()
+ dx = yx + yd[2] - gblk.docks[0][2] - bx
+ dy = yy + yd[3] - gblk.docks[0][3] - by
+ gblk.spr.move_relative((dx, dy))
# Since the shapes have changed, the dock positions have too.
- _newdx = _blk.docks[1][2]
- _newdy = _blk.docks[1][3]
- _dx += _newdx - _olddx
- _dy += _newdy - _olddy
+ dx += gblk.docks[1][2] - olddx
+ dy += gblk.docks[1][3] - olddy
else:
- if not _hit_bottom:
- _blk.spr.hide()
- _blk.status = 'collapsed'
+ if not hit_bottom:
+ gblk.spr.hide()
+ gblk.status = 'collapsed'
else:
- _blk.spr.move_relative((_dx, _dy))
+ gblk.spr.move_relative((dx, dy))
# Remove 'sandwichtop' arm
if top.name == 'sandwichtop' or top.name == 'sandwichtop_no_arm':
top.name = 'sandwichtop_no_arm'
@@ -601,7 +600,7 @@ def collapse_stack(top):
def collapsed(blk):
- """ Is this stack collapsed? """
+ ''' Is this stack collapsed? '''
if blk is not None and blk.name in COLLAPSIBLE and\
len(blk.values) == 1 and blk.values[0] != 0:
return True
@@ -609,7 +608,7 @@ def collapsed(blk):
def collapsible(blk):
- """ Can this stack be collapsed? """
+ ''' Can this stack be collapsed? '''
if blk is None or blk.name not in COLLAPSIBLE:
return False
if find_sandwich_top(blk) is None:
@@ -618,32 +617,32 @@ def collapsible(blk):
def hide_button_hit(spr, x, y):
- """ Did the sprite's hide (contract) button get hit? """
- _red, _green, _blue, _alpha = spr.get_pixel((x, y))
- if _red == HIT_HIDE:
+ ''' Did the sprite's hide (contract) button get hit? '''
+ red, green, blue, alpha = spr.get_pixel((x, y))
+ if red == HIT_HIDE:
return True
else:
return False
def show_button_hit(spr, x, y):
- """ Did the sprite's show (expand) button get hit? """
- _red, _green, _blue, _alpha = spr.get_pixel((x, y))
- if _green == HIT_SHOW:
+ ''' Did the sprite's show (expand) button get hit? '''
+ red, green, blue, alpha = spr.get_pixel((x, y))
+ if green == HIT_SHOW:
return True
else:
return False
def numeric_arg(value):
- """ Dock test: looking for a numeric value """
+ ''' Dock test: looking for a numeric value '''
if type(convert(value, float)) is float:
return True
return False
def zero_arg(value):
- """ Dock test: looking for a zero argument """
+ ''' Dock test: looking for a zero argument '''
if numeric_arg(value):
if convert(value, float) == 0:
return True
@@ -651,7 +650,7 @@ def zero_arg(value):
def neg_arg(value):
- """ Dock test: looking for a negative argument """
+ ''' Dock test: looking for a negative argument '''
if numeric_arg(value):
if convert(value, float) < 0:
return True
@@ -659,7 +658,7 @@ def neg_arg(value):
def journal_check(blk1, blk2, dock1, dock2):
- """ Dock blocks only if arg is Journal block """
+ ''' Dock blocks only if arg is Journal block '''
if blk1 == None or blk2 == None:
return True
if (blk1.name == 'skin' and dock1 == 1) and blk2.name != 'journal':
@@ -670,7 +669,7 @@ def journal_check(blk1, blk2, dock1, dock2):
def arithmetic_check(blk1, blk2, dock1, dock2):
- """ Dock strings only if they convert to numbers. Avoid /0 and root(-1)"""
+ ''' Dock strings only if they convert to numbers. Avoid /0 and root(-1)'''
if blk1 == None or blk2 == None:
return True
if blk1.name in ['sqrt', 'number', 'string'] and\
@@ -704,7 +703,7 @@ def arithmetic_check(blk1, blk2, dock1, dock2):
if not numeric_arg(blk2.values[0]):
return False
elif blk1.name in ['greater2', 'less2'] and blk2.name == 'string':
- # Non-numeric stings are OK if only both args are strings;
+ # Non-numeric stings are OK only if both args are strings;
# Lots of test conditions...
if dock1 == 1 and blk1.connections[2] is not None:
if blk1.connections[2].name == 'number':
@@ -745,25 +744,24 @@ def arithmetic_check(blk1, blk2, dock1, dock2):
def xy(event):
- """ Where is the mouse event? """
+ ''' Where is the mouse event? '''
return map(int, event.get_coords())
-"""
-Utilities related to finding blocks in stacks.
-"""
+
+# Utilities related to finding blocks in stacks.
def find_block_to_run(blk):
- """ Find a stack to run (any stack without a 'def action'on the top). """
- _top = find_top_block(blk)
- if blk == _top and blk.name[0:3] is not 'def':
+ ''' Find a stack to run (any stack without a 'def action'on the top). '''
+ top = find_top_block(blk)
+ if blk == top and blk.name[0:3] is not 'def':
return True
else:
return False
def find_top_block(blk):
- """ Find the top block in a stack. """
+ ''' Find the top block in a stack. '''
if blk is None:
return None
if len(blk.connections) == 0:
@@ -774,7 +772,7 @@ def find_top_block(blk):
def find_start_stack(blk):
- """ Find a stack with a 'start' block on top. """
+ ''' Find a stack with a 'start' block on top. '''
if blk is None:
return False
if find_top_block(blk).name == 'start':
@@ -784,34 +782,34 @@ def find_start_stack(blk):
def find_group(blk):
- """ Find the connected group of block in a stack. """
+ ''' Find the connected group of block in a stack. '''
if blk is None:
return []
- _group = [blk]
+ group = [blk]
if blk.connections is not None:
- for _blk2 in blk.connections[1:]:
- if _blk2 is not None:
- _group.extend(find_group(_blk2))
- return _group
+ for cblk in blk.connections[1:]:
+ if cblk is not None:
+ group.extend(find_group(cblk))
+ return group
def find_blk_below(blk, name):
- """ Find a specific block below this block. """
+ ''' Find a specific block below this block. '''
if blk == None or len(blk.connections) == 0:
return
- _group = find_group(blk)
- for _gblk in _group:
- if _gblk.name == name:
- return _gblk
+ group = find_group(blk)
+ for gblk in _group:
+ if gblk.name == name:
+ return gblk
return None
def get_hardware():
- """ Determine whether we are using XO 1.0, 1.5, or "unknown" hardware """
+ ''' Determine whether we are using XO 1.0, 1.5, or 'unknown' hardware '''
product = _get_dmi('product_name')
if product is None:
if os.path.exists('/sys/devices/platform/lis3lv02d/position'):
- return XO175 # FIXME: temporary check for XO 1.75
+ return XO175 # FIXME: temporary check for XO 1.75 and XO 3.0
elif os.path.exists('/etc/olpc-release') or \
os.path.exists('/sys/power/olpc-pm'):
return XO1
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index 4b097fb..77459e5 100644
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -49,11 +49,11 @@ from taconstants import HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, \
MACROS, TOP_LAYER, BLOCK_LAYER, OLD_NAMES, DEFAULT_TURTLE, TURTLE_LAYER, \
CURSOR, EXPANDABLE, COLLAPSIBLE, DEAD_DICTS, DEAD_KEYS, NO_IMPORT, \
TEMPLATES, PYTHON_SKIN, PALETTE_HEIGHT, STATUS_LAYER, OLD_DOCK, \
- EXPANDABLE_ARGS, XO1, XO15, XO175, TITLEXY, CONTENT_ARGS, \
- CONSTANTS, EXPAND_SKIN
+ EXPANDABLE_ARGS, XO1, XO15, XO175, XO30, TITLEXY, CONTENT_ARGS, \
+ CONSTANTS, EXPAND_SKIN, PROTO_LAYER
from tapalette import palette_names, palette_blocks, expandable_blocks, \
block_names, content_blocks, default_values, special_names, block_styles, \
- help_strings
+ help_strings, hidden_proto_blocks
from talogo import LogoCode
from tacanvas import TurtleGraphics
from tablock import Blocks, Block
@@ -73,6 +73,8 @@ from sprites import Sprites, Sprite
if GST_AVAILABLE:
from tagplay import stop_media
+MOTION_THRESHOLD = 6
+SNAP_THRESHOLD = 200
class TurtleArtWindow():
""" TurtleArt Window class abstraction """
@@ -190,6 +192,7 @@ class TurtleArtWindow():
self.selected_blk = None
self.selected_spr = None
self.selected_turtle = None
+ self.triangle_sprs = []
self.drag_group = None
self.drag_turtle = 'move', 0, 0
self.drag_pos = 0, 0
@@ -206,6 +209,8 @@ class TurtleArtWindow():
self.sprite_list = None
self.canvas = TurtleGraphics(self, self.width, self.height)
+ if self.hw == XO175 and self.canvas.width == 1024:
+ self.hw = XO30 # FIXME: temporary test
if self.interactive_mode:
self.sprite_list.set_cairo_context(self.canvas.canvas)
@@ -290,8 +295,8 @@ class TurtleArtWindow():
self._plugins.append(plugins.values()[0](self))
debug_output('successfully importing %s' % (plugin_class),
self.running_sugar)
- except ImportError:
- debug_output('failed to import %s' % (plugin_class),
+ except ImportError as e:
+ debug_output('failed to import %s: %s' % (plugin_class, str(e)),
self.running_sugar)
'''
exec f in globals(), plugins
@@ -313,8 +318,18 @@ class TurtleArtWindow():
icon_theme.append_search_path(icon_path)
self._icon_paths.append(icon_path)
+ def _get_plugin_instance(self, plugin_name):
+ """ Returns the plugin 'plugin_name' instance """
+ list_plugins = self._get_plugins_from_plugins_dir(
+ self._get_plugin_home())
+ if plugin_name in list_plugins:
+ number_plugin = list_plugins.index(plugin_name)
+ return self._plugins[number_plugin]
+ else:
+ return None
+
def _setup_plugins(self):
- """ Initial setup -- call just once. """
+ """ Initial setup -- called just once. """
for plugin in self._plugins:
plugin.setup()
@@ -329,7 +344,7 @@ class TurtleArtWindow():
plugin.stop()
def clear_plugins(self):
- """ Clear is called from the clean block and erase button """
+ """ Clear is called from the clean block and erase button. """
for plugin in self._plugins:
if hasattr(plugin, 'clear'):
plugin.clear()
@@ -344,7 +359,7 @@ class TurtleArtWindow():
for plugin in self._plugins:
plugin.return_to_foreground()
- def _quit_plugins(self):
+ def quit_plugins(self):
""" Quit is called upon program exit. """
for plugin in self._plugins:
plugin.quit()
@@ -407,6 +422,20 @@ class TurtleArtWindow():
self.toolbar_shapes[name].type = 'toolbar'
self.toolbar_shapes['stopiton'].hide()
+ # Make the triangle buttons for adjusting numeric values
+ svg = SVG()
+ svg.set_scale(self.scale)
+ self.triangle_sprs.append(Sprite(self.sprite_list, 0, 0,
+ svg_str_to_pixbuf(svg.triangle_up(["#FF00FF", "#A000A0"]))))
+ self.triangle_sprs[-1].set_label('+')
+ self.triangle_sprs[-1].type = 'control'
+ self.triangle_sprs[-1].hide()
+ self.triangle_sprs.append(Sprite(self.sprite_list, 0, 0,
+ svg_str_to_pixbuf(svg.triangle_down(["#FF00FF", "#A000A0"]))))
+ self.triangle_sprs[-1].set_label('-')
+ self.triangle_sprs[-1].type = 'control'
+ self.triangle_sprs[-1].hide()
+
def set_sharing(self, shared):
self._sharing = shared
@@ -587,8 +616,13 @@ class TurtleArtWindow():
self.activity.recenter()
self.show_palette()
- def show_palette(self, n=0):
- """ Show palette """
+ def show_palette(self, n=None):
+ """ Show palette. """
+ if n is None:
+ if self.selected_palette is None:
+ n = 0
+ else:
+ n = self.selected_palette
self.show_toolbar_palette(n)
self.palette_button[self.orientation].set_layer(TAB_LAYER)
self.palette_button[2].set_layer(TAB_LAYER)
@@ -674,7 +708,8 @@ class TurtleArtWindow():
if blk.name in BLOCKS_WITH_SKIN:
self._resize_skin(blk)
- def show_toolbar_palette(self, n, init_only=False, regenerate=False):
+ def show_toolbar_palette(self, n, init_only=False, regenerate=False,
+ show=True):
""" Show the toolbar palettes, creating them on init_only """
# If we are running the 0.86+ toolbar, the selectors are already
# created, as toolbar buttons. Otherwise, we need to create them.
@@ -692,36 +727,52 @@ class TurtleArtWindow():
if init_only:
return
- # Hide the previously displayed palette
- self._hide_previous_palette()
+ if show:
+ # Hide the previously displayed palette
+ self._hide_previous_palette()
+ else:
+ save_selected = self.selected_palette
+ save_previous = self.previous_palette
self.selected_palette = n
self.previous_palette = self.selected_palette
# Make sure all of the selectors are visible. (We don't need to do
# this for 0.86+ toolbars since the selectors are toolbar buttons.)
- if self.activity is None or not self.activity.has_toolbarbox:
+ if show and (self.activity is None or not self.activity.has_toolbarbox):
self.selected_selector = self.selectors[n]
self.selectors[n].set_shape(self.selector_shapes[n][1])
for i in range(len(palette_blocks)):
self.selectors[i].set_layer(TAB_LAYER)
- # Show the palette with the current orientation.
- if self.palette_sprs[n][self.orientation] is not None:
- self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
+ # Show the palette with the current orientation.
+ if self.palette_sprs[n][self.orientation] is not None:
+ self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
# Create 'proto' blocks for each palette entry
- self._create_proto_blocks(n, regenerate=regenerate)
+ self._create_proto_blocks(n)
- self._layout_palette(n, regenerate=regenerate)
+ if show or save_selected == n:
+ self._layout_palette(n, regenerate=regenerate)
+ else:
+ self._layout_palette(n, regenerate=regenerate, show=False)
for blk in self.palettes[n]:
- blk.spr.set_layer(TAB_LAYER)
+ if blk.get_visibility():
+ blk.spr.set_layer(PROTO_LAYER)
+ else:
+ blk.spr.hide()
if n == palette_names.index('trash'):
for blk in self.trash_stack:
for gblk in find_group(blk):
if gblk.status != 'collapsed':
gblk.spr.set_layer(TAB_LAYER)
+ if not show:
+ if not save_selected == n:
+ self._hide_previous_palette(palette=n)
+ self.selected_palette = save_selected
+ self.previous_palette = save_previous
+
def _create_the_selectors(self):
''' Create the palette selector buttons: only when running
old-style Sugar toolbars or from GNOME '''
@@ -798,30 +849,42 @@ class TurtleArtWindow():
self.palette_button[2].type = 'palette'
self.palette_button[2].set_layer(TAB_LAYER)
- def _create_proto_blocks(self, n, regenerate=False):
+ def _create_proto_blocks(self, n):
''' Create the protoblocks that will populate a palette. '''
- if regenerate:
- for blk in self.palettes[n]:
- blk.type = 'trash'
- self.palettes[n] = []
-
- if self.palettes[n] == []:
- for i, name in enumerate(palette_blocks[n]):
- self.palettes[n].append(Block(self.block_list,
- self.sprite_list, name, 0, 0, 'proto', [], PALETTE_SCALE))
- self.palettes[n][i].spr.set_layer(TAB_LAYER)
- self.palettes[n][i].unhighlight()
-
- # Some proto blocks get a skin.
- if name in block_styles['box-style-media']:
- self._proto_skin(name + 'small', n, i)
- elif name[:8] == 'template': # Deprecated
- self._proto_skin(name[8:], n, i)
- elif name[:7] == 'picture': # Deprecated
- self._proto_skin(name[7:], n, i)
- elif name in PYTHON_SKIN:
- self._proto_skin('pythonsmall', n, i)
+ # Reload the palette, but reuse the existing blocks
+ # If a block doesn't exist, add it
+ for blk in self.palettes[n]:
+ blk.spr.hide()
+ old_blocks = self.palettes[n][:]
+ self.palettes[n] = []
+ for i, name in enumerate(palette_blocks[n]):
+ found_block = False
+ for oblk in old_blocks:
+ if oblk.name == name:
+ self.palettes[n].append(oblk)
+ found_block = True
+ break
+ if not found_block:
+ self.palettes[n].append(Block(
+ self.block_list, self.sprite_list, name, 0, 0,
+ 'proto', [], PALETTE_SCALE))
+ if name in hidden_proto_blocks:
+ self.palettes[n][i].set_visibility(False)
+ self.palettes[n][i].spr.set_layer(PROTO_LAYER)
+ self.palettes[n][i].unhighlight()
+
+ # Some proto blocks get a skin.
+ if name in block_styles['box-style-media']:
+ self._proto_skin(name + 'small', n, i)
+ elif name[:8] == 'template': # Deprecated
+ self._proto_skin(name[8:], n, i)
+ elif name[:7] == 'picture': # Deprecated
+ self._proto_skin(name[7:], n, i)
+ elif name in PYTHON_SKIN:
+ self._proto_skin('pythonsmall', n, i)
+ return
+
def _hide_toolbar_palette(self):
""" Hide the toolbar palettes """
self._hide_previous_palette()
@@ -833,24 +896,24 @@ class TurtleArtWindow():
not self.activity.has_toolbarbox:
self.activity.palette_buttons[self.selected_palette].set_icon(
palette_names[self.selected_palette] + 'off')
- self.selected_palette = None
- self.previous_palette = None
- def _hide_previous_palette(self):
+ def _hide_previous_palette(self, palette=None):
""" Hide just the previously viewed toolbar palette """
- # Hide previous palette
- if self.previous_palette is not None:
- for proto in self.palettes[self.previous_palette]:
+ if palette is None:
+ palette = self.previous_palette
+ # Hide previously selected palette
+ if palette is not None:
+ for proto in self.palettes[palette]:
proto.spr.hide()
- self.palette_sprs[self.previous_palette][self.orientation].hide()
+ self.palette_sprs[palette][self.orientation].hide()
if self.activity is None or not self.activity.has_toolbarbox:
- self.selectors[self.previous_palette].set_shape(
- self.selector_shapes[self.previous_palette][0])
- elif self.previous_palette is not None and \
- self.previous_palette != self.selected_palette:
- self.activity.palette_buttons[self.previous_palette].set_icon(
- palette_names[self.previous_palette] + 'off')
- if self.previous_palette == palette_names.index('trash'):
+ self.selectors[palette].set_shape(
+ self.selector_shapes[palette][0])
+ elif palette is not None and palette != self.selected_palette \
+ and not self.activity.has_toolbarbox:
+ self.activity.palette_buttons[palette].set_icon(
+ palette_names[palette] + 'off')
+ if palette == palette_names.index('trash'):
for blk in self.trash_stack:
for gblk in find_group(blk):
gblk.spr.hide()
@@ -859,6 +922,8 @@ class TurtleArtWindow():
""" Position prototypes in a horizontal palette. """
max_w = 0
for blk in blocks:
+ if not blk.get_visibility():
+ continue
w, h = self._width_and_height(blk)
if y + h > PALETTE_HEIGHT + self.toolbar_offset:
x += int(max_w + 3)
@@ -872,7 +937,7 @@ class TurtleArtWindow():
g.spr.save_xy = g.spr.get_xy()
if self.running_sugar and not self.hw in [XO1]:
g.spr.move_relative((self.activity.hadj_value,
- self.activity.vadj_value,))
+ self.activity.vadj_value))
y += int(h + 3)
if w > max_w:
max_w = w
@@ -883,8 +948,10 @@ class TurtleArtWindow():
row = []
row_w = 0
max_h = 0
- for b in blocks:
- w, h = self._width_and_height(b)
+ for blk in blocks:
+ if not blk.get_visibility():
+ continue
+ w, h = self._width_and_height(blk)
if x + w > PALETTE_WIDTH:
# Recenter row.
dx = int((PALETTE_WIDTH - row_w) / 2)
@@ -898,17 +965,17 @@ class TurtleArtWindow():
x = 4
y += int(max_h + 3)
max_h = 0
- row.append(b)
+ row.append(blk)
row_w += (4 + w)
- (bx, by) = b.spr.get_xy()
+ (bx, by) = blk.spr.get_xy()
dx = int(x - bx)
dy = int(y - by)
- for g in find_group(b):
+ for g in find_group(blk):
g.spr.move_relative((dx, dy))
g.spr.save_xy = g.spr.get_xy()
if self.running_sugar and not self.hw in [XO1]:
g.spr.move_relative((self.activity.hadj_value,
- self.activity.vadj_value,))
+ self.activity.vadj_value))
x += int(w + 4)
if h > max_h:
max_h = h
@@ -920,7 +987,7 @@ class TurtleArtWindow():
g.spr.save_xy = (g.spr.save_xy[0] + dx, g.spr.save_xy[1])
return x, y, max_h
- def _layout_palette(self, n, regenerate=False):
+ def _layout_palette(self, n, regenerate=False, show=True):
""" Layout prototypes in a palette. """
if n is not None:
if self.orientation == HORIZONTAL_PALETTE:
@@ -932,11 +999,8 @@ class TurtleArtWindow():
w = x + max_w + 25
self._make_palette_spr(n, 0, self.toolbar_offset,
w, PALETTE_HEIGHT, regenerate)
- self.palette_button[2].move((w - 20, self.toolbar_offset))
- self.palette_button[2].save_xy = self.palette_button[2].get_xy()
- if self.running_sugar and not self.hw in [XO1]:
- self.palette_button[2].move_relative(
- (self.activity.hadj_value, self.activity.vadj_value))
+ if show:
+ self.palette_button[2].move((w - 20, self.toolbar_offset))
else:
x, y = 5, self.toolbar_offset + 15
x, y, max_h = self._vertical_layout(x, y, self.palettes[n])
@@ -946,13 +1010,15 @@ class TurtleArtWindow():
h = y + max_h + 25 - self.toolbar_offset
self._make_palette_spr(n, 0, self.toolbar_offset,
PALETTE_WIDTH, h, regenerate)
- self.palette_button[2].move((PALETTE_WIDTH - 20,
- self.toolbar_offset))
+ if show:
+ self.palette_button[2].move((PALETTE_WIDTH - 20,
+ self.toolbar_offset))
+ if show:
self.palette_button[2].save_xy = self.palette_button[2].get_xy()
if self.running_sugar and not self.hw in [XO1]:
self.palette_button[2].move_relative(
(self.activity.hadj_value, self.activity.vadj_value))
- self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
+ self.palette_sprs[n][self.orientation].set_layer(CATEGORY_LAYER)
def _make_palette_spr(self, n, x, y, w, h, regenerate=False):
''' Make the background for the palette. '''
@@ -987,8 +1053,25 @@ class TurtleArtWindow():
def button_press(self, mask, x, y):
self.block_operation = 'click'
+ # Find out what was clicked
+ spr = self.sprite_list.find_sprite((x, y))
+
# Unselect things that may have been selected earlier
if self.selected_blk is not None:
+ if self.selected_blk.name == 'number' and spr in self.triangle_sprs:
+ # increment or decrement a number block
+ nf = float(self.selected_blk.spr.labels[0].replace(CURSOR, ''))
+ ni = int(nf)
+ if ni == nf:
+ n = ni
+ else:
+ n = nf
+ if spr == self.triangle_sprs[0]:
+ n += 1
+ else:
+ n -= 1
+ self.selected_blk.spr.set_label(str(n) + CURSOR)
+ return True
self._unselect_block()
self.selected_turtle = None
@@ -996,8 +1079,6 @@ class TurtleArtWindow():
if self.status_spr is not None:
self.status_spr.hide()
- # Find out what was clicked
- spr = self.sprite_list.find_sprite((x, y))
self.dx = 0
self.dy = 0
if spr is None:
@@ -1035,22 +1116,25 @@ class TurtleArtWindow():
# You cannot mix and match sensor blocks
elif blk.name in ['sound', 'volume', 'pitch']:
if len(self.block_list.get_similar_blocks(
- 'block', ['resistance', 'voltage'])) > 0:
+ 'block', ['resistance', 'voltage',
+ 'resistance2', 'voltage2'])) > 0:
self.showlabel('incompatible')
return True
- elif blk.name in ['resistance', 'voltage']:
+ elif blk.name in ['resistance', 'voltage',
+ 'resistance2', 'voltage2']:
if len(self.block_list.get_similar_blocks(
'block', ['sound', 'volume', 'pitch'])) > 0:
self.showlabel('incompatible')
return True
- if blk.name == 'resistance':
+ if blk.name in ['resistance', 'resistance2']:
if len(self.block_list.get_similar_blocks(
- 'block', 'voltage')) > 0:
+ 'block', ['voltage', 'voltage2'])) > 0:
self.showlabel('incompatible')
return True
- elif blk.name == 'voltage':
+ elif blk.name in ['voltage', 'voltage2']:
if len(self.block_list.get_similar_blocks(
- 'block', 'resistance')) > 0:
+ 'block', ['resistance',
+ 'resistance2'])) > 0:
self.showlabel('incompatible')
return True
blk.highlight()
@@ -1224,12 +1308,7 @@ class TurtleArtWindow():
self.trash_stack = []
def _in_the_trash(self, x, y):
- """ Is x, y over the trash can? """
- """
- if self.selected_palette == palette_names.index('trash') and \
- self.palette_sprs[palette_names.index('trash')][self.orientation].hit((x, y)):
- return True
- """
+ """ Is x, y over a palette? """
if self.selected_palette is not None and \
self.palette_sprs[self.selected_palette][self.orientation].hit(
(x, y)):
@@ -1257,6 +1336,8 @@ class TurtleArtWindow():
# After unselecting a 'number' block, we need to check its value
if self.selected_blk.name == 'number':
self._number_check()
+ for spr in self.triangle_sprs:
+ spr.hide()
elif self.selected_blk.name == 'string':
self._string_check()
self.selected_blk.unhighlight()
@@ -1332,11 +1413,14 @@ class TurtleArtWindow():
newblk.connections[i + 1] = argblk
self.drag_group = find_group(newblk)
self.block_operation = 'new'
+ debug_output(newblk.name, True)
if len(newblk.spr.labels) > 0 and newblk.spr.labels[0] is not None \
and newblk.name not in ['', 'number', 'string']:
- if newblk.spr.labels[0] not in self.used_block_list:
- if len(self.used_block_list) > 0:
- self.used_block_list.append(', ')
+ if len(self.used_block_list) > 0:
+ self.used_block_list.append(', ')
+ if newblk.name in special_names:
+ self.used_block_list.append(special_names[newblk.name])
+ elif newblk.spr.labels[0] not in self.used_block_list:
self.used_block_list.append(newblk.spr.labels[0])
def _new_macro(self, name, x, y):
@@ -1545,7 +1629,7 @@ class TurtleArtWindow():
dx = x - dragx - sx
dy = y - dragy - sy
- # Take no action if there was a move of 0,0.
+ # Take no action if there was a move of 0, 0.
if dx == 0 and dy == 0:
return
@@ -1661,6 +1745,7 @@ class TurtleArtWindow():
def _buttonrelease_cb(self, win, event):
""" Button release """
x, y = xy(event)
+ self.mouse_flag = 0
self.mouse_x = x
self.mouse_y = y
self.button_release(x, y)
@@ -1701,7 +1786,7 @@ class TurtleArtWindow():
return
blk = self.drag_group[0]
- # Remove blocks by dragging them onto the trash palette.
+ # Remove blocks by dragging them onto any palette.
if self.block_operation == 'move' and self._in_the_trash(x, y):
self._put_in_trash(blk, x, y)
self.drag_group = None
@@ -1726,7 +1811,11 @@ class TurtleArtWindow():
self.drag_group = None
# Find the block we clicked on and process it.
- if self.block_operation == 'click':
+ # Consider a very small move a click (for touch interfaces)
+ if self.block_operation == 'click' or \
+ (self.hw in [XO175, XO30] and self.block_operation == 'move' and (
+ abs(self.dx) < MOTION_THRESHOLD and \
+ abs(self.dy < MOTION_THRESHOLD))):
self._click_block(x, y)
def remote_turtle(self, name):
@@ -1776,6 +1865,14 @@ class TurtleArtWindow():
if blk.name == 'number' or blk.name == 'string':
self.saved_string = blk.spr.labels[0]
blk.spr.labels[0] += CURSOR
+ if blk.name == 'number':
+ bx, by = blk.spr.get_xy()
+ bw, bh = blk.spr.get_dimensions()
+ tw, th = self.triangle_sprs[0].get_dimensions()
+ for spr in self.triangle_sprs:
+ spr.set_layer(TOP_LAYER)
+ self.triangle_sprs[0].move((int(bx + (bw - tw) / 2), by - th))
+ self.triangle_sprs[1].move((int(bx + (bw - tw) / 2), by + bh))
elif blk.name in block_styles['box-style-media'] and \
blk.name not in NO_IMPORT:
@@ -1828,7 +1925,7 @@ class TurtleArtWindow():
return
if blk.name in block_styles['boolean-style']:
- self._expand_boolean(blk, blk.connections[1], dy)
+ self._expand_boolean(blk, blk.connections[2], dy)
else:
self._expand_expandable(blk, blk.connections[1], dy)
@@ -1926,7 +2023,8 @@ class TurtleArtWindow():
for gblk in find_group(blk):
if gblk not in group:
gblk.spr.move_relative((0, dy * blk.scale))
- if blk.name in block_styles['compare-style']:
+ if blk.name in block_styles['compare-style'] or \
+ blk.name in block_styles['compare-porch-style']:
for gblk in find_group(blk):
gblk.spr.move_relative((0, -dy * blk.scale))
@@ -1963,7 +2061,8 @@ class TurtleArtWindow():
for gblk in find_group(blk):
if gblk not in group:
gblk.spr.move_relative((0, dy * blk.scale))
- if blk.name in block_styles['compare-style']:
+ if blk.name in block_styles['compare-style'] or \
+ blk.name in block_styles['compare-porch-style']:
for gblk in find_group(blk):
gblk.spr.move_relative((0, -dy * blk.scale))
else:
@@ -2014,7 +2113,7 @@ class TurtleArtWindow():
"""
selected_block = self.drag_group[0]
best_destination = None
- d = 200
+ d = SNAP_THRESHOLD
for selected_block_dockn in range(len(selected_block.docks)):
for destination_block in self.just_blocks():
# Don't link to a block that is hidden
@@ -2034,7 +2133,8 @@ class TurtleArtWindow():
best_destination = destination_block
best_destination_dockn = destination_dockn
best_selected_block_dockn = selected_block_dockn
- if d < 200:
+ if d < SNAP_THRESHOLD:
+ # Some combinations of blocks are not valid
if not arithmetic_check(selected_block, best_destination,
best_selected_block_dockn,
best_destination_dockn):
@@ -2043,6 +2143,8 @@ class TurtleArtWindow():
best_selected_block_dockn,
best_destination_dockn):
return
+
+ # Move the selected blocks into the docked position
for blk in self.drag_group:
(sx, sy) = blk.spr.get_xy()
blk.spr.move((sx + best_xy[0], sy + best_xy[1]))
@@ -2053,17 +2155,27 @@ class TurtleArtWindow():
blk_in_dock.connections[0] = None
self._put_in_trash(blk_in_dock)
+ # Note the connection in destination dock
best_destination.connections[best_destination_dockn] = \
selected_block
+
+ # And in the selected block dock
if selected_block.connections is not None:
if best_selected_block_dockn < len(selected_block.connections):
selected_block.connections[best_selected_block_dockn] = \
best_destination
+ # Some destination blocks expand to accomodate large blocks
if best_destination.name in block_styles['boolean-style']:
if best_destination_dockn == 2 and \
- selected_block.name in block_styles['compare-style']:
+ (selected_block.name in block_styles['boolean-style'] or \
+ selected_block.name in block_styles['compare-style'] or \
+ selected_block.name in block_styles['compare-porch-style']):
dy = selected_block.ey - best_destination.ey
+ if selected_block.name in block_styles['boolean-style']:
+ # Even without expanding, boolean blocks are
+ # too large to fit in the lower dock position
+ dy += 45
best_destination.expand_in_y(dy)
self._expand_boolean(best_destination, selected_block, dy)
elif best_destination.name in expandable_blocks and \
@@ -2083,8 +2195,8 @@ class TurtleArtWindow():
if best_destination.ey > 0:
dy = best_destination.reset_y()
if dy != 0:
- self._expand_expandable(best_destination, selected_block,
- dy)
+ self._expand_expandable(
+ best_destination, selected_block, dy)
self._cascade_expandable(best_destination)
grow_stack_arm(find_sandwich_top(best_destination))
@@ -2210,7 +2322,7 @@ class TurtleArtWindow():
if keyname == "p":
self.hideshow_button()
elif keyname == 'q':
- self._quit_plugins()
+ self.quit_plugins()
if self.gst_available:
stop_media(self.lc)
exit()
@@ -2481,8 +2593,14 @@ class TurtleArtWindow():
else:
n = 0
self.selected_blk.spr.set_label(str(n))
- self.selected_blk.values[0] = float(str(n).replace(self.decimal_point,
- '.'))
+ debug_output(str(n), True)
+ try:
+ self.selected_blk.values[0] = \
+ float(str(n).replace(self.decimal_point, '.'))
+ except ValueError:
+ self.selected_blk.values[0] = float(str(n))
+ except IndexError:
+ self.selected_blk.values[0] = float(str(n))
def _string_check(self):
s = self.selected_blk.spr.labels[0].replace(CURSOR, '')
@@ -2818,9 +2936,11 @@ class TurtleArtWindow():
blk.connections = 'check'
if self.running_sugar and len(blk.spr.labels) > 0 and \
blk.name not in ['', ' ', 'number', 'string']:
- if blk.spr.labels[0] not in self.used_block_list:
- if len(self.used_block_list) > 0:
- self.used_block_list.append(', ')
+ if len(self.used_block_list) > 0:
+ self.used_block_list.append(', ')
+ if blk.name in special_names:
+ self.used_block_list.append(special_names[blk.name])
+ elif blk.spr.labels[0] not in self.used_block_list:
self.used_block_list.append(blk.spr.labels[0])
return blk
@@ -2937,8 +3057,7 @@ class TurtleArtWindow():
shp = shp[1:]
label = ''
self.status_spr.set_shape(self.status_shapes[shp])
- self.status_spr.set_label_attributes(6.0 * self.block_scale,
- rescale=False)
+ self.status_spr.set_label_attributes(12.0, rescale=False)
self.status_spr.set_label(str(label))
self.status_spr.set_layer(STATUS_LAYER)
if shp == 'info':
@@ -3005,9 +3124,6 @@ class TurtleArtWindow():
else:
save_picture(self.canvas, file_path)
- # keep a log of the saved pictures for export to HTML
- self.saved_pictures.append(file_path)
-
if self.running_sugar:
from sugar.datastore import datastore
from sugar import profile
@@ -3026,6 +3142,10 @@ class TurtleArtWindow():
dsobject.set_file_path(file_path)
datastore.write(dsobject)
dsobject.destroy()
+ self.saved_pictures.append((dsobject.object_id, svg))
+ os.remove(file_path)
+ else:
+ self.saved_pictures.append((file_path, svg))
def just_blocks(self):
""" Filter out 'proto', 'trash', and 'deleted' blocks """