From 86e2111461fab98d8c188d50ec8bd0c72c527f73 Mon Sep 17 00:00:00 2001 From: Pootle daemon Date: Mon, 13 Jun 2011 14:23:40 +0000 Subject: Merge branch 'master' of git.sugarlabs.org:turtleart/mainline --- (limited to 'turtleart.py') diff --git a/turtleart.py b/turtleart.py index b8ffa08..ca9153a 100755 --- a/turtleart.py +++ b/turtleart.py @@ -1,6 +1,7 @@ #!/usr/bin/env python #Copyright (c) 2007-8, Playful Invention Company -#Copyright (c) 2008-10, Walter Bender +#Copyright (c) 2008-11, Walter Bender +#Copyright (c) 2011 Collabora Ltd. #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal @@ -32,7 +33,7 @@ import cStringIO import errno try: - # Try to use XDG Base Directory standard for config files + # Try to use XDG Base Directory standard for config files. import xdg.BaseDirectory CONFIG_HOME = os.path.join(xdg.BaseDirectory.xdg_config_home, 'turtleart') except ImportError, e: @@ -42,33 +43,44 @@ except ImportError, e: argv = sys.argv[:] # Workaround for import behavior of gst in tagplay sys.argv[1:] = [] # Execution of import gst cannot see '--help' or '-h' -from gettext import gettext as _ +import gettext -from TurtleArt.taconstants import OVERLAY_LAYER +# Commenting out bindtextdomain so that GNOME can find the installed .mo files, +# but it will not find the .mo files in the local locale directory when running +# from a git repository or the unzipped .xo file. +# gettext.bindtextdomain('org.laptop.TurtleArtActivity', 'locale') + +gettext.textdomain('org.laptop.TurtleArtActivity') +_ = gettext.gettext + +from TurtleArt.taconstants import OVERLAY_LAYER, DEFAULT_TURTLE_COLORS from TurtleArt.tautils import data_to_string, data_from_string, get_save_name from TurtleArt.tawindow import TurtleArtWindow from TurtleArt.taexporthtml import save_html from TurtleArt.taexportlogo import save_logo -from extra.upload import Uploader -from extra.collaborationplugin import CollaborationPlugin + from util.menubuilder import MenuBuilder + class TurtleMain(): - """ Launch Turtle Art from outside of Sugar """ + ''' Launch Turtle Art from outside of Sugar. ''' - _HELP_MSG = 'turtleart.py: ' + _('usage is') + """ + _HELP_MSG = 'turtleart.py: ' + _('usage is') + ''' \tturtleart.py \tturtleart.py project.ta \tturtleart.py --output_png project.ta - \tturtleart.py -o project""" - _INSTALL_PATH = '/usr/share/turtleart' - _ALTERNATE_INSTALL_PATH = '/usr/local/share/turtleart' + \tturtleart.py -o project''' + _INSTALL_PATH = '/usr/share/sugar/activities/TurtleArt.activity' + _ALTERNATIVE_INSTALL_PATH = \ + '/usr/local/share/sugar/activities/TurtleArt.activity' _ICON_SUBPATH = 'images/turtle.png' + _GNOME_PLUGIN_SUBPATH = 'gnome_plugins' def __init__(self): self._init_vars() self._parse_command_line() self._ensure_sugar_paths() + self._plugins = [] if self.output_png: pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, @@ -85,18 +97,47 @@ class TurtleMain(): self._run_plugins() self._start_gtk() + def get_config_home(self): + return CONFIG_HOME + + def _get_gnome_plugin_home(self): + ''' Use plugin directory associated with execution path. ''' + if os.path.exists(os.path.join(self._dirname, + self._GNOME_PLUGIN_SUBPATH)): + return os.path.join(self._dirname, self._GNOME_PLUGIN_SUBPATH) + else: + return None + + def _get_plugin_candidates(self, path): + ''' Look for plugin files in plugin directory. ''' + plugin_files = [] + if path is not None: + candidates = os.listdir(path) + for c in candidates: + if c[-10:] == '_plugin.py' and c[0] != '#' and c[0] != '.': + plugin_files.append(c.split('.')[0]) + return plugin_files + def _init_plugins(self): - config_file_path = os.path.join(CONFIG_HOME, 'turtleartrc.collab') - self._collab_plugin = CollaborationPlugin(self, config_file_path) - self._uploader = Uploader() + ''' Try launching any plugins we may have found. ''' + for p in self._get_plugin_candidates(self._get_gnome_plugin_home()): + P = p.capitalize() + f = \ + "def f(self): from gnome_plugins.%s import %s; return %s(self)" % (p, P, P) + plugin = {} + try: + exec f in globals(), plugin + self._plugins.append(plugin.values()[0](self)) + except ImportError: + print 'failed to import %s' % (P) def _run_plugins(self): - self._uploader.set_tw(self.tw) - self._collab_plugin.set_tw(self.tw) - self._collab_plugin.setup() + ''' Tell the plugin about the TurtleWindow instance. ''' + for p in self._plugins: + p.set_tw(self.tw) - def _mkdir_p(path): - '''Create a directory in a fashion similar to `mkdir -p`''' + def _mkdir_p(self, path): + '''Create a directory in a fashion similar to `mkdir -p`.''' try: os.makedirs(path) except OSError as exc: @@ -106,7 +147,7 @@ class TurtleMain(): raise def _makepath(self, path): - """ Make a path if it doesn't previously exist """ + ''' Make a path if it doesn't previously exist ''' from os import makedirs from os.path import normpath, dirname, exists @@ -115,8 +156,9 @@ class TurtleMain(): makedirs(dpath) def _start_gtk(self): + ''' Get a main window set up. ''' self.win.connect('configure_event', self.tw.update_overlay_position) - self.tw.win = self.win + self.tw.parent = self.win if self.ta_file is None: self.tw.load_start() else: @@ -127,26 +169,24 @@ class TurtleMain(): gtk.main() def _draw_and_quit(self): + ''' Non-interactive mode: run the project, save it to a file + and quit. ''' self.tw.load_start(self.ta_file) self.tw.lc.trace = 0 self.tw.run_button(0) self.tw.save_as_image(self.ta_file, self.canvas) def _build_window(self): - if os.path.exists(self._INSTALL_PATH): - self.tw = TurtleArtWindow(self.canvas, self._INSTALL_PATH) - elif os.path.exists(self._ALTERNATE_INSTALL_PATH): - self.tw = TurtleArtWindow(self.canvas, self._ALTERNATE_INSTALL_PATH) - else: - self.tw = TurtleArtWindow(self.canvas, os.path.abspath('.')) - + ''' Initialize the TurtleWindow instance. ''' + self.tw = TurtleArtWindow(self.canvas, self._dirname) self.tw.save_folder = os.path.expanduser('~') def _init_vars(self): - # If we are invoked to start a project from Gnome, we should make - # sure our current directory is TA's source dir. - os.chdir(os.path.dirname(__file__)) - + ''' If we are invoked to start a project from Gnome, we should make + sure our current directory is TA's source dir. ''' + self._dirname = self._get_execution_dir() + if self._dirname is not None: + os.chdir(self._dirname) self.ta_file = None self.output_png = False self.i = 0 # FIXME: use a better name for this variable @@ -154,6 +194,7 @@ class TurtleMain(): self.tw = None def _parse_command_line(self): + ''' Try to make sense of the command-line arguments. ''' try: opts, args = getopt.getopt(argv[1:], 'ho', ['help', 'output_png']) @@ -182,16 +223,15 @@ class TurtleMain(): if not os.path.exists(self.ta_file): assert False, ('%s: %s' % (self.ta_file, _('File not found'))) - """ - make sure Sugar paths are present - """ def _ensure_sugar_paths(self): + ''' Make sure Sugar paths are present. ''' tapath = os.path.join(os.environ['HOME'], '.sugar', 'default', 'org.laptop.TurtleArtActivity') map(self._makepath, (os.path.join(tapath, 'data/'), os.path.join(tapath, 'instance/'))) def _read_initial_pos(self): + ''' Read saved configuration. ''' try: data_file = open(os.path.join(CONFIG_HOME, 'turtleartrc'), 'r') except IOError: @@ -212,25 +252,27 @@ class TurtleMain(): data_file.write(str(800) + '\n') data_file.write(str(550) + '\n') data_file.seek(0) - self.x = int(data_file.readline()) - self.y = int(data_file.readline()) - self.width = int(data_file.readline()) - self.height = int(data_file.readline()) + try: + self.x = int(data_file.readline()) + self.y = int(data_file.readline()) + self.width = int(data_file.readline()) + self.height = int(data_file.readline()) + except ValueError: + self.x = 50 + self.y = 50 + self.width = 800 + self.height = 550 def _setup_gtk(self): + ''' Set up a scrolled window in which to run Turtle Blocks. ''' win = gtk.Window(gtk.WINDOW_TOPLEVEL) win.set_default_size(self.width, self.height) win.move(self.x, self.y) win.maximize() win.set_title(_('Turtle Art')) - if os.path.exists(os.path.join(self._INSTALL_PATH, self._ICON_SUBPATH)): - win.set_icon_from_file(os.path.join(self._INSTALL_PATH, + if os.path.exists(os.path.join(self._dirname, self._ICON_SUBPATH)): + win.set_icon_from_file(os.path.join(self._dirname, self._ICON_SUBPATH)) - else: - try: - win.set_icon_from_file(self._ICON_SUBPATH) - except IOError: - pass win.connect('delete_event', self._quit_ta) vbox = gtk.VBox(False, 0) @@ -257,27 +299,30 @@ class TurtleMain(): self.canvas = canvas def _get_menu_bar(self): + ''' Instead of Sugar toolbars, use GNOME menus. ''' menu = gtk.Menu() MenuBuilder.make_menu_item(menu, _('New'), self._do_new_cb) MenuBuilder.make_menu_item(menu, _('Open'), self._do_open_cb) MenuBuilder.make_menu_item(menu, _('Save'), self._do_save_cb) - MenuBuilder.make_menu_item(menu, _('Save As'), self._do_save_as_cb) - MenuBuilder.make_menu_item(menu, _('Save as image'), self._do_save_picture_cb) - MenuBuilder.make_menu_item(menu, _('Save as HTML'), self._do_save_html_cb) - MenuBuilder.make_menu_item(menu, _('Save as Logo'), self._do_save_logo_cb) - if self._uploader.enabled(): - MenuBuilder.make_menu_item(menu, _('Upload to Web'), - self._uploader.do_upload_to_web) + MenuBuilder.make_menu_item(menu, _('Save as'), self._do_save_as_cb) + MenuBuilder.make_menu_item(menu, _('Save as image'), + self._do_save_picture_cb) + MenuBuilder.make_menu_item(menu, _('Save as HTML'), + self._do_save_html_cb) + MenuBuilder.make_menu_item(menu, _('Save as Logo'), + self._do_save_logo_cb) MenuBuilder.make_menu_item(menu, _('Quit'), self.destroy) activity_menu = MenuBuilder.make_sub_menu(menu, _('File')) menu = gtk.Menu() MenuBuilder.make_menu_item(menu, _('Cartesian coordinates'), self._do_cartesian_cb) - MenuBuilder.make_menu_item(menu, _('Polar coordinates'), self._do_polar_cb) + MenuBuilder.make_menu_item(menu, _('Polar coordinates'), + self._do_polar_cb) MenuBuilder.make_menu_item(menu, _('Rescale coordinates'), self._do_rescale_cb) - MenuBuilder.make_menu_item(menu, _('Grow blocks'), self._do_resize_cb, 1.5) + MenuBuilder.make_menu_item(menu, _('Grow blocks'), + self._do_resize_cb, 1.5) MenuBuilder.make_menu_item(menu, _('Shrink blocks'), self._do_resize_cb, 0.667) MenuBuilder.make_menu_item(menu, _('Reset block size'), @@ -290,9 +335,12 @@ class TurtleMain(): edit_menu = MenuBuilder.make_sub_menu(menu, _('Edit')) menu = gtk.Menu() - MenuBuilder.make_menu_item(menu, _('Show palette'), self._do_palette_cb) - MenuBuilder.make_menu_item(menu, _('Hide palette'), self._do_hide_palette_cb) - MenuBuilder.make_menu_item(menu, _('Show/hide blocks'), self._do_hideshow_cb) + MenuBuilder.make_menu_item(menu, _('Show palette'), + self._do_palette_cb) + MenuBuilder.make_menu_item(menu, _('Hide palette'), + self._do_hide_palette_cb) + MenuBuilder.make_menu_item(menu, _('Show/hide blocks'), + self._do_hideshow_cb) tool_menu = MenuBuilder.make_sub_menu(menu, _('Tools')) menu = gtk.Menu() @@ -303,19 +351,20 @@ class TurtleMain(): MenuBuilder.make_menu_item(menu, _('Stop'), self._do_stop_cb) turtle_menu = MenuBuilder.make_sub_menu(menu, _('Turtle')) - collaboration_menu = self._collab_plugin.get_menu() - menu_bar = gtk.MenuBar() menu_bar.append(activity_menu) menu_bar.append(edit_menu) menu_bar.append(view_menu) menu_bar.append(tool_menu) menu_bar.append(turtle_menu) - menu_bar.append(collaboration_menu) + + # Add menus for plugins + for p in self._plugins: + menu_bar.append(p.get_menu()) return menu_bar def _quit_ta(self, widget=None, e=None): - """ Save changes on exit """ + ''' Save changes on exit ''' project_empty = self.tw.is_project_empty() if not project_empty: if self.tw.is_new_project(): @@ -326,11 +375,11 @@ class TurtleMain(): gtk.main_quit() def _show_save_dialog(self, new_project=True): - """ Dialog for save project """ + ''' Dialog for save project ''' dlg = gtk.MessageDialog(parent=None, type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK_CANCEL, - message_format= \ - _('You have unsaved work. Would you like to save before quitting?')) + message_format=_( + 'You have unsaved work. Would you like to save before quitting?')) dlg.set_title(_('Save project?')) dlg.set_property('skip-taskbar-hint', False) @@ -343,38 +392,38 @@ class TurtleMain(): self._save_changes() def _do_new_cb(self, widget): - """ Callback for new project. """ + ''' Callback for new project. ''' self.tw.new_project() self.tw.load_start() def _do_open_cb(self, widget): - """ Callback for open project. """ + ''' Callback for open project. ''' self.tw.load_file(True) def _do_save_cb(self, widget): - """ Callback for save project. """ + ''' Callback for save project. ''' self.tw.save_file() def _do_save_as_cb(self, widget): - """ Callback for save-as project. """ + ''' Callback for save-as project. ''' self._save_as() def _save_as(self): - """ Save as is called from callback and quit """ + ''' Save as is called from callback and quit ''' self.tw.save_file_name = None self.tw.save_file() def _save_changes(self): - """ Save changes to current project """ + ''' Save changes to current project ''' self.tw.save_file_name = None self.tw.save_file(self.tw._loaded_project) def _do_save_picture_cb(self, widget): - """ Callback for save canvas. """ + ''' Callback for save canvas. ''' self.tw.save_as_image() def _do_save_html_cb(self, widget): - """ Callback for save project to HTML. """ + ''' Callback for save project to HTML. ''' html = save_html(self, self.tw, False) if len(html) == 0: return @@ -390,7 +439,7 @@ class TurtleMain(): self.tw.saved_pictures = [] def _do_save_logo_cb(self, widget): - """ Callback for save project to Logo. """ + ''' Callback for save project to Logo. ''' logocode = save_logo(self.tw) if len(logocode) == 0: return @@ -402,7 +451,7 @@ class TurtleMain(): f.close() def _do_resize_cb(self, widget, factor): - """ Callback to resize blocks. """ + ''' Callback to resize blocks. ''' if factor == -1: self.tw.block_scale = 2.0 else: @@ -410,7 +459,7 @@ class TurtleMain(): self.tw.resize_blocks() def _do_cartesian_cb(self, button): - """ Callback to display/hide Cartesian coordinate overlay. """ + ''' Callback to display/hide Cartesian coordinate overlay. ''' if self.tw.cartesian is True: if self.tw.coord_scale == 1: self.tw.overlay_shapes['Cartesian_labeled'].hide() @@ -426,7 +475,7 @@ class TurtleMain(): self.tw.cartesian = True def _do_polar_cb(self, button): - """ Callback to display/hide Polar coordinate overlay. """ + ''' Callback to display/hide Polar coordinate overlay. ''' if self.tw.polar is True: self.tw.overlay_shapes['polar'].hide() self.tw.polar = False @@ -435,7 +484,7 @@ class TurtleMain(): self.tw.polar = True def _do_rescale_cb(self, button): - """ Callback to rescale coordinate space. """ + ''' Callback to rescale coordinate space. ''' if self.tw.coord_scale == 1: self.tw.coord_scale = self.tw.height / 200 self.tw.eraser_button() @@ -451,51 +500,51 @@ class TurtleMain(): OVERLAY_LAYER) def _do_palette_cb(self, widget): - """ Callback to show/hide palette of blocks. """ + ''' Callback to show/hide palette of blocks. ''' self.tw.show_palette(self.i) self.i += 1 if self.i == len(self.tw.palettes): self.i = 0 def _do_hide_palette_cb(self, widget): - """ Hide the palette of blocks. """ + ''' Hide the palette of blocks. ''' self.tw.hide_palette() def _do_hideshow_cb(self, widget): - """ Hide/show the blocks. """ + ''' Hide/show the blocks. ''' self.tw.hideshow_button() def _do_eraser_cb(self, widget): - """ Callback for eraser button. """ + ''' Callback for eraser button. ''' self.tw.eraser_button() return def _do_run_cb(self, widget): - """ Callback for run button (rabbit). """ + ''' Callback for run button (rabbit). ''' self.tw.lc.trace = 0 self.tw.run_button(0) return def _do_step_cb(self, widget): - """ Callback for step button (turtle). """ + ''' Callback for step button (turtle). ''' self.tw.lc.trace = 0 self.tw.run_button(3) return def _do_trace_cb(self, widget): - """ Callback for debug button (bug). """ + ''' Callback for debug button (bug). ''' self.tw.lc.trace = 1 self.tw.run_button(6) return def _do_stop_cb(self, widget): - """ Callback for stop button. """ + ''' Callback for stop button. ''' self.tw.lc.trace = 0 self.tw.stop_button() return def _do_copy_cb(self, button): - """ Callback for copy button. """ + ''' Callback for copy button. ''' clipBoard = gtk.Clipboard() data = self.tw.assemble_data_to_save(False, False) if data is not []: @@ -503,7 +552,7 @@ class TurtleMain(): clipBoard.set_text(text) def _do_paste_cb(self, button): - """ Callback for paste button. """ + ''' Callback for paste button. ''' clipBoard = gtk.Clipboard() text = clipBoard.wait_for_text() if text is not None: @@ -518,7 +567,7 @@ class TurtleMain(): self.tw.paste_offset += 20 def _window_event(self, event, data): - """ Callback for resize event. """ + ''' Callback for resize event. ''' data_file = open('.turtleartrc', 'w') data_file.write(str(data.x) + '\n') data_file.write(str(data.y) + '\n') @@ -526,8 +575,40 @@ class TurtleMain(): data_file.write(str(data.height) + '\n') def destroy(self, event, data=None): - """ Callback for destroy event. """ + ''' Callback for destroy event. ''' gtk.main_quit() -if __name__ == "__main__": + def nick_changed(self, nick): + ''' TODO: Rename default turtle in dictionary ''' + pass + + def color_changed(self, colors): + ''' Reskin turtle with collaboration colors ''' + turtle = self.tw.turtles.get_turtle(self.tw.default_turtle_name) + try: + turtle.colors = colors.split(',') + except: + turtle.colors = DEFAULT_TURTLE_COLORS + turtle.custom_shapes = True # Force regeneration of shapes + turtle.reset_shapes() + turtle.show() + + def _get_execution_dir(self): + ''' From whence is the program being executed? ''' + dirname = os.path.dirname(__file__) + if dirname == '': + if os.path.exists(os.path.join('~', 'Activities', + 'TurtleArt.activity')): + return os.path.join('~', 'Activities', 'TurtleArt.activity') + elif os.path.exists(self._INSTALL_PATH): + return self._INSTALL_PATH + elif os.path.exists(self._ALTERNATIVE_INSTALL_PATH): + return self._ALTERNATIVE_INSTALL_PATH + else: + return os.path.abspath('.') + else: + return os.path.abspath(dirname) + + +if __name__ == '__main__': TurtleMain() -- cgit v0.9.1