Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/turtleart.py
diff options
context:
space:
mode:
Diffstat (limited to 'turtleart.py')
-rwxr-xr-xturtleart.py263
1 files changed, 172 insertions, 91 deletions
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. <http://www.collabora.co.uk/>
#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()