Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter.bender@gmail.com>2011-03-12 14:17:31 (GMT)
committer Walter Bender <walter.bender@gmail.com>2011-03-12 14:17:31 (GMT)
commit37c35cbd0fa5215a88f802b36af8d0cc0d6c4a49 (patch)
tree48e7ad4085cec88b104b7a36c14bdd0b3b03d766
New project
-rw-r--r--InfusedActivity.py205
-rw-r--r--activity/activity-infused.svg21
-rw-r--r--activity/activity.info8
-rwxr-xr-xgenpieces.py153
-rw-r--r--gplay.py215
-rw-r--r--page.py345
-rw-r--r--sprites.py462
7 files changed, 1409 insertions, 0 deletions
diff --git a/InfusedActivity.py b/InfusedActivity.py
new file mode 100644
index 0000000..074a23c
--- /dev/null
+++ b/InfusedActivity.py
@@ -0,0 +1,205 @@
+#Copyright (c) 2011 Walter Bender
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import gtk
+import gobject
+
+import sugar
+from sugar.activity import activity
+from sugar import profile
+try:
+ from sugar.graphics.toolbarbox import ToolbarBox
+ _have_toolbox = True
+except ImportError:
+ _have_toolbox = False
+
+if _have_toolbox:
+ from sugar.bundle.activitybundle import ActivityBundle
+ from sugar.activity.widgets import ActivityToolbarButton
+ from sugar.activity.widgets import StopButton
+ from sugar.graphics.toolbarbox import ToolbarButton
+
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.icon import Icon
+from sugar.datastore import datastore
+
+from gettext import gettext as _
+import locale
+import os.path
+
+from page import Page
+
+SERVICE = 'org.sugarlabs.InfusedActivity'
+IFACE = SERVICE
+PATH = '/org/augarlabs/InfusedActivity'
+
+
+def _button_factory(icon_name, tooltip, callback, toolbar, cb_arg=None,
+ accelerator=None):
+ """Factory for making toolbar buttons"""
+ my_button = ToolButton(icon_name)
+ my_button.set_tooltip(tooltip)
+ my_button.props.sensitive = True
+ if accelerator is not None:
+ my_button.props.accelerator = accelerator
+ if cb_arg is not None:
+ my_button.connect('clicked', callback, cb_arg)
+ else:
+ my_button.connect('clicked', callback)
+ if hasattr(toolbar, 'insert'): # the main toolbar
+ toolbar.insert(my_button, -1)
+ else: # or a secondary toolbar
+ toolbar.props.page.insert(my_button, -1)
+ my_button.show()
+ return my_button
+
+
+def _label_factory(label, toolbar):
+ """ Factory for adding a label to a toolbar """
+ my_label = gtk.Label(label)
+ my_label.set_line_wrap(True)
+ my_label.show()
+ toolitem = gtk.ToolItem()
+ toolitem.add(my_label)
+ toolbar.insert(toolitem, -1)
+ toolitem.show()
+ return my_label
+
+
+def _separator_factory(toolbar, visible=True, expand=False):
+ """ Factory for adding a separator to a toolbar """
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = visible
+ separator.set_expand(expand)
+ toolbar.insert(separator, -1)
+ separator.show()
+
+
+class InfusedActivity(activity.Activity):
+ """ Infused Reading guide """
+
+ def __init__(self, handle):
+ """ Initialize the toolbars and the reading board """
+ super(InfusedActivity,self).__init__(handle)
+ self.reading = False
+
+ self._setup_toolbars(_have_toolbox)
+
+ # Create a canvas
+ canvas = gtk.DrawingArea()
+ canvas.set_size_request(gtk.gdk.screen_width(), \
+ gtk.gdk.screen_height())
+ self.set_canvas(canvas)
+ canvas.show()
+ self.show_all()
+
+ self._page = Page(canvas,
+ parent=self,
+ colors= profile.get_color().to_string().split(','))
+
+ # Restore game state from Journal or start new game
+ if 'page' in self.metadata:
+ self._restore()
+ else:
+ self._page.new_page()
+
+ def _setup_toolbars(self, have_toolbox):
+ """ Setup the toolbars.. """
+
+ # no sharing
+ self.max_participants = 1
+
+ if have_toolbox:
+ toolbox = ToolbarBox()
+
+ # Activity toolbar
+ activity_button = ActivityToolbarButton(self)
+
+ toolbox.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ self.set_toolbar_box(toolbox)
+ toolbox.show()
+ toolbar = toolbox.toolbar
+
+ else:
+ # Use pre-0.86 toolbar design
+ games_toolbar = gtk.Toolbar()
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.add_toolbar(_('Page'), page_toolbar)
+ toolbox.show()
+ toolbox.set_current_toolbar(1)
+ toolbar = page_toolbar
+
+ # no sharing
+ if hasattr(toolbox, 'share'):
+ toolbox.share.hide()
+ elif hasattr(toolbox, 'props'):
+ toolbox.props.visible = False
+
+ self._new_page_button = _button_factory('new-page',
+ _('Next letter'),
+ self._new_page_cb, toolbar)
+
+ _separator_factory(toolbar)
+
+ self._read_button = _button_factory('media-playback-start',
+ _('Read'),
+ self._read_cb, toolbar)
+
+ self.status = _label_factory(_(''), toolbar)
+
+ if _have_toolbox:
+ _separator_factory(toolbox.toolbar, False, True)
+
+ stop_button = StopButton(self)
+ stop_button.props.accelerator = '<Ctrl>q'
+ toolbox.toolbar.insert(stop_button, -1)
+ stop_button.show()
+
+ def _new_page_cb(self, button=None):
+ ''' Start a new letter. '''
+ self._page.page_index += 1
+ self._page.new_page()
+ self.reading = False
+ self._read_button.set_icon('media-playback-start')
+ self._read_button.set_tooltip(_('Show letter'))
+
+ def _read_cb(self, button=None):
+ ''' Start a new page. '''
+ if not self.reading:
+ self.reading = True
+ self._page.read()
+ self._read_button.set_icon('system-restart')
+ self._read_button.set_tooltip(_('Show letter'))
+ else:
+ self.reading = False
+ self._page.reload()
+ self._read_button.set_icon('media-playback-start')
+ self._read_button.set_tooltip(_('Read'))
+
+ def write_file(self, file_path):
+ ''' Write status to the Journal '''
+ if not hasattr(self, '_page'):
+ return
+ self.metadata['page'] = str(self._page.page_index)
+
+ def _restore(self):
+ ''' Load up cards until we get to the page we stopped on. '''
+ try:
+ n = int(self.metadata['page'])
+ except:
+ n = 0
+ for i in range(n):
+ self._new_page_cb()
diff --git a/activity/activity-infused.svg b/activity/activity-infused.svg
new file mode 100644
index 0000000..c562b97
--- /dev/null
+++ b/activity/activity-infused.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000">
+ <!ENTITY fill_color "#eee">
+]><svg height="55px" viewBox="0 0 55 55" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" stroke="&stroke_color;" fill="&fill_color;">
+ <rect
+ width="54"
+ height="35"
+ rx="1.5"
+ ry="1.5"
+ x="0.5"
+ y="10"
+ fill="&fill_color;"/>
+ <text
+ x="13.5"
+ y="40.5"
+ style="font-size:48px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans"><tspan
+ x="13.5"
+ y="40.5"
+ fill="&stroke_color;"
+ font-size="48px">a</tspan></text>
+ </svg>
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..d5b0cf1
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,8 @@
+[Activity]
+name = Infused
+activity_version = 1
+license = GPLv3
+bundle_id = org.sugarlabs.InfusedActivity
+exec = sugar-activity InfusedActivity.InfusedActivity
+icon = activity-infused
+show_launcher = yes
diff --git a/genpieces.py b/genpieces.py
new file mode 100755
index 0000000..97f2217
--- /dev/null
+++ b/genpieces.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#Copyright (c) 2009-11 Walter Bender
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import os
+
+#
+# SVG generators
+#
+class SVG:
+ def __init__(self):
+ self._scale = 1
+ self._stroke_width = 1
+ self._fill = '#FFFFFF'
+ self._stroke = '#000000'
+
+ def _svg_style(self, extras=""):
+ return "%s%s%s%s%s%f%s%s%s" % ("style=\"fill:", self._fill, ";stroke:",
+ self._stroke, ";stroke-width:",
+ self._stroke_width, ";", extras,
+ "\" />\n")
+
+ def _svg_text(self, x, y, font_size, text_string, stroke=False,
+ center=False):
+ if center:
+ align = 'text-align:center;text-anchor:middle'
+ else:
+ align = ''
+ svg_string = "<text x=\"%d\" y=\"%d\" " % (x, y)
+ if stroke:
+ svg_string += "style=\"font-size:%dpx;font-family:Sans;fill:%s;stroke:#000000;%s\">" % \
+ (font_size, self._stroke, align)
+ else:
+ svg_string += "style=\"font-size:%dpx;font-family:Sans;fill:%s;%s\">" % \
+ (font_size, self._stroke, align)
+ svg_string += "<tspan x=\"%d\" y=\"%d\">%s</tspan></text>" % \
+ (x, y, text_string)
+ return svg_string
+
+ def _svg_line(self, x1, y1, x2, y2):
+ svg_string = "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\"\n" % \
+ (x1, y1, x2, y2)
+ svg_string += self._svg_style("stroke-linecap:square;")
+ return svg_string
+
+ def _svg_rect(self, w, h, rx, ry, x, y):
+ svg_string = " <rect\n"
+ svg_string += " width=\"%f\"\n" % (w)
+ svg_string += " height=\"%f\"\n" % (h)
+ svg_string += " rx=\"%f\"\n" % (rx)
+ svg_string += " ry=\"%f\"\n" % (ry)
+ svg_string += " x=\"%f\"\n" % (x)
+ svg_string += " y=\"%f\"\n" % (y)
+ self.set_stroke_width(1.0)
+ svg_string += self._svg_style()
+ return svg_string
+
+ def _background(self, scale):
+ return self._svg_rect(59.5 * scale, 44.5 * scale, 1, 1, 0.25, 0.25)
+
+ def header(self, scale=1, background=True):
+ svg_string = "<?xml version=\"1.0\" encoding=\"UTF-8\""
+ svg_string += " standalone=\"no\"?>\n"
+ svg_string += "<!-- Created with Emacs -->\n"
+ svg_string += "<svg\n"
+ svg_string += " xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
+ svg_string += " xmlns=\"http://www.w3.org/2000/svg\"\n"
+ svg_string += " version=\"1.0\"\n"
+ svg_string += "%s%f%s" % (" width=\"", scale * 60 * self._scale,
+ "\"\n")
+ svg_string += "%s%f%s" % (" height=\"", scale * 45 * self._scale,
+ "\">\n")
+ svg_string += "%s%f%s%f%s" % ("<g\n transform=\"matrix(",
+ self._scale, ",0,0,", self._scale,
+ ",0,0)\">\n")
+ if background:
+ svg_string += self._background(scale)
+ return svg_string
+
+ def footer(self):
+ svg_string = "</g>\n"
+ svg_string += "</svg>\n"
+ return svg_string
+
+ #
+ # Utility functions
+ #
+ def set_scale(self, scale=1.0):
+ self._scale = scale
+
+ def set_colors(self, colors):
+ self._stroke = colors[0]
+ self._fill = colors[1]
+
+ def set_stroke_width(self, stroke_width=1.0):
+ self._stroke_width = stroke_width
+
+#
+# Card generators
+#
+def generate_card(string='a', colors=['#FF0000', '#FFFFFF'],
+ background=True, scale=1, stroke=False, center=False):
+ svg = SVG()
+ svg.set_scale(scale)
+ svg.set_colors(colors)
+ svg_string = svg.header(background=background)
+ if center:
+ x = 30
+ else:
+ x = 5
+ svg_string += svg._svg_text(x, 35, 40, string, stroke=stroke,
+ center=center)
+ svg_string += svg.footer()
+ return svg_string
+
+#
+# Command line utilities used for testing purposed only
+#
+def open_file(datapath, filename):
+ return file(os.path.join(datapath, filename), "w")
+
+
+def close_file(f):
+ f.close()
+
+
+def generator(datapath):
+ i = 1
+ filename = "tile-%d.svg" % (i)
+ f = open_file(datapath, filename)
+ f.write(generate_card(string='b', background=False))
+ close_file(f)
+
+
+def main():
+ return 0
+
+if __name__ == "__main__":
+ if not os.path.exists(os.path.join(os.path.abspath('.'), 'images')):
+ os.mkdir(os.path.join(os.path.abspath('.'), 'images'))
+ generator(os.path.join(os.path.abspath('.'), 'images'))
+ main()
diff --git a/gplay.py b/gplay.py
new file mode 100644
index 0000000..9ccf8b4
--- /dev/null
+++ b/gplay.py
@@ -0,0 +1,215 @@
+"""
+ gplay.py
+ refactored based on Jukebox Activity
+ Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
+ Copyright (C) 2007 Red Hat, Inc.
+ Copyright (C) 2008-2010 Kushal Das <kushal@fedoraproject.org>
+ Copyright (C) 2010-11 Walter Bender
+"""
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+
+import logging
+import os
+
+import pygtk
+pygtk.require('2.0')
+
+import gobject
+gobject.threads_init()
+
+import pygst
+import gst
+import gst.interfaces
+import gtk
+
+import urllib
+
+
+def play_audio_from_file(lc, file_path):
+ """ Called from Show block of audio media """
+ lc.gplay = Gplay(lc)
+ lc.gplay.start(file_path)
+
+
+class Gplay():
+ UPDATE_INTERVAL = 500
+
+ def __init__(self, lc):
+
+ self.player = None
+ self.uri = None
+ self.playlist = []
+ self.jobjectlist = []
+ self.playpath = None
+ self.only_audio = True
+ self.got_stream_info = False
+ self.currentplaying = 0
+
+ self.videowidget = VideoWidget()
+ self._want_document = True
+
+ def _player_eos_cb(self, widget):
+ pass
+
+ def _player_error_cb(self, widget, message, detail):
+ self.player.stop()
+ self.player.set_uri(None)
+ logging.debug('Error: %s - %s' % (message, detail))
+
+ def _player_stream_info_cb(self, widget, stream_info):
+ if not len(stream_info) or self.got_stream_info:
+ return
+
+ GST_STREAM_TYPE_VIDEO = 2
+
+ only_audio = True
+ for item in stream_info:
+ if item.props.type == GST_STREAM_TYPE_VIDEO:
+ only_audio = False
+ self.only_audio = only_audio
+ self.got_stream_info = True
+
+ def start(self, uri=None):
+ self._want_document = False
+ self.playpath = os.path.dirname(uri)
+ if not uri:
+ return False
+ self.playlist.append('file://' + urllib.quote(os.path.abspath(uri)))
+ if not self.player:
+ # lazy init the player so that videowidget is realized
+ # and has a valid widget allocation
+ self.player = GstPlayer(self.videowidget)
+ self.player.connect('eos', self._player_eos_cb)
+ self.player.connect('error', self._player_error_cb)
+ self.player.connect('stream-info', self._player_stream_info_cb)
+
+ try:
+ if not self.currentplaying:
+ logging.info('Playing: ' + self.playlist[0])
+ self.player.set_uri(self.playlist[0])
+ self.currentplaying = 0
+ self.play_toggled()
+ self.show_all()
+ else:
+ pass
+ except:
+ pass
+ return False
+
+ def play_toggled(self):
+ if self.player.is_playing():
+ self.player.pause()
+ else:
+ if self.player.error:
+ pass
+ else:
+ self.player.play()
+
+
+class GstPlayer(gobject.GObject):
+ __gsignals__ = {
+ 'error': (gobject.SIGNAL_RUN_FIRST, None, [str, str]),
+ 'eos': (gobject.SIGNAL_RUN_FIRST, None, []),
+ 'stream-info': (gobject.SIGNAL_RUN_FIRST, None, [object])}
+
+ def __init__(self, videowidget):
+ gobject.GObject.__init__(self)
+
+ self.playing = False
+ self.error = False
+
+ self.player = gst.element_factory_make('playbin', 'player')
+
+ self.videowidget = videowidget
+ self._init_video_sink()
+
+ bus = self.player.get_bus()
+ bus.enable_sync_message_emission()
+ bus.add_signal_watch()
+ bus.connect('sync-message::element', self.on_sync_message)
+ bus.connect('message', self.on_message)
+
+ def set_uri(self, uri):
+ self.player.set_property('uri', uri)
+
+ def on_sync_message(self, bus, message):
+ if message.structure is None:
+ return
+ if message.structure.get_name() == 'prepare-xwindow-id':
+ self.videowidget.set_sink(message.src)
+ message.src.set_property('force-aspect-ratio', True)
+
+ def on_message(self, bus, message):
+ t = message.type
+ if t == gst.MESSAGE_ERROR:
+ err, debug = message.parse_error()
+ logging.debug('Error: %s - %s' % (err, debug))
+ self.error = True
+ self.emit('eos')
+ self.playing = False
+ self.emit('error', str(err), str(debug))
+ elif t == gst.MESSAGE_EOS:
+ self.emit('eos')
+ self.playing = False
+ elif t == gst.MESSAGE_STATE_CHANGED:
+ old, new, pen = message.parse_state_changed()
+ if old == gst.STATE_READY and new == gst.STATE_PAUSED:
+ self.emit('stream-info',
+ self.player.props.stream_info_value_array)
+
+ def _init_video_sink(self):
+ return
+
+ def play(self):
+ self.player.set_state(gst.STATE_PLAYING)
+ self.playing = True
+ self.error = False
+ # logging.debug('playing player')
+
+ def stop(self):
+ self.player.set_state(gst.STATE_NULL)
+ self.playing = False
+ logging.debug('stopped player')
+
+ def get_state(self, timeout=1):
+ return self.player.get_state(timeout=timeout)
+
+ def is_playing(self):
+ return self.playing
+
+
+class VideoWidget(gtk.DrawingArea):
+
+ def __init__(self):
+ gtk.DrawingArea.__init__(self)
+ self.set_events(gtk.gdk.EXPOSURE_MASK)
+ self.imagesink = None
+ self.unset_flags(gtk.DOUBLE_BUFFERED)
+ self.set_flags(gtk.APP_PAINTABLE)
+
+ def do_expose_event(self, event):
+ if self.imagesink:
+ self.imagesink.expose()
+ return False
+ else:
+ return True
+
+ def set_sink(self, sink):
+ assert self.window.xid
+ self.imagesink = sink
+ self.imagesink.set_xwindow_id(self.window.xid)
diff --git a/page.py b/page.py
new file mode 100644
index 0000000..45b6409
--- /dev/null
+++ b/page.py
@@ -0,0 +1,345 @@
+# -*- coding: utf-8 -*-
+#Copyright (c) 2011 Walter Bender
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import gtk
+import os
+
+from random import uniform
+
+from gplay import play_audio_from_file
+
+from gettext import gettext as _
+import logging
+_logger = logging.getLogger('infused-activity')
+
+try:
+ from sugar.graphics import style
+ GRID_CELL_SIZE = style.GRID_CELL_SIZE
+except ImportError:
+ GRID_CELL_SIZE = 0
+
+from genpieces import generate_card
+from sprites import Sprites, Sprite
+
+CARDS = [['a', _('pat')],
+ ['u', _('up')],
+ ['i', _('it')],
+ ['e', _('pet')],
+ ['o', _('pot')],
+ ['y', _('tummy')],
+ ['p', _('pat')],
+ ['n', _('not, tennis')],
+ ['t', _('tap')],
+ ['d', _('dad')],
+ ['s', _('is, as, was, says')],
+ ['m', _('mom')],
+ ['s', _("sam, stop, it's")],
+ ['A', _('read a book')]]
+
+COLORS = [['#FFB0B0', _('light pink')],
+ ['#FFFF80', _('yellow')],
+ ['#FF6060', _('pink')],
+ ['#8080FF', _('blue')],
+ ['#FFFFFF', _('white')],
+ ['#FF0000', _('red')],
+ ['#A00000', _('brown')],
+ ['#A000A0', _('purple')],
+ ['#A08080', _('dark pink')],
+ ['#00A000', _('green')],
+ ['#A000A0', _('purple')],
+ ['#A08080', _('dark pink')],
+ ['#00A000', _('curly green')],
+ ['#FFFF00', _('bright yellow')]]
+
+
+SOUNDS = [['a-as-in-pat.ogg', 'ah'],
+ ['u-as-in-up.ogg', 'uh'],
+ ['i-as-in-it.ogg', 'ih'],
+ ['e-as-in-pet.ogg', 'eh'],
+ ['o-as-in-pot.ogg', 'ah'],
+ ['y-as-in-tummy.ogg', 'e'],
+ ['p-as-in-pat.ogg', 'p'],
+ ['n-as-in-tennis.ogg', 'n'],
+ ['t-as-in-tap.ogg', 't'],
+ ['d-as-in-dog.ogg', 'd'],
+ ['s-as-in-is.ogg', 's'],
+ ['m-as-in-mom.ogg', 'm'],
+ ['s-as-in-stop.ogg', 's'],
+ ['a-as-in-read-a-book.ogg', 'a']]
+
+
+WORDS = ['a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a',
+ 'a u a a a a a u a a u a a u a u a a a u u a u a a a a a u a a a a a',
+ 'a i u a i a i a i i i i a a i u i a a u a i i u a i a u a i a a i a',
+ 'a i u a i a i e a i i e i i a a i u i a a u a i i u a a i e a e a a',
+ 'a i u a i o a i e a i o a i e o o i i a e i u e a o e i i o a o a o',
+ 'a i u a i o a i e a i o u i e o e i i a a i u i o a a o u y a i i u y o y a a y i o a a o i u y u y a i u o a e a o u u a a i i a e o', # i o e a o i a i o o a o a o',
+ 'pa i up a i o pa i e a i o u i e o e i a a i up i op a a o u y a i i u y o y a a y ip pop a a po u y puppy a i up o a e a op up u', # a a i i a e po i o pe a o i a i o op a op a op',
+ 'pa i up a i no pa i en an i on u i en o en i in an a i up i op an an on u y a i i unny o y an a y ip pop a an po i nu y puppy an i up on an en a op up', # u an an i i a e po i o pe an o i a i o op an op an op',
+ 'pat i up a i not pa i ten an it on tu i tent o en ti in an a it up ti top an tan on u ty at it i unny o y an a y ip pop at tan pot i nutty puppy an i up on an tent a', # op up u an an it i a e pot i to pet an to i a i to op an op an op',
+ 'pat i up a i not pa i ten and it on tu i tent o end ti in and a it up ti top and tand on du ty at it i unny o y and daddy ip pop at tand pot i nutty puppy ad i up on an tent dad op up', # ud and and it i ad e pot i to pet and to i dad i to op and op and op',
+ "pat is up a is not pa is ten and it on tu i tent o ends ti in and a it up ti top and tands on du ty at it is unny o y and daddy ip pop op up ud and and it is ad e pot", # is to pet and to is dad's i to op and op and op",
+ "pat is up am is not pam is ten and it on tump in tent mom ends tim in and am it up tim top and tands on du ty mat it is unny mommy and daddy ip pop at tand pot is nutty puppy", # and is up on man's tent dad mop up mud and and it is ad ttempt pot is tom's pet and tom is dad's i tom mop and mop and mop",
+ "pat-is-up sam-is-not pam-is-ten-and-sits-on-stump-in-tent mom-sends-tim-in-and-sam-sits-up tim-stops-and-stands-on-dusty-mat it-is-sunny mommy-and-daddy-sip-pop-at-stand spot-is-nutty-puppy-and-is-up-on-man's-tent dad-mops-up-mund-and-sand it-is-sad ttempt spot-is-tom's-pet-and-tom-is-dad's-ssist tom-mops-and-mops-and-mops",
+ "pat-is-up sam-is-not pam-is-ten-and-sits-on-A-stump-in-A-tent mom-sends-tim-in-and-sam-sits-up tim-stops-and-stands-on-A-dusty-mat it-is-sunny mommy-and-daddy-sip-pop-at-A-stand spot-is-A-nutty-puppy-and-is-up-on-A-man's-tent dad-mops-up-mud-and-sand it-is-A-sad-Attempt spot-is-tom's-pet-and-tom-is-dad's-AssistAnt] # tom-mops-and-mops-and-mops"]
+
+STROKES = [1, 4, 13]
+
+# TRANS: e.g., This yellow sign is said u as in up.
+MSGS = [_('This %s sign is said\n%s like %s.'),
+ _('This %s sign is said\ntogether with other sounds\nas in: %s'),
+ _('This %s sign is\nlightly said\n%s like %s.'),
+ _('When it looks like this,\nwe read it the same way.')]
+
+MSG_INDEX = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2]
+
+KERN = {'i':0.6, 't':0.8, 'm':1.6, "'":0.4}
+
+ALIGN = 11
+
+# TODO: add color to a like pat
+# finish sound stuff
+
+
+class Page():
+
+ def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']):
+ self.activity = parent
+ self.colors = colors
+
+ # Starting from command line
+ if parent is None:
+ self.sugar = False
+ self.canvas = canvas
+ else:
+ self.sugar = True
+ self.canvas = canvas
+ parent.show_all()
+
+ self.canvas.set_flags(gtk.CAN_FOCUS)
+ self.canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ self.canvas.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
+ self.canvas.connect("expose-event", self._expose_cb)
+ self.canvas.connect("button-press-event", self._button_press_cb)
+ self.canvas.connect("button-release-event", self._button_release_cb)
+ self.canvas.connect("key_press_event", self._keypress_cb)
+ self.width = gtk.gdk.screen_width()
+ self.height = gtk.gdk.screen_height() - GRID_CELL_SIZE
+ self.scale = self.width / 240.
+ self.left = int((self.width - self.scale * 60) / 2.)
+ self.sprites = Sprites(self.canvas)
+ self.page_index = 0
+ self.background = Sprite(self.sprites, 0, 0, svg_str_to_pixbuf(
+ generate_card(string='', colors=['#FFFFFF', '#FFFFFF'],
+ scale=self.scale*4)))
+ self.background.set_layer(1)
+ self.background.set_label_attributes(40)
+ self.cards = []
+ self.letters = []
+ self.colored_letters = []
+ self.press = None
+ self.release = None
+ self.gplay = None
+
+ self.my_canvas = Sprite(self.sprites, 0, 0,
+ gtk.gdk.Pixmap(self.canvas.window, self.width,
+ self.height, -1))
+ self.my_canvas.set_layer(0)
+ self.gc = self.my_canvas.images[0].new_gc()
+ self.cm = self.gc.get_colormap()
+ self.bgcolor = self.cm.alloc_color('#FFFFFF')
+ self.gc.set_foreground(self.bgcolor)
+
+ self.punctuation = Sprite(self.sprites, 0, 0,
+ svg_str_to_pixbuf(generate_card(
+ string="'", colors=['#000000', '#000000'],
+ background=False)))
+
+ self.new_page()
+
+ def new_page(self, saved_state=None, deck_index=0):
+ ''' Load a new page. '''
+ if self.page_index == len(CARDS):
+ self.page_index = 0
+ self.activity.status.set_label('')
+ if self.page_index == len(self.cards):
+ self.cards.append(Sprite(self.sprites, self.left, GRID_CELL_SIZE,
+ svg_str_to_pixbuf(generate_card(
+ string=CARDS[self.page_index][0].lower(),
+ colors=[COLORS[self.page_index][0], '#000000'],
+ scale=self.scale,
+ center=True))))
+ self.activity.status.set_label('')
+ if self.page_index in STROKES:
+ stroke = True
+ else:
+ stroke = False
+ self.letters.append(Sprite(self.sprites, 0, 0,
+ svg_str_to_pixbuf(generate_card(
+ string=CARDS[self.page_index][0].lower(),
+ colors=['#000000', '#000000'],
+ background=False))))
+ self.colored_letters.append(Sprite(self.sprites, 0, 0,
+ svg_str_to_pixbuf(generate_card(
+ string=CARDS[self.page_index][0].lower(),
+ colors=[COLORS[self.page_index][0], '#000000'],
+ background=False, stroke=stroke))))
+
+ for c in self.cards:
+ c.set_layer(0)
+ self._load_card()
+
+ def _load_card(self):
+ self.cards[self.page_index].set_layer(2)
+ if MSG_INDEX[self.page_index] == 1:
+ self.background.set_label(MSGS[1] % (COLORS[self.page_index][1],
+ CARDS[self.page_index][1]))
+ else:
+ self.background.set_label(MSGS[MSG_INDEX[self.page_index]] % \
+ (COLORS[self.page_index][1],
+ CARDS[self.page_index][0].lower(),
+ CARDS[self.page_index][1]))
+
+ self.background.set_layer(1)
+ for l in self.letters:
+ l.set_layer(0)
+ for l in self.colored_letters:
+ l.set_layer(0)
+ self.punctuation.set_layer(0)
+ self.my_canvas.set_layer(0)
+
+ def reload(self):
+ self._load_card()
+
+ def read(self):
+ for c in self.cards:
+ c.set_layer(0)
+ self.background.set_label('')
+ self.background.set_layer(0)
+ self.activity.status.set_label(_('Read the sounds one at a time.'))
+ rect = gtk.gdk.Rectangle(0, 0, self.width, self.height)
+ self.my_canvas.images[0].draw_rectangle(self.gc, True, *rect)
+ self.invalt(0, 0, self.width, self.height)
+ self.my_canvas.set_layer(1)
+ p = 0
+ offset = self.width/30
+ my_list = WORDS[self.page_index].split(' ')
+
+ # Some pages are aligned left
+ if self.page_index > ALIGN:
+ x, y = 10, 10
+ else:
+ x, y = self._xy(0)
+
+ # Each list is a collection of phrases, separated by spaces
+ for phrase in my_list:
+ # The words in the list are separated by dashes
+ words = phrase.split('-')
+ for word in words:
+ # Will word run off the right edge?
+ if x + len(word) * offset > self.width:
+ x, y = self._xy(y)
+
+ # Process each character in the word
+ for c in range(len(word)):
+ if word[c] == CARDS[self.page_index][0]:
+ self._draw_pixbuf(
+ self.colored_letters[self.page_index].images[0],
+ x, y)
+ elif word[c] == "'":
+ self._draw_pixbuf(self.punctuation.images[0], x, y)
+ else:
+ for j in range(self.page_index):
+ if CARDS[j][0] == word[c]:
+ self._draw_pixbuf(self.letters[j].images[0],
+ x, y)
+ if word[c] in KERN:
+ x += offset * KERN[word[c]]
+ else:
+ x += offset
+
+ # Put a space after each word
+ if x > 10:
+ x += int(offset / 1.6)
+
+ # Put a long space between each phrase
+ if self.page_index > ALIGN:
+ x += offset
+ else:
+ x += int(uniform(30, self.width/8))
+ if x > self.width * 7 / 8.0:
+ x, y = self._xy(y)
+
+ def _draw_pixbuf(self, pixbuf, x, y):
+ w = pixbuf.get_width()
+ h = pixbuf.get_height()
+ self.my_canvas.images[0].draw_pixbuf(self.gc, pixbuf, 0, 0,
+ int(x), int(y))
+ self.invalt(x, y, w, h)
+
+ def _xy(self, y):
+ if self.page_index > ALIGN:
+ return 10, int(self.height / 10.0) + y
+ else:
+ return int(uniform(40, self.width / 8.0)), \
+ int(uniform(40, self.height / 10.0)) + y
+
+ def _button_press_cb(self, win, event):
+ win.grab_focus()
+ x, y = map(int, event.get_coords())
+ self.start_drag = [x, y]
+
+ spr = self.sprites.find_sprite((x, y))
+ self.press = spr
+ self.release = None
+
+ return True
+
+ def _button_release_cb(self, win, event):
+ win.grab_focus()
+
+ x, y = map(int, event.get_coords())
+ spr = self.sprites.find_sprite((x, y))
+ if spr == self.cards[self.page_index]:
+ play_audio_from_file(self, 'sounds/a-as-in-pat.ogg')
+ ''' os.system('espeak "%s" --stdout | aplay' % \
+ (SOUNDS[self.page_index][1])) '''
+
+ def _game_over(self, msg=_('Game over')):
+ if self.sugar:
+ self.activity.status.set_label(msg)
+
+ def _keypress_cb(self, area, event):
+ return True
+
+ def _expose_cb(self, win, event):
+ self.sprites.redraw_sprites()
+ return True
+
+ def _destroy_cb(self, win, event):
+ gtk.main_quit()
+
+ def invalt(self, x, y, w, h):
+ """ Mark a region for refresh """
+ self.canvas.window.invalidate_rect(
+ gtk.gdk.Rectangle(int(x), int(y), int(w), int(h)), False)
+
+#
+# Load pixbuf from SVG string
+#
+def svg_str_to_pixbuf(svg_string):
+ pl = gtk.gdk.PixbufLoader('svg')
+ pl.write(svg_string)
+ pl.close()
+ pixbuf = pl.get_pixbuf()
+ return pixbuf
diff --git a/sprites.py b/sprites.py
new file mode 100644
index 0000000..9e37694
--- /dev/null
+++ b/sprites.py
@@ -0,0 +1,462 @@
+# -*- coding: utf-8 -*-
+
+#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.
+
+"""
+
+sprites.py is a simple sprites library for managing graphics objects,
+'sprites', on a canvas. It manages multiple sprites with methods such
+as move, hide, set_layer, etc.
+
+There are two classes:
+
+class Sprites maintains a collection of sprites.
+class Sprite manages individual sprites within the collection.
+
+Example usage:
+ # Import the classes into your program.
+ from sprites import Sprites Sprite
+
+ # In your expose callback event handler, call refresh
+ def _expose_cb(self, win, event):
+ self.sprite_list.refresh(event)
+ return True
+
+ # Create a new sprite collection for a gtk Drawing Area.
+ my_drawing_area = gtk.DrawingArea()
+ self.sprite_list = Sprites(my_drawing_area)
+
+ # Create a "pixbuf" (in this example, from SVG).
+ my_pixbuf = svg_str_to_pixbuf("<svg>...some svg code...</svg>")
+
+ # Create a sprite at position x1, y1.
+ my_sprite = sprites.Sprite(self.sprite_list, x1, y1, my_pixbuf)
+
+ # Move the sprite to a new position.
+ my_sprite.move_relative((dx, dy))
+
+ # Create another "pixbuf".
+ your_pixbuf = svg_str_to_pixbuf("<svg>...some svg code...</svg>")
+
+ # Create a sprite at position x2, y2.
+ your_sprite = sprites.Sprite(self.sprite_list, x2, y2, my_pixbuf)
+
+ # Assign the sprites to layers.
+ # In this example, your_sprite will be on top of my_sprite.
+ my_sprite.set_layer(100)
+ your_sprite.set_layer(200)
+
+ # Now put my_sprite on top of your_sprite.
+ my_sprite.set_layer(300)
+
+# method for converting SVG to a gtk pixbuf
+def svg_str_to_pixbuf(svg_string):
+ pl = gtk.gdk.PixbufLoader('svg')
+ pl.write(svg_string)
+ pl.close()
+ pixbuf = pl.get_pixbuf()
+ return pixbuf
+"""
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import pango
+
+
+class Sprites:
+ """ A class for the list of sprites and everything they share in common """
+
+ def __init__(self, canvas, area=None, gc=None):
+ """ Initialize an empty array of sprites """
+ self.canvas = canvas
+ if area == None:
+ self.area = self.canvas.window
+ self.gc = self.area.new_gc()
+ else:
+ self.area = area
+ self.gc = gc
+ self.cm = self.gc.get_colormap()
+ self.list = []
+
+ def get_sprite(self, i):
+ """ Return a sprint from the array """
+ if i < 0 or i > len(self.list) - 1:
+ return(None)
+ else:
+ return(self.list[i])
+
+ def length_of_list(self):
+ """ How many sprites are there? """
+ return(len(self.list))
+
+ def append_to_list(self, spr):
+ """ Append a new sprite to the end of the list. """
+ self.list.append(spr)
+
+ def insert_in_list(self, spr, i):
+ """ Insert a sprite at position i. """
+ if i < 0:
+ self.list.insert(0, spr)
+ elif i > len(self.list) - 1:
+ self.list.append(spr)
+ else:
+ self.list.insert(i, spr)
+
+ def remove_from_list(self, spr):
+ """ Remove a sprite from the list. """
+ if spr in self.list:
+ self.list.remove(spr)
+
+ def find_sprite(self, pos, alpha=True):
+ """ Search based on (x, y) position. Return the 'top/first' one. """
+ list = self.list[:]
+ list.reverse()
+ for spr in list:
+ if spr.hit(pos):
+ if not alpha or spr.get_pixel(pos)[3] == 255:
+ return spr
+ return None
+
+ def refresh(self, event):
+ """ Handle expose event refresh """
+ self.redraw_sprites(event.area)
+
+ def redraw_sprites(self, area=None):
+ """ Redraw the sprites that intersect area. """
+ for spr in self.list:
+ if area == None:
+ spr.draw()
+ else:
+ intersection = spr.rect.intersect(area)
+ if intersection.width > 0 or intersection.height > 0:
+ spr.draw()
+
+
+class Sprite:
+ """ A class for the individual sprites """
+
+ def __init__(self, sprites, x, y, image):
+ """ Initialize an individual sprite """
+ self._sprites = sprites
+ self.rect = gtk.gdk.Rectangle(int(x), int(y), 0, 0)
+ self._scale = [12]
+ self._rescale = [True]
+ self._horiz_align = ["center"]
+ self._vert_align = ["middle"]
+ self._fd = None
+ self._bold = False
+ self._italic = False
+ self._color = None
+ self._margins = [0, 0, 0, 0]
+ self.layer = 100
+ self.labels = []
+ self.images = []
+ self._dx = [] # image offsets
+ self._dy = []
+ self.set_image(image)
+ if self._sprites is not None:
+ self._sprites.append_to_list(self)
+
+ def set_image(self, image, i=0, dx=0, dy=0):
+ """ Add an image to the sprite. """
+ while len(self.images) < i + 1:
+ self.images.append(None)
+ self._dx.append(0)
+ self._dy.append(0)
+ self.images[i] = image
+ self._dx[i] = dx
+ self._dy[i] = dy
+ if isinstance(self.images[i], gtk.gdk.Pixbuf):
+ w = self.images[i].get_width()
+ h = self.images[i].get_height()
+ else:
+ w, h = self.images[i].get_size()
+ if i == 0: # Always reset width and height when base image changes.
+ self.rect.width = w + dx
+ self.rect.height = h + dy
+ else:
+ if w + dx > self.rect.width:
+ self.rect.width = w + dx
+ if h + dy > self.rect.height:
+ self.rect.height = h + dy
+
+ def move(self, pos, visible=True):
+ """ Move to new (x, y) position """
+ if visible:
+ self.inval()
+ self.rect.x, self.rect.y = int(pos[0]), int(pos[1])
+ if visible:
+ self.inval()
+
+ def move_relative(self, pos, visible=True):
+ """ Move to new (x+dx, y+dy) position """
+ if visible:
+ self.inval()
+ self.rect.x += int(pos[0])
+ self.rect.y += int(pos[1])
+ if visible:
+ self.inval()
+
+ def get_xy(self):
+ """ Return current (x, y) position """
+ return (self.rect.x, self.rect.y)
+
+ def get_dimensions(self):
+ """ Return current size """
+ return (self.rect.width, self.rect.height)
+
+ def get_layer(self):
+ """ Return current layer """
+ return self.layer
+
+ def set_shape(self, image, i=0):
+ """ Set the current image associated with the sprite """
+ self.inval()
+ self.set_image(image, i)
+ self.inval()
+
+ def set_layer(self, layer):
+ """ Set the layer for a sprite """
+ if self._sprites is None:
+ return
+ self._sprites.remove_from_list(self)
+ self.layer = layer
+ for i in range(self._sprites.length_of_list()):
+ if layer < self._sprites.get_sprite(i).layer:
+ self._sprites.insert_in_list(self, i)
+ self.inval()
+ return
+ self._sprites.append_to_list(self)
+ self.inval()
+
+ def set_label(self, new_label, i=0):
+ """ Set the label drawn on the sprite """
+ self._extend_labels_array(i)
+ if type(new_label) is str or type(new_label) is unicode:
+ # pango doesn't like nulls
+ self.labels[i] = new_label.replace("\0", " ")
+ else:
+ self.labels[i] = str(new_label)
+ self.inval()
+
+ def set_margins(self, l=0, t=0, r=0, b=0):
+ """ Set the margins for drawing the label """
+ self._margins = [l, t, r, b]
+
+ def _extend_labels_array(self, i):
+ """ Append to the labels attribute list """
+ if self._fd is None:
+ self.set_font('Sans')
+ if self._color is None:
+ self._color = self._sprites.cm.alloc_color('black')
+ while len(self.labels) < i + 1:
+ self.labels.append(" ")
+ self._scale.append(self._scale[0])
+ self._rescale.append(self._rescale[0])
+ self._horiz_align.append(self._horiz_align[0])
+ self._vert_align.append(self._vert_align[0])
+
+ def set_font(self, font):
+ """ Set the font for a label """
+ self._fd = pango.FontDescription(font)
+
+ def set_label_color(self, rgb):
+ """ Set the font color for a label """
+ self._color = self._sprites.cm.alloc_color(rgb)
+
+ def set_label_attributes(self, scale, rescale=True, horiz_align="center",
+ vert_align="middle", i=0):
+ """ Set the various label attributes """
+ self._extend_labels_array(i)
+ self._scale[i] = scale
+ self._rescale[i] = rescale
+ self._horiz_align[i] = horiz_align
+ self._vert_align[i] = vert_align
+
+ def hide(self):
+ """ Hide a sprite """
+ if self._sprites is None:
+ return
+ self.inval()
+ self._sprites.remove_from_list(self)
+
+ def inval(self):
+ """ Force a region redraw by gtk """
+ if self._sprites is None:
+ return
+ self._sprites.area.invalidate_rect(self.rect, False)
+
+ def draw(self):
+ """ Draw the sprite (and label) """
+ if self._sprites is None:
+ return
+ for i, img in enumerate(self.images):
+ if isinstance(img, gtk.gdk.Pixbuf):
+ self._sprites.area.draw_pixbuf(self._sprites.gc, img, 0, 0,
+ self.rect.x + self._dx[i],
+ self.rect.y + self._dy[i])
+ elif img is not None:
+ self._sprites.area.draw_drawable(self._sprites.gc, img, 0, 0,
+ self.rect.x + self._dx[i],
+ self.rect.y + self._dy[i],
+ -1, -1)
+ if len(self.labels) > 0:
+ self.draw_label()
+
+ def hit(self, pos):
+ """ Is (x, y) on top of the sprite? """
+ x, y = pos
+ if x < self.rect.x:
+ return False
+ if x > self.rect.x + self.rect.width:
+ return False
+ if y < self.rect.y:
+ return False
+ if y > self.rect.y + self.rect.height:
+ return False
+ return True
+
+ def draw_label(self):
+ """ Draw the label based on its attributes """
+ if self._sprites is None:
+ return
+ my_width = self.rect.width - self._margins[0] - self._margins[2]
+ if my_width < 0:
+ my_width = 0
+ my_height = self.rect.height - self._margins[1] - self._margins[3]
+ for k in range(len(self.labels)):
+ label_segments = self.labels[k].split('\n')
+ for i in range(len(label_segments)):
+ pl = self._sprites.canvas.create_pango_layout(
+ str(label_segments[i]))
+ self._fd.set_size(int(self._scale[k] * pango.SCALE))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ if w > my_width:
+ if self._rescale[k]:
+ self._fd.set_size(
+ int(self._scale[k] * pango.SCALE * my_width / w))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ else:
+ j = len(self.labels[k]) - 1
+ while(w > my_width and j > 0):
+ pl = self._sprites.canvas.create_pango_layout(
+ "…" + self.labels[k][len(self.labels[k]) - j:])
+ self._fd.set_size(int(self._scale[i] * pango.SCALE))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ j -= 1
+ if self._horiz_align[k] == "center":
+ x = int(self.rect.x + self._margins[0] + (my_width - w) / 2)
+ elif self._horiz_align[k] == 'left':
+ x = int(self.rect.x + self._margins[0])
+ else: # right
+ x = int(self.rect.x + self.rect.width - \
+ w - self._margins[2])
+ h = pl.get_size()[1] / pango.SCALE
+ if self._vert_align[k] == "middle":
+ # yoff = int(len(label_segments) * h / 2.)
+ yoff = 0
+ y = int(self.rect.y + self._margins[1] + \
+ (my_height - h) / 2 - yoff + i * h)
+ elif self._vert_align[k] == "top":
+ y = int(self.rect.y + self._margins[1] + i * h)
+ else: # bottom
+ yoff = int(len(label_segments) * h)
+ y = int(self.rect.y + self.rect.height - \
+ h - self._margins[3] - yoff + i * h)
+ self._sprites.gc.set_foreground(self._color)
+ self._sprites.area.draw_layout(self._sprites.gc, x, y, pl)
+
+ def label_width(self):
+ """ Calculate the width of a label """
+ max = 0
+ for i in range(len(self.labels)):
+ pl = self._sprites.canvas.create_pango_layout(self.labels[i])
+ self._fd.set_size(int(self._scale[i] * pango.SCALE))
+ pl.set_font_description(self._fd)
+ w = pl.get_size()[0] / pango.SCALE
+ if w > max:
+ max = w
+ return max
+
+ def label_safe_width(self):
+ """ Return maximum width for a label """
+ return self.rect.width - self._margins[0] - self._margins[2]
+
+ def label_safe_height(self):
+ """ Return maximum height for a label """
+ return self.rect.height - self._margins[1] - self._margins[3]
+
+ def label_left_top(self):
+ """ Return the upper-left corner of the label safe zone """
+ return(self._margins[0], self._margins[1])
+
+ def get_pixel(self, pos, i=0, mode='888'):
+ """ Return the pixel at (x, y) """
+ x, y = pos
+ x = x - self.rect.x
+ y = y - self.rect.y
+ if isinstance(self.images[i], gtk.gdk.Pixbuf):
+ if y > self.images[i].get_height() - 1:
+ return(-1, -1, -1, -1)
+ array = self.images[i].get_pixels()
+ if array is not None:
+ try:
+ if self.images[i].get_has_alpha():
+ offset = (y * self.images[i].get_width() + x) * 4
+ a = ord(array[offset + 3])
+ else:
+ offset = (y * self.images[i].get_width() + x) * 3
+ a = 255
+ r = ord(array[offset])
+ g = ord(array[offset + 1])
+ b = ord(array[offset + 2])
+ return(r, g, b, a)
+ except IndexError:
+ """
+ print "Index Error: %d %d (%d, %d) (w: %d, h: %d) (%dx%d)"\
+ % (len(array), offset, x, y,
+ self.images[i].get_width(),
+ self.images[i].get_height(),
+ self.rect.width, self.rect.height)
+ """
+ pass
+ return(-1, -1, -1, -1)
+ else:
+ w, h = self.images[i].get_size()
+ if x < 0 or x > (w - 1) or y < 0 or y > (h - 1):
+ return(-1, -1, -1, -1)
+ image = self.images[i].get_image(x, y, 1, 1)
+ pixel = image.get_pixel(0, 0)
+ visual = self.images[i].get_visual()
+ r = int((pixel & visual.red_mask) >> visual.red_shift)
+ g = int((pixel & visual.green_mask) >> visual.green_shift)
+ b = int((pixel & visual.blue_mask) >> visual.blue_shift)
+ # Rescale to 8 bits
+ if mode == '565':
+ r = r << 3
+ g = g << 2
+ b = b << 3
+ return(r, g, b, 0)