From 4d641db4ad2dcad5aa44f82cabb14d237702ac84 Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Thu, 29 Aug 2013 01:55:46 +0000 Subject: resync with master --- (limited to 'TurtleArt') diff --git a/TurtleArt/tabasics.py b/TurtleArt/tabasics.py index 3f960da..0346d0e 100644 --- a/TurtleArt/tabasics.py +++ b/TurtleArt/tabasics.py @@ -19,7 +19,7 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -""" +''' This file contains the constants that by-in-large determine the behavior of Turtle Art. Notably, the block palettes are defined below. If you want to add a new block to Turtle Art, you could @@ -43,9 +43,13 @@ add_block method in the Palette class. # Next, you need to define what your block will do: # def_prim takes 3 arguments: the primitive name, the number of # arguments -- 0 in this case -- and the function to call -- in this - # case, the canvas.seth function to set the heading. - self.tw.lc.def_prim('uturn', 0, - lambda self: self.tw.canvas.seth(self.tw.canvas.heading + 180)) + # case, we define the _prim_uturn function to set heading += 180. + self.tw.lc.def_prim('uturn', 0, lambda self: self._prim_uturn) + def _prim_uturn(self): + value = self.tw.turtles.get_active_turtle().get_heading() + 180 + self.tw.turtles.get_active_turtle().set_heading(value) + if self.tw.lc.update_values: + self.tw.lc.update_label_value('heading', value) That's it. When you next run Turtle Art, you will have a 'uturn' block on the 'mypalette' palette. @@ -56,7 +60,7 @@ mypaletteoff.svg and mypaletteon.svg, where 'mypalette' is the same string as the entry you used in instantiating the Palette class. Note that the icons should be the same size (55x55) as the others. (This is the default icon size for Sugar toolbars.) -""" +''' from time import time, sleep from math import sqrt @@ -78,22 +82,22 @@ def _color_to_num(c): def _num_type(x): - """ Is x a number type? """ + ''' Is x a number type? ''' if isinstance(x, (int, float)): return True return False def _millisecond(): - """ Current time in milliseconds """ + ''' Current time in milliseconds ''' return time() * 1000 class Palettes(): - """ a class for creating the palettes of blocks """ + ''' a class for creating the palettes of blocks ''' - def __init__(self, parent): - self.tw = parent + def __init__(self, turtle_window): + self.tw = turtle_window self._turtle_palette() @@ -113,7 +117,7 @@ class Palettes(): # Palette definitions def _turtle_palette(self): - """ The basic Turtle Art turtle palette """ + ''' The basic Turtle Art turtle palette ''' palette = make_palette('turtle', colors=["#00FF00", "#00A000"], @@ -127,10 +131,11 @@ class Palettes(): default=100, logo_command='forward', help_string=_('moves turtle forward')) - self.tw.lc.def_prim('forward', 1, - lambda self, x: - primitive_dictionary['move'] - (self.tw.canvas.forward, x)) + self.tw.lc.def_prim( + 'forward', + 1, + lambda self, x: primitive_dictionary['move']( + self.tw.turtles.get_active_turtle().forward, x)) palette.add_block('back', style='basic-style-1arg', @@ -142,9 +147,10 @@ class Palettes(): self.tw.lc.def_prim('back', 1, lambda self, x: primitive_dictionary['move'] - (self.tw.canvas.forward, x, reverse=True)) + (self.tw.turtles.get_active_turtle().forward, x, + reverse=True)) - primitive_dictionary['clean'] = self.tw.lc.prim_clear + primitive_dictionary['clean'] = self._prim_clear palette.add_block('clean', style='basic-style-extended-vertical', label=_('clean'), @@ -152,8 +158,10 @@ class Palettes(): logo_command='clean', help_string=_('clears the screen and reset the \ turtle')) - self.tw.lc.def_prim('clean', 0, - lambda self: primitive_dictionary['clean']()) + self.tw.lc.def_prim( + 'clean', + 0, + lambda self: primitive_dictionary['clean']()) primitive_dictionary['right'] = self._prim_right palette.add_block('left', @@ -176,8 +184,10 @@ in degrees)')) logo_command='right', help_string=_('turns turtle clockwise (angle in \ degrees)')) - self.tw.lc.def_prim('right', 1, - lambda self, x: primitive_dictionary['right'](x)) + self.tw.lc.def_prim( + 'right', + 1, + lambda self, x: primitive_dictionary['right'](x)) primitive_dictionary['arc'] = self._prim_arc palette.add_block('arc', @@ -187,10 +197,11 @@ degrees)')) default=[90, 100], logo_command='taarc', help_string=_('moves turtle along an arc')) - self.tw.lc.def_prim('arc', 2, - lambda self, x, y: - primitive_dictionary['arc'] - (self.tw.canvas.arc, x, y)) + self.tw.lc.def_prim( + 'arc', + 2, + lambda self, x, y: primitive_dictionary['arc']( + self.tw.turtles.get_active_turtle().arc, x, y)) define_logo_function('taarc', 'to taarc :a :r\nrepeat round :a \ [right 1 forward (0.0175 * :r)]\nend\n') @@ -202,10 +213,11 @@ degrees)')) default=[0, 0], help_string=_('moves turtle to position xcor, ycor; \ (0, 0) is in the center of the screen.')) - self.tw.lc.def_prim('setxy2', 2, - lambda self, x, y: - primitive_dictionary['move'] - (self.tw.canvas.setxy, x, y)) + self.tw.lc.def_prim( + 'setxy2', + 2, + lambda self, x, y: primitive_dictionary['move']( + self.tw.turtles.get_active_turtle().set_xy, (x, y))) define_logo_function('tasetxy', 'to tasetxy :x :y\nsetxy :x :y\nend\n') primitive_dictionary['set'] = self._prim_set @@ -217,10 +229,11 @@ degrees)')) logo_command='seth', help_string=_('sets the heading of the turtle (0 is \ towards the top of the screen.)')) - self.tw.lc.def_prim('seth', 1, - lambda self, x: - primitive_dictionary['set'] - ('heading', self.tw.canvas.seth, x)) + self.tw.lc.def_prim( + 'seth', + 1, + lambda self, x: primitive_dictionary['set']( + 'heading', self.tw.turtles.get_active_turtle().set_heading, x)) palette.add_block('xcor', style='box-style', @@ -231,7 +244,10 @@ the turtle (can be used in place of a number block)'), prim_name='xcor', logo_command='xcor') self.tw.lc.def_prim( - 'xcor', 0, lambda self: self.tw.canvas.xcor / self.tw.coord_scale) + 'xcor', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_xy()[0] / + self.tw.coord_scale) palette.add_block('ycor', style='box-style', @@ -242,7 +258,10 @@ the turtle (can be used in place of a number block)'), prim_name='ycor', logo_command='ycor') self.tw.lc.def_prim( - 'ycor', 0, lambda self: self.tw.canvas.ycor / self.tw.coord_scale) + 'ycor', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_xy()[1] / + self.tw.coord_scale) palette.add_block('heading', style='box-style', @@ -253,7 +272,9 @@ turtle (can be used in place of a number block)'), prim_name='heading', logo_command='heading') self.tw.lc.def_prim( - 'heading', 0, lambda self: self.tw.canvas.heading) + 'heading', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_heading()) palette.add_block('turtle-label', hidden=True, @@ -270,15 +291,17 @@ turtle (can be used in place of a number block)'), logo_command='tasetxypenup', help_string=_('moves turtle to position xcor, ycor; \ (0, 0) is in the center of the screen.')) - self.tw.lc.def_prim('setxy', 2, - lambda self, x, y: - primitive_dictionary['move'] - (self.tw.canvas.setxy, x, y, pendown=False)) + self.tw.lc.def_prim( + 'setxy', + 2, + lambda self, x, y: primitive_dictionary['move']( + 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') def _pen_palette(self): - """ The basic Turtle Art pen palette """ + ''' The basic Turtle Art pen palette ''' palette = make_palette('pen', colors=["#00FFFF", "#00A0A0"], @@ -293,8 +316,10 @@ setxy :x :y\npendown\nend\n') logo_command='tasetbackground', help_string=_('fills the background with (color, \ shade)')) - self.tw.lc.def_prim('fillscreen', 2, - lambda self, x, y: self.tw.canvas.fillscreen(x, y)) + self.tw.lc.def_prim( + 'fillscreen', + 2, + lambda self, x, y: self.tw.canvas.fillscreen(x, y)) palette.add_block('fillscreen2', style='basic-style-3arg', @@ -305,9 +330,10 @@ shade)')) logo_command='tasetbackground', help_string=_('fills the background with (color, \ shade)')) - self.tw.lc.def_prim('fillscreen2', 3, - lambda self, x, y, z: - self.tw.canvas.fillscreen_with_gray(x, y, z)) + self.tw.lc.def_prim( + 'fillscreen2', + 3, + lambda self, x, y, z: self.tw.canvas.fillscreen_with_gray(x, y, z)) define_logo_function('tasetbackground', 'to tasetbackground :color \ :shade\ntasetshade :shade\nsetbackground :color\nend\n') @@ -320,10 +346,11 @@ shade)')) logo_command='tasetpencolor', help_string=_('sets color of the line drawn by the \ turtle')) - self.tw.lc.def_prim('setcolor', 1, - lambda self, x: - primitive_dictionary['set'] - ('color', self.tw.canvas.setcolor, x)) + self.tw.lc.def_prim( + 'setcolor', + 1, + lambda self, x: primitive_dictionary['set']( + 'color', self.tw.turtles.get_active_turtle().set_color, x)) palette.add_block('setshade', style='basic-style-1arg', @@ -333,10 +360,11 @@ turtle')) logo_command='tasetshade', help_string=_('sets shade of the line drawn by the \ turtle')) - self.tw.lc.def_prim('setshade', 1, - lambda self, x: - primitive_dictionary['set'] - ('shade', self.tw.canvas.setshade, x)) + self.tw.lc.def_prim( + 'setshade', + 1, + lambda self, x: primitive_dictionary['set']( + 'shade', self.tw.turtles.get_active_turtle().set_shade, x)) palette.add_block('setgray', style='basic-style-1arg', @@ -345,10 +373,11 @@ turtle')) default=100, help_string=_('sets gray level of the line drawn by \ the turtle')) - self.tw.lc.def_prim('setgray', 1, - lambda self, x: - primitive_dictionary['set'] - ('gray', self.tw.canvas.setgray, x)) + self.tw.lc.def_prim( + 'setgray', + 1, + lambda self, x: primitive_dictionary['set']( + 'gray', self.tw.turtles.get_active_turtle().set_gray, x)) palette.add_block('color', style='box-style', @@ -358,7 +387,10 @@ 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.canvas.color) + self.tw.lc.def_prim( + 'color', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_color()) palette.add_block('shade', style='box-style', @@ -367,7 +399,10 @@ 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.canvas.shade) + self.tw.lc.def_prim( + 'shade', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_shade()) palette.add_block('gray', style='box-style', @@ -376,7 +411,8 @@ 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.canvas.gray) + self.tw.lc.def_prim('gray', 0, lambda self: + self.tw.turtles.get_active_turtle().get_gray()) palette.add_block('penup', style='basic-style-extended-vertical', @@ -384,8 +420,11 @@ used in place of a number block)'), prim_name='penup', logo_command='penup', help_string=_('Turtle will not draw when moved.')) - self.tw.lc.def_prim('penup', 0, - lambda self: self.tw.canvas.setpen(False)) + self.tw.lc.def_prim( + 'penup', + 0, + lambda self: + self.tw.turtles.get_active_turtle().set_pen_state(False)) palette.add_block('pendown', style='basic-style-extended-vertical', @@ -393,8 +432,21 @@ used in place of a number block)'), prim_name='pendown', logo_command='pendown', help_string=_('Turtle will draw when moved.')) - self.tw.lc.def_prim('pendown', 0, - lambda self: self.tw.canvas.setpen(True)) + self.tw.lc.def_prim( + 'pendown', + 0, + lambda self: + self.tw.turtles.get_active_turtle().set_pen_state(True)) + + palette.add_block('penstate', + style='boolean-block-style', + label=_('pen down?'), + prim_name='penstate', + help_string=_('returns True if pen is down')) + self.tw.lc.def_prim( + 'penstate', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_pen_state()) palette.add_block('setpensize', style='basic-style-1arg', @@ -404,12 +456,12 @@ used in place of a number block)'), logo_command='setpensize', help_string=_('sets size of the line drawn by the \ turtle')) - self.tw.lc.def_prim('setpensize', 1, - lambda self, x: - primitive_dictionary['set'] - ('pensize', self.tw.canvas.setpensize, x)) - define_logo_function('tasetpensize', 'to tasetpensize :a\nsetpensize \ -round :a\nend\n') + self.tw.lc.def_prim( + 'setpensize', 1, + lambda self, x: primitive_dictionary['set'] + ('pensize', self.tw.turtles.get_active_turtle().set_pen_size, x)) + define_logo_function('tasetpensize', + 'to tasetpensize :a\nsetpensize round :a\nend\n') palette.add_block('startfill', style='basic-style-extended-vertical', @@ -417,8 +469,10 @@ round :a\nend\n') prim_name='startfill', help_string=_('starts filled polygon (used with end \ fill block)')) - self.tw.lc.def_prim('startfill', 0, - lambda self: self.tw.canvas.start_fill()) + self.tw.lc.def_prim( + 'startfill', + 0, + lambda self: self.tw.turtles.get_active_turtle().start_fill()) palette.add_block('stopfill', style='basic-style-extended-vertical', @@ -426,8 +480,10 @@ fill block)')) prim_name='stopfill', help_string=_('completes filled polygon (used with \ start fill block)')) - self.tw.lc.def_prim('stopfill', 0, - lambda self: self.tw.canvas.stop_fill()) + self.tw.lc.def_prim( + 'stopfill', + 0, + lambda self: self.tw.turtles.get_active_turtle().stop_fill()) palette.add_block('pensize', style='box-style', @@ -437,12 +493,15 @@ in place of a number block)'), value_block=True, prim_name='pensize', logo_command='pensize') - self.tw.lc.def_prim('pensize', 0, lambda self: self.tw.canvas.pensize) + self.tw.lc.def_prim( + 'pensize', + 0, + lambda self: self.tw.turtles.get_active_turtle().get_pen_size()) define_logo_function('tapensize', 'to tapensize\noutput first round \ pensize\nend\n') def _color_palette(self): - """ The basic Turtle Art color palette """ + ''' The basic Turtle Art color palette ''' palette = make_palette('colors', colors=["#00FFFF", "#00A0A0"], @@ -461,29 +520,6 @@ pensize\nend\n') self._make_constant(palette, 'white', _('white'), CONSTANTS['white']) self._make_constant(palette, 'black', _('black'), CONSTANTS['black']) - # deprecated blocks - palette.add_block('settextcolor', - hidden=True, - style='basic-style-1arg', - label=_('set text color'), - prim_name='settextcolor', - default=0, - help_string=_('sets color of text drawn by the \ -turtle')) - self.tw.lc.def_prim('settextcolor', 1, - lambda self, x: self.tw.canvas.settextcolor(x)) - - palette.add_block('settextsize', - hidden=True, - style='basic-style-1arg', - label=_('set text size'), - prim_name='settextsize', - default=0, - help_string=_('sets size of text drawn by the \ -turtle')) - self.tw.lc.def_prim('settextsize', 1, - lambda self, x: self.tw.canvas.settextsize(x)) - # In order to map Turtle Art colors to the standard UCB Logo palette, # we need to define a somewhat complex set of functions. define_logo_function('tacolor', '\ @@ -558,7 +594,7 @@ make "shade 50 \n\ tasetshade :shade \n') def _numbers_palette(self): - """ The basic Turtle Art numbers palette """ + ''' The basic Turtle Art numbers palette ''' palette = make_palette('numbers', colors=["#FF00FF", "#A000A0"], @@ -741,7 +777,7 @@ operators')) 'or', 2, lambda self, x, y: primitive_dictionary['or'](x, y)) def _flow_palette(self): - """ The basic Turtle Art flow palette """ + ''' The basic Turtle Art flow palette ''' palette = make_palette('flow', colors=["#FFC000", "#A08000"], @@ -841,7 +877,7 @@ boolean operators from Numbers palette')) lambda self: primitive_dictionary['stopstack']()) def _blocks_palette(self): - """ The basic Turtle Art blocks palette """ + ''' The basic Turtle Art blocks palette ''' palette = make_palette('blocks', colors=["#FFFF00", "#A0A000"], @@ -1002,7 +1038,7 @@ variable')) self.tw.lc.def_prim('stack2', 0, primitive_dictionary['stack2'], True) def _trash_palette(self): - """ The basic Turtle Art turtle palette """ + ''' The basic Turtle Art turtle palette ''' palette = make_palette('trash', colors=["#FFFF00", "#A0A000"], @@ -1025,22 +1061,32 @@ variable')) # Block primitives + def _prim_clear(self): + self.tw.lc.prim_clear() + self.tw.turtles.reset_turtles() + def _prim_and(self, x, y): - """ Logical and """ + ''' Logical and ''' return x & y def _prim_arc(self, cmd, value1, value2): - """ Turtle draws an arc of degree, radius """ + ''' Turtle draws an arc of degree, radius ''' cmd(float(value1), float(value2)) if self.tw.lc.update_values: self.tw.lc.update_label_value( - 'xcor', self.tw.canvas.xcor / self.tw.coord_scale) + 'xcor', + self.tw.turtles.get_active_turtle().get_xy()[0] / + self.tw.coord_scale) self.tw.lc.update_label_value( - 'ycor', self.tw.canvas.ycor / self.tw.coord_scale) - self.tw.lc.update_label_value('heading', self.tw.canvas.heading) + 'ycor', + self.tw.turtles.get_active_turtle().get_xy()[1] / + self.tw.coord_scale) + self.tw.lc.update_label_value( + 'heading', + self.tw.turtles.get_active_turtle().get_heading()) def _prim_box(self, x): - """ Retrieve value from named box """ + ''' Retrieve value from named box ''' if isinstance(convert(x, float, False), float): if int(float(x)) == x: x = int(x) @@ -1050,7 +1096,7 @@ variable')) raise logoerror("#emptybox") def _prim_forever(self, blklist): - """ Do list forever """ + ''' Do list forever ''' while True: self.tw.lc.icall(self.tw.lc.evline, blklist[:]) yield True @@ -1060,7 +1106,7 @@ variable')) yield True def _prim_if(self, boolean, blklist): - """ If bool, do list """ + ''' If bool, do list ''' if boolean: self.tw.lc.icall(self.tw.lc.evline, blklist[:]) yield True @@ -1068,7 +1114,7 @@ variable')) yield True def _prim_ifelse(self, boolean, list1, list2): - """ If bool, do list1, else do list2 """ + ''' If bool, do list1, else do list2 ''' if boolean: self.tw.lc.ijmp(self.tw.lc.evline, list1[:]) yield True @@ -1078,7 +1124,12 @@ variable')) def _prim_move(self, cmd, value1, value2=None, pendown=True, reverse=False): - """ Turtle moves by method specified in value1 """ + ''' 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: @@ -1089,19 +1140,26 @@ variable')) else: if not _num_type(value2): raise logoerror("#notanumber") - cmd(float(value1), float(value2), pendown=pendown) + if pos is not None: + cmd((float(value1), float(value2)), pendown=pendown) + else: + cmd(float(value1), float(value2), pendown=pendown) if self.tw.lc.update_values: self.tw.lc.update_label_value( - 'xcor', self.tw.canvas.xcor / self.tw.coord_scale) + 'xcor', + self.tw.turtles.get_active_turtle().get_xy()[0] / + self.tw.coord_scale) self.tw.lc.update_label_value( - 'ycor', self.tw.canvas.ycor / self.tw.coord_scale) + 'ycor', + self.tw.turtles.get_active_turtle().get_xy()[1] / + self.tw.coord_scale) def _prim_or(self, x, y): - """ Logical or """ + ''' Logical or ''' return x | y def _prim_repeat(self, num, blklist): - """ Repeat list num times. """ + ''' Repeat list num times. ''' if not _num_type(num): raise logoerror("#notanumber") num = self.tw.lc.int(num) @@ -1114,25 +1172,27 @@ variable')) yield True def _prim_right(self, value, reverse=False): - """ Turtle rotates clockwise """ + ''' Turtle rotates clockwise ''' if not _num_type(value): raise logoerror("#notanumber") if reverse: - self.tw.canvas.right(float(-value)) + self.tw.turtles.get_active_turtle().right(float(-value)) else: - self.tw.canvas.right(float(value)) + self.tw.turtles.get_active_turtle().right(float(value)) if self.tw.lc.update_values: - self.tw.lc.update_label_value('heading', self.tw.canvas.heading) + self.tw.lc.update_label_value( + 'heading', + self.tw.turtles.get_active_turtle().get_heading()) def _prim_set(self, name, cmd, value=None): - """ Set a value and update the associated value blocks """ + ''' Set a value and update the associated value blocks ''' if value is not None: cmd(value) 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 """ + ''' Define value of named box ''' if x is not None: if isinstance(convert(x, float, False), float): if int(float(x)) == x: @@ -1146,7 +1206,7 @@ variable')) self.tw.lc.update_label_value(name, val) def _prim_stack(self, x): - """ Process a named stack """ + ''' Process a named stack ''' if isinstance(convert(x, float, False), float): if int(float(x)) == x: x = int(x) @@ -1161,7 +1221,7 @@ variable')) yield True def _prim_stack1(self): - """ Process Stack 1 """ + ''' Process Stack 1 ''' if self.tw.lc.stacks['stack1'] is None: raise logoerror("#nostack") self.tw.lc.icall(self.tw.lc.evline, @@ -1172,7 +1232,7 @@ variable')) yield True def _prim_stack2(self): - """ Process Stack 2 """ + ''' Process Stack 2 ''' if self.tw.lc.stacks['stack2'] is None: raise logoerror("#nostack") self.tw.lc.icall(self.tw.lc.evline, self.tw.lc.stacks['stack2'][:]) @@ -1182,29 +1242,29 @@ variable')) yield True def _prim_start(self): - """ Start block: recenter """ + ''' Start block: recenter ''' if self.tw.running_sugar: self.tw.activity.recenter() def _prim_stopstack(self): - """ Stop execution of a stack """ + ''' Stop execution of a stack ''' self.tw.lc.procstop = True def _prim_wait(self, wait_time): - """ Show the turtle while we wait """ - self.tw.active_turtle.show() + ''' Show the turtle while we wait ''' + self.tw.turtles.get_active_turtle().show() endtime = _millisecond() + wait_time * 1000. while _millisecond() < endtime: sleep(wait_time / 10.) yield True - self.tw.active_turtle.hide() + self.tw.turtles.get_active_turtle().hide() self.tw.lc.ireturn() yield True # Math primitivies def _prim_careful_divide(self, x, y): - """ Raise error on divide by zero """ + ''' Raise error on divide by zero ''' if isinstance(x, list) and _num_type(y): z = [] for i in range(len(x)): @@ -1228,7 +1288,7 @@ variable')) raise logoerror("#notanumber") def _prim_equal(self, x, y): - """ Numeric and logical equal """ + ''' Numeric and logical equal ''' if isinstance(x, list) and isinstance(y, list): for i in range(len(x)): if x[i] != y[i]: @@ -1250,7 +1310,7 @@ variable')) raise logoerror("#syntaxerror") def _prim_less(self, x, y): - """ Compare numbers and strings """ + ''' Compare numbers and strings ''' if isinstance(x, list) or isinstance(y, list): raise logoerror("#syntaxerror") try: @@ -1269,11 +1329,11 @@ variable')) raise logoerror("#notanumber") def _prim_more(self, x, y): - """ Compare numbers and strings """ + ''' Compare numbers and strings ''' return self._prim_less(y, x) def _prim_plus(self, x, y): - """ Add numbers, concat strings """ + ''' Add numbers, concat strings ''' if x in COLORDICT: x = _color_to_num(x) if y in COLORDICT: @@ -1297,7 +1357,7 @@ variable')) return(xx + yy) def _prim_minus(self, x, y): - """ Numerical subtraction """ + ''' Numerical subtraction ''' if _num_type(x) and _num_type(y): return(x - y) elif isinstance(x, list) and isinstance(y, list): @@ -1311,7 +1371,7 @@ variable')) raise logoerror("#notanumber") def _prim_product(self, x, y): - """ Numerical multiplication """ + ''' Numerical multiplication ''' if _num_type(x) and _num_type(y): return(x * y) elif isinstance(x, list) and _num_type(y): @@ -1330,7 +1390,7 @@ variable')) raise logoerror("#notanumber") def _prim_mod(self, x, y): - """ Numerical mod """ + ''' Numerical mod ''' if _num_type(x) and _num_type(y): return(x % y) try: @@ -1341,7 +1401,7 @@ variable')) raise logoerror("#syntaxerror") def _prim_sqrt(self, x): - """ Square root """ + ''' Square root ''' if _num_type(x): if x < 0: raise logoerror("#negroot") @@ -1354,7 +1414,7 @@ variable')) raise logoerror("#notanumber") def _prim_random(self, x, y): - """ Random integer """ + ''' Random integer ''' if _num_type(x) and _num_type(y): return(int(round(uniform(x, y), 0))) xx, xflag = chr_to_ord(x) @@ -1371,13 +1431,13 @@ variable')) raise logoerror("#notanumber") def _prim_identity(self, x): - """ Identity function """ + ''' Identity function ''' return(x) # Utilities def _string_to_num(self, x): - """ Try to comvert a string to a number """ + ''' Try to comvert a string to a number ''' if isinstance(x, (int, float)): return(x) try: @@ -1399,7 +1459,7 @@ variable')) raise logoerror("#syntaxerror") def _make_constant(self, palette, block_name, label, constant): - """ Factory for constant blocks """ + ''' Factory for constant blocks ''' if constant in COLORDICT: if COLORDICT[constant][0] is not None: value = str(COLORDICT[constant][0]) diff --git a/TurtleArt/tablock.py b/TurtleArt/tablock.py index 2658624..8d8ed4a 100644 --- a/TurtleArt/tablock.py +++ b/TurtleArt/tablock.py @@ -125,7 +125,31 @@ class Blocks: class Block: - """ A class for the individual blocks """ + """ A class for the individual blocks + + Attributes: + docks -- a list of docks, i.e. connection points where other blocks + could be attached. Each dock is a list of the form + [type_of_dock, flow_is_in, x, y, parenthesis] + with the last element being optional. + type_of_dock may be one of the following strings: + flow -- connect to the previous or next block ('slot' or 'tab') + bool, media, number, string -- argument slot ('innie') or + return value ('outie') of the given kind + unavailable -- nothing can be attached here ('cap' or 'tail') + flow_is_in is True if the flow is into the block, or False for out. + x and y are coodinates for positioning the block on the dock. + parenthesis is only used with arguments and ensures a known order + of arguments for arithmetic and logical operations. + connections -- a list of blocks that are attached to this one (or that + this one is attached to). This list corresponds to the docks list + as it uses the same indices. Slots where nothing is attached are + None on this list. + primitive -- a callable that is called when the block is executed + type -- type of the block: + block -- block that is part of the user's program + proto -- block on a palette, used to generate other blocks + trash -- block in the trash """ def __init__(self, block_list, sprite_list, name, x, y, type='block', values=[], scale=BLOCK_SCALE[0], diff --git a/TurtleArt/tacanvas.py b/TurtleArt/tacanvas.py index 5eaa4a8..89b8ed1 100644 --- a/TurtleArt/tacanvas.py +++ b/TurtleArt/tacanvas.py @@ -1,4 +1,4 @@ -#Copyright (c) 2007-8, Playful Invention Company. +31#Copyright (c) 2007-8, Playful Invention Company. #Copyright (c) 2008-11, Walter Bender #Copyright (c) 2011 Collabora Ltd. @@ -21,16 +21,14 @@ #THE SOFTWARE. import gtk -import gobject -from math import sin, cos, pi +from math import pi import os import pango import cairo import pangocairo -from tautils import (image_to_base64, get_path, data_to_string, round_int, - debug_output) -from taconstants import COLORDICT +from tautils import get_path +from taconstants import COLORDICT, TMP_SVG_PATH def wrap100(n): @@ -101,67 +99,38 @@ COLOR_TABLE = ( class TurtleGraphics: ''' A class for the Turtle graphics canvas ''' - def __init__(self, tw, width, height): + def __init__(self, turtle_window, width, height): ''' Create a sprite to hold the canvas. ''' - self.tw = tw + self.turtle_window = turtle_window self.width = width self.height = height + self.textsize = 48 + self._fgrgb = [255, 0, 0] + self._bgrgb = [255, 248, 222] + self._shade = 0 + self._color = 0 + self._gray = 100 + self.cr_svg = None # Surface used for saving to SVG # Build a cairo.Context from a cairo.XlibSurface - self.canvas = cairo.Context(self.tw.turtle_canvas) + self.canvas = cairo.Context(self.turtle_window.turtle_canvas) cr = gtk.gdk.CairoContext(self.canvas) cr.set_line_cap(1) # Set the line cap to be round - self.cr_svg = None # Surface used for saving to SVG - self.cx = 0 - self.cy = 0 - self.fgrgb = [255, 0, 0] - self.bgrgb = [255, 248, 222] - self.textsize = 48 # deprecated - self.shade = 0 - self.pendown = False - self.xcor = 0 - self.ycor = 0 - self.heading = 0 - self.pensize = 5 - self.color = 0 - self.gray = 100 - self.fill = False - self.poly_points = [] + + self.set_pen_size(5) def setup_svg_surface(self): ''' Set up a surface for saving to SVG ''' - if self.tw.running_sugar: + if self.turtle_window.running_sugar: svg_surface = cairo.SVGSurface( - os.path.join(get_path(self.tw.activity, 'instance'), + os.path.join(get_path(self.turtle_window.activity, 'instance'), 'output.svg'), self.width, self.height) else: svg_surface = cairo.SVGSurface( - os.path.join(os.getcwd(), 'output.svg'), - self.width, self.height) + TMP_SVG_PATH, self.width, self.height) self.cr_svg = cairo.Context(svg_surface) self.cr_svg.set_line_cap(1) # Set the line cap to be round - def start_fill(self): - ''' Start accumulating points of a polygon to fill. ''' - self.fill = True - self.poly_points = [] - - def stop_fill(self): - ''' Fill the polygon. ''' - self.fill = False - if len(self.poly_points) == 0: - return - self.fill_polygon(self.poly_points) - if self.tw.sharing(): - shared_poly_points = [] - for p in self.poly_points: - shared_poly_points.append((self.screen_to_turtle_coordinates - (p[0], p[1]))) - event = 'F|%s' % (data_to_string([self._get_my_nick(), - shared_poly_points])) - self.tw.send_event(event) - self.poly_points = [] - def fill_polygon(self, poly_points): ''' Draw the polygon... ''' def _fill_polygon(cr, poly_points): @@ -188,10 +157,10 @@ class TurtleGraphics: def _clearscreen(cr): cr.move_to(0, 0) - self.bgrgb = [255, 248, 222] - cr.set_source_rgb(self.bgrgb[0] / 255., - self.bgrgb[1] / 255., - self.bgrgb[2] / 255.) + self._bgrgb = [255, 248, 222] + cr.set_source_rgb(self._bgrgb[0] / 255., + self._bgrgb[1] / 255., + self._bgrgb[2] / 255.) cr.rectangle(0, 0, self.width * 2, self.height * 2) cr.fill() @@ -200,359 +169,101 @@ class TurtleGraphics: if self.cr_svg is not None: _clearscreen(self.cr_svg) - self.setpensize(5, share) - self.setgray(100, share) - self.setcolor(0, share) - self.setshade(50, share) - self.fill = False - self.poly_points = [] - for turtle_key in iter(self.tw.turtles.dict): - # Don't reset remote turtles - if not self.tw.remote_turtle(turtle_key): - self.set_turtle(turtle_key) - self.tw.active_turtle.set_color(0) - self.tw.active_turtle.set_shade(50) - self.tw.active_turtle.set_gray(100) - self.tw.active_turtle.set_pen_size(5) - self.tw.active_turtle.reset_shapes() - self.seth(0.0, share) - self.setpen(False, share) - self.setxy(0.0, 0.0, share) - self.setpen(True, share) - self.tw.active_turtle.hide() - self.set_turtle(self.tw.default_turtle_name) - - def forward(self, n, share=True): - ''' Move the turtle forward.''' - nn = n * self.tw.coord_scale - self.canvas.set_source_rgb(self.fgrgb[0] / 255., self.fgrgb[1] / 255., - self.fgrgb[2] / 255.) - if self.cr_svg is not None: - debug_output('in forward', True) - self.cr_svg.set_source_rgb(self.fgrgb[0] / 255., - self.fgrgb[1] / 255., - self.fgrgb[2] / 255.) - oldx, oldy = self.xcor, self.ycor - try: - self.xcor += nn * sin(self.heading * DEGTOR) - self.ycor += nn * cos(self.heading * DEGTOR) - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - if self.pendown: - self.draw_line(oldx, oldy, self.xcor, self.ycor) - - self.move_turtle() - - if self.tw.sharing() and share: - event = 'f|%s' % (data_to_string([self._get_my_nick(), int(n)])) - self.tw.send_event(event) + def rarc(self, x, y, r, a, heading): + ''' draw a clockwise arc ''' + def _rarc(cr, x, y, r, a, h): + cr.arc(x, y, r, (h - 180) * DEGTOR, (h - 180 + a) * DEGTOR) + cr.stroke() + + _rarc(self.canvas, x, y, r, a, heading) self.inval() - def seth(self, n, share=True): - ''' Set the turtle heading. ''' - try: - self.heading = n - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - self.heading %= 360 - self.turn_turtle() - if self.tw.sharing() and share: - event = 'r|%s' % (data_to_string([self._get_my_nick(), - round_int(self.heading)])) - self.tw.send_event(event) - - def right(self, n, share=True): - ''' Rotate turtle clockwise ''' - try: - self.heading += n - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - self.heading %= 360 - self.turn_turtle() - if self.tw.sharing() and share: - event = 'r|%s' % (data_to_string([self._get_my_nick(), - round_int(self.heading)])) - self.tw.send_event(event) - - def arc(self, a, r, share=True): - ''' Draw an arc ''' - self.canvas.set_source_rgb(self.fgrgb[0] / 255., self.fgrgb[1] / 255., - self.fgrgb[2] / 255.) if self.cr_svg is not None: - self.cr_svg.set_source_rgb(self.fgrgb[0] / 255., - self.fgrgb[1] / 255., - self.fgrgb[2] / 255.) - try: - if a < 0: - self.larc(-a, r) - else: - self.rarc(a, r) - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - self.move_turtle() - if self.tw.sharing() and share: - event = 'a|%s' % (data_to_string([self._get_my_nick(), - [round_int(a), round_int(r)]])) - self.tw.send_event(event) - - def rarc(self, a, r): - ''' draw a clockwise arc ''' - r *= self.tw.coord_scale - if r < 0: - r = -r - a = -a - oldx, oldy = self.xcor, self.ycor - cx = self.xcor + r * cos(self.heading * DEGTOR) - cy = self.ycor - r * sin(self.heading * DEGTOR) - if self.pendown: - x, y = self.turtle_to_screen_coordinates(cx, cy) - - def _rarc(cr, x, y, r, a, h): - cr.arc(x, y, r, (h - 180) * DEGTOR, (h - 180 + a) * DEGTOR) - cr.stroke() - - _rarc(self.canvas, x, y, r, a, self.heading) - self.inval() - if self.cr_svg is not None: - _rarc(self.cr_svg, x, y, r, a, self.heading) - - if self.fill: - if self.poly_points == []: - self.poly_points.append(('move', x, y)) - self.poly_points.append(('rarc', x, y, r, - (self.heading - 180) * DEGTOR, - (self.heading - 180 + a) * DEGTOR)) - - self.right(a, False) - self.xcor = cx - r * cos(self.heading * DEGTOR) - self.ycor = cy + r * sin(self.heading * DEGTOR) - - def larc(self, a, r): + _rarc(self.cr_svg, x, y, r, a, heading) + + def larc(self, x, y, r, a, heading): ''' draw a counter-clockwise arc ''' - r *= self.tw.coord_scale - if r < 0: - r = -r - a = -a - oldx, oldy = self.xcor, self.ycor - cx = self.xcor - r * cos(self.heading * DEGTOR) - cy = self.ycor + r * sin(self.heading * DEGTOR) - if self.pendown: - x, y = self.turtle_to_screen_coordinates(cx, cy) - - def _larc(cr, x, y, r, a, h): - cr.arc_negative(x, y, r, h * DEGTOR, (h - a) * DEGTOR) - cr.stroke() - - _larc(self.canvas, x, y, r, a, self.heading) - self.inval() - if self.cr_svg is not None: - _larc(self.cr_svg, x, y, r, a, self.heading) - - if self.fill: - if self.poly_points == []: - self.poly_points.append(('move', x, y)) - self.poly_points.append(('larc', x, y, r, - (self.heading) * DEGTOR, - (self.heading - a) * DEGTOR)) - - self.right(-a, False) - self.xcor = cx + r * cos(self.heading * DEGTOR) - self.ycor = cy - r * sin(self.heading * DEGTOR) - - def setxy(self, x, y, share=True, pendown=True): - ''' Move turtle to position x,y ''' - oldx, oldy = self.xcor, self.ycor - x *= self.tw.coord_scale - y *= self.tw.coord_scale - try: - self.xcor, self.ycor = x, y - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - - if self.pendown and pendown: - self.canvas.set_source_rgb(self.fgrgb[0] / 255., - self.fgrgb[1] / 255., - self.fgrgb[2] / 255.) - if self.cr_svg is not None: - self.cr_svg.set_source_rgb(self.fgrgb[0] / 255., - self.fgrgb[1] / 255., - self.fgrgb[2] / 255.) - self.draw_line(oldx, oldy, self.xcor, self.ycor) - self.inval() - self.move_turtle() - - if self.tw.sharing() and share: - event = 'x|%s' % (data_to_string([self._get_my_nick(), - [round_int(x), round_int(y)]])) - self.tw.send_event(event) - - def setpensize(self, ps, share=True): - ''' Set the pen size ''' - try: - if ps < 0: - ps = 0 - self.pensize = ps - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - self.tw.active_turtle.set_pen_size(ps) - self.canvas.set_line_width(ps) - if self.cr_svg is not None: - self.cr_svg.set_line_width(ps) - if self.tw.sharing() and share: - event = 'w|%s' % (data_to_string([self._get_my_nick(), - round_int(ps)])) - self.tw.send_event(event) + def _larc(cr, x, y, r, a, h): + cr.arc_negative(x, y, r, h * DEGTOR, (h - a) * DEGTOR) + cr.stroke() - def setcolor(self, c, share=True): - ''' Set the pen color ''' + _larc(self.canvas, x, y, r, a, heading) + self.inval() + if self.cr_svg is not None: + _larc(self.cr_svg, x, y, r, a, heading) - # Special case for color blocks - if c in COLORDICT: - self.setshade(COLORDICT[c][1], share) - self.setgray(COLORDICT[c][2], share) - if COLORDICT[c][0] is not None: - self.setcolor(COLORDICT[c][0], share) - c = COLORDICT[c][0] - else: - c = self.color - - try: - self.color = c - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - self.tw.active_turtle.set_color(c) - self.set_fgcolor() - if self.tw.sharing() and share: - event = 'c|%s' % (data_to_string([self._get_my_nick(), - round_int(c)])) - self.tw.send_event(event) - - def setgray(self, g, share=True): - ''' Set the gray level ''' - try: - self.gray = g - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - if self.gray < 0: - self.gray = 0 - if self.gray > 100: - self.gray = 100 - self.set_fgcolor() - self.tw.active_turtle.set_gray(self.gray) - if self.tw.sharing() and share: - event = 'g|%s' % (data_to_string([self._get_my_nick(), - round_int(self.gray)])) - self.tw.send_event(event) - - def set_textcolor(self): - ''' Deprecated: Set the text color to foreground color. ''' - return - - def settextcolor(self, c): # deprecated - ''' Set the text color ''' - return - - def settextsize(self, c): # deprecated - ''' Set the text size ''' - try: - self.tw.textsize = c - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - - def setshade(self, s, share=True): - ''' Set the color shade ''' - try: - self.shade = s - except (TypeError, ValueError): - debug_output('bad value sent to %s' % (__name__), - self.tw.running_sugar) - return - self.tw.active_turtle.set_shade(s) - self.set_fgcolor() - if self.tw.sharing() and share: - event = 's|%s' % (data_to_string([self._get_my_nick(), - round_int(s)])) - self.tw.send_event(event) + def set_pen_size(self, pen_size): + ''' Set the pen size ''' + self.canvas.set_line_width(pen_size) + if self.cr_svg is not None: + self.cr_svg.set_line_width(pen_size) def fillscreen(self, c, s): ''' Deprecated method: Fill screen with color/shade ''' - self.fillscreen_with_gray(c, s, self.gray) + self.fillscreen_with_gray(c, s, self._gray) - def fillscreen_with_gray(self, c, s, g): + def fillscreen_with_gray(self, color, shade, gray): ''' Fill screen with color/shade/gray and reset to defaults ''' - oldc, olds, oldg = self.color, self.shade, self.gray + + save_rgb = self._fgrgb[:] # Special case for color blocks - if c in COLORDICT: - if COLORDICT[c][0] is None: - s = COLORDICT[c][1] - c = self.color + if color in COLORDICT: + if COLORDICT[color][0] is None: + self._shade = COLORDICT[color][1] else: - c = COLORDICT[c][0] - if s in COLORDICT: - s = COLORDICT[s][1] - if g in COLORDICT: - g = COLORDICT[g][2] + self._color = COLORDICT[color][0] + else: + self._color = color + if shade in COLORDICT: + self._shade = COLORDICT[shade][1] + else: + self._shade = shade + if gray in COLORDICT: + self._gray = COLORDICT[gray][2] + else: + self._gray = gray + + if self._gray < 0: + self._gray = 0 + if self._gray > 100: + self._gray = 100 - self.setcolor(c, False) - self.setshade(s, False) - self.setgray(g, False) - self.bgrgb = self.fgrgb[:] + self.set_fgcolor(shade=self._shade, gray=self._gray, color=self._color) + self._bgrgb = self._fgrgb[:] def _fillscreen(cr, rgb, w, h): cr.set_source_rgb(rgb[0] / 255., rgb[1] / 255., rgb[2] / 255.) cr.rectangle(0, 0, w * 2, h * 2) cr.fill() - _fillscreen(self.canvas, self.fgrgb, self.width, self.height) + _fillscreen(self.canvas, self._fgrgb, self.width, self.height) self.inval() if self.cr_svg is not None: - _fillscreen(self.cr_svg, self.fgrgb, self.width, self.height) - self.setcolor(oldc, False) - self.setshade(olds, False) - self.setgray(oldg, False) - self.fill = False - self.poly_points = [] - - def set_fgcolor(self): + _fillscreen(self.cr_svg, self._fgrgb, self.width, self.height) + + self._fgrgb = save_rgb[:] + + def set_fgcolor(self, shade=None, gray=None, color=None): ''' Set the foreground color ''' - sh = (wrap100(self.shade) - 50) / 50.0 - rgb = COLOR_TABLE[wrap100(self.color)] + if shade is not None: + self._shade = shade + if gray is not None: + self._gray = gray + if color is not None: + self._color = color + sh = (wrap100(self._shade) - 50) / 50.0 + rgb = COLOR_TABLE[wrap100(self._color)] r = (rgb >> 8) & 0xff00 - r = calc_gray(r, self.gray) + r = calc_gray(r, self._gray) r = calc_shade(r, sh) g = rgb & 0xff00 - g = calc_gray(g, self.gray) + g = calc_gray(g, self._gray) g = calc_shade(g, sh) b = (rgb << 8) & 0xff00 - b = calc_gray(b, self.gray) + b = calc_gray(b, self._gray) b = calc_shade(b, sh) - self.fgrgb = [r >> 8, g >> 8, b >> 8] - - def setpen(self, bool, share=True): - ''' Lower or raise the pen ''' - self.pendown = bool - self.tw.active_turtle.set_pen_state(bool) - if self.tw.sharing() and share: - event = 'p|%s' % (data_to_string([self._get_my_nick(), bool])) - self.tw.send_event(event) + self._fgrgb = [r >> 8, g >> 8, b >> 8] def draw_surface(self, surface, x, y, w, h): ''' Draw a surface ''' @@ -568,7 +279,7 @@ class TurtleGraphics: if self.cr_svg is not None: _draw_surface(self.cr_svg, surface, x, y, w, h) - def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): + def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, heading): ''' Draw a pixbuf ''' def _draw_pixbuf(cr, pixbuf, a, b, x, y, w, h, heading): @@ -585,37 +296,15 @@ class TurtleGraphics: cc.fill() cc.restore() - _draw_pixbuf(self.canvas, pixbuf, a, b, x, y, w, h, self.heading) + _draw_pixbuf(self.canvas, pixbuf, a, b, x, y, w, h, heading) self.inval() if self.cr_svg is not None: - _draw_pixbuf(self.cr_svg, pixbuf, a, b, x, y, w, h, self.heading) - if self.tw.sharing() and share: - if self.tw.running_sugar: - tmp_path = get_path(self.tw.activity, 'instance') - else: - tmp_path = '/tmp' - tmp_file = os.path.join(get_path(self.tw.activity, 'instance'), - 'tmpfile.png') - pixbuf.save(tmp_file, 'png', {'quality': '100'}) - data = image_to_base64(tmp_file, tmp_path) - height = pixbuf.get_height() - width = pixbuf.get_width() - x, y = self.screen_to_turtle_coordinates(x, y) - event = 'P|%s' % (data_to_string([self._get_my_nick(), - [round_int(a), round_int(b), - round_int(x), round_int(y), - round_int(w), round_int(h), - round_int(width), - round_int(height), - data]])) - gobject.idle_add(self.tw.send_event, event) - os.remove(tmp_file) - - def draw_text(self, label, x, y, size, w, share=True): + _draw_pixbuf(self.cr_svg, pixbuf, a, b, x, y, w, h, heading) + + def draw_text(self, label, x, y, size, width, heading, scale): ''' Draw text ''' - w *= self.tw.coord_scale - def _draw_text(cr, label, x, y, size, w, scale, heading, rgb): + def _draw_text(cr, label, x, y, size, width, scale, heading, rgb): cc = pangocairo.CairoContext(cr) pl = cc.create_layout() fd = pango.FontDescription('Sans') @@ -627,7 +316,7 @@ class TurtleGraphics: pl.set_text(str(label)) else: pl.set_text(str(label)) - pl.set_width(int(w) * pango.SCALE) + pl.set_width(int(width) * pango.SCALE) cc.save() cc.translate(x, y) cc.rotate(heading * DEGTOR) @@ -636,36 +325,24 @@ class TurtleGraphics: cc.show_layout(pl) cc.restore() - _draw_text(self.canvas, label, x, y, size, w, self.tw.coord_scale, - self.heading, self.fgrgb) + width *= scale + _draw_text(self.canvas, label, x, y, size, width, scale, heading, + self._fgrgb) self.inval() if self.cr_svg is not None: # and self.pendown: - _draw_text(self.cr_svg, label, x, y, size, w, self.tw.coord_scale, - self.heading, self.fgrgb) - if self.tw.sharing() and share: - event = 'W|%s' % (data_to_string([self._get_my_nick(), - [label, round_int(x), - round_int(y), round_int(size), - round_int(w)]])) - self.tw.send_event(event) - - def turtle_to_screen_coordinates(self, x, y): - ''' The origin of turtle coordinates is the center of the screen ''' - return self.width / 2. + x, self.invert_y_coordinate(y) - - def screen_to_turtle_coordinates(self, x, y): - ''' The origin of the screen coordinates is the upper left corner ''' - return x - self.width / 2., self.invert_y_coordinate(y) - - def invert_y_coordinate(self, y): - ''' Positive y goes up in turtle coordinates, down in sceeen - coordinates ''' - return self.height / 2. - y + _draw_text(self.cr_svg, label, x, y, size, width, scale, heading, + self._fgrgb) + + def set_source_rgb(self): + r = self._fgrgb[0] / 255. + g = self._fgrgb[1] / 255. + b = self._fgrgb[2] / 255. + self.canvas.set_source_rgb(r, g, b) + if self.cr_svg is not None: + self.cr_svg.set_source_rgb(r, g, b) def draw_line(self, x1, y1, x2, y2): ''' Draw a line ''' - x1, y1 = self.turtle_to_screen_coordinates(x1, y1) - x2, y2 = self.turtle_to_screen_coordinates(x2, y2) def _draw_line(cr, x1, y1, x2, y2): cr.move_to(x1, y1) @@ -675,40 +352,23 @@ class TurtleGraphics: _draw_line(self.canvas, x1, y1, x2, y2) if self.cr_svg is not None: _draw_line(self.cr_svg, x1, y1, x2, y2) - if self.fill: - if self.poly_points == []: - self.poly_points.append(('move', x1, y1)) - self.poly_points.append(('line', x2, y2)) - - def turn_turtle(self): - ''' Change the orientation of the turtle ''' - self.tw.active_turtle.set_heading(self.heading) - - def move_turtle(self): - ''' Move the turtle ''' - x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor) - if self.tw.interactive_mode: - self.tw.active_turtle.move( - (self.cx + x - self.tw.active_turtle.spr.rect.width / 2., - self.cy + y - self.tw.active_turtle.spr.rect.height / 2.)) - else: - self.tw.active_turtle.move((self.cx + x, self.cy + y)) + self.inval() def get_color_index(self, r, g, b, a=0): ''' Find the closest palette entry to the rgb triplet ''' - if self.shade != 50 or self.gray != 100: + if self._shade != 50 or self._gray != 100: r <<= 8 g <<= 8 b <<= 8 - if self.shade != 50: - sh = (wrap100(self.shade) - 50) / 50. + if self._shade != 50: + sh = (wrap100(self._shade) - 50) / 50. r = calc_shade(r, sh, True) g = calc_shade(g, sh, True) b = calc_shade(b, sh, True) - if self.gray != 100: - r = calc_gray(r, self.gray, True) - g = calc_gray(g, self.gray, True) - b = calc_gray(b, self.gray, True) + if self._gray != 100: + r = calc_gray(r, self._gray, True) + g = calc_gray(g, self._gray, True) + b = calc_gray(b, self._gray, True) r >>= 8 g >>= 8 b >>= 8 @@ -727,20 +387,19 @@ class TurtleGraphics: closest_color = i return closest_color - def get_pixel(self): + def get_pixel(self, x, y): ''' Read the pixel at x, y ''' - if self.tw.interactive_mode: - x, y = self.turtle_to_screen_coordinates(self.xcor, self.ycor) + if self.turtle_window.interactive_mode: x = int(x) y = int(y) - w = self.tw.turtle_canvas.get_width() - h = self.tw.turtle_canvas.get_height() + w = self.turtle_window.turtle_canvas.get_width() + h = self.turtle_window.turtle_canvas.get_height() if x < 0 or x > (w - 1) or y < 0 or y > (h - 1): return(-1, -1, -1, -1) # create a new 1x1 cairo surface cs = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1) cr = cairo.Context(cs) - cr.set_source_surface(self.tw.turtle_canvas, -x, -y) + cr.set_source_surface(self.turtle_window.turtle_canvas, -x, -y) cr.rectangle(0, 0, 1, 1) cr.set_operator(cairo.OPERATOR_SOURCE) cr.fill() @@ -750,32 +409,6 @@ class TurtleGraphics: else: return(-1, -1, -1, -1) - def set_turtle(self, k, colors=None): - ''' Select the current turtle and associated pen status ''' - if k not in self.tw.turtles.dict: - # if it is a new turtle, start it in the center of the screen - self.tw.active_turtle = self.tw.turtles.get_turtle(k, True, colors) - self.seth(0.0, False) - self.setxy(0.0, 0.0, False, pendown=False) - self.tw.active_turtle.set_pen_state(True) - elif colors is not None: - self.tw.active_turtle = self.tw.turtles.get_turtle(k, False) - self.tw.active_turtle.set_turtle_colors(colors) - else: - self.tw.active_turtle = self.tw.turtles.get_turtle(k, False) - self.tw.active_turtle.show() - tx, ty = self.tw.active_turtle.get_xy() - self.xcor, self.ycor = self.screen_to_turtle_coordinates(tx, ty) - if self.tw.interactive_mode: - self.xcor += self.tw.active_turtle.spr.rect.width / 2. - self.ycor -= self.tw.active_turtle.spr.rect.height / 2. - self.heading = self.tw.active_turtle.get_heading() - self.setcolor(self.tw.active_turtle.get_color(), False) - self.setgray(self.tw.active_turtle.get_gray(), False) - self.setshade(self.tw.active_turtle.get_shade(), False) - self.setpensize(self.tw.active_turtle.get_pen_size(), False) - self.setpen(self.tw.active_turtle.get_pen_state(), False) - def svg_close(self): ''' Close current SVG graphic ''' self.cr_svg.show_page() @@ -784,9 +417,6 @@ class TurtleGraphics: ''' Reset svg flags ''' self.cr_svg = None - def _get_my_nick(self): - return self.tw.nick - def inval(self): ''' Invalidate a region for gtk ''' - self.tw.inval_all() + self.turtle_window.inval_all() diff --git a/TurtleArt/tacollaboration.py b/TurtleArt/tacollaboration.py index 32df65b..26a21d7 100644 --- a/TurtleArt/tacollaboration.py +++ b/TurtleArt/tacollaboration.py @@ -200,7 +200,7 @@ class Collaboration(): return # Save active Turtle - save_active_turtle = self._tw.active_turtle + save_active_turtle = self._tw.turtles.get_active_turtle() try: command, payload = event_message.split('|', 2) @@ -211,7 +211,7 @@ class Collaboration(): self._processing_methods[command](payload) # Restore active Turtle - self._tw.canvas.set_turtle( + self._tw.turtles.set_turtle( self._tw.turtles.get_turtle_key(save_active_turtle)) def send_event(self, entry): @@ -229,13 +229,13 @@ class Collaboration(): # Make sure it is not a "rejoin". if not nick in self._tw.remote_turtle_dictionary: # Add new turtle for the joiner. - self._tw.canvas.set_turtle(nick, colors) + self._tw.turtles.set_turtle(nick, colors) self._tw.label_remote_turtle(nick, colors) self._tw.remote_turtle_dictionary[nick] = colors else: self._tw.remote_turtle_dictionary = self._get_dictionary() # Add new turtle for the joiner. - self._tw.canvas.set_turtle(nick, colors) + self._tw.turtles.set_turtle(nick, colors) self._tw.label_remote_turtle(nick, colors) # Sharer should send the updated remote turtle dictionary to everyone. @@ -264,7 +264,7 @@ class Collaboration(): # Add new the turtle. colors = remote_turtle_dictionary[nick] self._tw.remote_turtle_dictionary[nick] = colors - self._tw.canvas.set_turtle(nick, colors) + self._tw.turtles.set_turtle(nick, colors) # Label the remote turtle. self._tw.label_remote_turtle(nick, colors) debug_output('adding %s to remote turtle dictionary' % @@ -278,23 +278,23 @@ class Collaboration(): def send_my_xy(self): ''' Set xy location so joiner can sync turtle positions. Should be used to sync positions after turtle drag. ''' - self._tw.canvas.set_turtle(self._get_nick()) - if self._tw.canvas.pendown: + self._tw.turtles.set_turtle(self._get_nick()) + if self._tw.turtles.get_active_turtle().get_pen_state(): self.send_event('p|%s' % (data_to_string([self._get_nick(), False]))) put_pen_back_down = True else: put_pen_back_down = False - self.send_event('x|%s' % - (data_to_string([self._get_nick(), - [int(self._tw.canvas.xcor), - int(self._tw.canvas.ycor)]]))) + self.send_event('x|%s' % (data_to_string( + [self._get_nick(), + [int(self._tw.turtles.get_active_turtle().get_xy()[0]), + int(self._tw.turtles.get_active_turtle().get_xy()[1])]]))) if put_pen_back_down: self.send_event('p|%s' % (data_to_string([self._get_nick(), True]))) - self.send_event('r|%s' % - (data_to_string([self._get_nick(), - int(self._tw.canvas.heading)]))) + self.send_event('r|%s' % (data_to_string( + [self._get_nick(), + int(self._tw.turtles.get_active_turtle().get_heading())]))) def _reskin_turtle(self, payload): if len(payload) > 0: @@ -307,8 +307,8 @@ class Collaboration(): file_name = base64_to_image(data, tmp_path) pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name, width, height) - self._tw.canvas.set_turtle(nick) - self._tw.active_turtle.set_shapes([pixbuf]) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_shapes([pixbuf]) def _draw_pixbuf(self, payload): if len(payload) > 0: @@ -322,89 +322,93 @@ class Collaboration(): file_name = base64_to_image(data, tmp_path) pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file_name, width, height) - x, y = self._tw.canvas.turtle_to_screen_coordinates(x, y) - self._tw.canvas.draw_pixbuf(pixbuf, a, b, x, y, w, h, - file_name, False) + pos = self._tw.turtles.turtle_to_screen_coordinates((x, y)) + self._tw.turtles.get_active_turtle().draw_pixbuf( + pixbuf, a, b, pos[0], pos[1], w, h, file_name, False) def _move_forward(self, payload): if len(payload) > 0: [nick, x] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.forward(x, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().forward(x, False) def _move_in_arc(self, payload): if len(payload) > 0: [nick, [a, r]] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.arc(a, r, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().arc(a, r, False) def _rotate_turtle(self, payload): if len(payload) > 0: [nick, h] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.seth(h, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_heading(h, False) def _setxy(self, payload): if len(payload) > 0: [nick, [x, y]] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setxy(x, y, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_xy(x, y, False) def _draw_text(self, payload): if len(payload) > 0: [nick, [label, x, y, size, w]] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.draw_text(label, x, y, size, w, False) + self._tw.turtles.get_active_turtle().draw_text( + label, x, y, size, w, False) def _set_pen_color(self, payload): if len(payload) > 0: [nick, x] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setcolor(x, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_color(x, False) def _set_pen_gray_level(self, payload): if len(payload) > 0: [nick, x] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setgray(x, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_gray(x, False) def _set_pen_shade(self, payload): if len(payload) > 0: [nick, x] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setshade(x, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_shade(x, False) def _set_pen_width(self, payload): if len(payload) > 0: [nick, x] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setpensize(x, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_pen_size(x, False) def _set_pen_state(self, payload): if len(payload) > 0: [nick, x] = data_from_string(payload) if nick != self._tw.nick: - self._tw.canvas.set_turtle(nick) - self._tw.canvas.setpen(x, False) + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_pen_state(x, False) def _fill_polygon(self, payload): - # Check to make sure that the poly_point array is passed properly if len(payload) > 0: [nick, poly_points] = data_from_string(payload) shared_poly_points = [] for i in range(len(poly_points)): shared_poly_points.append( - (self._tw.canvas.turtle_to_screen_coordinates + (self._tw.turtles.turtle_to_screen_coordinates (poly_points[i][0], poly_points[i][1]))) - self._tw.canvas.fill_polygon(shared_poly_points) + if nick != self._tw.nick: + self._tw.turtles.set_turtle(nick) + self._tw.turtles.get_active_turtle().set_poly_points( + shared_poly_points) + self._tw.turtles.get_active_turtle().stop_fill(False) def _speak(self, payload): if len(payload) > 0: diff --git a/TurtleArt/taconstants.py b/TurtleArt/taconstants.py index ffacac2..835209e 100644 --- a/TurtleArt/taconstants.py +++ b/TurtleArt/taconstants.py @@ -77,6 +77,7 @@ XO175 = 'xo1.75' XO30 = 'xo3.0' XO4 = 'xo4' UNKNOWN = 'unknown' +TMP_SVG_PATH = '/tmp/turtle_output.svg' CONSTANTS = {'leftpos': None, 'toppos': None, 'rightpos': None, 'bottompos': None, 'width': None, 'height': None, diff --git a/TurtleArt/talogo.py b/TurtleArt/talogo.py index 29d05d9..0b178c6 100644 --- a/TurtleArt/talogo.py +++ b/TurtleArt/talogo.py @@ -166,7 +166,7 @@ class LogoCode: if self.tw.gst_available: from tagplay import stop_media stop_media(self) - self.tw.active_turtle.show() + self.tw.turtles.get_active_turtle().show() self.tw.running_blocks = False # If we disabled hover help, reenable it if self._disable_help: @@ -188,10 +188,14 @@ class LogoCode: return sym def run_blocks(self, code): + """Run code generated by generate_code(). + """ self.start_time = time() self._setup_cmd(code) def generate_code(self, blk, blocks): + """ Generate code to be passed to run_blocks() from a stack of blocks. + """ for k in self.stacks.keys(): self.stacks[k] = None self.stacks['stack1'] = None @@ -323,7 +327,7 @@ class LogoCode: def _setup_cmd(self, string): """ Execute the psuedocode. """ - self.hidden_turtle = self.tw.active_turtle + self.hidden_turtle = self.tw.turtles.get_active_turtle() self.hidden_turtle.hide() # Hide the turtle while we are running. self.procstop = False blklist = self._readline(string) @@ -390,6 +394,7 @@ 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. """ @@ -413,12 +418,12 @@ class LogoCode: # In debugging modes, we pause between steps and show the turtle. if self.tw.step_time > 0: - self.tw.active_turtle.show() + self.tw.turtles.get_active_turtle().show() endtime = _millisecond() + self.tw.step_time * 100. while _millisecond() < endtime: sleep(0.1) yield True - self.tw.active_turtle.hide() + self.tw.turtles.get_active_turtle().hide() # 'Stand-alone' booleans are handled here. if token == self.symopar: @@ -537,7 +542,7 @@ class LogoCode: self.hidden_turtle.show() self.hidden_turtle = None else: - self.tw.active_turtle.show() + self.tw.turtles.get_active_turtle().show() self.tw.running_blocks = False return False except logoerror, e: @@ -599,10 +604,12 @@ class LogoCode: 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() def clear_value_blocks(self): if not hasattr(self, 'value_blocks_to_update'): @@ -689,11 +696,13 @@ class LogoCode: def x2tx(self): """ Convert screen coordinates to turtle coordinates """ - return int(self.tw.canvas.width / 2) + int(self.tw.canvas.xcor) + return int(self.tw.canvas.width / 2) + \ + int(self.tw.turtles.get_active_turtle().get_xy()[0]) def y2ty(self): """ Convert screen coordinates to turtle coordinates """ - return int(self.tw.canvas.height / 2) - int(self.tw.canvas.ycor) + return int(self.tw.canvas.height / 2) - \ + int(self.tw.turtles.get_active_turtle().get_xy()[1]) def wpercent(self): """ width as a percentage of screen coordinates """ @@ -742,20 +751,23 @@ class LogoCode: w *= self.tw.coord_scale h *= self.tw.coord_scale if center: - self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0, - self.x2tx() - int(w / 2), - self.y2ty() - int(h / 2), w, h, - self.filepath) + self.tw.turtles.get_active_turtle().draw_pixbuf( + self.pixbuf, 0, 0, + self.x2tx() - int(w / 2), + self.y2ty() - int(h / 2), w, h, + self.filepath) elif offset: - self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0, - self.x2tx(), - self.y2ty() - h, - w, h, self.filepath) + self.tw.turtles.get_active_turtle().draw_pixbuf( + self.pixbuf, 0, 0, + self.x2tx(), + self.y2ty() - h, + w, h, self.filepath) else: - self.tw.canvas.draw_pixbuf(self.pixbuf, 0, 0, - self.x2tx(), - self.y2ty(), - w, h, self.filepath) + self.tw.turtles.get_active_turtle().draw_pixbuf( + self.pixbuf, 0, 0, + self.x2tx(), + self.y2ty(), + w, h, self.filepath) def insert_desc(self, mimetype=None, description=None): """ Description text only (at current x, y) """ @@ -786,8 +798,8 @@ class LogoCode: else: text = self.filepath if text is not None: - self.tw.canvas.draw_text(text, self.x2tx(), self.y2ty(), - self.body_height, w) + self.tw.turtles.get_active_turtle().draw_text( + text, self.x2tx(), self.y2ty(), self.body_height, w) def media_wait(self): """ Wait for media to stop playing """ @@ -849,7 +861,9 @@ class LogoCode: def _expand_forever(self, b, blk, blocks): """ Expand a while or until block into: forever, ifelse, stopstack - Expand a forever block to run in a separate stack """ + Expand a forever block to run in a separate stack + Parameters: the loop block, the top block, all blocks. + Return the start block of the expanded loop, and all blocks.""" # TODO: create a less brittle way of doing this; having to # manage the connections and flows locally means we may run diff --git a/TurtleArt/tasprite_factory.py b/TurtleArt/tasprite_factory.py index 200fb78..2bd8993 100755 --- a/TurtleArt/tasprite_factory.py +++ b/TurtleArt/tasprite_factory.py @@ -30,6 +30,20 @@ from taconstants import HIT_RED, HIT_GREEN, HIDE_WHITE, SHOW_WHITE, \ class SVG: + """ Interface to the graphical representation of blocks, turtles, + palettes, etc. on screen + + terms used here: + docks -- list of connection points of a block to other blocks + innies -- right hand side docks of a block, argument slots + outie -- left hand side dock of a block + slot -- top dock of a block that can be attached to other blocks + cap -- top dock of a block that cannot be attached to other blocks + tab -- bottom dock of a block if other blocks can be attached + tail -- bottom dock of a block if no other blocks can be attached + arm -- connection point of a branching block (if-then, loops) where + inner blocks are attached + else -- optional second `arm' for if-then-else blocks """ def __init__(self): self._x = 0 @@ -496,13 +510,13 @@ C 36.1 12.6 43.1 20.6 43.1 30.4 Z" stroke-width="3.5" \ fill="%s" stroke="%s" />\n' % (self._fill, self._stroke) svg += ' \n' % (self._fill, self._stroke) +fill="%s" stroke="none" />\n' % (self._stroke) svg += ' \n' % (self._fill, self._stroke) +stroke-width="3.5" fill="%s" stroke="none" />\n' % (self._stroke) svg += ' \n' % (self._fill, self._stroke) +stroke-width="3.5" fill="%s" stroke="none" />\n' % (self._stroke) svg += ' \n' % (self._stroke) @@ -1224,6 +1238,13 @@ def close_file(f): def generator(datapath): svg = SVG() + f = open_file(datapath, "turtle.svg") + svg.set_scale(2) + svg_str = svg.turtle(['#00FF00','#00AA00']) + f.write(svg_str) + close_file(f) + + svg = SVG() f = open_file(datapath, "boolean_notnot.svg") svg.set_scale(2) svg.expand(30, 0, 0, 0) @@ -1248,7 +1269,6 @@ def generator(datapath): f.write(svg_str) close_file(f) - ''' svg = SVG() f = open_file(datapath, "basic.svg") svg.set_scale(2) @@ -1473,7 +1493,6 @@ def generator(datapath): svg_str = svg.clamp() f.write(svg_str) close_file(f) - ''' def main(): diff --git a/TurtleArt/taturtle.py b/TurtleArt/taturtle.py index 7db72e6..1ed557c 100644 --- a/TurtleArt/taturtle.py +++ b/TurtleArt/taturtle.py @@ -19,22 +19,29 @@ #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. -from random import uniform -from math import sin, cos, pi, sqrt +import os + import gtk +import gobject import cairo -from taconstants import (TURTLE_LAYER, DEFAULT_TURTLE_COLORS) -from tasprite_factory import (SVG, svg_str_to_pixbuf) -from tacanvas import (wrap100, COLOR_TABLE) +from random import uniform +from math import sin, cos, pi, sqrt +from taconstants import (TURTLE_LAYER, DEFAULT_TURTLE_COLORS, DEFAULT_TURTLE, + COLORDICT) +from tasprite_factory import SVG, svg_str_to_pixbuf +from tacanvas import wrap100, COLOR_TABLE from sprites import Sprite -from tautils import debug_output +from tautils import (debug_output, data_to_string, round_int, get_path, + image_to_base64) SHAPES = 36 +DEGTOR = pi / 180. +RTODEG = 180. / pi def generate_turtle_pixbufs(colors): - """ Generate pixbufs for generic turtles """ + ''' Generate pixbufs for generic turtles ''' shapes = [] svg = SVG() svg.set_scale(1.0) @@ -46,106 +53,202 @@ def generate_turtle_pixbufs(colors): class Turtles: - def __init__(self, sprite_list): - """ Class to hold turtles """ - self.dict = dict() - self.sprite_list = sprite_list - self.default_pixbufs = [] - - def get_turtle(self, k, append=False, colors=None): - """ Find a turtle """ - if k in self.dict: - return self.dict[k] + def __init__(self, turtle_window): + ''' Class to hold turtles ''' + self.turtle_window = turtle_window + self.sprite_list = turtle_window.sprite_list + self.width = turtle_window.width + self.height = turtle_window.height + self.dict = {} + self._default_pixbufs = [] + self._active_turtle = None + self._default_turtle_name = DEFAULT_TURTLE + + def get_turtle(self, turtle_name, append=False, colors=None): + ''' Find a turtle ''' + if turtle_name in self.dict: + return self.dict[turtle_name] elif not append: return None else: if colors is None: - Turtle(self, k) + Turtle(self, turtle_name) elif isinstance(colors, (list, tuple)): - Turtle(self, k, colors) + Turtle(self, turtle_name, colors) else: - Turtle(self, k, colors.split(',')) - return self.dict[k] + Turtle(self, turtle_name, colors.split(',')) + return self.dict[turtle_name] def get_turtle_key(self, turtle): - """ Find a turtle's name """ - for k in iter(self.dict): - if self.dict[k] == turtle: - return k + ''' Find a turtle's name ''' + for turtle_name in iter(self.dict): + if self.dict[turtle_name] == turtle: + return turtle_name return None def turtle_count(self): - """ How many turtles are there? """ + ''' How many turtles are there? ''' return(len(self.dict)) - def add_to_dict(self, k, turtle): - """ Add a new turtle """ - self.dict[k] = turtle + def add_to_dict(self, turtle_name, turtle): + ''' Add a new turtle ''' + self.dict[turtle_name] = turtle - def remove_from_dict(self, k): - """ Delete a turtle """ - if k in self.dict: - del(self.dict[k]) + def remove_from_dict(self, turtle_name): + ''' Delete a turtle ''' + if turtle_name in self.dict: + del(self.dict[turtle_name]) def show_all(self): - """ Make all turtles visible """ - for k in iter(self.dict): - self.dict[k].show() + ''' Make all turtles visible ''' + for turtle_name in iter(self.dict): + self.dict[turtle_name].show() def spr_to_turtle(self, spr): - """ Find the turtle that corresponds to sprite spr. """ - for k in iter(self.dict): - if spr == self.dict[k].spr: - return self.dict[k] + ''' Find the turtle that corresponds to sprite spr. ''' + for turtle_name in iter(self.dict): + if spr == self.dict[turtle_name].spr: + return self.dict[turtle_name] return None def get_pixbufs(self): - """ Get the pixbufs for the default turtle shapes. """ - if self.default_pixbufs == []: - self.default_pixbufs = generate_turtle_pixbufs( + ''' Get the pixbufs for the default turtle shapes. ''' + if self._default_pixbufs == []: + self._default_pixbufs = generate_turtle_pixbufs( ["#008000", "#00A000"]) - return(self.default_pixbufs) + return(self._default_pixbufs) + + def turtle_to_screen_coordinates(self, pos): + ''' The origin of turtle coordinates is the center of the screen ''' + return [self.width / 2.0 + pos[0], self._invert_y_coordinate(pos[1])] + + def screen_to_turtle_coordinates(self, pos): + ''' The origin of the screen coordinates is the upper-left corner ''' + return [pos[0] - self.width / 2.0, self._invert_y_coordinate(pos[1])] + + def _invert_y_coordinate(self, y): + ''' Positive y goes up in turtle coordinates, down in sceeen + coordinates ''' + return self.height / 2.0 - y + + def reset_turtles(self): + for turtle_name in iter(self.dict): + self.set_turtle(turtle_name) + if not self._active_turtle.get_remote(): + self._active_turtle.set_color(0) + self._active_turtle.set_shade(50) + self._active_turtle.set_gray(100) + if self.turtle_window.coord_scale == 1: + self._active_turtle.set_pen_size(5) + else: + self._active_turtle.set_pen_size(1) + self._active_turtle.reset_shapes() + self._active_turtle.set_heading(0.0) + self._active_turtle.set_pen_state(False) + self._active_turtle.move_turtle((0.0, 0.0)) + self._active_turtle.set_pen_state(True) + self._active_turtle.set_fill(False) + self._active_turtle.hide() + self.set_turtle(self._default_turtle_name) + + def set_turtle(self, turtle_name, colors=None): + ''' Select the current turtle and associated pen status ''' + if turtle_name not in self.dict: + # if it is a new turtle, start it in the center of the screen + self._active_turtle = self.get_turtle(turtle_name, True, colors) + self._active_turtle.set_heading(0.0, False) + self._active_turtle.set_xy((0.0, 0.0), False, pendown=False) + self._active_turtle.set_pen_state(True) + elif colors is not None: + self._active_turtle = self.get_turtle(turtle_name, False) + self._active_turtle.set_turtle_colors(colors) + else: + self._active_turtle = self.get_turtle(turtle_name, False) + self._active_turtle.show() + self._active_turtle.set_color(share=False) + self._active_turtle.set_gray(share=False) + self._active_turtle.set_shade(share=False) + self._active_turtle.set_pen_size(share=False) + self._active_turtle.set_pen_state(share=False) + + def set_default_turtle_name(self, name): + self._default_turtle_name = name + + def get_default_turtle_name(self): + return self._default_turtle_name + + def set_active_turtle(self, active_turtle): + self._active_turtle = active_turtle + + def get_active_turtle(self): + return self._active_turtle class Turtle: - def __init__(self, turtles, key, turtle_colors=None): - """ The turtle is not a block, just a sprite with an orientation """ - self.x = 0.0 - self.y = 0.0 - self.hidden = False - self.shapes = [] - self.custom_shapes = False - self.type = 'turtle' - self.name = key - self.heading = 0.0 - self.pen_shade = 50 - self.pen_color = 0 - self.pen_gray = 100 - self.pen_size = 5 - self.pen_state = True + def __init__(self, turtles, turtle_name, turtle_colors=None): + ''' The turtle is not a block, just a sprite with an orientation ''' + self.spr = None self.label_block = None + self._turtles = turtles + self._shapes = [] + self._custom_shapes = False + self._name = turtle_name + self._hidden = False + self._remote = False + self._x = 0.0 + self._y = 0.0 + self._heading = 0.0 + self._half_width = 0 + self._half_height = 0 + self._drag_radius = None + self._pen_shade = 50 + self._pen_color = 0 + self._pen_gray = 100 + if self._turtles.turtle_window.coord_scale == 1: + self._pen_size = 5 + else: + self._pen_size = 1 + self._pen_state = True + self._pen_fill = False + self._poly_points = [] - self._prep_shapes(key, turtles, turtle_colors) + self._prep_shapes(turtle_name, self._turtles, turtle_colors) - # Choose a random angle from which to attach the turtle label. + # Create a sprite for the turtle in interactive mode. if turtles.sprite_list is not None: - self.spr = Sprite(turtles.sprite_list, 0, 0, self.shapes[0]) + self.spr = Sprite(self._turtles.sprite_list, 0, 0, self._shapes[0]) + + self._calculate_sizes() + + # Choose a random angle from which to attach the turtle + # label to be used when sharing. angle = uniform(0, pi * 4 / 3.0) # 240 degrees - w = self.shapes[0].get_width() - r = w * 0.67 - # Restrict angle the the sides 30-150; 210-330 + width = self._shapes[0].get_width() + radius = width * 0.67 + # Restrict the angle to the sides: 30-150; 210-330 if angle > pi * 2 / 3.0: angle += pi / 2.0 # + 90 - self.label_xy = [int(r * sin(angle)), - int(r * cos(angle) + w / 2.0)] + self.label_xy = [int(radius * sin(angle)), + int(radius * cos(angle) + width / 2.0)] else: angle += pi / 6.0 # + 30 - self.label_xy = [int(r * sin(angle) + w / 2.0), - int(r * cos(angle) + w / 2.0)] - else: - self.spr = None - turtles.add_to_dict(key, self) + self.label_xy = [int(radius * sin(angle) + width / 2.0), + int(radius * cos(angle) + width / 2.0)] + + self._turtles.add_to_dict(turtle_name, self) + + def _calculate_sizes(self): + self._half_width = int(self.spr.rect.width / 2.0) + self._half_height = int(self.spr.rect.height / 2.0) + self._drag_radius = ((self._half_width * self._half_width) + + (self._half_height * self._half_height)) / 6 + + def set_remote(self): + self._remote = True + + def get_remote(self): + return self._remote def _prep_shapes(self, name, turtles=None, turtle_colors=None): # If the turtle name is an int, we'll use a palette color as the @@ -158,38 +261,38 @@ class Turtle: if turtle_colors is not None: self.colors = turtle_colors[:] - self.shapes = generate_turtle_pixbufs(self.colors) + self._shapes = generate_turtle_pixbufs(self.colors) elif use_color_table: fill = wrap100(int_key) stroke = wrap100(fill + 10) self.colors = ['#%06x' % (COLOR_TABLE[fill]), '#%06x' % (COLOR_TABLE[stroke])] - self.shapes = generate_turtle_pixbufs(self.colors) + self._shapes = generate_turtle_pixbufs(self.colors) else: if turtles is not None: self.colors = DEFAULT_TURTLE_COLORS - self.shapes = turtles.get_pixbufs() + self._shapes = turtles.get_pixbufs() def set_turtle_colors(self, turtle_colors): ''' reset the colors of a preloaded turtle ''' if turtle_colors is not None: self.colors = turtle_colors[:] - self.shapes = generate_turtle_pixbufs(self.colors) - self.set_heading(self.heading) + self._shapes = generate_turtle_pixbufs(self.colors) + self.set_heading(self._heading, share=False) def set_shapes(self, shapes, i=0): - """ Reskin the turtle """ + ''' Reskin the turtle ''' n = len(shapes) if n == 1 and i > 0: # set shape[i] - if i < len(self.shapes): - self.shapes[i] = shapes[0] + if i < len(self._shapes): + self._shapes[i] = shapes[0] elif n == SHAPES: # all shapes have been precomputed - self.shapes = shapes[:] + self._shapes = shapes[:] else: # rotate shapes if n != 1: debug_output("%d images passed to set_shapes: ignoring" % (n), - self.tw.running_sugar) - if self.heading == 0.0: # rotate the shapes + self._turtles.turtle_window.running_sugar) + if self._heading == 0.0: # rotate the shapes images = [] w, h = shapes[0].get_width(), shapes[0].get_height() nw = nh = int(sqrt(w * w + h * h)) @@ -197,113 +300,452 @@ class Turtle: surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context = gtk.gdk.CairoContext(context) - context.translate(nw / 2., nh / 2.) + context.translate(nw / 2.0, nh / 2.0) context.rotate(i * 10 * pi / 180.) - context.translate(-nw / 2., -nh / 2.) - context.set_source_pixbuf(shapes[0], (nw - w) / 2., - (nh - h) / 2.) + context.translate(-nw / 2.0, -nh / 2.0) + context.set_source_pixbuf(shapes[0], (nw - w) / 2.0, + (nh - h) / 2.0) context.rectangle(0, 0, nw, nh) context.fill() images.append(surface) - self.shapes = images[:] + self._shapes = images[:] else: # associate shape with image at current heading - j = int(self.heading + 5) % 360 / (360 / SHAPES) - self.shapes[j] = shapes[0] - self.custom_shapes = True + j = int(self._heading + 5) % 360 / (360 / SHAPES) + self._shapes[j] = shapes[0] + self._custom_shapes = True self.show() + self._calculate_sizes() def reset_shapes(self): - """ Reset the shapes to the standard turtle """ - if self.custom_shapes: - self.shapes = generate_turtle_pixbufs(self.colors) - self.custom_shapes = False - - def set_heading(self, heading): - """ Set the turtle heading (one shape per 360/SHAPES degrees) """ - self.heading = heading - i = (int(self.heading + 5) % 360) / (360 / SHAPES) - if not self.hidden and self.spr is not None: + ''' Reset the shapes to the standard turtle ''' + if self._custom_shapes: + self._shapes = generate_turtle_pixbufs(self.colors) + self._custom_shapes = False + self._calculate_sizes() + + def set_heading(self, heading, share=True): + ''' Set the turtle heading (one shape per 360/SHAPES degrees) ''' + try: + self._heading = heading + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + self._heading %= 360 + + i = (int(self._heading + 5) % 360) / (360 / SHAPES) + if not self._hidden and self.spr is not None: try: - self.spr.set_shape(self.shapes[i]) + self.spr.set_shape(self._shapes[i]) except IndexError: - self.spr.set_shape(self.shapes[0]) - - def set_color(self, color): - """ Set the pen color for this turtle. """ - self.pen_color = color - - def set_gray(self, gray): - """ Set the pen gray level for this turtle. """ - self.pen_gray = gray - - def set_shade(self, shade): - """ Set the pen shade for this turtle. """ - self.pen_shade = shade - - def set_pen_size(self, pen_size): - """ Set the pen size for this turtle. """ - self.pen_size = pen_size + self.spr.set_shape(self._shapes[0]) + + if self._turtles.turtle_window.sharing() and share: + event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick, + round_int(self._heading)])) + self._turtles.turtle_window.send_event(event) + + def set_color(self, color=None, share=True): + ''' Set the pen color for this turtle. ''' + # 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] + else: + color = self._pen_color + elif color is None: + color = self._pen_color - def set_pen_state(self, pen_state): - """ Set the pen state (down==True) for this turtle. """ - self.pen_state = pen_state + try: + self._pen_color = color + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, + gray=self._pen_gray, + color=self._pen_color) + + if self._turtles.turtle_window.sharing() and share: + event = 'c|%s' % (data_to_string([self._turtles.turtle_window.nick, + round_int(self._pen_color)])) + self._turtles.turtle_window.send_event(event) + + def set_gray(self, gray=None, share=True): + ''' Set the pen gray level for this turtle. ''' + if gray is not None: + try: + self._pen_gray = gray + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + if self._pen_gray < 0: + self._pen_gray = 0 + if self._pen_gray > 100: + self._pen_gray = 100 + + self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, + gray=self._pen_gray, + color=self._pen_color) + + if self._turtles.turtle_window.sharing() and share: + event = 'g|%s' % (data_to_string([self._turtles.turtle_window.nick, + round_int(self._pen_gray)])) + self._turtles.turtle_window.send_event(event) + + def set_shade(self, shade=None, share=True): + ''' Set the pen shade for this turtle. ''' + if shade is not None: + try: + self._pen_shade = shade + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + self._turtles.turtle_window.canvas.set_fgcolor(shade=self._pen_shade, + gray=self._pen_gray, + color=self._pen_color) + + if self._turtles.turtle_window.sharing() and share: + event = 's|%s' % (data_to_string([self._turtles.turtle_window.nick, + round_int(self._pen_shade)])) + self._turtles.turtle_window.send_event(event) + + def set_pen_size(self, pen_size=None, share=True): + ''' Set the pen size for this turtle. ''' + if pen_size is not None: + try: + self._pen_size = max(0, pen_size) + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + self._turtles.turtle_window.canvas.set_pen_size( + self._pen_size * self._turtles.turtle_window.coord_scale) + + if self._turtles.turtle_window.sharing() and share: + event = 'w|%s' % (data_to_string([self._turtles.turtle_window.nick, + round_int(self._pen_size)])) + self._turtles.turtle_window.send_event(event) + + def set_pen_state(self, pen_state=None, share=True): + ''' Set the pen state (down==True) for this turtle. ''' + if pen_state is not None: + self._pen_state = pen_state + + if self._turtles.turtle_window.sharing() and share: + event = 'p|%s' % (data_to_string([self._turtles.turtle_window.nick, + self._pen_state])) + self._turtles.turtle_window.send_event(event) + + def set_fill(self, state=False): + self._pen_fill = state + if not self._pen_fill: + self._poly_points = [] + + def set_poly_points(self, poly_points=None): + if poly_points is not None: + self._poly_points = poly_points[:] + + def start_fill(self): + self._pen_fill = True + self._poly_points = [] + + def stop_fill(self, share=True): + self._pen_fill = False + if len(self._poly_points) == 0: + return + + self._turtles.turtle_window.canvas.fill_polygon(self._poly_points) + + if self._turtles.turtle_window.sharing() and share: + shared_poly_points = [] + for p in self._poly_points: + shared_poly_points.append( + (self._turtles.screen_to_turtle_coordinates(p))) + event = 'F|%s' % (data_to_string( + [self._turtles.turtle_window.nick, + shared_poly_points])) + self._turtles.turtle_window.send_event(event) + self._poly_points = [] def hide(self): - """ Hide the turtle. """ if self.spr is not None: self.spr.hide() if self.label_block is not None: self.label_block.spr.hide() - self.hidden = True + self._hidden = True def show(self): - """ Show the turtle. """ if self.spr is not None: self.spr.set_layer(TURTLE_LAYER) - self.hidden = False - self.move((self.x, self.y)) - self.set_heading(self.heading) + self._hidden = False + self.move_turtle_spr((self._x, self._y)) + self.set_heading(self._heading) if self.label_block is not None: self.label_block.spr.set_layer(TURTLE_LAYER + 1) - def move(self, pos): - """ Move the turtle. """ - self.x, self.y = pos[0], pos[1] - if not self.hidden and self.spr is not None: - self.spr.move((int(pos[0]), int(pos[1]))) + def move_turtle(self, pos=None): + ''' Move the turtle's position ''' + if pos is None: + pos = self.get_xy() + + self._x, self._y = pos[0], pos[1] + if self.spr is not None: + self.move_turtle_spr(pos) + + def move_turtle_spr(self, pos): + ''' Move the turtle's sprite ''' + pos = self._turtles.turtle_to_screen_coordinates(pos) + + pos[0] -= self._half_width + pos[1] -= self._half_height + + if not self._hidden and self.spr is not None: + self.spr.move(pos) if self.label_block is not None: - self.label_block.spr.move((int(pos[0] + self.label_xy[0]), - int(pos[1] + self.label_xy[1]))) - return(self.x, self.y) + self.label_block.spr.move((pos[0] + self.label_xy[0], + pos[1] + self.label_xy[1])) + + def right(self, degrees, share=True): + ''' Rotate turtle clockwise ''' + try: + self._heading += degrees + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + self._heading %= 360 + + if self._turtles.turtle_window.sharing() and share: + event = 'r|%s' % (data_to_string([self._turtles.turtle_window.nick, + round_int(self._heading)])) + self._turtles.turtle_window.send_event(event) + + def _draw_line(self, old, new, pendown): + if self._pen_state and pendown: + self._turtles.turtle_window.canvas.set_source_rgb() + pos1 = self._turtles.turtle_to_screen_coordinates(old) + pos2 = self._turtles.turtle_to_screen_coordinates(new) + self._turtles.turtle_window.canvas.draw_line(pos1[0], pos1[1], + pos2[0], pos2[1]) + if self._pen_fill: + if self._poly_points == []: + self._poly_points.append(('move', pos1[0], pos1[1])) + self._poly_points.append(('line', pos2[0], pos2[1])) + + def forward(self, distance, share=True): + scaled_distance = distance * self._turtles.turtle_window.coord_scale + + old = self.get_xy() + try: + xcor = old[0] + scaled_distance * sin(self._heading * DEGTOR) + ycor = old[1] + scaled_distance * cos(self._heading * DEGTOR) + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + self._draw_line(old, (xcor, ycor), True) + self.move_turtle((xcor, ycor)) + + if self._turtles.turtle_window.sharing() and share: + event = 'f|%s' % (data_to_string([self._turtles.turtle_window.nick, + int(distance)])) + self._turtles.turtle_window.send_event(event) + + def set_xy(self, pos, share=True, pendown=True, dragging=False): + old = self.get_xy() + + try: + if dragging: + xcor = pos[0] + ycor = pos[1] + else: + xcor = pos[0] * self._turtles.turtle_window.coord_scale + ycor = pos[1] * self._turtles.turtle_window.coord_scale + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + self._draw_line(old, (xcor, ycor), pendown) + self.move_turtle((xcor, ycor)) + + if self._turtles.turtle_window.sharing() and share: + event = 'x|%s' % (data_to_string([self._turtles.turtle_window.nick, + [round_int(xcor), + round_int(ycor)]])) + self._turtles.turtle_window.send_event(event) + + def arc(self, a, r, share=True): + ''' Draw an arc ''' + if self._pen_state: + self._turtles.turtle_window.canvas.set_source_rgb() + try: + if a < 0: + pos = self.larc(-a, r) + else: + pos = self.rarc(a, r) + except (TypeError, ValueError): + debug_output('bad value sent to %s' % (__name__), + self._turtles.turtle_window.running_sugar) + return + + self.move_turtle(pos) + + if self._turtles.turtle_window.sharing() and share: + event = 'a|%s' % (data_to_string([self._turtles.turtle_window.nick, + [round_int(a), round_int(r)]])) + self._turtles.turtle_window.send_event(event) + + def rarc(self, a, r): + ''' draw a clockwise arc ''' + r *= self._turtles.turtle_window.coord_scale + if r < 0: + r = -r + a = -a + pos = self.get_xy() + cx = pos[0] + r * cos(self._heading * DEGTOR) + cy = pos[1] - r * sin(self._heading * DEGTOR) + if self._pen_state: + npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) + self._turtles.turtle_window.canvas.rarc(npos[0], npos[1], r, a, + self._heading) + + if self._pen_fill: + if self._poly_points == []: + self._poly_points.append(('move', npos[0], npos[1])) + self._poly_points.append(('rarc', npos[0], npos[1], r, + (self._heading - 180) * DEGTOR, + (self._heading - 180 + a) + * DEGTOR)) + + self.right(a, False) + return [cx - r * cos(self._heading * DEGTOR), + cy + r * sin(self._heading * DEGTOR)] + + def larc(self, a, r): + ''' draw a counter-clockwise arc ''' + r *= self._turtles.turtle_window.coord_scale + if r < 0: + r = -r + a = -a + pos = self.get_xy() + cx = pos[0] - r * cos(self._heading * DEGTOR) + cy = pos[1] + r * sin(self._heading * DEGTOR) + if self._pen_state: + npos = self._turtles.turtle_to_screen_coordinates((cx, cy)) + self._turtles.turtle_window.canvas.larc(npos[0], npos[1], r, a, + self._heading) + + if self._pen_fill: + if self._poly_points == []: + self._poly_points.append(('move', npos[0], npos[1])) + self._poly_points.append(('larc', npos[0], npos[1], r, + (self._heading) * DEGTOR, + (self._heading - a) * DEGTOR)) + + self.right(-a, False) + return [cx + r * cos(self._heading * DEGTOR), + cy - r * sin(self._heading * DEGTOR)] + + def draw_pixbuf(self, pixbuf, a, b, x, y, w, h, path, share=True): + ''' Draw a pixbuf ''' + + self._turtles.turtle_window.canvas.draw_pixbuf( + pixbuf, a, b, x, y, w, h, self._heading) + + if self._turtles.turtle_window.sharing() and share: + if self._turtles.turtle_window.running_sugar: + tmp_path = get_path(self._turtles.turtle_window.activity, + 'instance') + else: + tmp_path = '/tmp' + tmp_file = os.path.join( + get_path(self._turtles.turtle_window.activity, 'instance'), + 'tmpfile.png') + pixbuf.save(tmp_file, 'png', {'quality': '100'}) + data = image_to_base64(tmp_file, tmp_path) + height = pixbuf.get_height() + width = pixbuf.get_width() + + pos = self._turtles.screen_to_turtle_coordinates((x, y)) + + event = 'P|%s' % (data_to_string([self._turtles.turtle_window.nick, + [round_int(a), round_int(b), + round_int(pos[0]), + round_int(pos[1]), + round_int(w), round_int(h), + round_int(width), + round_int(height), + data]])) + gobject.idle_add(self._turtles.turtle_window.send_event, event) + + os.remove(tmp_file) + + def draw_text(self, label, x, y, size, w, share=True): + ''' Draw text ''' + self._turtles.turtle_window.canvas.draw_text( + label, x, y, size, w, self._heading, + self._turtles.turtle_window.coord_scale) + + if self._turtles.turtle_window.sharing() and share: + event = 'W|%s' % (data_to_string([self._turtles.turtle_window.nick, + [label, round_int(x), + round_int(y), round_int(size), + round_int(w)]])) + self._turtles.turtle_window.send_event(event) def get_name(self): - ''' return turtle name (key) ''' - return self.name + return self._name def get_xy(self): - """ Return the turtle's x, y coordinates. """ - return(self.x, self.y) + return [self._x, self._y] + + def get_x(self): + return self._x + + def get_y(self): + return self._y def get_heading(self): - """ Return the turtle's heading. """ - return(self.heading) + return self._heading def get_color(self): - """ Return the turtle's color. """ - return(self.pen_color) + return self._pen_color def get_gray(self): - """ Return the turtle's gray level. """ - return(self.pen_gray) + return self._pen_gray def get_shade(self): - """ Return the turtle's shade. """ - return(self.pen_shade) + return self._pen_shade def get_pen_size(self): - """ Return the turtle's pen size. """ - return(self.pen_size) + return self._pen_size def get_pen_state(self): - """ Return the turtle's pen state. """ - return(self.pen_state) + return self._pen_state + + def get_fill(self): + return self._pen_fill + + def get_poly_points(self): + return self._poly_points + + def get_pixel(self): + pos = self._turtles.turtle_to_screen_coordinates(self.get_xy()) + return self._turtles.turtle_window.canvas.get_pixel(pos[0], pos[1]) + + def get_drag_radius(self): + if self._drag_radius is None: + self._calculate_sizes() + return self._drag_radius diff --git a/TurtleArt/tawindow.py b/TurtleArt/tawindow.py index 6152f20..50691c8 100644 --- a/TurtleArt/tawindow.py +++ b/TurtleArt/tawindow.py @@ -58,7 +58,7 @@ from taconstants import (HORIZONTAL_PALETTE, VERTICAL_PALETTE, BLOCK_SCALE, PYTHON_SKIN, PALETTE_HEIGHT, STATUS_LAYER, OLD_DOCK, EXPANDABLE_ARGS, XO1, XO15, XO175, XO30, XO4, TITLEXY, CONTENT_ARGS, CONSTANTS, EXPAND_SKIN, PROTO_LAYER, - EXPANDABLE_FLOW, SUFFIX) + EXPANDABLE_FLOW, SUFFIX, TMP_SVG_PATH) from tapalette import (palette_names, palette_blocks, expandable_blocks, block_names, content_blocks, default_values, special_names, block_styles, help_strings, @@ -99,9 +99,16 @@ _MACROS_SUBPATH = 'macros' class TurtleArtWindow(): ''' TurtleArt Window class abstraction ''' - def __init__(self, canvas_window, path, parent=None, + def __init__(self, canvas_window, path, parent=None, activity=None, mycolors=None, mynick=None, turtle_canvas=None, - running_sugar=True): + running_sugar=True, running_turtleart=True): + ''' + parent: the GTK Window that TA runs in + activity: the object that instantiated this TurtleArtWindow (in + GNOME, a TurtleMain instance, in Sugar, the Activity + instance) + running_turtleart: are we running TA or exported python code? + ''' self.parent = parent self.turtle_canvas = turtle_canvas self._loaded_project = '' @@ -111,6 +118,7 @@ class TurtleArtWindow(): self.gst_available = _GST_AVAILABLE self.running_sugar = False self.nick = None + self.running_turtleart = running_turtleart if isinstance(canvas_window, gtk.DrawingArea): self.interactive_mode = True self.window = canvas_window @@ -135,8 +143,13 @@ class TurtleArtWindow(): self.interactive_mode = False self.window = canvas_window self.running_sugar = False - self.activity = parent + if activity is not None: + self.activity = activity + else: + self.activity = parent + + # loading and saving self.path = path self.load_save_folder = os.path.join(path, 'samples') self.py_load_save_folder = os.path.join(path, 'pysamples') @@ -144,6 +157,8 @@ class TurtleArtWindow(): self.used_block_list = [] # Which blocks has the user used? self.save_folder = None self.save_file_name = None + + # dimensions self.width = gtk.gdk.screen_width() self.height = gtk.gdk.screen_height() self.rect = gtk.gdk.Rectangle(0, 0, 0, 0) @@ -166,6 +181,7 @@ class TurtleArtWindow(): self.sharing_blocks = False self.deleting_blocks = False + # find out which character to use as decimal point try: locale.setlocale(locale.LC_NUMERIC, '') except locale.Error: @@ -174,8 +190,8 @@ class TurtleArtWindow(): if self.decimal_point == '' or self.decimal_point is None: self.decimal_point = '.' + # settings that depend on the hardware self.orientation = HORIZONTAL_PALETTE - self.hw = get_hardware() self.lead = 1.0 if self.hw in (XO1, XO15, XO175, XO4): @@ -200,8 +216,9 @@ class TurtleArtWindow(): self.nop = 'nop' self.loaded = 0 self.step_time = 0 - self.hide = False - self.palette = True + # show/ hide palettes depending on whether we're running in TA or not + self.hide = not self.running_turtleart + self.palette = self.running_turtleart self.coord_scale = 1 self.buddies = [] self._saved_string = '' @@ -239,6 +256,7 @@ class TurtleArtWindow(): self.turtle_movement_to_share = None self.paste_offset = 20 # Don't paste on top of where you copied. + # common properties of all blocks (font size, decimal point, ...) self.block_list = Blocks(font_scale_factor=self.scale, decimal_point=self.decimal_point) if self.interactive_mode: @@ -246,23 +264,24 @@ class TurtleArtWindow(): else: self.sprite_list = None + # canvas object that supports the basic drawing functionality self.canvas = TurtleGraphics(self, self.width, self.height) if self.hw == XO175 and self.canvas.width == 1024: self.hw = XO30 if self.interactive_mode: self.sprite_list.set_cairo_context(self.canvas.canvas) - self.turtles = Turtles(self.sprite_list) - if self.nick is None: - self.default_turtle_name = DEFAULT_TURTLE - else: - self.default_turtle_name = self.nick + self.turtles = Turtles(self) + if self.nick is not None: + self.turtles.set_default_turtle_name(self.nick) if mycolors is None: - Turtle(self.turtles, self.default_turtle_name) + Turtle(self.turtles, self.turtles.get_default_turtle_name()) else: - Turtle(self.turtles, self.default_turtle_name, mycolors.split(',')) - self.active_turtle = self.turtles.get_turtle(self.default_turtle_name) - self.active_turtle.show() + Turtle(self.turtles, self.turtles.get_default_turtle_name(), + mycolors.split(',')) + self.turtles.set_active_turtle( + self.turtles.get_turtle(self.turtles.get_default_turtle_name())) + self.turtles.get_active_turtle().show() self.canvas.clearscreen(False) @@ -276,8 +295,10 @@ class TurtleArtWindow(): self.saved_pictures = [] self.block_operation = '' - from tabasics import Palettes - self._basic_palettes = Palettes(self) + # only in TA: setup basic palettes + if self.running_turtleart: + from tabasics import Palettes + self._basic_palettes = Palettes(self) if self.interactive_mode: gobject.idle_add(self._lazy_init) @@ -289,16 +310,20 @@ class TurtleArtWindow(): self._init_plugins() self._setup_plugins() self._setup_misc() - for name in palette_init_on_start: - debug_output('initing palette %s' % (name), self.running_sugar) - self.show_toolbar_palette(palette_names.index(name), - init_only=False, regenerate=True, - show=False) - self._basic_palettes.make_trash_palette() + if self.running_turtleart: + self._basic_palettes.make_trash_palette() + for name in palette_init_on_start: + debug_output('initing palette %s' % (name), self.running_sugar) + self.show_toolbar_palette(palette_names.index(name), + init_only=False, + regenerate=True, + show=False) - self.show_toolbar_palette(0, init_only=False, regenerate=True, - show=True) + self.show_toolbar_palette(0, + init_only=False, + regenerate=True, + show=True) if self.running_sugar: self.activity.check_buttons_for_fit() @@ -331,7 +356,7 @@ class TurtleArtWindow(): pname = os.path.join(path, dirname, dirname + '.py') if os.path.exists(pname): plugin_files.append(dirname) - return plugin_files + return plugin_files def _init_plugins(self): ''' Try importing plugin files from the plugin dir. ''' @@ -342,7 +367,7 @@ class TurtleArtWindow(): def init_plugin(self, plugin_dir): ''' Initialize plugin in plugin_dir ''' plugin_class = plugin_dir.capitalize() - f = "def f(self): from plugins.%s.%s import %s; return %s(self)" \ + f = 'def f(self): from plugins.%s.%s import %s; return %s(self)' \ % (plugin_dir, plugin_dir, plugin_class, plugin_class) plugins = {} # NOTE: When debugging plugins, it may be useful to not trap errors @@ -418,15 +443,15 @@ class TurtleArtWindow(): self.window.add_events(gtk.gdk.BUTTON_RELEASE_MASK) self.window.add_events(gtk.gdk.POINTER_MOTION_MASK) self.window.add_events(gtk.gdk.KEY_PRESS_MASK) - self.window.connect("expose-event", self._expose_cb) - self.window.connect("button-press-event", self._buttonpress_cb) - self.window.connect("button-release-event", self._buttonrelease_cb) - self.window.connect("motion-notify-event", self._move_cb) - self.window.connect("key-press-event", self._keypress_cb) + self.window.connect('expose-event', self._expose_cb) + self.window.connect('button-press-event', self._buttonpress_cb) + self.window.connect('button-release-event', self._buttonrelease_cb) + self.window.connect('motion-notify-event', self._move_cb) + self.window.connect('key-press-event', self._keypress_cb) gtk.gdk.screen_get_default().connect('size-changed', self._configure_cb) - target = [("text/plain", 0, 0)] + target = [('text/plain', 0, 0)] self.window.drag_dest_set(gtk.DEST_DEFAULT_ALL, target, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE) self.window.connect('drag_data_received', self._drag_data_received) @@ -506,16 +531,20 @@ class TurtleArtWindow(): self._autohide_shape = True for name in OVERLAY_SHAPES: + if name == 'Cartesian': + continue self.overlay_shapes[name] = Sprite( self.sprite_list, int(self.width / 2 - 600), int(self.height / 2 - 450), svg_str_to_pixbuf( - svg_from_file("%s/images/%s.svg" % (self.path, name)))) + svg_from_file('%s/images/%s.svg' % (self.path, name)))) self.overlay_shapes[name].hide() self.overlay_shapes[name].type = 'overlay' - if not self.running_sugar: + self._create_scaled_cartesian_coordinates() + + if self.running_turtleart and not self.running_sugar: # offset = 2 * self.width - 55 * len(TOOLBAR_SHAPES) offset = 55 * (1 + len(palette_blocks)) for i, name in enumerate(TOOLBAR_SHAPES): @@ -530,6 +559,27 @@ class TurtleArtWindow(): self.toolbar_shapes[name].type = 'toolbar' self.toolbar_shapes['stopiton'].hide() + def _create_scaled_cartesian_coordinates(self): + # Cartesian overlay has to be scaled to match the coordinate_scale + # 200 pixels in the graphic == height / 4. (10 units) + pixbuf = svg_str_to_pixbuf( + svg_from_file('%s/images/%s.svg' % (self.path, 'Cartesian'))) + + if self.running_sugar: + scale = self.height / 800. + else: + scale = self.height / 800. + # scale = (self.height + ICON_SIZE) / 800. + self.overlay_shapes['Cartesian'] = Sprite( + self.sprite_list, + int(self.width / 2 - 600), + int(self.height / 2 - 450), + pixbuf.scale_simple(int(1200 * scale), + int(900 * scale), + gtk.gdk.INTERP_BILINEAR)) + self.overlay_shapes['Cartesian'].set_layer(TAB_LAYER) + self.overlay_shapes['Cartesian'].hide() + def set_sharing(self, shared): self._sharing = shared @@ -625,7 +675,7 @@ class TurtleArtWindow(): if find_start_stack(blk): self.step_time = time if self.running_sugar: - debug_output("running stack starting from %s" % (blk.name), + debug_output('running stack starting from %s' % (blk.name), self.running_sugar) if running_from_button_push: self.selected_blk = None @@ -639,7 +689,7 @@ class TurtleArtWindow(): if find_block_to_run(blk): self.step_time = time if self.running_sugar: - debug_output("running stack starting from %s" % (blk.name), + debug_output('running stack starting from %s' % (blk.name), self.running_sugar) if running_from_button_push: self.selected_blk = None @@ -686,20 +736,27 @@ class TurtleArtWindow(): def draw_overlay(self, overlay): ''' Draw a coordinate grid onto the canvas. ''' - save_heading = self.canvas.heading - self.canvas.heading = 0 - w = self.overlay_shapes[overlay].rect[2] - h = self.overlay_shapes[overlay].rect[3] + width = self.overlay_shapes[overlay].rect[2] + height = self.overlay_shapes[overlay].rect[3] + if self.running_sugar: + y_offset = 0 + else: + y_offset = 0 + # y_offset = ICON_SIZE self.canvas.draw_surface( self.overlay_shapes[overlay].cached_surfaces[0], - (self.canvas.width - w) / 2., - (self.canvas.height - h) / 2., w, h) - self.canvas.heading = save_heading + (self.canvas.width - width) / 2.0, + (self.canvas.height - height + y_offset) / 2.0, + width, + height) - def update_overlay_position(self, widget, event): + def update_overlay_position(self, widget=None, event=None): ''' Reposition the overlays when window size changes ''' - self.width = event.width - self.height = event.height + # self.width = event.width + # self.height = event.height + self.width = gtk.gdk.screen_width() + self.height = gtk.gdk.screen_height() + for name in OVERLAY_SHAPES: if not name in self.overlay_shapes: continue @@ -708,23 +765,30 @@ class TurtleArtWindow(): if shape in shape._sprites.list: shape.hide() showing = True + self.overlay_shapes[name].move((int(self.width / 2 - 600), + int(self.height / 2 - 450))) + ''' self.overlay_shapes[name] = Sprite( self.sprite_list, int(self.width / 2 - 600), int(self.height / 2 - 450), svg_str_to_pixbuf( - svg_from_file("%s/images/%s.svg" % (self.path, name)))) + svg_from_file('%s/images/%s.svg' % (self.path, name)))) + ''' if showing: self.overlay_shapes[name].set_layer(OVERLAY_LAYER) else: self.overlay_shapes[name].hide() + ''' self.overlay_shapes[name].type = 'overlay' + ''' + self.cartesian = False self.polar = False self.metric = False self.canvas.width = self.width self.canvas.height = self.height - self.canvas.move_turtle() + self.turtles.get_active_turtle().move_turtle() def hideshow_button(self): ''' Hide/show button ''' @@ -776,6 +840,7 @@ class TurtleArtWindow(): if not self.running_sugar or not self.activity.has_toolbarbox: self.toolbar_spr.set_layer(CATEGORY_LAYER) self.palette = True + self._set_coordinates_label(palette_names[n]) def hide_palette(self): ''' Hide the palette. ''' @@ -1064,7 +1129,7 @@ class TurtleArtWindow(): self.toolbar_offset, svg_str_to_pixbuf( svg_from_file( - "%s/images/palettehorizontal.svg" % (self.path))))) + '%s/images/palettehorizontal.svg' % (self.path))))) self.palette_button.append( Sprite( self.sprite_list, @@ -1072,7 +1137,7 @@ class TurtleArtWindow(): self.toolbar_offset, svg_str_to_pixbuf( svg_from_file( - "%s/images/palettevertical.svg" % (self.path))))) + '%s/images/palettevertical.svg' % (self.path))))) self.palette_button[0].name = _('orientation') self.palette_button[1].name = _('orientation') self.palette_button[0].type = 'palette' @@ -1087,7 +1152,7 @@ class TurtleArtWindow(): self.toolbar_offset, svg_str_to_pixbuf( svg_from_file( - "%s/images/palettenext.svg" % (self.path))))) + '%s/images/palettenext.svg' % (self.path))))) self.palette_button[2].name = _('next') self.palette_button[2].type = 'palette' self.palette_button[2].set_layer(TAB_LAYER) @@ -1101,7 +1166,7 @@ class TurtleArtWindow(): self.toolbar_offset + dims[1], svg_str_to_pixbuf( svg_from_file( - "%s/images/palettehshift.svg" % (self.path))))) + '%s/images/palettehshift.svg' % (self.path))))) self.palette_button.append( Sprite( self.sprite_list, @@ -1109,7 +1174,7 @@ class TurtleArtWindow(): self.toolbar_offset, svg_str_to_pixbuf( svg_from_file( - "%s/images/palettevshift.svg" % (self.path))))) + '%s/images/palettevshift.svg' % (self.path))))) self.palette_button.append( Sprite( self.sprite_list, @@ -1117,7 +1182,7 @@ class TurtleArtWindow(): self.toolbar_offset + dims[1], svg_str_to_pixbuf( svg_from_file( - "%s/images/palettehshift2.svg" % (self.path))))) + '%s/images/palettehshift2.svg' % (self.path))))) self.palette_button.append( Sprite( self.sprite_list, @@ -1125,7 +1190,7 @@ class TurtleArtWindow(): self.toolbar_offset, svg_str_to_pixbuf( svg_from_file( - "%s/images/palettevshift2.svg" % (self.path))))) + '%s/images/palettevshift2.svg' % (self.path))))) for i in range(4): self.palette_button[3 + i].name = _('shift') self.palette_button[3 + i].type = 'palette' @@ -1384,6 +1449,13 @@ class TurtleArtWindow(): if spr is not None: blk = self.block_list.spr_to_block(spr) if blk is not None: + # Make sure stop button is visible + if self.running_sugar: + self.activity.stop_turtle_button.set_icon("stopiton") + self.activity.stop_turtle_button.set_tooltip( + _('Stop turtle')) + elif self.interactive_mode: + self.toolbar_shapes['stopiton'].set_layer(TAB_LAYER) self.showlabel('status', label=_('Please hit the Stop Button \ before making changes to your Turtle Blocks program')) @@ -1691,13 +1763,13 @@ before making changes to your Turtle Blocks program')) def _look_for_a_turtle(self, spr, x, y): # Next, look for a turtle - t = self.turtles.spr_to_turtle(spr) - if t is not None: + turtle = self.turtles.spr_to_turtle(spr) + if turtle is not None: # If turtle is shared, ignore click - if self.remote_turtle(t.get_name()): + if self.remote_turtle(turtle.get_name()): return True - self.selected_turtle = t - self.canvas.set_turtle(self.turtles.get_turtle_key(t)) + self.selected_turtle = turtle + self.turtles.set_turtle(self.turtles.get_turtle_key(turtle)) self._turtle_pressed(x, y) self.update_counter = 0 return True @@ -2245,7 +2317,7 @@ before making changes to your Turtle Blocks program')) self.used_block_list.append(newblk.spr.labels[0]) def new_macro(self, name, x, y): - ''' Create a "macro" (predefined stack of blocks). ''' + ''' Create a 'macro' (predefined stack of blocks). ''' macro = MACROS[name] macro[0][2] = x macro[0][3] = y @@ -2295,7 +2367,7 @@ before making changes to your Turtle Blocks program')) else: cons.append(blocks[c]) else: - debug_output("connection error %s" % + debug_output('connection error %s' % (str(self._process_block_data[i])), self.running_sugar) cons.append(None) @@ -2322,7 +2394,7 @@ before making changes to your Turtle Blocks program')) blocks[c].connections[3] = None else: # Connection was to a block we haven't seen yet. - debug_output("Warning: dock to the future", + debug_output('Warning: dock to the future', self.running_sugar) else: if self._process_block_data[i][4][0] is not None: @@ -2338,10 +2410,10 @@ before making changes to your Turtle Blocks program')) blocks[c].connections[1] = None else: # Connection was to a block we haven't seen yet. - debug_output("Warning: dock to the future", + debug_output('Warning: dock to the future', self.running_sugar) else: - debug_output("Warning: unknown connection state %s" % + debug_output('Warning: unknown connection state %s' % (str(blk.connections)), self.running_sugar) blk.connections = cons[:] @@ -2410,20 +2482,22 @@ before making changes to your Turtle Blocks program')) self._adjust_dock_positions(c) def _turtle_pressed(self, x, y): - (tx, ty) = self.selected_turtle.get_xy() - w = self.selected_turtle.spr.rect.width / 2 - h = self.selected_turtle.spr.rect.height / 2 - dx = x - tx - w - dy = y - ty - h - # if x, y is near the edge, rotate + pos = self.selected_turtle.get_xy() + tpos = self.turtles.turtle_to_screen_coordinates(pos) + dx = x - tpos[0] + dy = y - tpos[1] if not hasattr(self.lc, 'value_blocks'): self.lc.find_value_blocks() self.lc.update_values = True - if (dx * dx) + (dy * dy) > ((w * w) + (h * h)) / 6: - self.drag_turtle = \ - ('turn', self.canvas.heading - atan2(dy, dx) / DEGTOR, 0) + # Compare distance squared of drag position to sprite radius. + # If x, y is near the edge, rotate. + if (dx * dx) + (dy * dy) > self.selected_turtle.get_drag_radius(): + self.drag_turtle = ( + 'turn', + self.selected_turtle.get_heading() - atan2(dy, dx) / DEGTOR, + 0) else: - self.drag_turtle = ('move', x - tx, y - ty) + self.drag_turtle = ('move', x - tpos[0], y - tpos[1]) def _move_cb(self, win, event): x, y = xy(event) @@ -2436,18 +2510,18 @@ before making changes to your Turtle Blocks program')) ''' Share turtle movement and rotation after button up ''' if self.sharing(): nick = self.turtle_movement_to_share.get_name() - self.send_event("r|%s" % - (data_to_string([nick, - round_int(self.canvas.heading)]))) - if self.canvas.pendown: + self.send_event('r|%s' % (data_to_string( + [nick, + round_int(self.turtles.get_active_turtle().get_heading())]))) + if self.turtles.get_active_turtle().get_pen_state(): self.send_event('p|%s' % (data_to_string([nick, False]))) put_pen_back_down = True else: put_pen_back_down = False - self.send_event("x|%s" % - (data_to_string([nick, - [round_int(self.canvas.xcor), - round_int(self.canvas.ycor)]]))) + self.send_event('x|%s' % (data_to_string( + [nick, + [round_int(self.turtles.get_active_turtle().get_xy()[0]), + round_int(self.turtles.get_active_turtle().get_xy()[1])]]))) if put_pen_back_down: self.send_event('p|%s' % (data_to_string([nick, True]))) self.turtle_movement_to_share = None @@ -2474,34 +2548,37 @@ before making changes to your Turtle Blocks program')) # First, check to see if we are dragging or rotating a turtle. if self.selected_turtle is not None: - dtype, dragx, dragy = self.drag_turtle - (sx, sy) = self.selected_turtle.get_xy() - # self.canvas.set_turtle(self.selected_turtle.get_name()) + drag_type, dragx, dragy = self.drag_turtle self.update_counter += 1 - if dtype == 'move': - dx = x - dragx - sx + self.selected_turtle.spr.rect.width / 2 - dy = y - dragy - sy + self.selected_turtle.spr.rect.height / 2 + if drag_type == 'move': + dx = x - dragx + dy = y - dragy self.selected_turtle.spr.set_layer(TOP_LAYER) - tx, ty = self.canvas.screen_to_turtle_coordinates(sx + dx, - sy + dy) - if self.canvas.pendown: - self.canvas.setpen(False) - self.canvas.setxy(tx, ty, share=False) - self.canvas.setpen(True) + pos = self.turtles.screen_to_turtle_coordinates((dx, dy)) + if self.selected_turtle.get_pen_state(): + self.selected_turtle.set_pen_state(False) + self.selected_turtle.set_xy(pos, share=False, dragging=True) + self.selected_turtle.set_pen_state(True) else: - self.canvas.setxy(tx, ty, share=False) + self.selected_turtle.set_xy(pos, share=False, dragging=True) if self.update_counter % 5: self.lc.update_label_value( - 'xcor', self.canvas.xcor / self.coord_scale) + 'xcor', self.selected_turtle.get_xy()[0] / + self.coord_scale) self.lc.update_label_value( - 'ycor', self.canvas.ycor / self.coord_scale) + 'ycor', self.selected_turtle.get_xy()[1] / + self.coord_scale) else: - dx = x - sx - self.selected_turtle.spr.rect.width / 2 - dy = y - sy - self.selected_turtle.spr.rect.height / 2 - self.canvas.seth(int(dragx + atan2(dy, dx) / DEGTOR + 5) / - 10 * 10, share=False) + spos = self.turtles.turtle_to_screen_coordinates( + self.selected_turtle.get_xy()) + dx = x - spos[0] + dy = y - spos[1] + self.turtles.get_active_turtle().set_heading( + int(dragx + atan2(dy, dx) / DEGTOR + 5) / 10 * 10, + share=False) if self.update_counter % 5: - self.lc.update_label_value('heading', self.canvas.heading) + self.lc.update_label_value( + 'heading', self.selected_turtle.get_heading()) if self.update_counter % 20: self.display_coordinates() self.turtle_movement_to_share = self.selected_turtle @@ -2658,30 +2735,26 @@ before making changes to your Turtle Blocks program')) # We may have been moving the turtle if self.selected_turtle is not None: - (tx, ty) = self.selected_turtle.get_xy() - k = self.turtles.get_turtle_key(self.selected_turtle) - + pos = self.selected_turtle.get_xy() + spos = self.turtles.turtle_to_screen_coordinates(pos) + turtle_name = self.turtles.get_turtle_key(self.selected_turtle) # Remove turtles by dragging them onto the trash palette. - if self._in_the_trash(tx, ty): + if self._in_the_trash(spos[0], spos[1]): # If it is the default turtle, just recenter it. - if k == self.default_turtle_name: + if turtle_name == self.turtles.get_default_turtle_name(): self._move_turtle(0, 0) - self.canvas.heading = 0 - self.canvas.turn_turtle() - self.lc.update_label_value('heading', self.canvas.heading) + self.selected_turtle.set_heading(0) + self.lc.update_label_value('heading', 0) else: self.selected_turtle.hide() - self.turtles.remove_from_dict(k) - self.active_turtle = None + self.turtles.remove_from_dict(turtle_name) + self.turtles.set_active_turtle(None) else: - self._move_turtle( - tx - self.canvas.width / 2. + - self.active_turtle.spr.rect.width / 2., - self.canvas.height / 2. - ty - - self.active_turtle.spr.rect.height / 2.) + self._move_turtle(pos[0], pos[1]) + self.selected_turtle = None - if self.active_turtle is None: - self.canvas.set_turtle(self.default_turtle_name) + if self.turtles.get_active_turtle() is None: + self.turtles.set_turtle(self.turtles.get_default_turtle_name()) self.display_coordinates() return @@ -2757,21 +2830,23 @@ before making changes to your Turtle Blocks program')) turtle.label_block.spr.set_label(name[0:4] + '…') else: turtle.label_block.spr.set_label(name) + turtle.set_remote() turtle.show() def _move_turtle(self, x, y): ''' Move the selected turtle to (x, y). ''' - self.canvas.xcor = x - self.canvas.ycor = y - self.canvas.move_turtle() + if self.drag_turtle[0] == 'move': + self.turtles.get_active_turtle().move_turtle((x, y)) if self.interactive_mode: self.display_coordinates() if self.running_sugar: self.selected_turtle.spr.set_layer(TURTLE_LAYER) - self.lc.update_label_value('xcor', - self.canvas.xcor / self.coord_scale) - self.lc.update_label_value('ycor', - self.canvas.ycor / self.coord_scale) + self.lc.update_label_value( + 'xcor', self.turtles.get_active_turtle().get_xy()[0] / + self.coord_scale) + self.lc.update_label_value( + 'ycor', self.turtles.get_active_turtle().get_xy()[1] / + self.coord_scale) def _click_block(self, x, y): ''' Click block: lots of special cases to handle... ''' @@ -3043,10 +3118,8 @@ before making changes to your Turtle Blocks program')) if blk is None: return self.lc.find_value_blocks() # Are there blocks to update? - # Is there a savesvg block? - if len(self.block_list.get_similar_blocks('block', 'savesvg')) > 0: - if self.canvas.cr_svg is None: - self.canvas.setup_svg_surface() + if self.canvas.cr_svg is None: + self.canvas.setup_svg_surface() self.running_blocks = True self._start_plugins() # Let the plugins know we are running. top = find_top_block(blk) @@ -3328,7 +3401,7 @@ before making changes to your Turtle Blocks program')) else: dy += delta gblk = gblk.connections[-1] - # Clamp has room for one "standard" block by default + # Clamp has room for one 'standard' block by default if dy > 0: dy -= 21 # Fixme: don't hardcode if blk.name in block_styles['clamp-style-else'] and dockn == 3: @@ -3456,7 +3529,7 @@ before making changes to your Turtle Blocks program')) self.keypress = keyname if alt_mask: - if keyname == "p": + if keyname == 'p': self.hideshow_button() elif keyname == 'q': self.quit_plugins() @@ -3526,13 +3599,15 @@ before making changes to your Turtle Blocks program')) def _jog_turtle(self, dx, dy): ''' Jog turtle ''' if dx == -1 and dy == -1: - self.canvas.xcor = 0 - self.canvas.ycor = 0 + x = 0 + y = 0 else: - self.canvas.xcor += dx - self.canvas.ycor += dy - self.active_turtle = self.turtles.spr_to_turtle(self.selected_spr) - self.canvas.move_turtle() + pos = self.turtles.get_active_turtle().get_xy() + x = pos[0] + dx + y = pos[1] + dy + self.turtles.set_active_turtle( + self.turtles.spr_to_turtle(self.selected_spr)) + self.turtles.get_active_turtle().move_turtle((x, y)) self.display_coordinates() self.selected_turtle = None @@ -3604,15 +3679,15 @@ before making changes to your Turtle Blocks program')) num = float(text.replace(self.decimal_point, '.')) if num > 1000000: num = 1 - self.showlabel("#overflowerror") + self.showlabel('#overflowerror') elif num < -1000000: num = -1 - self.showlabel("#overflowerror") + self.showlabel('#overflowerror') if int(num) == num: num = int(num) except ValueError: num = 0 - self.showlabel("#notanumber") + self.showlabel('#notanumber') else: num = 0 self.selected_blk.spr.set_label(str(num)) @@ -3664,7 +3739,7 @@ before making changes to your Turtle Blocks program')) f.close() id = fname except IOError: - error_output("Unable to read Python code from %s" % (fname), + error_output('Unable to read Python code from %s' % (fname), self.running_sugar) return id @@ -3686,20 +3761,20 @@ before making changes to your Turtle Blocks program')) try: datastore.write(dsobject) id = dsobject.object_id - debug_output("Copied %s to the datastore" % (fname), + debug_output('Copied %s to the datastore' % (fname), self.running_sugar) # Don't copy the same file more than once self._py_cache[fname] = id except IOError: - error_output("Error copying %s to the datastore" % (fname), + error_output('Error copying %s to the datastore' % (fname), self.running_sugar) id = None dsobject.destroy() if add_new_block: # add a new block for this code at turtle position - (tx, ty) = self.active_turtle.get_xy() - self._new_block('userdefined', tx, ty) + pos = self.turtles.get_active_turtle().get_xy() + self._new_block('userdefined', pos[0], pos[1]) self.myblock[self.block_list.list.index(self.drag_group[0])] =\ self.python_code self.set_userdefined(self.drag_group[0]) @@ -3725,11 +3800,11 @@ before making changes to your Turtle Blocks program')) if dsobject is None: return try: - file_handle = open(dsobject.file_path, "r") + file_handle = open(dsobject.file_path, 'r') self.python_code = file_handle.read() file_handle.close() except IOError: - debug_output("couldn't open %s" % dsobject.file_path, + debug_output('Could not open %s' % dsobject.file_path, self.running_sugar) # Save the object id as the block value if blk is None: @@ -3745,7 +3820,6 @@ before making changes to your Turtle Blocks program')) if self.running_sugar: chooser_dialog(self.parent, 'org.laptop.Pippy', self.load_python_code_from_journal) - dsobject.destroy() else: self.load_python_code_from_file(fname=None, add_new_block=False) @@ -3757,7 +3831,7 @@ before making changes to your Turtle Blocks program')) def new_project(self): ''' Start a new project ''' self.lc.stop_logo() - self._loaded_project = "" + self._loaded_project = '' # Put current project in the trash. while len(self.just_blocks()) > 0: blk = self.just_blocks()[0] @@ -3768,7 +3842,7 @@ before making changes to your Turtle Blocks program')) def is_new_project(self): ''' Is this a new project or was a old project loaded from a file? ''' - return self._loaded_project == "" + return self._loaded_project == '' def project_has_changed(self): ''' WARNING: order of JSON serialized data may have changed. ''' @@ -3777,11 +3851,10 @@ before making changes to your Turtle Blocks program')) saved_project_data = f.read() f.close() except: - debug_output("problem loading saved project data from %s" % + debug_output('problem loading saved project data from %s' % (self._loaded_project), self.running_sugar) - saved_project_data = "" + saved_project_data = '' current_project_data = data_to_string(self.assemble_data_to_save()) - return saved_project_data != current_project_data def load_files(self, ta_file, create_new_project=True): @@ -3825,12 +3898,13 @@ before making changes to your Turtle Blocks program')) def load_turtle(self, blk, key=1): ''' Restore a turtle from its saved state ''' tid, name, xcor, ycor, heading, color, shade, pensize = blk - self.canvas.set_turtle(key) - self.canvas.setxy(xcor, ycor, pendown=False) - self.canvas.seth(heading) - self.canvas.setcolor(color) - self.canvas.setshade(shade) - self.canvas.setpensize(pensize) + self.turtles.set_turtle(key) + self.turtles.get_active_turtle().set_xy(xcor, ycor, pendown=False) + self.turtles.get_active_turtle().set_heading(heading) + self.turtles.get_active_turtle().set_color(color) + self.turtles.get_active_turtle().set_shade(shade) + self.turtles.get_active_turtle().set_gray(100) + self.turtles.get_active_turtle().set_pen_size(pensize) def load_block(self, b, offset=0): ''' Restore individual blocks from saved state ''' @@ -3933,8 +4007,8 @@ before making changes to your Turtle Blocks program')) btype = OLD_NAMES[btype] blk = Block(self.block_list, self.sprite_list, btype, - b[2] + self.canvas.cx + offset, - b[3] + self.canvas.cy + offset, + b[2] + offset, + b[3] + offset, 'block', values, self.block_scale) # If it was an unknown block type, we need to match the number @@ -3966,7 +4040,7 @@ before making changes to your Turtle Blocks program')) dsobject = datastore.get(value) except: # Should be IOError, but dbus error is raised dsobject = None - debug_output("couldn't get dsobject %s" % (value), + debug_output('Could not get dsobject %s' % (value), self.running_sugar) if dsobject is not None: self.load_python_code_from_journal(dsobject, blk) @@ -4018,7 +4092,7 @@ before making changes to your Turtle Blocks program')) x, y = self._calc_image_offset('', blk.spr) blk.set_image(pixbuf, x, y) except: - debug_output("Couldn't open dsobject (%s)" % + debug_output('Could not open dsobject (%s)' % (blk.values[0]), self.running_sugar) self._block_skin('journaloff', blk) else: @@ -4098,11 +4172,13 @@ before making changes to your Turtle Blocks program')) def load_start(self, ta_file=None): ''' Start a new project with a 'start' brick ''' if ta_file is None: - self.process_data([[0, "start", PALETTE_WIDTH + 20, - self.toolbar_offset + PALETTE_HEIGHT + 20, - [None, None]]]) + self.process_data( + [[0, 'start', PALETTE_WIDTH + 20, + self.toolbar_offset + PALETTE_HEIGHT + 20 + ICON_SIZE, + [None, None]]]) else: self.process_data(data_from_file(ta_file)) + self._loaded_project = ta_file def save_file(self, file_name=None): ''' Start a project to a file ''' @@ -4167,8 +4243,7 @@ before making changes to your Turtle Blocks program')) if not save_project: sx += 20 sy += 20 - data.append((blk.id, name, sx - self.canvas.cx, - sy - self.canvas.cy, connections)) + data.append((blk.id, name, sx, sy, connections)) if save_turtle: for turtle in iter(self.turtles.dict): # Don't save remote turtles @@ -4176,44 +4251,51 @@ before making changes to your Turtle Blocks program')) # Save default turtle as 'Yertle' if turtle == self.nick: turtle = DEFAULT_TURTLE + pos = self.turtles.get_active_turtle().get_xy() data.append( (-1, ['turtle', turtle], - self.canvas.xcor, self.canvas.ycor, - self.canvas.heading, self.canvas.color, - self.canvas.shade, self.canvas.pensize)) + pos[0], pos[1], + self.turtles.get_active_turtle().get_heading(), + self.turtles.get_active_turtle().get_color(), + self.turtles.get_active_turtle().get_shade(), + self.turtles.get_active_turtle().get_pen_size())) return data def display_coordinates(self, clear=False): ''' Display the coordinates of the current turtle on the toolbar ''' if clear: - if self.running_sugar: - self.activity.coordinates_label.set_text('') - self.activity.coordinates_label.show() - elif self.interactive_mode: - self.parent.set_title('') + self._set_coordinates_label('') else: - x = round_int(float(self.canvas.xcor) / self.coord_scale) - y = round_int(float(self.canvas.ycor) / self.coord_scale) - h = round_int(self.canvas.heading) + x = round_int(float(self.turtles.get_active_turtle().get_xy()[0]) / + self.coord_scale) + y = round_int(float(self.turtles.get_active_turtle().get_xy()[1]) / + self.coord_scale) + h = round_int(self.turtles.get_active_turtle().get_heading()) if self.running_sugar: if int(x) == x and int(y) == y and int(h) == h: formatting = '(%d, %d) %d' else: formatting = '(%0.2f, %0.2f) %0.2f' - self.activity.coordinates_label.set_text( - formatting % (x, y, h)) - self.activity.coordinates_label.show() + self._set_coordinates_label(formatting % (x, y, h)) elif self.interactive_mode: if int(x) == x and int(y) == y and int(h) == h: formatting = '%s — %s: %d %s: %d %s: %d' else: formatting = '%s — %s: %0.2f %s: %0.2f %s: %0.2f' - self.parent.set_title( + self._set_coordinates_label( formatting % (self.activity.name, _('xcor'), x, _('ycor'), y, _('heading'), h)) self.update_counter = 0 + def _set_coordinates_label(self, text): + if self.running_sugar: + self.activity.coordinates_label.set_text(text) + self.activity.coordinates_label.show() + elif self.interactive_mode: + self.parent.set_title(text) + + def showlabel(self, shp, label=''): ''' Display a message on a status block ''' if not self.interactive_mode: @@ -4278,16 +4360,17 @@ before making changes to your Turtle Blocks program')) save_picture(self.canvas, image_file) return ta_file, image_file - def save_as_image(self, name="", svg=False): + def save_as_image(self, name='', svg=False): ''' Grab the current canvas and save it. ''' if svg: suffix = '.svg' else: suffix = '.png' - if not self.interactive_mode: + if not self.interactive_mode: # png only save_picture(self.canvas, name[:-3] + suffix) return + if self.running_sugar: if len(name) == 0: filename = 'turtleblocks' + suffix @@ -4304,6 +4387,7 @@ before making changes to your Turtle Blocks program')) else: datapath = os.getcwd() filename = name + suffix + if filename is None: return @@ -4311,6 +4395,7 @@ before making changes to your Turtle Blocks program')) if svg: if self.canvas.cr_svg is None: return + self.canvas.svg_close() self.canvas.svg_reset() else: save_picture(self.canvas, file_path) @@ -4321,14 +4406,14 @@ before making changes to your Turtle Blocks program')) dsobject = datastore.create() if len(name) == 0: - dsobject.metadata['title'] = "%s %s" % \ - (self.activity.metadata['title'], _("image")) + dsobject.metadata['title'] = '%s %s' % \ + (self.activity.metadata['title'], _('image')) else: dsobject.metadata['title'] = name dsobject.metadata['icon-color'] = profile.get_color().to_string() if svg: dsobject.metadata['mime_type'] = 'image/svg+xml' - dsobject.set_file_path(os.path.join(datapath, 'output.svg')) + dsobject.set_file_path(TMP_SVG_PATH) else: dsobject.metadata['mime_type'] = 'image/png' dsobject.set_file_path(file_path) @@ -4336,14 +4421,13 @@ before making changes to your Turtle Blocks program')) dsobject.destroy() self.saved_pictures.append((dsobject.object_id, svg)) if svg: - os.remove(os.path.join(datapath, 'output.svg')) + os.remove(TMP_SVG_PATH) else: os.remove(file_path) else: if svg: subprocess.check_output( - ['mv', os.path.join(datapath, 'output.svg'), - os.path.join(datapath, filename)]) + ['cp', TMP_SVG_PATH, os.path.join(datapath, filename)]) self.saved_pictures.append((file_path, svg)) def just_blocks(self): @@ -4444,6 +4528,8 @@ before making changes to your Turtle Blocks program')) def _find_proto_name(self, name, label, palette='blocks'): ''' Look for a protoblock with this name ''' + if not self.interactive_mode: + return False if isinstance(name, unicode): name = name.encode('ascii', 'replace') if isinstance(label, unicode): @@ -4466,6 +4552,8 @@ before making changes to your Turtle Blocks program')) def _new_stack_block(self, name): ''' Add a stack block to the 'blocks' palette ''' + if not self.interactive_mode: + return if isinstance(name, (float, int)): return if isinstance(name, unicode): @@ -4493,6 +4581,8 @@ before making changes to your Turtle Blocks program')) def _new_box_block(self, name): ''' Add a box block to the 'blocks' palette ''' + if not self.interactive_mode: + return if isinstance(name, (float, int)): return if isinstance(name, unicode): @@ -4521,6 +4611,8 @@ before making changes to your Turtle Blocks program')) def _new_storein_block(self, name): ''' Add a storin block to the 'blocks' palette ''' + if not self.interactive_mode: + return if isinstance(name, (float, int)): return if isinstance(name, unicode): @@ -4557,7 +4649,7 @@ variable')) x = int(x) if 'stack3' + str(x) not in self.lc.stacks or \ self.lc.stacks['stack3' + str(x)] is None: - raise logoerror("#nostack") + raise logoerror('#nostack') self.lc.icall(self.lc.evline, self.lc.stacks['stack3' + str(x)][:]) yield True @@ -4573,10 +4665,10 @@ variable')) try: return self.lc.boxes['box3' + str(x)] except KeyError: - raise logoerror("#emptybox") + raise logoerror('#emptybox') def _prim_setbox(self, name, x, val): - """ Define value of named box """ + ''' Define value of named box ''' if x is not None: if isinstance(convert(x, float, False), float): if int(float(x)) == x: -- cgit v0.9.1