Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/TurtleArt
diff options
context:
space:
mode:
Diffstat (limited to 'TurtleArt')
-rw-r--r--TurtleArt/tabasics.py544
-rw-r--r--TurtleArt/tablock.py57
-rw-r--r--TurtleArt/tacanvas.py20
-rw-r--r--TurtleArt/taconstants.py99
-rw-r--r--TurtleArt/talogo.py278
-rw-r--r--TurtleArt/taturtle.py17
-rw-r--r--TurtleArt/tautils.py32
-rw-r--r--TurtleArt/tawindow.py10
8 files changed, 667 insertions, 390 deletions
diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py
index 3d1eb10..08d0864 100644
--- a/TurtleArt/tabasics.py
+++ b/TurtleArt/tabasics.py
@@ -71,14 +71,9 @@ 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, debug_output)
-from taconstants import (COLORDICT, CONSTANTS)
-
-
-def _color_to_num(c):
- if COLORDICT[c][0] is None:
- return(COLORDICT[c][1])
- else:
- return(COLORDICT[c][0])
+from taconstants import (Color, CONSTANTS)
+from taprimitive import Primitive
+from taturtle import Turtle
def _num_type(x):
@@ -99,6 +94,16 @@ class Palettes():
def __init__(self, turtle_window):
self.tw = turtle_window
+ self.prim_cache = {
+ "check_number": Primitive(self.check_number, export_me=False),
+ "convert_value_for_move": Primitive(self.convert_value_for_move,
+ export_me=False),
+ "convert_for_cmp": Primitive(Primitive.convert_for_cmp,
+ constant_args={'decimal_point': self.tw.decimal_point}),
+ "convert_to_number": Primitive(Primitive.convert_to_number,
+ constant_args={'decimal_point': self.tw.decimal_point})
+ } # avoid several Primitives of the same function
+
self._turtle_palette()
self._pen_palette()
@@ -125,7 +130,6 @@ class Palettes():
colors=["#00FF00", "#00A000"],
help_string=_('Palette of turtle commands'))
- primitive_dictionary['move'] = self._prim_move
palette.add_block('forward',
style='basic-style-1arg',
label=_('forward'),
@@ -136,8 +140,9 @@ class Palettes():
self.tw.lc.def_prim(
'forward',
1,
- lambda self, x: primitive_dictionary['move'](
- self.tw.turtles.get_active_turtle().forward, x))
+ Primitive(Turtle.forward,
+ slot_wrappers={0: self.prim_cache["convert_value_for_move"]},
+ call_afterwards=self.after_move))
palette.add_block('back',
style='basic-style-1arg',
@@ -147,12 +152,12 @@ class Palettes():
logo_command='back',
help_string=_('moves turtle backward'))
self.tw.lc.def_prim('back', 1,
- lambda self, x:
- primitive_dictionary['move']
- (self.tw.turtles.get_active_turtle().forward, x,
- reverse=True))
+ Primitive(Turtle.forward,
+ slot_wrappers={0: Primitive(Primitive.minus,
+ slot_wrappers={0: self.prim_cache["convert_value_for_move"]
+ })},
+ call_afterwards=self.after_move))
- primitive_dictionary['clean'] = self._prim_clear
palette.add_block('clean',
style='basic-style-extended-vertical',
label=_('clean'),
@@ -163,9 +168,13 @@ turtle'))
self.tw.lc.def_prim(
'clean',
0,
- lambda self: primitive_dictionary['clean']())
+ Primitive(Primitive.group, constant_args={0: [
+ Primitive(self.tw.clear_plugins, call_me=False),
+ Primitive(self.tw.lc.prim_clear_helper, call_me=False,
+ export_me=False),
+ Primitive(self.tw.canvas.clearscreen, call_me=False),
+ Primitive(self.tw.turtles.reset_turtles, call_me=False)]}))
- primitive_dictionary['right'] = self._prim_right
palette.add_block('left',
style='basic-style-1arg',
label=_('left'),
@@ -175,8 +184,11 @@ turtle'))
help_string=_('turns turtle counterclockwise (angle \
in degrees)'))
self.tw.lc.def_prim(
- 'left', 1, lambda self,
- x: primitive_dictionary['right'](x, reverse=True))
+ 'left', 1,
+ Primitive(Turtle.right,
+ slot_wrappers={0: Primitive(Primitive.minus,
+ slot_wrappers={0: self.prim_cache["check_number"]})},
+ call_afterwards=self.after_right))
palette.add_block('right',
style='basic-style-1arg',
@@ -189,9 +201,10 @@ degrees)'))
self.tw.lc.def_prim(
'right',
1,
- lambda self, x: primitive_dictionary['right'](x))
+ Primitive(Turtle.right,
+ slot_wrappers={0: self.prim_cache["check_number"]},
+ call_afterwards=self.after_right))
- primitive_dictionary['arc'] = self._prim_arc
palette.add_block('arc',
style='basic-style-2arg',
label=[_('arc'), _('angle'), _('radius')],
@@ -202,8 +215,10 @@ degrees)'))
self.tw.lc.def_prim(
'arc',
2,
- lambda self, x, y: primitive_dictionary['arc'](
- self.tw.turtles.get_active_turtle().arc, x, y))
+ Primitive(Turtle.arc,
+ slot_wrappers={0: Primitive(float, export_me=False),
+ 1: Primitive(float, export_me=False)},
+ call_afterwards=self.after_arc))
define_logo_function('taarc', 'to taarc :a :r\nrepeat round :a \
[right 1 forward (0.0175 * :r)]\nend\n')
@@ -218,8 +233,12 @@ degrees)'))
self.tw.lc.def_prim(
'setxy2',
2,
- lambda self, x, y: primitive_dictionary['move'](
- self.tw.turtles.get_active_turtle().set_xy, x, y))
+ Primitive(Turtle.set_xy,
+ slot_wrappers={(0, 2): Primitive(Primitive.make_tuple,
+ slot_wrappers={0:self.prim_cache["convert_value_for_move"],
+ 1:self.prim_cache["convert_value_for_move"]
+ })},
+ call_afterwards=self.after_move))
define_logo_function('tasetxy', 'to tasetxy :x :y\nsetxy :x :y\nend\n')
primitive_dictionary['set'] = self._prim_set
@@ -234,8 +253,9 @@ towards the top of the screen.)'))
self.tw.lc.def_prim(
'seth',
1,
- lambda self, x: primitive_dictionary['set'](
- 'heading', self.tw.turtles.get_active_turtle().set_heading, x))
+ Primitive(Turtle.set_heading,
+ slot_wrappers={0: Primitive(float, export_me=False)},
+ call_afterwards=lambda value: self.after_set('heading',value)))
palette.add_block('xcor',
style='box-style',
@@ -248,8 +268,11 @@ the turtle (can be used in place of a number block)'),
self.tw.lc.def_prim(
'xcor',
0,
- lambda self: self.tw.turtles.get_active_turtle().get_xy()[0] /
- self.tw.coord_scale)
+ Primitive(Primitive.divide, constant_args={
+ 0: Primitive(Turtle.get_x, constant_args={
+ 0: Primitive(self.tw.turtles.get_active_turtle,
+ export_me=False)}),
+ 1: Primitive(self.tw.get_coord_scale)}))
palette.add_block('ycor',
style='box-style',
@@ -262,8 +285,11 @@ the turtle (can be used in place of a number block)'),
self.tw.lc.def_prim(
'ycor',
0,
- lambda self: self.tw.turtles.get_active_turtle().get_xy()[1] /
- self.tw.coord_scale)
+ Primitive(Primitive.divide, constant_args={
+ 0: Primitive(Turtle.get_y, constant_args={
+ 0: Primitive(self.tw.turtles.get_active_turtle,
+ export_me=False)}),
+ 1: Primitive(self.tw.get_coord_scale)}))
palette.add_block('heading',
style='box-style',
@@ -273,18 +299,15 @@ turtle (can be used in place of a number block)'),
value_block=True,
prim_name='heading',
logo_command='heading')
- self.tw.lc.def_prim(
- 'heading',
- 0,
- lambda self: self.tw.turtles.get_active_turtle().get_heading())
+ self.tw.lc.def_prim('heading', 0, Primitive(Turtle.get_heading))
- # This block is used for holding the remote turtle name
palette.add_block('turtle-label',
hidden=True,
style='blank-style',
- label=['remote turtle name'])
+ label=['turtle'])
# Deprecated
+ primitive_dictionary['move'] = self._prim_move
palette.add_block('setxy',
hidden=True,
style='basic-style-2arg',
@@ -298,7 +321,7 @@ turtle (can be used in place of a number block)'),
'setxy',
2,
lambda self, x, y: primitive_dictionary['move'](
- self.tw.turtles.get_active_turtle().set_xy, x, y,
+ self.tw.turtles.get_active_turtle().set_xy, (x, y),
pendown=False))
define_logo_function('tasetxypenup', 'to tasetxypenup :x :y\npenup\n\
setxy :x :y\npendown\nend\n')
@@ -324,7 +347,7 @@ shade)'))
self.tw.lc.def_prim(
'fillscreen',
2,
- lambda self, x, y: self.tw.canvas.fillscreen(x, y))
+ Primitive(self.tw.canvas.fillscreen))
palette.add_block('fillscreen2',
style='basic-style-3arg',
@@ -338,7 +361,7 @@ shade)'))
self.tw.lc.def_prim(
'fillscreen2',
3,
- lambda self, x, y, z: self.tw.canvas.fillscreen_with_gray(x, y, z))
+ Primitive(self.tw.canvas.fillscreen_with_gray))
define_logo_function('tasetbackground', 'to tasetbackground :color \
:shade\ntasetshade :shade\nsetbackground :color\nend\n')
@@ -354,8 +377,8 @@ turtle'))
self.tw.lc.def_prim(
'setcolor',
1,
- lambda self, x: primitive_dictionary['set'](
- 'color', self.tw.turtles.get_active_turtle().set_color, x))
+ Primitive(Turtle.set_color,
+ call_afterwards=lambda value: self.after_set('color', value)))
palette.add_block('setshade',
style='basic-style-1arg',
@@ -368,8 +391,8 @@ turtle'))
self.tw.lc.def_prim(
'setshade',
1,
- lambda self, x: primitive_dictionary['set'](
- 'shade', self.tw.turtles.get_active_turtle().set_shade, x))
+ Primitive(Turtle.set_shade,
+ call_afterwards=lambda value: self.after_set('shade', value)))
palette.add_block('setgray',
style='basic-style-1arg',
@@ -381,8 +404,8 @@ the turtle'))
self.tw.lc.def_prim(
'setgray',
1,
- lambda self, x: primitive_dictionary['set'](
- 'gray', self.tw.turtles.get_active_turtle().set_gray, x))
+ Primitive(Turtle.set_gray,
+ call_afterwards=lambda value: self.after_set('gray', value)))
palette.add_block('color',
style='box-style',
@@ -392,10 +415,7 @@ in place of a number block)'),
value_block=True,
prim_name='color',
logo_command='pencolor')
- self.tw.lc.def_prim(
- 'color',
- 0,
- lambda self: self.tw.turtles.get_active_turtle().get_color())
+ self.tw.lc.def_prim('color', 0, Primitive(Turtle.get_color))
palette.add_block('shade',
style='box-style',
@@ -404,10 +424,7 @@ in place of a number block)'),
value_block=True,
prim_name='shade',
logo_command=':shade')
- self.tw.lc.def_prim(
- 'shade',
- 0,
- lambda self: self.tw.turtles.get_active_turtle().get_shade())
+ self.tw.lc.def_prim('shade', 0, Primitive(Turtle.get_shade))
palette.add_block('gray',
style='box-style',
@@ -416,8 +433,7 @@ in place of a number block)'),
used in place of a number block)'),
value_block=True,
prim_name='gray')
- self.tw.lc.def_prim('gray', 0, lambda self:
- self.tw.turtles.get_active_turtle().get_gray())
+ self.tw.lc.def_prim('gray', 0, Primitive(Turtle.get_gray))
palette.add_block('penup',
style='basic-style-extended-vertical',
@@ -428,8 +444,7 @@ used in place of a number block)'),
self.tw.lc.def_prim(
'penup',
0,
- lambda self:
- self.tw.turtles.get_active_turtle().set_pen_state(False))
+ Primitive(Turtle.set_pen_state, constant_args={0: False}))
palette.add_block('pendown',
style='basic-style-extended-vertical',
@@ -440,8 +455,7 @@ used in place of a number block)'),
self.tw.lc.def_prim(
'pendown',
0,
- lambda self:
- self.tw.turtles.get_active_turtle().set_pen_state(True))
+ Primitive(Turtle.set_pen_state, constant_args={0: True}))
palette.add_block('penstate',
style='boolean-block-style',
@@ -451,7 +465,7 @@ used in place of a number block)'),
self.tw.lc.def_prim(
'penstate',
0,
- lambda self: self.tw.turtles.get_active_turtle().get_pen_state())
+ Primitive(Turtle.get_pen_state))
palette.add_block('setpensize',
style='basic-style-1arg',
@@ -463,8 +477,8 @@ used in place of a number block)'),
turtle'))
self.tw.lc.def_prim(
'setpensize', 1,
- lambda self, x: primitive_dictionary['set']
- ('pensize', self.tw.turtles.get_active_turtle().set_pen_size, x))
+ Primitive(Turtle.set_pen_size,
+ call_afterwards=lambda val: self.after_set('pensize', val)))
define_logo_function('tasetpensize',
'to tasetpensize :a\nsetpensize round :a\nend\n')
@@ -477,7 +491,7 @@ fill block)'))
self.tw.lc.def_prim(
'startfill',
0,
- lambda self: self.tw.turtles.get_active_turtle().start_fill())
+ Primitive(Turtle.start_fill))
palette.add_block('stopfill',
style='basic-style-extended-vertical',
@@ -488,7 +502,7 @@ start fill block)'))
self.tw.lc.def_prim(
'stopfill',
0,
- lambda self: self.tw.turtles.get_active_turtle().stop_fill())
+ Primitive(Turtle.stop_fill))
palette.add_block('pensize',
style='box-style',
@@ -501,7 +515,7 @@ in place of a number block)'),
self.tw.lc.def_prim(
'pensize',
0,
- lambda self: self.tw.turtles.get_active_turtle().get_pen_size())
+ Primitive(Turtle.get_pen_size))
define_logo_function('tapensize', 'to tapensize\noutput first round \
pensize\nend\n')
@@ -609,7 +623,6 @@ tasetshade :shade \n')
colors=["#FF00FF", "#A000A0"],
help_string=_('Palette of numeric operators'))
- primitive_dictionary['plus'] = self._prim_plus
palette.add_block('plus2',
style='number-style',
label='+',
@@ -618,10 +631,11 @@ tasetshade :shade \n')
prim_name='plus',
logo_command='sum',
help_string=_('adds two alphanumeric inputs'))
- self.tw.lc.def_prim(
- 'plus', 2, lambda self, x, y: primitive_dictionary['plus'](x, y))
+ self.tw.lc.def_prim('plus', 2,
+ # TODO re-enable use with lists
+ Primitive(Primitive.plus, slot_wrappers={
+ (0, 2): Primitive(Primitive.convert_for_plus)}))
- primitive_dictionary['minus'] = self._prim_minus
palette.add_block('minus2',
style='number-style-porch',
label=' –',
@@ -630,12 +644,20 @@ tasetshade :shade \n')
logo_command='taminus',
help_string=_('subtracts bottom numeric input from \
top numeric input'))
- self.tw.lc.def_prim(
- 'minus', 2, lambda self, x, y: primitive_dictionary['minus'](x, y))
+ self.tw.lc.def_prim('minus', 2,
+ # TODO re-enable use with lists
+ Primitive(Primitive.minus, slot_wrappers={
+ 0: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]}),
+ 1: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]})}))
define_logo_function('taminus', 'to taminus :y :x\noutput sum :x \
minus :y\nend\n')
- primitive_dictionary['product'] = self._prim_product
palette.add_block('product2',
style='number-style',
label='×',
@@ -643,11 +665,18 @@ minus :y\nend\n')
prim_name='product',
logo_command='product',
help_string=_('multiplies two numeric inputs'))
- self.tw.lc.def_prim(
- 'product', 2,
- lambda self, x, y: primitive_dictionary['product'](x, y))
+ self.tw.lc.def_prim('product', 2,
+ # TODO re-enable use with lists
+ Primitive(Primitive.multiply, slot_wrappers={
+ 0: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]}),
+ 1: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]})}))
- primitive_dictionary['division'] = self._prim_careful_divide
palette.add_block('division2',
style='number-style-porch',
label=' /',
@@ -656,11 +685,21 @@ minus :y\nend\n')
logo_command='quotient',
help_string=_('divides top numeric input \
(numerator) by bottom numeric input (denominator)'))
- self.tw.lc.def_prim(
- 'division', 2,
- lambda self, x, y: primitive_dictionary['division'](x, y))
+ self.tw.lc.def_prim('division', 2,
+ # TODO re-enable use with lists
+ Primitive(Primitive.divide, slot_wrappers={
+ 0: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]}),
+ 1: Primitive(self.check_non_zero,
+ export_me=False,
+ slot_wrappers={
+ 0: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]})})}))
- primitive_dictionary['id'] = self._prim_identity
palette.add_block('identity2',
style='number-style-1arg',
label='←',
@@ -668,10 +707,8 @@ minus :y\nend\n')
prim_name='id',
help_string=_('identity operator used for extending \
blocks'))
- self.tw.lc.def_prim('id', 1,
- lambda self, x: primitive_dictionary['id'](x))
+ self.tw.lc.def_prim('id', 1, Primitive(Primitive.identity))
- primitive_dictionary['remainder'] = self._prim_mod
palette.add_block('remainder2',
style='number-style-porch',
label=_('mod'),
@@ -680,10 +717,19 @@ blocks'))
logo_command='remainder',
help_string=_('modular (remainder) operator'))
self.tw.lc.def_prim('remainder', 2,
- lambda self, x, y:
- primitive_dictionary['remainder'](x, y))
+ Primitive(Primitive.modulo, slot_wrappers={
+ 0: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]}),
+ 1: Primitive(self.check_non_zero,
+ export_me=False,
+ slot_wrappers={
+ 0: Primitive(self.check_number,
+ export_me=False,
+ slot_wrappers={
+ 0: self.prim_cache["convert_to_number"]})})}))
- primitive_dictionary['sqrt'] = self._prim_sqrt
palette.add_block('sqrt',
style='number-style-1arg',
label=_('√'),
@@ -692,7 +738,13 @@ blocks'))
logo_command='tasqrt',
help_string=_('calculates square root'))
self.tw.lc.def_prim('sqrt', 1,
- lambda self, x: primitive_dictionary['sqrt'](x))
+ Primitive(sqrt,
+ slot_wrappers={0: Primitive(self.check_non_negative,
+ slot_wrappers={0: Primitive(self.check_number,
+ slot_wrappers={0:
+ self.prim_cache["convert_to_number"]},
+ export_me=False)},
+ export_me=False)}))
primitive_dictionary['random'] = self._prim_random
palette.add_block('random',
@@ -717,7 +769,6 @@ output (random (:max - :min)) + :min\nend\n')
help_string=_('used as numeric input in mathematic \
operators'))
- primitive_dictionary['more'] = self._prim_more
palette.add_block('greater2',
style='compare-porch-style',
label=' >',
@@ -726,11 +777,11 @@ operators'))
prim_name='greater?',
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))
+ self.tw.lc.def_prim('greater?', 2,
+ Primitive(Primitive.greater,
+ slot_wrappers={0: self.prim_cache["convert_for_cmp"],
+ 1: self.prim_cache["convert_for_cmp"]}))
- primitive_dictionary['less'] = self._prim_less
palette.add_block('less2',
style='compare-porch-style',
label=' <',
@@ -739,10 +790,11 @@ operators'))
prim_name='less?',
logo_command='less?',
help_string=_('logical less-than operator'))
- self.tw.lc.def_prim(
- 'less?', 2, lambda self, x, y: primitive_dictionary['less'](x, y))
+ self.tw.lc.def_prim('less?', 2,
+ Primitive(Primitive.less,
+ slot_wrappers={0: self.prim_cache["convert_for_cmp"],
+ 1: self.prim_cache["convert_for_cmp"]}))
- primitive_dictionary['equal'] = self._prim_equal
palette.add_block('equal2',
style='compare-style',
label='=',
@@ -752,8 +804,9 @@ operators'))
logo_command='equal?',
help_string=_('logical equal-to operator'))
self.tw.lc.def_prim('equal?', 2,
- lambda self, x, y:
- primitive_dictionary['equal'](x, y))
+ Primitive(Primitive.equals,
+ slot_wrappers={0: self.prim_cache["convert_for_cmp"],
+ 1: self.prim_cache["convert_for_cmp"]}))
palette.add_block('not',
style='not-style',
@@ -761,9 +814,8 @@ operators'))
prim_name='not',
logo_command='not',
help_string=_('logical NOT operator'))
- self.tw.lc.def_prim('not', 1, lambda self, x: not x)
+ self.tw.lc.def_prim('not', 1, Primitive(Primitive.not_))
- primitive_dictionary['and'] = self._prim_and
palette.add_block('and2',
style='boolean-style',
label=_('and'),
@@ -772,9 +824,8 @@ operators'))
special_name=_('and'),
help_string=_('logical AND operator'))
self.tw.lc.def_prim(
- 'and', 2, lambda self, x, y: primitive_dictionary['and'](x, y))
+ 'and', 2, Primitive(Primitive.and_))
- primitive_dictionary['or'] = self._prim_or
palette.add_block('or2',
style='boolean-style',
label=_('or'),
@@ -783,7 +834,7 @@ operators'))
special_name=_('or'),
help_string=_('logical OR operator'))
self.tw.lc.def_prim(
- 'or', 2, lambda self, x, y: primitive_dictionary['or'](x, y))
+ 'or', 2, Primitive(Primitive.or_))
def _flow_palette(self):
''' The basic Turtle Art flow palette '''
@@ -813,8 +864,11 @@ number of seconds'))
default=[None, None],
logo_command='forever',
help_string=_('loops forever'))
- self.tw.lc.def_prim('forever', 1, primitive_dictionary['forever'],
- True)
+ self.tw.lc.def_prim('forever', 1,
+ Primitive(self.tw.lc.prim_loop,
+ constant_args={0: Primitive(Primitive.controller_forever,
+ call_me=False)}),
+ True)
primitive_dictionary['repeat'] = self._prim_repeat
palette.add_block('repeat',
@@ -825,9 +879,14 @@ number of seconds'))
logo_command='repeat',
special_name=_('repeat'),
help_string=_('loops specified number of times'))
- self.tw.lc.def_prim('repeat', 2, primitive_dictionary['repeat'], True)
+ self.tw.lc.def_prim('repeat', 2,
+ Primitive(self.tw.lc.prim_loop,
+ slot_wrappers={0: Primitive(Primitive.controller_repeat,
+ slot_wrappers={0: Primitive(self.tw.lc.int,
+ slot_wrappers={0: self.prim_cache["check_number"]
+ })})}),
+ True)
- primitive_dictionary['if'] = self._prim_if
palette.add_block('if',
style='clamp-style-boolean',
label=[_('if'), _('then'), ''],
@@ -837,9 +896,8 @@ number of seconds'))
logo_command='if',
help_string=_('if-then operator that uses boolean \
operators from Numbers palette'))
- self.tw.lc.def_prim('if', 2, primitive_dictionary['if'], True)
+ self.tw.lc.def_prim('if', 2, Primitive(self.tw.lc.prim_if), True)
- primitive_dictionary['ifelse'] = self._prim_ifelse
palette.add_block('ifelse',
hidden=True, # Too big to fit palette
style='clamp-style-else',
@@ -850,7 +908,8 @@ operators from Numbers palette'))
special_name=_('if then else'),
help_string=_('if-then-else operator that uses \
boolean operators from Numbers palette'))
- self.tw.lc.def_prim('ifelse', 3, primitive_dictionary['ifelse'], True)
+ self.tw.lc.def_prim('ifelse', 3, Primitive(self.tw.lc.prim_ifelse),
+ True)
# macro
palette.add_block('ifthenelse',
@@ -867,7 +926,8 @@ boolean operators from Numbers palette'))
prim_name='nop',
special_name=_('horizontal space'),
help_string=_('jogs stack right'))
- self.tw.lc.def_prim('nop', 0, lambda self: None)
+ self.tw.lc.def_prim('nop', 0,
+ Primitive(Primitive.do_nothing, export_me=False))
palette.add_block('vspace',
style='basic-style-extended-vertical',
@@ -875,7 +935,8 @@ boolean operators from Numbers palette'))
prim_name='nop',
special_name=_('vertical space'),
help_string=_('jogs stack down'))
- self.tw.lc.def_prim('nop', 0, lambda self: None)
+ self.tw.lc.def_prim('nop', 0,
+ Primitive(Primitive.do_nothing, export_me=False))
primitive_dictionary['stopstack'] = self._prim_stopstack
palette.add_block('stopstack',
@@ -896,7 +957,6 @@ boolean operators from Numbers palette'))
colors=["#FFFF00", "#A0A000"],
help_string=_('Palette of variable blocks'))
- primitive_dictionary['start'] = self._prim_start
palette.add_block('start',
style='basic-style-head',
label=_('start'),
@@ -905,7 +965,11 @@ boolean operators from Numbers palette'))
help_string=_('connects action to toolbar run \
buttons'))
self.tw.lc.def_prim('start', 0,
- lambda self: primitive_dictionary['start']())
+ Primitive(Primitive.group, constant_args={0: [
+ Primitive(self.tw.lc.prim_start, call_me=False,
+ export_me=False),
+ Primitive(self.tw.lc.prim_define_stack,
+ constant_args={0: 'start'}, call_me=False)]}))
palette.add_block('string',
style='box-style',
@@ -922,9 +986,9 @@ buttons'))
default=_('action'),
logo_command='to action',
help_string=_('top of nameable action stack'))
- self.tw.lc.def_prim('nop3', 1, lambda self, x: None)
+ self.tw.lc.def_prim('nop3', 1, Primitive(self.tw.lc.prim_define_stack))
- primitive_dictionary['stack'] = self._prim_stack
+ primitive_dictionary['stack'] = Primitive(self.tw.lc.prim_invoke_stack)
palette.add_block('stack',
style='basic-style-1arg',
label=_('action'),
@@ -933,9 +997,10 @@ buttons'))
logo_command='action',
default=_('action'),
help_string=_('invokes named action stack'))
- self.tw.lc.def_prim('stack', 1, primitive_dictionary['stack'], True)
+ self.tw.lc.def_prim('stack', 1,
+ Primitive(self.tw.lc.prim_invoke_stack), True)
- primitive_dictionary['setbox'] = self._prim_setbox
+ primitive_dictionary['setbox'] = Primitive(self.tw.lc.prim_set_box)
palette.add_block('storeinbox1',
hidden=True,
style='basic-style-1arg',
@@ -946,9 +1011,7 @@ buttons'))
logo_command='make "box1',
help_string=_('stores numeric value in Variable 1'))
self.tw.lc.def_prim('storeinbox1', 1,
- lambda self, x:
- primitive_dictionary['setbox']
- ('box1', None, x))
+ Primitive(self.tw.lc.prim_set_box, constant_args={0: 'box1'}))
palette.add_block('storeinbox2',
hidden=True,
@@ -960,9 +1023,7 @@ buttons'))
logo_command='make "box2',
help_string=_('stores numeric value in Variable 2'))
self.tw.lc.def_prim('storeinbox2', 1,
- lambda self, x:
- primitive_dictionary['setbox']
- ('box2', None, x))
+ Primitive(self.tw.lc.prim_set_box, constant_args={0: 'box2'}))
palette.add_block('box1',
hidden=True,
@@ -972,7 +1033,8 @@ buttons'))
logo_command=':box1',
help_string=_('Variable 1 (numeric value)'),
value_block=True)
- self.tw.lc.def_prim('box1', 0, lambda self: self.tw.lc.boxes['box1'])
+ self.tw.lc.def_prim('box1', 0,
+ Primitive(self.tw.lc.prim_get_box, constant_args={0: 'box1'}))
palette.add_block('box2',
hidden=True,
@@ -982,7 +1044,8 @@ buttons'))
logo_command=':box2',
help_string=_('Variable 2 (numeric value)'),
value_block=True)
- self.tw.lc.def_prim('box2', 0, lambda self: self.tw.lc.boxes['box2'])
+ self.tw.lc.def_prim('box2', 0,
+ Primitive(self.tw.lc.prim_get_box, constant_args={0: 'box2'}))
palette.add_block('storein',
style='basic-style-2arg',
@@ -994,11 +1057,9 @@ buttons'))
help_string=_('stores numeric value in named \
variable'))
self.tw.lc.def_prim('storeinbox', 2,
- lambda self, x, y:
- primitive_dictionary['setbox']
- ('box3', x, y))
+ Primitive(self.tw.lc.prim_set_box))
- primitive_dictionary['box'] = self._prim_box
+ primitive_dictionary['box'] = Primitive(self.tw.lc.prim_get_box)
palette.add_block('box',
style='number-style-1strarg',
hidden=True,
@@ -1010,7 +1071,7 @@ variable'))
value_block=True,
help_string=_('named variable (numeric value)'))
self.tw.lc.def_prim('box', 1,
- lambda self, x: primitive_dictionary['box'](x))
+ Primitive(self.tw.lc.prim_get_box))
palette.add_block('hat1',
hidden=True,
@@ -1019,7 +1080,9 @@ variable'))
prim_name='nop1',
logo_command='to stack1\n',
help_string=_('top of Action 1 stack'))
- self.tw.lc.def_prim('nop1', 0, lambda self: None)
+ self.tw.lc.def_prim('nop1', 0,
+ Primitive(self.tw.lc.prim_define_stack,
+ constant_args={0: 'stack1'}))
palette.add_block('hat2',
hidden=True,
@@ -1028,9 +1091,10 @@ variable'))
prim_name='nop2',
logo_command='to stack2\n',
help_string=_('top of Action 2 stack'))
- self.tw.lc.def_prim('nop2', 0, lambda self: None)
+ self.tw.lc.def_prim('nop2', 0,
+ Primitive(self.tw.lc.prim_define_stack,
+ constant_args={0: 'stack2'}))
- primitive_dictionary['stack1'] = self._prim_stack1
palette.add_block('stack1',
hidden=True,
style='basic-style-extended-vertical',
@@ -1038,9 +1102,11 @@ variable'))
prim_name='stack1',
logo_command='stack1',
help_string=_('invokes Action 1 stack'))
- self.tw.lc.def_prim('stack1', 0, primitive_dictionary['stack1'], True)
+ self.tw.lc.def_prim('stack1', 0,
+ Primitive(self.tw.lc.prim_invoke_stack,
+ constant_args={0: 'stack1'}),
+ True)
- primitive_dictionary['stack2'] = self._prim_stack2
palette.add_block('stack2',
hidden=True,
style='basic-style-extended-vertical',
@@ -1048,7 +1114,10 @@ variable'))
prim_name='stack2',
logo_command='stack2',
help_string=_('invokes Action 2 stack'))
- self.tw.lc.def_prim('stack2', 0, primitive_dictionary['stack2'], True)
+ self.tw.lc.def_prim('stack2', 0,
+ Primitive(self.tw.lc.prim_invoke_stack,
+ constant_args={0: 'stack2'}),
+ True)
def _trash_palette(self):
''' The basic Turtle Art turtle palette '''
@@ -1084,9 +1153,7 @@ variable'))
''' Logical and '''
return x & y
- def _prim_arc(self, cmd, value1, value2):
- ''' Turtle draws an arc of degree, radius '''
- cmd(float(value1), float(value2))
+ def after_arc(self, *ignored_args):
if self.tw.lc.update_values:
self.tw.lc.update_label_value(
'xcor',
@@ -1119,46 +1186,39 @@ variable'))
break
self.tw.lc.ireturn()
yield True
-
- def _prim_if(self, boolean, blklist):
- ''' If bool, do list '''
- if boolean:
- self.tw.lc.icall(self.tw.lc.evline, blklist[:])
- yield True
- self.tw.lc.ireturn()
- yield True
-
- def _prim_ifelse(self, boolean, list1, list2):
- ''' If bool, do list1, else do list2 '''
- if boolean:
- self.tw.lc.ijmp(self.tw.lc.evline, list1[:])
- yield True
+
+ def convert_value_for_move(self, value):
+ ''' Perform type conversion and other preprocessing on the parameter,
+ so it can be passed to the 'move' primitive. '''
+ if value is None:
+ return value
+
+ def _convert_to_float(val):
+ if not _num_type(val):
+ raise logoerror("#notanumber")
+ return float(val)
+
+ if isinstance(value, (tuple, list)):
+ (val1, val2) = value
+ val1_float = _convert_to_float(val1)
+ val2_float = _convert_to_float(val2)
+ value_converted = (val1_float, val2_float)
else:
- self.tw.lc.ijmp(self.tw.lc.evline, list2[:])
- yield True
+ value_converted = _convert_to_float(value)
+ return value_converted
- def _prim_move(self, cmd, value1, value2=None, pendown=True,
+ def _prim_move(self, cmd, value1, pendown=True,
reverse=False):
''' Turtle moves by method specified in value1 '''
- pos = None
- if isinstance(value1, (tuple, list)):
- pos = value1
- value1 = pos[0]
- value2 = pos[1]
- if not _num_type(value1):
- raise logoerror("#notanumber")
- if value2 is None:
- if reverse:
- cmd(float(-value1))
- else:
- cmd(float(value1))
- else:
- if not _num_type(value2):
- raise logoerror("#notanumber")
- if pos is not None:
- cmd((float(value1), float(value2)), pendown=pendown)
- else:
- cmd(float(value1), float(value2), pendown=pendown)
+
+ value1_conv = self.convert_value_for_move(value1)
+
+ cmd(value1_conv, pendown=pendown)
+
+ self.after_move()
+
+ def after_move(self, *ignored_args):
+ ''' Update labels after moving the turtle '''
if self.tw.lc.update_values:
self.tw.lc.update_label_value(
'xcor',
@@ -1185,15 +1245,31 @@ variable'))
break
self.tw.lc.ireturn()
yield True
-
- def _prim_right(self, value, reverse=False):
- ''' Turtle rotates clockwise '''
+
+ def check_number(self, value):
+ ''' Check if value is a number. If yes, return the value. If no,
+ raise a logoerror. '''
if not _num_type(value):
raise logoerror("#notanumber")
- if reverse:
- self.tw.turtles.get_active_turtle().right(float(-value))
- else:
- self.tw.turtles.get_active_turtle().right(float(value))
+ return value
+
+ def check_non_negative(self, x, msg="#negroot"):
+ ''' Raise a logoerror iff x is negative. Otherwise, return x
+ unchanged.
+ msg -- the name of the logoerror message '''
+ if x < 0:
+ raise logoerror(msg)
+ return x
+
+ def check_non_zero(self, x, msg="#zerodivide"):
+ ''' Raise a logoerror iff x is zero. Otherwise, return x
+ unchanged.
+ msg -- the name of the logoerror message '''
+ if x == 0:
+ raise logoerror(msg)
+ return x
+
+ def after_right(self, *ignored_args):
if self.tw.lc.update_values:
self.tw.lc.update_label_value(
'heading',
@@ -1206,6 +1282,12 @@ variable'))
if self.tw.lc.update_values:
self.tw.lc.update_label_value(name, value)
+ def after_set(self, name, value=None):
+ ''' Update the associated value blocks '''
+ if value is not None:
+ if self.tw.lc.update_values:
+ self.tw.lc.update_label_value(name, value)
+
def _prim_setbox(self, name, x, val):
''' Define value of named box '''
if x is not None:
@@ -1256,11 +1338,6 @@ variable'))
self.tw.lc.ireturn()
yield True
- def _prim_start(self):
- ''' Start block: recenter '''
- if self.tw.running_sugar:
- self.tw.activity.recenter()
-
def _prim_stopstack(self):
''' Stop execution of a stack '''
self.tw.lc.procstop = True
@@ -1276,7 +1353,7 @@ variable'))
self.tw.lc.ireturn()
yield True
- # Math primitivies
+ # Math primitives
def _prim_careful_divide(self, x, y):
''' Raise error on divide by zero '''
@@ -1302,57 +1379,12 @@ variable'))
except TypeError:
raise logoerror("#notanumber")
- def _prim_equal(self, x, y):
- ''' Numeric and logical equal '''
- if isinstance(x, list) and isinstance(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:
- typex, typey = False, False
- if strtype(x):
- typex = True
- if strtype(y):
- typey = True
- if typex and typey:
- return x == y
- try:
- return self._string_to_num(x) == self._string_to_num(y)
- except TypeError:
- raise logoerror("#syntaxerror")
-
- def _prim_less(self, x, y):
- ''' Compare numbers and strings '''
- if isinstance(x, list) or isinstance(y, list):
- raise logoerror("#syntaxerror")
- try:
- return float(x) < float(y)
- except ValueError:
- typex, typey = False, False
- if strtype(x):
- typex = True
- if strtype(y):
- typey = True
- if typex and typey:
- return x < y
- try:
- return self._string_to_num(x) < self._string_to_num(y)
- except TypeError:
- raise logoerror("#notanumber")
-
- def _prim_more(self, x, y):
- ''' Compare numbers and strings '''
- return self._prim_less(y, x)
-
def _prim_plus(self, x, y):
''' Add numbers, concat strings '''
- if x in COLORDICT:
- x = _color_to_num(x)
- if y in COLORDICT:
- y = _color_to_num(y)
+ if isinstance(x, Color):
+ x = int(x)
+ if isinstance(y, Color):
+ y = int(y)
if _num_type(x) and _num_type(y):
return(x + y)
elif isinstance(x, list) and isinstance(y, list):
@@ -1415,19 +1447,6 @@ variable'))
except ValueError:
raise logoerror("#syntaxerror")
- def _prim_sqrt(self, x):
- ''' Square root '''
- if _num_type(x):
- if x < 0:
- raise logoerror("#negroot")
- return sqrt(x)
- try:
- return sqrt(self._string_to_num(x))
- except ValueError:
- raise logoerror("#negroot")
- except TypeError:
- raise logoerror("#notanumber")
-
def _prim_random(self, x, y):
''' Random integer '''
if _num_type(x) and _num_type(y):
@@ -1445,14 +1464,10 @@ variable'))
except TypeError:
raise logoerror("#notanumber")
- def _prim_identity(self, x):
- ''' Identity function '''
- return(x)
-
# Utilities
def _string_to_num(self, x):
- ''' Try to comvert a string to a number '''
+ ''' Try to convert a string to a number '''
if isinstance(x, (int, float)):
return(x)
try:
@@ -1461,8 +1476,8 @@ variable'))
pass
if isinstance(x, list):
raise logoerror("#syntaxerror")
- if x in COLORDICT:
- return _color_to_num(x)
+ if isinstance(x, Color):
+ return int(x)
xx = convert(x.replace(self.tw.decimal_point, '.'), float)
if isinstance(xx, float):
return xx
@@ -1475,12 +1490,12 @@ variable'))
def _make_constant(self, palette, block_name, label, constant):
''' Factory for constant blocks '''
- if constant in COLORDICT:
- if COLORDICT[constant][0] is not None:
- value = str(COLORDICT[constant][0])
+ if isinstance(constant, Color):
+ if constant.color is not None:
+ value = str(constant.color)
else:
# Black or White
- value = '0 tasetshade %d' % (COLORDICT[constant][1])
+ value = '0 tasetshade %d' % (constant.shade)
else:
value = constant
palette.add_block(block_name,
@@ -1488,4 +1503,5 @@ variable'))
label=label,
prim_name=block_name,
logo_command=value)
- self.tw.lc.def_prim(block_name, 0, lambda self: constant)
+ self.tw.lc.def_prim(block_name, 0,
+ Primitive(Primitive.identity, constant_args={0: constant}))
diff --git a/TurtleArt/tablock.py b/TurtleArt/tablock.py
index ff392e0..f8826ff 100644
--- a/TurtleArt/tablock.py
+++ b/TurtleArt/tablock.py
@@ -24,7 +24,8 @@ import cairo
from taconstants import (EXPANDABLE, EXPANDABLE_ARGS, OLD_NAMES, CONSTANTS,
STANDARD_STROKE_WIDTH, BLOCK_SCALE, BOX_COLORS,
- GRADIENT_COLOR, EXPANDABLE_FLOW, COLORDICT)
+ GRADIENT_COLOR, EXPANDABLE_FLOW, Color,
+ PREFIX_DICTIONARY)
from tapalette import (palette_blocks, block_colors, expandable_blocks,
content_blocks, block_names, block_primitives,
block_styles, special_block_colors)
@@ -34,6 +35,9 @@ import sprites
from tautils import (debug_output, error_output)
+media_blocks_dictionary = {} # new media blocks get added here
+
+
class Blocks:
""" A class for the list of blocks and everything they share in common """
@@ -277,6 +281,47 @@ class Block:
return False
return True
+ def is_value_block(self):
+ """ Return True iff this block is a value block (numeric, string,
+ media, etc.) """
+ return self.primitive is None and self.values
+
+ def get_value(self, add_type_prefix=True):
+ """ Return the value stored in this value block or None if this is
+ not a value block
+ add_type_prefix -- prepend a prefix to indicate the type of the
+ 'raw' value """
+ if not self.is_value_block():
+ return None
+
+ result = ''
+ if self.name == 'number':
+ try:
+ return float(self.values[0])
+ except ValueError:
+ return float(ord(self.values[0][0]))
+ elif (self.name == 'string' or
+ self.name == 'title'): # deprecated block
+ if add_type_prefix:
+ result = '#s'
+ if isinstance(self.values[0], (float, int)):
+ if int(self.values[0]) == self.values[0]:
+ self.values[0] = int(self.values[0])
+ result += str(self.values[0])
+ else:
+ result += self.values[0]
+ elif self.name in PREFIX_DICTIONARY:
+ if add_type_prefix:
+ result = PREFIX_DICTIONARY[self.name]
+ result += str(self.values[0])
+ elif self.name in media_blocks_dictionary:
+ if add_type_prefix:
+ result = '#smedia_'
+ result += self.name.upper()
+ else:
+ return None
+ return result
+
def highlight(self):
""" We may want to highlight a block... """
if self.spr is not None and self.status is not 'collapsed':
@@ -529,10 +574,8 @@ class Block:
else:
self._set_labels(i, str(v))
elif self.type == 'block' and self.name in CONSTANTS:
- if CONSTANTS[self.name] in COLORDICT:
- v = COLORDICT[CONSTANTS[self.name]][0]
- if v is None:
- v = COLORDICT[CONSTANTS[self.name]][1]
+ if isinstance(CONSTANTS[self.name], Color):
+ v = int(CONSTANTS[self.name])
else:
v = CONSTANTS[self.name]
self._set_labels(0, block_names[self.name][0] + ' = ' + str(v))
@@ -555,7 +598,7 @@ class Block:
if self.spr is None:
return
if isinstance(self.name, unicode):
- self.name = self.name.encode('utf-8')
+ self.name = self.name.encode('utf8')
if self.name in content_blocks:
n = len(self.values)
if n == 0:
@@ -636,7 +679,7 @@ class Block:
self.svg.set_stroke_width(STANDARD_STROKE_WIDTH)
self.svg.clear_docks()
if isinstance(self.name, unicode):
- self.name = self.name.encode('utf-8')
+ self.name = self.name.encode('utf8')
for k in block_styles.keys():
if self.name in block_styles[k]:
if isinstance(self.block_methods[k], list):
diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py
index 89b8ed1..4bac442 100644
--- a/TurtleArt/tacanvas.py
+++ b/TurtleArt/tacanvas.py
@@ -1,4 +1,4 @@
-31#Copyright (c) 2007-8, Playful Invention Company.
+#Copyright (c) 2007-8, Playful Invention Company.
#Copyright (c) 2008-11, Walter Bender
#Copyright (c) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
@@ -28,7 +28,7 @@ import cairo
import pangocairo
from tautils import get_path
-from taconstants import COLORDICT, TMP_SVG_PATH
+from taconstants import Color, TMP_SVG_PATH
def wrap100(n):
@@ -208,19 +208,19 @@ class TurtleGraphics:
save_rgb = self._fgrgb[:]
# Special case for color blocks
- if color in COLORDICT:
- if COLORDICT[color][0] is None:
- self._shade = COLORDICT[color][1]
+ if isinstance(color, Color):
+ if color.color is None:
+ self._shade = color.shade
else:
- self._color = COLORDICT[color][0]
+ self._color = color.color
else:
self._color = color
- if shade in COLORDICT:
- self._shade = COLORDICT[shade][1]
+ if isinstance(shade, Color):
+ self._shade = shade.shade
else:
self._shade = shade
- if gray in COLORDICT:
- self._gray = COLORDICT[gray][2]
+ if isinstance(gray, Color):
+ self._gray = gray.gray
else:
self._gray = gray
diff --git a/TurtleArt/taconstants.py b/TurtleArt/taconstants.py
index 835209e..ba0b5a7 100644
--- a/TurtleArt/taconstants.py
+++ b/TurtleArt/taconstants.py
@@ -79,20 +79,101 @@ XO4 = 'xo4'
UNKNOWN = 'unknown'
TMP_SVG_PATH = '/tmp/turtle_output.svg'
+
+
+class Color(object):
+ """ A color used in block programs (e.g., as pen color). """
+
+ def __init__(self, name, color=0, shade=50, gray=100):
+ """ name -- a string with the name of the color, e.g., 'red'
+ color -- the hue (0-100, or None for white, gray, and black)
+ shade -- the lightness (0 is black, 100 is white)
+ gray -- the saturation (0 is gray, 100 is fully saturated) """
+ self.name = name
+ self.color = color
+ self.shade = shade
+ self.gray = gray
+
+ def __int__(self):
+ if self.color is None:
+ return int(self.shade)
+ else:
+ return int(self.color)
+
+ def __float__(self):
+ return float(int(self))
+
+ def __str__(self):
+ return str(self.name)
+
+ def __repr__(self):
+ return '%s (%s/%d/%d)' % (str(self.name), str(self.color),
+ self.shade, self.gray)
+
+ def __eq__(self, other):
+ """ A Color is equivalent to
+ * another Color with the same color, shade, and gray values
+ * an integer, float, or long that equals int(self) """
+ if isinstance(other, Color):
+ return (self.color == other.color and self.shade == other.shade
+ and self.gray == other.gray)
+ elif isinstance(other, (int, float, long)):
+ return int(self) == other
+ ## * a basestring that equals str(self)
+ #elif isinstance(other, basestring):
+ # return str(self) == other
+ else:
+ return False
+
+ def __lt__(self, other):
+ """ A Color is less than
+ * another Color whose name appears earlier in the alphabet
+ * a number that is less than int(self)
+ * a string that appears before the underscore in the ASCII table """
+ if isinstance(other, Color):
+ return str(self) < str(other)
+ elif isinstance(other, (int, float, long)):
+ return int(self) < other
+ elif isinstance(other, basestring):
+ return '_' + str(self) < other
+ else:
+ return False
+
+ def __gt__(self, other):
+ """ A Color is greater than
+ * another Color whose name appears later in the alphabet
+ * a number that is greater than int(self)
+ * a string that appears after the underscore in the ASCII table """
+ if isinstance(other, Color):
+ return str(self) > str(other)
+ elif isinstance(other, (int, float, long)):
+ return int(self) > other
+ elif isinstance(other, basestring):
+ return '_' + str(self) > other
+ else:
+ return False
+
+ def is_gray(self):
+ """ Return True iff this color is white, gray, or black, i.e. if its
+ hue is not set or its saturation is zero. """
+ return self.color is None or not self.gray
+
+
+
CONSTANTS = {'leftpos': None, 'toppos': None, 'rightpos': None,
'bottompos': None, 'width': None, 'height': None,
- 'black': '_black', 'white': '_white', 'red': '_red',
- 'orange': '_orange', 'yellow': '_yellow', 'green': '_green',
- 'cyan': '_cyan', 'blue': '_blue', 'purple': '_purple',
+ 'black': Color('black', None, 0, 0),
+ 'white': Color('white', None, 100, 0),
+ 'red': Color('red', 0, 50, 100),
+ 'orange': Color('orange', 10, 50, 100),
+ 'yellow': Color('yellow', 20, 50, 100),
+ 'green': Color('green', 40, 50, 100),
+ 'cyan': Color('cyan', 50, 50, 100),
+ 'blue': Color('blue', 70, 50, 100),
+ 'purple': Color('purple', 90, 50, 100),
'titlex': None, 'titley': None, 'leftx': None,
'topy': None, 'rightx': None, 'bottomy': None}
-COLORDICT = {'_black': [None, 0, 0], '_white': [None, 100, 0],
- '_red': [0, 50, 100], '_orange': [10, 50, 100],
- '_yellow': [20, 50, 100], '_green': [40, 50, 100],
- '_cyan': [50, 50, 100], '_blue': [70, 50, 100],
- '_purple': [90, 50, 100]}
-
# Blocks that are expandable
EXPANDABLE_STYLE = ['boolean-style', 'compare-porch-style', 'compare-style',
'number-style-porch', 'number-style', 'basic-style-2arg',
diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py
index 0b178c6..ef482a1 100644
--- a/TurtleArt/talogo.py
+++ b/TurtleArt/talogo.py
@@ -33,10 +33,14 @@ try:
except ImportError:
GRID_CELL_SIZE = 55
-from taconstants import (TAB_LAYER, DEFAULT_SCALE, PREFIX_DICTIONARY)
+import traceback
+
+from tablock import (Block, media_blocks_dictionary)
+from taconstants import (TAB_LAYER, DEFAULT_SCALE)
from tapalette import (block_names, value_blocks)
from tautils import (get_pixbuf_from_journal, convert, data_from_file,
- text_media_type, round_int, debug_output, find_group)
+ text_media_type, round_int, debug_output, find_group,
+ get_stack_name)
try:
from util.RtfParser import RtfTextOnly
@@ -46,7 +50,6 @@ except ImportError:
from gettext import gettext as _
-media_blocks_dictionary = {} # new media blocks get added here
primitive_dictionary = {} # new block primitives get added here
@@ -79,7 +82,7 @@ class logoerror(Exception):
return str(self.value)
-class HiddenBlock:
+class HiddenBlock(Block):
def __init__(self, name, value=None):
self.name = name
@@ -92,6 +95,7 @@ class HiddenBlock:
self.connections = []
self.docks = []
+
# Utility functions
@@ -187,6 +191,10 @@ class LogoCode:
self.oblist[string] = sym
return sym
+ def get_prim_callable(self, name):
+ """ Return the callable primitive associated with the given name """
+ return self.oblist[name].fcn
+
def run_blocks(self, code):
"""Run code generated by generate_code().
"""
@@ -234,27 +242,17 @@ class LogoCode:
blk = action_blk
for b in blocks:
- if b.name == 'hat1':
- code = self._blocks_to_code(b)
- self.stacks['stack1'] = self._readline(code)
- elif b.name == 'hat2':
- code = self._blocks_to_code(b)
- self.stacks['stack2'] = self._readline(code)
- elif b.name == 'hat':
- if b.connections is not None and len(b.connections) > 1 and \
- b.connections[1] is not None:
+ if b.name in ('hat', 'hat1', 'hat2'):
+ stack_name = get_stack_name(b)
+ if stack_name:
+ stack_key = self._get_stack_key(stack_name)
code = self._blocks_to_code(b)
- try:
- x = b.connections[1].values[0]
- except IndexError:
- self.tw.showlabel('#nostack')
- self.tw.showblocks()
- self.tw.running_blocks = False
- return None
- if isinstance(convert(x, float, False), float):
- if int(float(x)) == x:
- x = int(x)
- self.stacks['stack3' + str(x)] = self._readline(code)
+ self.stacks[stack_key] = self._readline(code)
+ else:
+ self.tw.showlabel('#nostack')
+ self.tw.showblocks()
+ self.tw.running_blocks = False
+ return None
code = self._blocks_to_code(blk)
@@ -278,7 +276,7 @@ class LogoCode:
return ['%nothing%', '%nothing%']
code = []
dock = blk.docks[0]
- if len(dock) > 4: # There could be a '(', ')', '[' or ']'.
+ if len(dock) > 4 and dock[4] in ('[', ']', ']['): # There could be a '(', ')', '[' or ']'.
code.append(dock[4])
if blk.primitive is not None: # make a tuple (prim, blk)
if blk in self.tw.block_list.list:
@@ -286,37 +284,19 @@ class LogoCode:
self.tw.block_list.list.index(blk)))
else:
code.append(blk.primitive) # Hidden block
- elif len(blk.values) > 0: # Extract the value from content blocks.
- if blk.name == 'number':
- try:
- code.append(float(blk.values[0]))
- except ValueError:
- code.append(float(ord(blk.values[0][0])))
- elif blk.name == 'string' or \
- blk.name == 'title': # deprecated block
- if isinstance(blk.values[0], (float, 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 in PREFIX_DICTIONARY:
- if blk.values[0] is not None:
- code.append(PREFIX_DICTIONARY[blk.name] +
- str(blk.values[0]))
- else:
- code.append(PREFIX_DICTIONARY[blk.name] + 'None')
- elif blk.name in media_blocks_dictionary:
- code.append('#smedia_' + blk.name.upper())
- else:
+ elif blk.is_value_block(): # Extract the value from content blocks.
+ value = blk.get_value()
+ if value is None:
return ['%nothing%']
+ else:
+ code.append(value)
else:
return ['%nothing%']
if blk.connections is not None and len(blk.connections) > 0:
for i in range(1, len(blk.connections)):
b = blk.connections[i]
dock = blk.docks[i]
- if len(dock) > 4: # There could be a '(', ')', '[' or ']'.
+ if len(dock) > 4 and dock[4] in ('[', ']', ']['): # There could be a '(', ')', '[' or ']'.
for c in dock[4]:
code.append(c)
if b is not None:
@@ -394,14 +374,13 @@ class LogoCode:
if self._disable_help:
self.tw.no_help = False
self._disable_help = False
- self.tw.display_coordinates()
def icall(self, fcn, *args):
""" Add a function and its arguments to the program stack. """
self.istack.append(self.step)
self.step = fcn(*(args))
- def evline(self, blklist):
+ def evline(self, blklist, call_me=True):
""" Evaluate a line of code from the list. """
oldiline = self.iline
self.iline = blklist[:]
@@ -432,7 +411,7 @@ class LogoCode:
(token, self.bindex) = self.iline[1]
# Process the token and any arguments.
- self.icall(self._eval)
+ self.icall(self._eval, call_me)
yield True
# Time to unhighlight the current block.
@@ -455,7 +434,7 @@ class LogoCode:
self.tw.display_coordinates()
yield True
- def _eval(self):
+ def _eval(self, call_me=True):
""" Evaluate the next token on the line of code we are processing. """
token = self.iline.pop(0)
bindex = None
@@ -467,7 +446,7 @@ class LogoCode:
# We highlight blocks here in case an error occurs...
if not self.tw.hide and bindex is not None:
self.tw.block_list.list[bindex].highlight()
- self.icall(self._evalsym, token)
+ self.icall(self._evalsym, token, call_me)
yield True
# and unhighlight if everything was OK.
if not self.tw.hide and bindex is not None:
@@ -479,7 +458,7 @@ class LogoCode:
self.ireturn(res)
yield True
- def _evalsym(self, token):
+ def _evalsym(self, token, call_me):
""" Process primitive associated with symbol token """
self._undefined_check(token)
oldcfun, oldarglist = self.cfun, self.arglist
@@ -489,35 +468,50 @@ class LogoCode:
self.tw.showblocks()
self.tw.display_coordinates()
raise logoerror("#noinput")
+ call_args = type(self.cfun.fcn).__name__ != 'Primitive'
for i in range(token.nargs):
self._no_args_check()
- self.icall(self._eval)
+ self.icall(self._eval, call_args)
yield True
self.arglist.append(self.iresult)
+ need_to_pop_istack = False
if self.cfun.rprim:
if isinstance(self.cfun.fcn, list):
# debug_output('evalsym rprim list: %s' % (str(token)),
# self.tw.running_sugar)
- self.icall(self._ufuncall, self.cfun.fcn)
+ self.icall(self._ufuncall, self.cfun.fcn, call_args)
yield True
+ need_to_pop_istack = True
+ result = None
else:
- self.icall(self.cfun.fcn, *self.arglist)
- yield True
- result = None
+ if call_me:
+ self.icall(self.cfun.fcn, *self.arglist)
+ yield True
+ need_to_pop_istack = True
+ result = None
+ else:
+ result = (self.cfun.fcn, ) + tuple(self.arglist)
else:
- result = self.cfun.fcn(self, *self.arglist)
+ need_to_pop_istack = True
+ if call_me:
+ result = self.cfun.fcn(self, *self.arglist)
+ else:
+ result = (self.cfun.fcn, self) + tuple(self.arglist)
self.cfun, self.arglist = oldcfun, oldarglist
if self.arglist is not None and result is None:
self.tw.showblocks()
raise logoerror("%s %s %s" %
(oldcfun.name, _("did not output to"),
self.cfun.name))
- self.ireturn(result)
- yield True
+ if need_to_pop_istack:
+ self.ireturn(result)
+ yield True
+ else:
+ self.iresult = result
- def _ufuncall(self, body):
+ def _ufuncall(self, body, call_me):
""" ufuncall """
- self.ijmp(self.evline, body)
+ self.ijmp(self.evline, body, call_me)
yield True
def doevalstep(self):
@@ -529,28 +523,40 @@ class LogoCode:
if self.step is not None:
try:
self.step.next()
- except ValueError:
- debug_output('generator already executing',
- self.tw.running_sugar)
- self.tw.running_blocks = False
+ except ValueError, ve:
+ if self.tw.running_turtleart:
+ debug_output('generator already executing',
+ self.tw.running_sugar)
+ self.tw.running_blocks = False
+ else:
+ traceback.print_exc()
+ self.tw.showlabel('status', 'ValueError: ' +
+ str(ve))
return False
else:
return False
except StopIteration:
- # self.tw.turtles.show_all()
- if self.hidden_turtle is not None:
- self.hidden_turtle.show()
- self.hidden_turtle = None
+ if self.tw.running_turtleart:
+ # self.tw.turtles.show_all()
+ if self.hidden_turtle is not None:
+ self.hidden_turtle.show()
+ self.hidden_turtle = None
+ else:
+ self.tw.turtles.get_active_turtle().show()
+ self.tw.running_blocks = False
+ return False
else:
- self.tw.turtles.get_active_turtle().show()
- self.tw.running_blocks = False
- return False
+ self.ireturn()
except logoerror, e:
- self.tw.showblocks()
- self.tw.display_coordinates()
- self.tw.showlabel('syntaxerror', str(e))
- self.tw.turtles.show_all()
- self.tw.running_blocks = False
+ if self.tw.running_turtleart:
+ self.tw.showblocks()
+ self.tw.display_coordinates()
+ self.tw.showlabel('syntaxerror', str(e))
+ self.tw.turtles.show_all()
+ self.tw.running_blocks = False
+ else:
+ traceback.print_exc()
+ self.tw.showlabel('status', 'logoerror: ' + str(e))
return False
return True
@@ -597,19 +603,119 @@ class LogoCode:
name.nargs, name.fcn = 0, body
name.rprim = True
+ def prim_start(self, *ignored_args):
+ ''' Start block: recenter '''
+ if self.tw.running_sugar:
+ self.tw.activity.recenter()
+
def prim_clear(self):
""" Clear screen """
self.tw.clear_plugins()
+ self.prim_clear_helper()
+ self.tw.canvas.clearscreen()
+ self.tw.turtles.reset_turtles()
+
+ def prim_clear_helper(self):
if self.tw.gst_available:
from tagplay import stop_media
stop_media(self)
- self.tw.canvas.clearscreen()
- self.tw.turtles.reset_turtles()
self.scale = DEFAULT_SCALE
self.hidden_turtle = None
self.start_time = time()
self.clear_value_blocks()
- self.tw.activity.restore_state()
+ if self.tw.running_turtleart:
+ self.tw.activity.restore_state()
+
+ def prim_loop(self, controller, blklist):
+ """ Execute a loop
+ controller -- iterator that yields True iff the loop should be run
+ once more OR a callable that returns such an iterator
+ blklist -- list of callables that form the loop body """
+ if not hasattr(controller, "next"):
+ if callable(controller):
+ controller = controller()
+ else:
+ raise TypeError("a loop controller must be either an iterator "
+ "or a callable that returns an iterator")
+ while next(controller):
+ self.icall(self.evline, blklist[:])
+ yield True
+ if self.procstop:
+ break
+ self.ireturn()
+ yield True
+
+ def prim_if(self, boolean, blklist):
+ """ If bool, do list """
+ if boolean:
+ self.icall(self.evline, blklist[:])
+ yield True
+ self.ireturn()
+ yield True
+
+ def prim_ifelse(self, boolean, list1, list2):
+ """ If bool, do list1, else do list2 """
+ if boolean:
+ self.ijmp(self.evline, list1[:])
+ yield True
+ else:
+ self.ijmp(self.evline, list2[:])
+ yield True
+
+ def prim_set_box(self, name, value):
+ """ Store value in named box """
+ (key, is_native) = self._get_box_key(name)
+ self.boxes[key] = value
+ if is_native:
+ if self.update_values:
+ self.update_label_value(name, value)
+ else:
+ if self.update_values:
+ self.update_label_value('box', value, label=name)
+
+ def prim_get_box(self, name):
+ """ Retrieve value from named box """
+ (key, is_native) = self._get_box_key(name)
+ try:
+ return self.boxes[key]
+ except KeyError:
+ raise logoerror("#emptybox")
+
+ def _get_box_key(self, name):
+ """ Return the key used for this box in the boxes dictionary and a
+ boolean indicating whether it is a 'native' box """
+ if name in ('box1', 'box2'):
+ return (name, True)
+ else:
+ # make sure '5' and '5.0' point to the same box
+ if isinstance(name, (int, long)):
+ name = float(name)
+ return ('box3' + str(name), False)
+
+ def prim_define_stack(self, name):
+ """ Top of a named stack """
+ pass
+
+ def prim_invoke_stack(self, name):
+ """ Process a named stack """
+ key = self._get_stack_key(name)
+ if self.stacks.get(key) is None:
+ raise logoerror("#nostack")
+ self.icall(self.evline, self.stacks[key][:])
+ yield True
+ self.procstop = False
+ self.ireturn()
+ yield True
+
+ def _get_stack_key(self, name):
+ """ Return the key used for this stack in the stacks dictionary """
+ if name in ('stack1', 'stack2'):
+ return name
+ else:
+ # make sure '5' and '5.0' point to the same action stack
+ if isinstance(name, (int, long)):
+ name = float(name)
+ return 'stack3' + str(name)
def clear_value_blocks(self):
if not hasattr(self, 'value_blocks_to_update'):
@@ -980,14 +1086,14 @@ class LogoCode:
# 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)
+ self.stacks[self._get_stack_key(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.stacks[self._get_stack_key(action_flow_name)] = \
self._readline(code)
whileflow.connections[0] = c
diff --git a/TurtleArt/taturtle.py b/TurtleArt/taturtle.py
index ac72bdb..abea538 100644
--- a/TurtleArt/taturtle.py
+++ b/TurtleArt/taturtle.py
@@ -28,7 +28,7 @@ import cairo
from random import uniform
from math import sin, cos, pi, sqrt
from taconstants import (TURTLE_LAYER, DEFAULT_TURTLE_COLORS, DEFAULT_TURTLE,
- COLORDICT)
+ Color)
from tasprite_factory import SVG, svg_str_to_pixbuf
from tacanvas import wrap100, COLOR_TABLE
from sprites import Sprite
@@ -373,17 +373,16 @@ class Turtle:
def set_color(self, color=None, share=True):
''' Set the pen color for this turtle. '''
+ if color is None:
+ color = self._pen_color
# Special case for color blocks
- if color is not None and color in COLORDICT:
- self.set_shade(COLORDICT[color][1], share)
- self.set_gray(COLORDICT[color][2], share)
- if COLORDICT[color][0] is not None:
- self.set_color(COLORDICT[color][0], share)
- color = COLORDICT[color][0]
+ elif isinstance(color, Color):
+ self.set_shade(color.shade, share)
+ self.set_gray(color.gray, share)
+ if color.color is not None:
+ color = color.color
else:
color = self._pen_color
- elif color is None:
- color = self._pen_color
try:
self._pen_color = color
diff --git a/TurtleArt/tautils.py b/TurtleArt/tautils.py
index b0aa368..634ee01 100644
--- a/TurtleArt/tautils.py
+++ b/TurtleArt/tautils.py
@@ -107,7 +107,7 @@ def chr_to_ord(x):
''' Try to comvert a string to an ord '''
if strtype(x) and len(x) == 1:
try:
- return ord(x[0]), True
+ return ord(x), True
except ValueError:
return x, False
return x, False
@@ -115,9 +115,7 @@ def chr_to_ord(x):
def strtype(x):
''' Is x a string type? '''
- if isinstance(x, (str, unicode)):
- return True
- return False
+ return isinstance(x, basestring)
def increment_name(name):
@@ -311,7 +309,7 @@ def get_save_name(filefilter, load_save_folder, save_file_name):
gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK))
dialog.set_default_response(gtk.RESPONSE_OK)
- if filefilter in ['.png', '.svg', '.lg']:
+ if filefilter in ['.png', '.svg', '.lg', '.py']:
suffix = filefilter
else:
suffix = SUFFIX[1]
@@ -808,6 +806,30 @@ def find_blk_below(blk, namelist):
return None
+def get_stack_name(blk):
+ ''' Return the name of the action stack that the given block belongs to.
+ If the top block of this stack is not a stack-defining block, return
+ None. '''
+ top_block = find_top_block(blk)
+ if top_block.name == 'start':
+ return 'start'
+ elif top_block.name == 'hat1':
+ return 'stack1'
+ elif top_block.name == 'hat2':
+ return 'stack2'
+ elif top_block.name == 'hat':
+ try:
+ return top_block.connections[1].values[0]
+ except (AttributeError, TypeError, IndexError):
+ # AttributeError: t_b has no attribute 'connections' or t_b.c[1]
+ # has no attribute 'value'
+ # TypeError: t_b.c or t_b.c[1].v is not a subscriptable sequence
+ # IndexError: t_b.c or t_b.c[1].v is too short
+ return None
+ else:
+ return None
+
+
def get_hardware():
''' Determine whether we are using XO 1.0, 1.5, ... or 'unknown'
hardware '''
diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py
index 5072ab2..17ca81d 100644
--- a/TurtleArt/tawindow.py
+++ b/TurtleArt/tawindow.py
@@ -95,6 +95,8 @@ _UNFULLSCREEN_VISIBILITY_TIMEOUT = 2
_PLUGIN_SUBPATH = 'plugins'
_MACROS_SUBPATH = 'macros'
+global_objects = {} # the global instances of single-instance classes
+
class TurtleArtWindow():
''' TurtleArt Window class abstraction '''
@@ -300,6 +302,11 @@ class TurtleArtWindow():
from tabasics import Palettes
self._basic_palettes = Palettes(self)
+ global_objects["window"] = self
+ global_objects["canvas"] = self.canvas
+ global_objects["logo"] = self.lc
+ global_objects["turtles"] = self.turtles
+
if self.interactive_mode:
gobject.idle_add(self._lazy_init)
else:
@@ -762,6 +769,9 @@ class TurtleArtWindow():
self.draw_overlay('Cartesian')
return
+ def get_coord_scale(self):
+ return self.coord_scale
+
def set_polar(self, flag):
''' Turn on/off polar coordinates '''
self.draw_overlay('polar')