From 629722d8859d1e8db47417673cb78963d41ce435 Mon Sep 17 00:00:00 2001 From: Marion Zepf Date: Sun, 27 Oct 2013 02:06:05 +0000 Subject: python export --- 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. @@ -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') diff --git a/plugins/turtle_blocks_extras/turtle_blocks_extras.py b/plugins/turtle_blocks_extras/turtle_blocks_extras.py index e31cb27..ae257d6 100644 --- a/plugins/turtle_blocks_extras/turtle_blocks_extras.py +++ b/plugins/turtle_blocks_extras/turtle_blocks_extras.py @@ -33,12 +33,13 @@ from TurtleArt.talogo import (primitive_dictionary, logoerror, from TurtleArt.taconstants import (DEFAULT_SCALE, ICON_SIZE, CONSTANTS, MEDIA_SHAPES, SKIN_PATHS, BLOCKS_WITH_SKIN, PYTHON_SKIN, PREFIX_DICTIONARY, VOICES, - MACROS, COLORDICT) + MACROS, Color) from TurtleArt.tautils import (round_int, debug_output, get_path, data_to_string, find_group, image_to_base64, hat_on_top, listify, data_from_file, data_to_file, chooser_dialog, get_load_name) from TurtleArt.tajail import (myfunc, myfunc_import) +from TurtleArt.taprimitive import Primitive def _num_type(x): @@ -100,6 +101,11 @@ class Turtle_blocks_extras(Plugin): special_name=_('while'), help_string=_('do-while-True operator that uses \ boolean operators from Numbers palette')) + # Primitive is only used for exporting this block, not for running it + self.tw.lc.def_prim('while', 2, + Primitive(self.tw.lc.prim_loop, + slot_wrappers={0: Primitive(Primitive.controller_while)}), + True) # internally expanded macro palette.add_block('until', @@ -110,6 +116,11 @@ boolean operators from Numbers palette')) special_name=_('until'), help_string=_('do-until-True operator that uses \ boolean operators from Numbers palette')) + # Primitive is only used for exporting this block, not for running it + self.tw.lc.def_prim('until', 2, + Primitive(self.tw.lc.prim_loop, + slot_wrappers={0: Primitive(Primitive.controller_until)}), + True) primitive_dictionary['clamp'] = self._prim_clamp palette.add_block('sandwichclamp', @@ -1135,7 +1146,7 @@ Journal objects')) def _prim_myfunction(self, f, x): """ Programmable block """ for i, v in enumerate(x): - if type(v) == int: # Pass float values to Python block + if isinstance(v, int): # Pass float values to Python block x[i] = float(v) try: y = myfunc(f, x) @@ -1195,20 +1206,20 @@ Journal objects')) """ Print object n """ if flag and (self.tw.hide or self.tw.step_time == 0): return - if type(n) == list: + if isinstance(n, list): self.tw.showlabel('print', n) - elif type(n) == str or type(n) == unicode: - if n in COLORDICT: - if COLORDICT[n][0] is None: - self.tw.showlabel('print', '%s %d, %s %d' % - (_('shade'), COLORDICT[n][1], - _('gray'), COLORDICT[n][2])) - else: - self.tw.showlabel('print', '%s %d, %s %d, %s %d' % - (_('color'), COLORDICT[n][0], - _('shade'), COLORDICT[n][1], - _('gray'), COLORDICT[n][2])) - elif n[0:6] == 'media_' and \ + elif isinstance(n, Color): + if n.color is None: + self.tw.showlabel('print', '%s %d, %s %d' % + (_('shade'), n.shade, + _('gray'), n.gray)) + else: + self.tw.showlabel('print', '%s %d, %s %d, %s %d' % + (_('color'), n.color, + _('shade'), n.shade, + _('gray'), n.gray)) + elif isinstance(n, basestring): + if n[0:6] == 'media_' and \ n[6:].lower not in media_blocks_dictionary: try: if self.tw.running_sugar: @@ -1226,7 +1237,7 @@ Journal objects')) self.tw.showlabel('print', n) else: self.tw.showlabel('print', n) - elif type(n) == int: + elif isinstance(n, int): self.tw.showlabel('print', n) else: self.tw.showlabel( diff --git a/turtleblocks.py b/turtleblocks.py index 94c0fb7..b806c55 100755 --- a/turtleblocks.py +++ b/turtleblocks.py @@ -57,6 +57,7 @@ from TurtleArt.tautils import (data_from_string, get_save_name) from TurtleArt.tapalette import default_values from TurtleArt.tawindow import TurtleArtWindow from TurtleArt.taexportlogo import save_logo +from TurtleArt.taexportpython import save_python from util.menubuilder import MenuBuilder @@ -405,6 +406,8 @@ return %s(self)" % (p, P, P) self._do_save_picture_cb) MenuBuilder.make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb) + MenuBuilder.make_menu_item(menu, _('Save as Python'), + self._do_save_python_cb) MenuBuilder.make_menu_item(menu, _('Quit'), self._quit_ta) activity_menu = MenuBuilder.make_sub_menu(menu, _('File')) @@ -556,6 +559,34 @@ Would you like to save before quitting?')) f.write(logocode) f.close() + def _do_save_python_cb(self, widget): + ''' Callback for saving the project as Python code. ''' + # catch PyExportError and display a user-friendly message instead + try: + pythoncode = save_python(self.tw) + except PyExportError as pyee: + if pyee.block is not None: + pyee.block.highlight() + self.tw.showlabel('status', str(pyee)) + return + if not pythoncode: + return + # use name of TA project if it has been saved already + default_name = self.tw.save_file_name + if default_name is None: + default_name = _("myproject") + elif default_name.endswith(".ta") or default_name.endswith(".tb"): + default_name = default_name[:-3] + save_type = '.py' + (filename, self.tw.load_save_folder) = get_save_name( + save_type, self.tw.load_save_folder, default_name) + if isinstance(filename, unicode): + filename = filename.encode('ascii', 'replace') + if filename is not None: + f = file(filename, 'w') + f.write(pythoncode) + f.close() + def _do_resize_cb(self, widget, factor): ''' Callback to resize blocks. ''' if factor == -1: -- cgit v0.9.1