From b1da81cd14f54d0f6596c58175de27e1aaeb21ca Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Mon, 01 Feb 2010 20:11:17 +0000 Subject: general code cleanup --- diff --git a/TurtleArtActivity.py b/TurtleArtActivity.py index 1cbe1ce..949408d 100644 --- a/TurtleArtActivity.py +++ b/TurtleArtActivity.py @@ -248,7 +248,7 @@ class TurtleArtActivity(activity.Activity): # FIXME: this was like this before refactoring, the save_pict # belongs to taproject (not tawindow) - tawindow.save_pict(self.tw,file_path) + self.tw.save_pict(file_path) # Create a datastore object dsobject = datastore.create() @@ -283,7 +283,7 @@ class TurtleArtActivity(activity.Activity): print tafile try: # FIXME: encapsulation? - tawindow.save_data(self.tw,tafile) + self.tw.save_data(tafile) except: _logger.debug("couldn't save snapshot to journal") @@ -391,7 +391,7 @@ class TurtleArtActivity(activity.Activity): """ Sample projects open dialog """ def _do_samples_cb(self, button): # FIXME: encapsulation! - tawindow.load_file(self.tw, True) + self.tw.load_file(True) # run the activity self.stop_button.set_icon("stopiton") self.tw.run_button(0) @@ -568,7 +568,7 @@ class TurtleArtActivity(activity.Activity): # sharer should send current state to joiner if self.initiating is True: _logger.debug("serialize the project and send to joiner") - text = tawindow.save_string(self.tw) + text = self.tw.save_string() self._send_event("I:" + text) self.tw.show_palette() elif text[0] == 'I': # receiving current state @@ -576,7 +576,7 @@ class TurtleArtActivity(activity.Activity): _logger.debug("receiving project from sharer") e,text = re.split(":",text,2) # unpack data - tawindow.load_string(self.tw,text) + self.tw.load_string(text) # all caught up self.waiting_for_blocks = False @@ -943,6 +943,7 @@ class TurtleArtActivity(activity.Activity): """ def _setup_canvas(self, canvas, lang): bundle_path = activity.get_bundle_path() + print "turtle colors = %s" % (profile.get_color().to_string()) self.tw = tawindow.TurtleArtWindow(canvas, bundle_path, lang, self) self.tw.activity = self self.tw.window.grab_focus() @@ -952,7 +953,7 @@ class TurtleArtActivity(activity.Activity): if self._jobject and self._jobject.file_path: self.read_file(self._jobject.file_path) else: # if new, load a start brick onto the canvas - tawindow.load_start(self.tw) + self.tw.load_start() """ Check to see if there is Python code to be loaded @@ -1011,7 +1012,7 @@ class TurtleArtActivity(activity.Activity): def write_file(self, file_path): _logger.debug("Write file: %s" % file_path) self.metadata['mime_type'] = 'application/x-turtle-art' - tawindow.save_data(self.tw,file_path) + self.tw.save_data(file_path) """ Read a project in and then run it @@ -1030,8 +1031,7 @@ class TurtleArtActivity(activity.Activity): # but we will ignore the .png file # If run_it is True, we want to create a new project tar_fd.extractall(tmpdir) - tawindow.load_files(self.tw, \ - os.path.join(tmpdir,'ta_code.ta'), \ + self.tw.load_files(os.path.join(tmpdir,'ta_code.ta'), \ run_it) # create a new project flag finally: shutil.rmtree(tmpdir) @@ -1039,7 +1039,7 @@ class TurtleArtActivity(activity.Activity): # Otherwise, assume it is a .ta file else: print "trying to open a .ta file:" + file_path - tawindow.load_files(self.tw, file_path, run_it) + self.tw.load_files(file_path, run_it) # run the activity if run_it: diff --git a/sprite_factory.py b/sprite_factory.py index c3be33a..9d4056a 100755 --- a/sprite_factory.py +++ b/sprite_factory.py @@ -627,9 +627,12 @@ class SVG: return self._rline_to(self._slot_x, 0) def _do_tail(self): - return "%s%s" % ( - self._rline_to(-self._slot_x/2.0, self._slot_y*2.0), - self._rline_to(-self._slot_x/2.0, -self._slot_y*2.0)) + if self._outie is True: + return self._rline_to(-self._slot_x, 0) + else: + return "%s%s" % ( + self._rline_to(-self._slot_x/2.0, self._slot_y*2.0), + self._rline_to(-self._slot_x/2.0, -self._slot_y*2.0)) def _do_tab(self): s = "%s%s%s%s%s" % ( diff --git a/tacanvas.py b/tacanvas.py index 91a7b0c..bc42a63 100644 --- a/tacanvas.py +++ b/tacanvas.py @@ -20,10 +20,8 @@ #THE SOFTWARE. import gtk -from math import sin,cos,pi -from tasetup import load_image -import sprites -import taturtle +from math import sin, cos, pi +from sprites import Sprite import pango from constants import * @@ -71,7 +69,7 @@ class TurtleGraphics: self.tw = tw self.width = width self.height = height - self.canvas = sprites.Sprite(tw.sprite_list, 0, 0, + self.canvas = Sprite(tw.sprite_list, 0, 0, gtk.gdk.Pixmap(self.tw.area, self.width, self.height, -1)) (self.cx, self.cy) = self.canvas.get_xy() self.canvas.type = 'canvas' diff --git a/talogo.py b/talogo.py index bf2a96b..dbd84ad 100644 --- a/talogo.py +++ b/talogo.py @@ -150,6 +150,10 @@ def tasqrt(x): def identity(x): return(x) +def start_stack(tw): + if tw.running_sugar(): + tw.activity.recenter() + def display_coordinates(tw, a=-1, b=-1, d=-1): if a==-1 and b==-1 and d == -1: x = round_int(tw.canvas.xcor/tw.coord_scale) @@ -300,7 +304,7 @@ class LogoCode: 'stack1':[0, self.prim_stack1, True], 'stack':[1, self.prim_stack, True], 'stack2':[0, self.prim_stack2, True], - 'start':[0, lambda self: self.start_stack()], + 'start':[0, lambda self: start_stack(self.tw)], 'stopstack':[0, self.prim_stopstack], 'storeinbox1':[1, lambda self,x: self.setbox('box1',x)], 'storeinbox2':[1, lambda self,x: self.setbox('box2',x)], @@ -693,13 +697,9 @@ class LogoCode: def defprim(self, name, args, fcn, rprim=False): sym = self.intern(name) - sym.nargs, sym.fcn = args,fcn + sym.nargs, sym.fcn = args, fcn sym.rprim = rprim - def start_stack(self): - if self.tw.running_sugar(): - self.tw.activity.recenter() - def box(self, x): try: return self.boxes['box3'+str(x)] diff --git a/tautils.py b/tautils.py new file mode 100644 index 0000000..83305a8 --- /dev/null +++ b/tautils.py @@ -0,0 +1,108 @@ +#Copyright (c) 2007-8, Playful Invention Company. +#Copyright (c) 2008-10, Walter Bender + +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: + +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. + +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +#THE SOFTWARE. + +import gtk +import pickle +try: + _old_Sugar_system = False + import json + json.dumps + from json import load as jload + from json import dump as jdump +except (ImportError, AttributeError): + try: + import simplejson as json + from simplejson import load as jload + from simplejson import dump as jdump + except: + _old_Sugar_system = True + +from StringIO import StringIO +import os.path + +def json_load(text): + if _old_Sugar_system is True: + listdata = json.read(text) + else: + io = StringIO(text) + listdata = jload(io) + # json converts tuples to lists, so we need to convert back, + return _tuplify(listdata) + +def _tuplify(t): + if type(t) is not list: + return t + return tuple(map(_tuplify, t)) + +def get_id(c): + if c is None: + return None + return c.id + +def json_dump(data): + if _old_Sugar_system is True: + return json.write(data) + else: + io = StringIO() + jdump(data,io) + return io.getvalue() + +def get_load_name(suffix, load_save_folder): + print "get_load_name: %s %s" % (suffix, load_save_folder) + dialog = gtk.FileChooserDialog("Load...", None, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, gtk.RESPONSE_OK)) + dialog.set_default_response(gtk.RESPONSE_OK) + return do_dialog(dialog, suffix, load_save_folder) + +# +# We try to maintain read-compatibility with all versions of Turtle Art. +# Try pickle first; then selfo different versions of json. +# +def data_from_file(ta_file): + # Just open the .ta file, ignoring any .png file that might be present. + f = open(ta_file, "r") + try: + data = pickle.load(f) + except: + # Rewind necessary because of failed pickle.load attempt + f.seek(0) + text = f.read() + data = json_load(text) + f.close() + return data + +def do_dialog(dialog, suffix, load_save_folder): + print "do_dialog: %s %s" % (suffix, load_save_folder) + result = None + filter = gtk.FileFilter() + filter.add_pattern('*'+suffix) + filter.set_name("Turtle Art") + dialog.add_filter(filter) + dialog.set_current_folder(load_save_folder) + response = dialog.run() + if response == gtk.RESPONSE_OK: + result = dialog.get_filename() + load_save_folder = dialog.get_current_folder() + dialog.destroy() + return result, load_save_folder + diff --git a/tawindow.py b/tawindow.py index d0c1587..e4bce3b 100644 --- a/tawindow.py +++ b/tawindow.py @@ -47,7 +47,7 @@ except: from gettext import gettext as _ from tahoverhelp import * -from taproject import * +from tautils import * from sprite_factory import SVG, svg_str_to_pixbuf from talogo import LogoCode, stop_logo, get_pixbuf_from_journal,\ display_coordinates, movie_media_type, audio_media_type @@ -73,10 +73,8 @@ class TurtleArtWindow(): def _setup_initial_values(self, win, path, lang, parent): self.window = win - self.path = os.path.join(path,'images') - self.path_lang = os.path.join(path,'images',lang) - self.path_en = os.path.join(path,'images/en') # en as fallback - self.load_save_folder = os.path.join(path,'samples') + self.path = os.path.join(path, 'images') + self.load_save_folder = os.path.join(path, 'samples') self.save_folder = None self.save_file_name = None self.window.set_flags(gtk.CAN_FOCUS) @@ -1070,7 +1068,8 @@ class TurtleArtWindow(): chooser.destroy() del chooser else: - fname = get_load_name(self, '.*') + fname, self.load_save_folder = get_load_name('.*', + self.load_save_folder) if fname is None: return if movie_media_type(fname[-4:]): @@ -1304,7 +1303,7 @@ class TurtleArtWindow(): if self.running_sugar(): self.activity.import_py() else: - load_python_code(self) + self.load_python_code() self.set_userdefined() """ @@ -1336,4 +1335,241 @@ class TurtleArtWindow(): self.selected_blk.spr.set_label(s) self.selected_blk.values[0] = s - + def new_project(self): + stop_logo(self) + for b in self._just_blocks(): + b.spr.hide() + self.canvas.clearscreen() + self.save_file_name = None + + def load_file(self, create_new_project=True): + fname, self.load_save_folder = get_load_name('.ta', + self.load_save_folder) + if fname==None: + return + if fname[-3:] == '.ta': + fname=fname[0:-3] + self.load_files(fname+'.ta', create_new_project) + if create_new_project is True: + self.save_file_name = os.path.basename(fname) + + def load_python_code(self): + fname, self.load_save_folder = get_load_name('.py', + self.load_save_folder) + if fname==None: + return + f = open(fname, 'r') + self.myblock = f.read() + f.close() + + def load_files(self, ta_file, create_new_project=True): + if create_new_project is True: + self.new_project() + self.read_data(data_from_file(ta_file)) + + # Unpack serialized data sent across a share. + def load_string(self, text): + data = json_load(text) + self.new_project() + self.read_data(data) + + # Unpack serialized data from the clipboard. + def clone_stack(self, text): + data = json_load(text) + self.read_data(data) + + def read_data(self, data): + # Create the blocks. + blocks = [] + t = 0 + for b in data: + if b[1] == 'turtle': + self.load_turtle(b) + t = 1 + else: + blk = self.load_block(b) + blocks.append(blk) + # Make the connections. + for i in range(len(blocks)): + cons=[] + for c in data[i][4]: + if c is None: + cons.append(None) + else: + cons.append(blocks[c]) + blocks[i].connections = cons + # Adjust the x,y positions, as block sizes may have changed. + for b in blocks: + (sx, sy) = b.spr.get_xy() + for i, c in enumerate(b.connections): + if c is not None: + bdock = b.docks[i] + if len(c.docks) != len(c.connections): + print "dock-conn mismatch %s %s" % (b.name, c.name) + else: + for j in range(len(c.docks)): + if c.connections[j] == b: + cdock = c.docks[j] + nx, ny = sx+bdock[2]-cdock[2], sy+bdock[3]-cdock[3] + c.spr.move((nx, ny)) + + def load_block(self, b): + # A block is saved as: (i, (btype, value), x, y, (c0,... cn)) + # The x,y position is saved/loaded for backward compatibility + btype, value = b[1], None + if type(btype) == type((1,2)): + btype, value = btype + if btype in CONTENT_BLOCKS: + if btype == 'number': + try: + values = [int(value)] + except ValueError: + values = [float(value)] + else: + values = [value] + else: + values = [] + + if OLD_NAMES.has_key(btype): + btype = OLD_NAMES[btype] + + blk = Block(self.block_list, self.sprite_list, + btype, b[2]+self.canvas.cx, + b[3]+self.canvas.cy, 'block', values) + # Some blocks get a skin. + if btype == 'nop': + if self.nop == 'pythonloaded': + blk.spr.set_image(self.media_shapes['pythonon'], 1, 17, 8) + else: + blk.spr.set_image(self.media_shapes['pythonoff'], 1, 17, 8) + blk.spr.set_label(' ') + elif btype in EXPANDABLE: + if btype == 'vspace': + blk.expand_in_y(value) + elif btype == 'hspace': + blk.expand_in_x(value) + elif btype == 'list': + for i in range(len(b[4])-4): + dy = blk.add_arg() + elif btype in BOX_STYLE_MEDIA and len(blk.values)>0: + if btype == 'audio' or btype == 'description': + print "restoring %s to %s block" % (blk.values[0],blk.name) + blk.spr.set_image(self.media_shapes[btype+'on'], 1, 37, 6) + elif self.running_sugar(): + try: + if blk.values[0] != 'None': + dsobject = datastore.get(blk.values[0]) + if not movie_media_type(dsobject.file_path[-4:]): + pixbuf = get_pixbuf_from_journal(dsobject, 80, 60) + if pixbuf is not None: + blk.spr.set_image(pixbuf, 1, 17, 2) + else: + blk.spr.set_image( + self.media_shapes['journalon'], 1, 37, 6) + dsobject.destroy() + except: + print "couldn't open dsobject (%s)" % (blk.values[0]) + else: + if not movie_media_type(blk.values[0][-4:]): + pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(blk.values[0], + 80, 60) + if pixbuf is not None: + blk.spr.set_image(pixbuf, 1, 17, 2) + else: + blk.spr.set_image(self.media_shapes['journalon'], 1, 37, 6) + blk.spr.set_label(' ') + blk.resize() + elif btype in BOX_STYLE_MEDIA: + blk.spr.set_label(' ') + blk.spr.set_image(self.media_shapes[btype+'off'], 1, 37, 6) + + blk.spr.set_layer(BLOCK_LAYER) + return blk + + def load_turtle(self, b): + id, name, xcor, ycor, heading, color, shade, pensize = b + self.canvas.setxy(xcor, ycor) + self.canvas.seth(heading) + self.canvas.setcolor(color) + self.canvas.setshade(shade) + self.canvas.setpensize(pensize) + + # start a new project with a start brick + def load_start(self): + self.clone_stack("%s%s%s" % ("[[0,[\"start\",\"", _("start"), + "\"],250,250,[null,null]]]")) + + def save_file(self): + if self.save_folder is not None: + self.load_save_folder = self.save_folder + fname = self._get_save_name() + if fname is None: + return + if fname[-3:]=='.ta': + fname=fname[0:-3] + save_data(self,fname+".ta") + self.save_file_name = os.path.basename(fname) + + def _get_save_name(self): + dialog = gtk.FileChooserDialog("Save...", None, + gtk.FILE_CHOOSER_ACTION_SAVE, + (gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_SAVE, + gtk.RESPONSE_OK)) + dialog.set_default_response(gtk.RESPONSE_OK) + if self.save_file_name is not None: + dialog.set_current_name(self.save_file_name+'.ta') + result, self.load_save_folder = self.do_dialog(dialog, '.ta') + return result + + def save_data(self, fname): + f = file(fname, "w") + data = self._assemble_data_to_save() + f.write(json_dump(data)) + f.close() + + # Used to send data across a shared session + def save_string(self, save_turtle=True): + data = self._assemble_data_to_save(save_turtle) + return json_dump(data) + + def _assemble_data_to_save(self, save_turtle=True): + data = [] + for i, b in enumerate(self._just_blocks()): + b.id = i + for b in self._just_blocks(): + if b.name in CONTENT_BLOCKS: + name = (b.name, b.values[0]) + elif b.name in EXPANDABLE: + ex, ey = b.get_expand_x_y() + if ex > 0: + name = (b.name, ex) + elif ey > 0: + name = (b.name, ey) + else: + name = (b.name, 0) + else: + name = (b.name) + if hasattr(b, 'connections'): + connections = [get_id(c) for c in b.connections] + else: + connections = None + (sx, sy) = b.spr.get_xy() + data.append((b.id, name, sx-self.canvas.cx, sy-self.canvas.cy, + connections)) + if save_turtle is True: + data.append((-1,'turtle', + self.canvas.xcor, self.canvas.ycor, self.canvas.heading, + self.canvas.color, self.canvas.shade, + self.canvas.pensize)) + return data + + # Serialize a stack to save to the clipboard + # TODO: check to make sure just the stack and not the project is saved + def serialize_stack(self): + data = self._assemble_data_to_save(False) + if data == []: + return None + return json_dump(data) + diff --git a/turtleart.py b/turtleart.py index f1a3218..0b30929 100755 --- a/turtleart.py +++ b/turtleart.py @@ -144,10 +144,10 @@ class TurtleMain(): self.tw.win = win def _do_open_cb(self, widget): - load_file(self.tw, True) + self.tw.load_file(True) def _do_save_cb(self, widget): - save_file(self.tw) + self.tw.save_file() def _do_palette_cb(self, widget): self.tw.show_toolbar_palette(self.i) @@ -177,7 +177,7 @@ class TurtleMain(): def _do_stop_cb(self, widget): self.tw.lc.trace = 0 - stop_button(self.tw) + self.stop_button() return -- cgit v0.9.1