diff options
author | Ignacio Rodríguez <ignaciorodriguez@sugarlabs.org> | 2012-10-13 17:28:26 (GMT) |
---|---|---|
committer | Ignacio Rodríguez <ignaciorodriguez@sugarlabs.org> | 2012-10-13 17:28:26 (GMT) |
commit | b062293858158924957cdbea01715c700bb17710 (patch) | |
tree | 0d45acd8d6b1dd5fa190bf098eb49f6f275d456f | |
parent | 8acea3eecb9e01374667acc5ab4f4920f13bd4af (diff) |
None
-rw-r--r-- | StoryActivity.pyo | bin | 14802 -> 0 bytes | |||
-rw-r--r-- | StoryActivity.py~ | 405 | ||||
-rw-r--r-- | game.pyo | bin | 9460 -> 0 bytes | |||
-rw-r--r-- | game.py~ | 268 | ||||
-rw-r--r-- | grecord.pyo | bin | 7704 -> 0 bytes | |||
-rw-r--r-- | sprites.pyc | bin | 0 -> 16561 bytes | |||
-rw-r--r-- | sprites.pyo | bin | 13199 -> 0 bytes | |||
-rw-r--r-- | toolbar_utils.py~ | 436 | ||||
-rw-r--r-- | utils.pyo | bin | 1599 -> 0 bytes |
9 files changed, 0 insertions, 1109 deletions
diff --git a/StoryActivity.pyo b/StoryActivity.pyo Binary files differdeleted file mode 100644 index 9d8fa25..0000000 --- a/StoryActivity.pyo +++ /dev/null diff --git a/StoryActivity.py~ b/StoryActivity.py~ deleted file mode 100644 index 8c94029..0000000 --- a/StoryActivity.py~ +++ /dev/null @@ -1,405 +0,0 @@ -#Copyright (c) 2012 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 General Public License -# along with this library; if not, write to the Free Software -# Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA - - -from gi.repository import Gdk -from gi.repository import Gtk -from gi.repository import GObject -import subprocess -import cairo -import os -import time - -from sugar3.activity import activity -from sugar3 import profile -from sugar3.datastore import datastore -try: # Intente , siempre da TRUE, En Sugar 3 ya esta.. - from sugar3.graphics.toolbarbox import ToolbarBox - _have_toolbox = True -except ImportError: # Si hay error - _have_toolbox = False - -if _have_toolbox: - from sugar3.activity.widgets import ActivityToolbarButton - from sugar3.activity.widgets import StopButton - -from sugar3.graphics.alert import Alert - -from toolbar_utils import button_factory, label_factory, separator_factory -from utils import json_load, json_dump, play_audio_from_file -from grecord import Grecord - -import telepathy -import dbus -from dbus.service import signal -from dbus.gobject_service import ExportedGObject -from sugar3.presence import presenceservice -from sugar3.presence.tubeconn import TubeConnection - -from gettext import gettext as _ - -from game import Game - -import logging -_logger = logging.getLogger('story-activity') - - -SERVICE = 'org.sugarlabs.StoryActivity' -IFACE = SERVICE - - -class StoryActivity(activity.Activity): - """ Storytelling game """ - - def __init__(self, handle): - """ Initialize the toolbars and the game board """ - try: - super(StoryActivity, self).__init__(handle) - except dbus.exceptions.DBusException, e: - _logger.error(str(e)) - - self.path = activity.get_bundle_path() - - self.nick = profile.get_nick_name() - if profile.get_color() is not None: - self.colors = profile.get_color().to_string().split(',') - else: - self.colors = ['#A0FFA0', '#FF8080'] - - self._recording = False - self._grecord = None - self._alert = None - - self._setup_toolbars(_have_toolbox) - self._setup_dispatch_table() - - # Create a canvas - canvas = Gtk.DrawingArea() - canvas.set_size_request(Gdk.screen_width(), \ - Gdk.screen_height()) - self.set_canvas(canvas) - canvas.show() - self.show_all() - - self._game = Game(canvas, parent=self, path=self.path, - colors=self.colors) - self._setup_presence_service() - - if 'dotlist' in self.metadata: - self._restore() - else: - self._game.new_game() - - def _setup_toolbars(self, have_toolbox): - """ Setup the toolbars. """ - - self.max_participants = 9 - - 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() - self.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(_('Game'), games_toolbar) - toolbox.show() - toolbox.set_current_toolbar(1) - self.toolbar = games_toolbar - - self._new_game_button_h = button_factory( - 'view-refresh', self.toolbar, self._new_game_cb, - tooltip=_('Load new images.')) - - separator_factory(self.toolbar) - - self.save_as_image = button_factory( - 'image-saveoff', self.toolbar, self._do_save_as_image_cb, - tooltip=_('Save as image')) - - separator_factory(self.toolbar) - - self._record_button = button_factory( - 'media-record', self.toolbar, - self._record_cb, tooltip=_('Start recording')) - - self._playback_button = button_factory( - 'media-playback-start-insensitive', self.toolbar, - self._playback_recording_cb, tooltip=_('Nothing to play')) - - if _have_toolbox: - separator_factory(toolbox.toolbar, True, False) - - if _have_toolbox: - stop_button = StopButton(self) - stop_button.props.accelerator = '<Ctrl>q' - toolbox.toolbar.insert(stop_button, -1) - stop_button.show() - - def _new_game_cb(self, button=None): - ''' Start a new game. ''' - self._game.new_game() - - def write_file(self, file_path): - """ Write the grid status to the Journal """ - dot_list = self._game.save_game() - self.metadata['dotlist'] = '' - for dot in dot_list: - self.metadata['dotlist'] += str(dot) - if dot_list.index(dot) < len(dot_list) - 1: - self.metadata['dotlist'] += ' ' - - def _restore(self): - """ Restore the game state from metadata """ - dot_list = [] - dots = self.metadata['dotlist'].split() - for dot in dots: - dot_list.append(int(dot)) - self._game.restore_game(dot_list) - - def _do_save_as_image_cb(self, button=None): - """ Grab the current canvas and save it to the Journal. """ - self._notify_successful_save(title=_('Save as image')) - file_path = os.path.join(activity.get_activity_root(), - 'instance', 'story.png') - png_surface = self._game.export() - png_surface.write_to_png(file_path) - - dsobject = datastore.create() - dsobject.metadata['title'] = "%s %s" % \ - (self.metadata['title'], _("image")) - dsobject.metadata['icon-color'] = profile.get_color().to_string() - dsobject.metadata['mime_type'] = 'image/png' - dsobject.set_file_path(file_path) - datastore.write(dsobject) - dsobject.destroy() - os.remove(file_path) - if self._alert is not None: - self.remove_alert(self._alert) - self._alert = None - - def _record_cb(self, button=None): - ''' Start/stop audio recording ''' - if self._grecord is None: - _logger.debug('setting up grecord') - self._grecord = Grecord(self) - if self._recording: # Was recording, so stop (and save?) - _logger.debug('recording...True. Preparing to save.') - self._grecord.stop_recording_audio() - self._recording = False - self._record_button.set_icon('media-record') - self._record_button.set_tooltip(_('Start recording')) - self._playback_button.set_icon('media-playback-start') - self._playback_button.set_tooltip(_('Play recording')) - self._notify_successful_save(title=_('Save recording')) - gobject.timeout_add(100, self._wait_for_transcoding_to_finish) - else: # Wasn't recording, so start - _logger.debug('recording...False. Start recording.') - self._grecord.record_audio() - self._recording = True - self._record_button.set_icon('media-recording') - self._record_button.set_tooltip(_('Stop recording')) - - def _wait_for_transcoding_to_finish(self, button=None): - while not self._grecord.transcoding_complete(): - time.sleep(1) - if self._alert is not None: - self.remove_alert(self._alert) - self._alert = None - self._save_recording() - - def _playback_recording_cb(self, button=None): - ''' Play back current recording ''' - _logger.debug('Playback current recording from output.ogg...') - play_audio_from_file(os.path.join(activity.get_activity_root(), - 'instance', 'output.ogg')) - return - - def _save_recording(self): - if os.path.exists(os.path.join(activity.get_activity_root(), - 'instance', 'output.ogg')): - _logger.debug('Saving recording to Journal...') - dsobject = datastore.create() - dsobject.metadata['title'] = _('audio note for %s') % \ - (self.metadata['title']) - dsobject.metadata['icon-color'] = profile.get_color().to_string() - dsobject.metadata['mime_type'] = 'audio/ogg' - _logger.debug('setting file path to %s' % ( - os.path.join(activity.get_activity_root(), - 'instance', 'output.ogg'))) - dsobject.set_file_path(os.path.join(activity.get_activity_root(), - 'instance', 'output.ogg')) - datastore.write(dsobject) - dsobject.destroy() - # Always save an image with the recording. - self._do_save_as_image_cb() - else: - _logger.debug('Nothing to save...') - return - - def _notify_successful_save(self, title='', msg=''): - ''' Notify user when saves are completed ''' - self._alert = Alert() - self._alert.props.title = title - self._alert.props.msg = msg - self.add_alert(self._alert) - self._alert.show() - - # Collaboration-related methods - - def _setup_presence_service(self): - """ Setup the Presence Service. """ - self.pservice = presenceservice.get_instance() - self.initiating = None # sharing (True) or joining (False) - - owner = self.pservice.get_owner() - self.owner = owner - self._share = "" - self.connect('shared', self._shared_cb) - self.connect('joined', self._joined_cb) - - def _shared_cb(self, activity): - """ Either set up initial share...""" - self._new_tube_common(True) - - def _joined_cb(self, activity): - """ ...or join an exisiting share. """ - self._new_tube_common(False) - - def _new_tube_common(self, sharer): - """ Joining and sharing are mostly the same... """ - if self._shared_activity is None: - _logger.debug("Error: Failed to share or join activity ... \ - _shared_activity is null in _shared_cb()") - return - - self.initiating = sharer - self.waiting_for_hand = not sharer - - self.conn = self._shared_activity.telepathy_conn - self.tubes_chan = self._shared_activity.telepathy_tubes_chan - self.text_chan = self._shared_activity.telepathy_text_chan - - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( - 'NewTube', self._new_tube_cb) - - if sharer: - _logger.debug('This is my activity: making a tube...') - id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( - SERVICE, {}) - else: - _logger.debug('I am joining an activity: waiting for a tube...') - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( - reply_handler=self._list_tubes_reply_cb, - error_handler=self._list_tubes_error_cb) - self._game.set_sharing(True) - - def _list_tubes_reply_cb(self, tubes): - """ Reply to a list request. """ - for tube_info in tubes: - self._new_tube_cb(*tube_info) - - def _list_tubes_error_cb(self, e): - """ Log errors. """ - _logger.debug('Error: ListTubes() failed: %s' % (e)) - - def _new_tube_cb(self, id, initiator, type, service, params, state): - """ Create a new tube. """ - _logger.debug('New tube: ID=%d initator=%d type=%d service=%s \ -params=%r state=%d' % (id, initiator, type, service, params, state)) - - if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): - if state == telepathy.TUBE_STATE_LOCAL_PENDING: - self.tubes_chan[ \ - telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) - - tube_conn = TubeConnection(self.conn, - self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \ - group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) - - self.chattube = ChatTube(tube_conn, self.initiating, \ - self.event_received_cb) - - def _setup_dispatch_table(self): - ''' Associate tokens with commands. ''' - self._processing_methods = { - 'n': [self._receive_new_game, 'get a new game grid'], - 'p': [self._receive_dot_click, 'get a dot click'], - } - - def event_received_cb(self, event_message): - ''' Data from a tube has arrived. ''' - if len(event_message) == 0: - return - try: - command, payload = event_message.split('|', 2) - except ValueError: - _logger.debug('Could not split event message %s' % (event_message)) - return - self._processing_methods[command][0](payload) - - def send_new_game(self): - ''' Send a new grid to all players ''' - self.send_event('n|%s' % (json_dump(self._game.save_game()))) - - def _receive_new_game(self, payload): - ''' Sharer can start a new game. ''' - dot_list = json_load(payload) - self._game.restore_game(dot_list) - - def send_dot_click(self, dot, color): - ''' Send a dot click to all the players ''' - self.send_event('p|%s' % (json_dump([dot, color]))) - - def _receive_dot_click(self, payload): - ''' When a dot is clicked, everyone should change its color. ''' - (dot, color) = json_load(payload) - self._game.remote_button_press(dot, color) - - def send_event(self, entry): - """ Send event through the tube. """ - if hasattr(self, 'chattube') and self.chattube is not None: - self.chattube.SendText(entry) - - -class ChatTube(ExportedGObject): - """ Class for setting up tube for sharing """ - - def __init__(self, tube, is_initiator, stack_received_cb): - super(ChatTube, self).__init__(tube, PATH) - self.tube = tube - self.is_initiator = is_initiator # Are we sharing or joining activity? - self.stack_received_cb = stack_received_cb - self.stack = '' - - self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, - path=PATH, sender_keyword='sender') - - def send_stack_cb(self, text, sender=None): - if sender == self.tube.get_unique_name(): - return - self.stack = text - self.stack_received_cb(text) - - @signal(dbus_interface=IFACE, signature='s') - def SendText(self, text): - self.stack = text diff --git a/game.pyo b/game.pyo Binary files differdeleted file mode 100644 index c61fe58..0000000 --- a/game.pyo +++ /dev/null diff --git a/game.py~ b/game.py~ deleted file mode 100644 index c9b2467..0000000 --- a/game.py~ +++ /dev/null @@ -1,268 +0,0 @@ -# -*- coding: utf-8 -*- -#Copyright (c) 2012 Walter Bender -# Port to GTK3: -# Ignacio Rodriguez <ignaciorodriguez@sugarlabs.org> - -# 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 General Public License -# along with this library; if not, write to the Free Software -# Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA - -from gi.repository import Gdk,GdkPixbuf -from gi.repository import Gtk -from gi.repository import GObject -import cairo -import os -import glob -from random import uniform - -from gettext import gettext as _ - -import logging -_logger = logging.getLogger('search-activity') - -try: - from sugar3.graphics import style - GRID_CELL_SIZE = style.GRID_CELL_SIZE -except ImportError: - GRID_CELL_SIZE = 0 - -from sprites import Sprites, Sprite - - -DOT_SIZE = 40 -COLORS = ['#000000', '#a00000', '#907000', '#009000', '#0000ff', '#9000a0'] - - -class Game(): - - def __init__(self, canvas, parent=None, path=None, - colors=['#A0FFA0', '#FF8080']): - self._canvas = canvas - self._parent = parent - self._parent.show_all() - self._path = path - - self._colors = ['#FFFFFF'] - self._colors.append(colors[0]) - self._colors.append(colors[1]) - - # self._canvas.set_flags(Gtk.CAN_FOCUS) - # self._canvas.connect("expose-event", self._expose_cb) - - self._width = Gdk.Screen.width() - self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) - self._scale = self._height / (3 * DOT_SIZE * 1.2) - self._scale /= 1.5 - self._dot_size = int(DOT_SIZE * self._scale) - self._space = int(self._dot_size / 5.) - self.we_are_sharing = False - - self._start_time = 0 - self._timeout_id = None - - # Find the image files - self._PATHS = glob.glob(os.path.join(self._path, 'images', '*.svg')) - - # Generate the sprites we'll need... - self._sprites = Sprites(self._canvas) - self._dots = [] - yoffset = self._space * 2 # int(self._space / 2.) - for y in range(3): - for x in range(3): - xoffset = int((self._width - 3 * self._dot_size - \ - 2 * self._space) / 2.) - self._dots.append( - Sprite(self._sprites, - xoffset + x * (self._dot_size + self._space), - y * (self._dot_size + self._space) + yoffset, - self._new_dot_surface(color=self._colors[0]))) - self._dots[-1].type = -1 # No image - self._dots[-1].set_label_attributes(72) - - def _all_clear(self): - ''' Things to reinitialize when starting up a new game. ''' - if self._timeout_id is not None: - gobject.source_remove(self._timeout_id) - - for dot in self._dots: - if dot.type != -1: - dot.type = -1 - dot.set_shape(self._new_dot_surface( - self._colors[abs(dot.type)])) - dot.set_label('?') - self._dance_counter = 0 - self._dance_step() - - def _dance_step(self): - ''' Short animation before loading new game ''' - for dot in self._dots: - dot.set_shape(self._new_dot_surface( - self._colors[int(uniform(0, 3))])) - self._dance_counter += 1 - if self._dance_counter < 10: - self._timeout_id = gobject.timeout_add(500, self._dance_step) - else: - self._new_game() - - def new_game(self): - ''' Start a new game. ''' - self._all_clear() - - def _new_game(self): - ''' Select pictures at random ''' - for i in range(3 * 3): - self._dots[i].set_label('') - self._dots[i].type = int(uniform(0, len(self._PATHS))) - _logger.debug(self._dots[i].type) - self._dots[i].set_shape(self._new_dot_surface( - image=self._dots[i].type)) - - if self.we_are_sharing: - _logger.debug('sending a new game') - self._parent.send_new_game() - - def restore_game(self, dot_list): - ''' Restore a game from the Journal or share ''' - for i, dot in enumerate(dot_list): - self._dots[i].type = dot - self._dots[i].set_shape(self._new_dot_surface( - image=self._dots[i].type)) - - def save_game(self): - ''' Return dot list for saving to Journal or - sharing ''' - dot_list = [] - for dot in self._dots: - dot_list.append(dot.type) - return dot_list - - def set_sharing(self, share=True): - _logger.debug('enabling sharing') - self.we_are_sharing = share - - def _grid_to_dot(self, pos): - ''' calculate the dot index from a column and row in the grid ''' - return pos[0] + pos[1] * 3 - - def _dot_to_grid(self, dot): - ''' calculate the grid column and row for a dot ''' - return [dot % 3, int(dot / 3)] - - def _expose_cb(self, win, event): - self.do_expose_event(event) - - def do_expose_event(self, event): - ''' Handle the expose-event by drawing ''' - # Restrict Cairo to the exposed area - cr = self._canvas.window.cairo_create() - cr.rectangle(event.area.x, event.area.y, - event.area.width, event.area.height) - cr.clip() - # Refresh sprite list - self._sprites.redraw_sprites(cr=cr) - - def _destroy_cb(self, win, event): - Gtk.main_quit() - - def export(self): - ''' Write dot to cairo surface. ''' - w = h = int(4 * self._space + 3 * self._dot_size) - png_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h) - cr = cairo.Context(png_surface) - cr.set_source_rgb(192, 192, 192) - cr.rectangle(0, 0, w, h) - cr.fill() - for i in range(9): - y = self._space + int(i / 3.) * (self._dot_size + self._space) - x = self._space + (i % 3) * (self._dot_size + self._space) - cr.save() - cr = Gdk.CairoContext(cr) - cr.set_source_surface(self._dots[i].cached_surfaces[0], x, y) - cr.rectangle(x, y, self._dot_size, self._dot_size) - cr.fill() - cr.restore() - return png_surface - - def _new_dot_surface(self, color='#000000', image=None): - ''' generate a dot of a color color ''' - self._dot_cache = {} - if image is not None: - color = COLORS[int(uniform(0, 6))] - fd = open(os.path.join(self._path, self._PATHS[image]), 'r') - svg_string = '' - for line in fd: - svg_string += line.replace('#000000', color) - fd.close() - pixbuf = svg_str_to_pixbuf(svg_string, w=self._dot_size, - h = self._dot_size) - ''' - pixbuf = Gdk.pixbuf_new_from_file_at_size( - os.path.join(self._path, self._PATHS[image]), - self._dot_size, self._dot_size) - ''' - else: - if color in self._dot_cache: - return self._dot_cache[color] - self._stroke = color - self._fill = color - self._svg_width = self._dot_size - self._svg_height = self._dot_size - - i = self._colors.index(color) - pixbuf = svg_str_to_pixbuf( - self._header() + \ - self._circle(self._dot_size / 2., self._dot_size / 2., - self._dot_size / 2.) + \ - self._footer()) - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, - self._svg_width, self._svg_height) - context = cairo.Context(surface) - context = Gdk.CairoContext(context) - context.set_source_pixbuf(pixbuf, 0, 0) - context.rectangle(0, 0, self._svg_width, self._svg_height) - context.fill() - if image is None: - self._dot_cache[color] = surface - return surface - - def _header(self): - return '<svg\n' + 'xmlns:svg="http://www.w3.org/2000/svg"\n' + \ - 'xmlns="http://www.w3.org/2000/svg"\n' + \ - 'xmlns:xlink="http://www.w3.org/1999/xlink"\n' + \ - 'version="1.1"\n' + 'width="' + str(self._svg_width) + '"\n' + \ - 'height="' + str(self._svg_height) + '">\n' - - def _rect(self, w, h, x, y): - svg_string = ' <rect\n' - svg_string += ' width="%f"\n' % (w) - svg_string += ' height="%f"\n' % (h) - svg_string += ' rx="%f"\n' % (0) - svg_string += ' ry="%f"\n' % (0) - svg_string += ' x="%f"\n' % (x) - svg_string += ' y="%f"\n' % (y) - svg_string += 'style="fill:#000000;stroke:#000000;"/>\n' - return svg_string - - def _circle(self, r, cx, cy): - return '<circle style="fill:' + str(self._fill) + ';stroke:' + \ - str(self._stroke) + ';" r="' + str(r - 0.5) + '" cx="' + \ - str(cx) + '" cy="' + str(cy) + '" />\n' - - def _footer(self): - return '</svg>\n' - - -def svg_str_to_pixbuf(svg_string, w=None, h=None): - ''' Load pixbuf from SVG string ''' - # Admito que fue la parte mas dificil.. - pl = GdkPixbuf.PixbufLoader.new_with_type('svg') - if w is not None: - pl.set_size(w, h) - pl.write(svg_string) - pl.close() - return pl.get_pixbuf() diff --git a/grecord.pyo b/grecord.pyo Binary files differdeleted file mode 100644 index 8293c3f..0000000 --- a/grecord.pyo +++ /dev/null diff --git a/sprites.pyc b/sprites.pyc Binary files differnew file mode 100644 index 0000000..f4cbeca --- /dev/null +++ b/sprites.pyc diff --git a/sprites.pyo b/sprites.pyo Binary files differdeleted file mode 100644 index 1132095..0000000 --- a/sprites.pyo +++ /dev/null diff --git a/toolbar_utils.py~ b/toolbar_utils.py~ deleted file mode 100644 index e35f67b..0000000 --- a/toolbar_utils.py~ +++ /dev/null @@ -1,436 +0,0 @@ -# -*- coding: utf-8 -*- - -#Copyright (c) 2007-8, Playful Invention Company. -#Copyright (c) 2008-11 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 Gtk.DrawingArea. 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 - - # Create a new sprite collection associated with your widget - self.sprite_list = Sprites(widget) - - # 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((x1+dx, y1+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) - - cr = self.window.cairo_create() - # In your activity's do_expose_event, put in a call to redraw_sprites - self.sprites.redraw_sprites(event.area, cairo_context) - -# method for converting SVG to a gtk pixbuf -def svg_str_to_pixbuf(svg_string): - pl = GdkPixbuf.PixbufLoader('svg') - pl.write(svg_string) - pl.close() - pixbuf = pl.get_pixbuf() - return pixbuf - -''' - -import gi -from gi.repository import Gtk, GdkPixbuf, Gdk -from gi.repository import Pango, PangoCairo - - -class Sprites: - ''' A class for the list of sprites and everything they share in common ''' - - def __init__(self, widget): - ''' Initialize an empty array of sprites ''' - self.widget = widget - self.list = [] - - def set_cairo_context(self, cr): - ''' Cairo context may be set or reset after __init__ ''' - self.cr = cr - - 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): - ''' Search based on (x, y) position. Return the 'top/first' one. ''' - list = self.list[:] - list.reverse() - for spr in list: - if spr.hit(pos): - return spr - return None - - def redraw_sprites(self, area=None, cr=None): - ''' Redraw the sprites that intersect area. ''' - # I think I need to do this to save Cairo some work - if cr is None: - cr = self.cr - else: - self.cr = cr - if cr is None: - print 'sprites.redraw_sprites: no Cairo context' - return - for spr in self.list: - if area == None: - spr.draw(cr=cr) - else: - intersection = spr.rect.intersect(area) - if intersection.width > 0 or intersection.height > 0: - spr.draw(cr=cr) - - -class Sprite: - ''' A class for the individual sprites ''' - - def __init__(self, sprites, x, y, image): - ''' Initialize an individual sprite ''' - self._sprites = sprites - self.rect = [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.type = None - self.set_image(image) - 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], GdkPixbuf.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[2] = w + dx - self.rect[3] = h + dy - else: - if w + dx > self.rect[2]: - self.rect[2] = w + dx - if h + dy > self.rect[3]: - self.rect[3] = h + dy - - def move(self, pos): - ''' Move to new (x, y) position ''' - self.inval() - self.rect[0], self.rect[1] = int(pos[0]), int(pos[1]) - self.inval() - - def move_relative(self, pos): - ''' Move to new (x+dx, y+dy) position ''' - self.inval() - self.rect[0] += int(pos[0]) - self.rect[1] += int(pos[1]) - self.inval() - - def get_xy(self): - ''' Return current (x, y) position ''' - return (self.rect[0], self.rect[1]) - - def get_dimensions(self): - ''' Return current size ''' - return (self.rect[2], self.rect[3]) - - 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 ''' - 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 = (0.5, 0.5, 0.5) - 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 ''' - COLORTABLE = {'black': '#000000', 'white': '#FFFFFF', - 'red': '#FF0000', 'yellow': '#FFFF00', - 'green': '#00FF00', 'cyan': '#00FFFF', - 'blue': '#0000FF', 'purple': '#FF00FF', - 'gray': '#808080'} - if rgb.lower() in COLORTABLE: - rgb = COLORTABLE[rgb.lower()] - # Convert from '#RRGGBB' to floats - self._color = (int('0x' + rgb[1:3], 16) / 256., - int('0x' + rgb[3:5], 16) / 256., - int('0x' + rgb[5:7], 16) / 256.) - return - - 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 ''' - self.inval() - self._sprites.remove_from_list(self) - - def inval(self): - ''' Invalidate a region for gtk ''' - # self._sprites.window.invalidate_rect(self.rect, False) - self._sprites.widget.queue_draw_area(self.rect[0], - self.rect[1], - self.rect[2], - self.rect[3]) - - def draw(self, cr=None): - ''' Draw the sprite (and label) ''' - if cr is None: - cr = self._sprites.cr - if cr is None: - print 'sprite.draw: no Cairo context.' - return - for i, img in enumerate(self.images): - if isinstance(img, GdkPixbuf.Pixbuf): - Gdk.cairo_set_source_pixbuf(cr, img, - self.rect[0] + self._dx[i], - self.rect[1] + self._dy[i]) - cr.rectangle(self.rect[0] + self._dx[i], - self.rect[1] + self._dy[i], - self.rect[2], - self.rect[3]) - cr.fill() - else: - print 'sprite.draw: source not a pixbuf (%s)' % (type(img)) - if len(self.labels) > 0: - self.draw_label(cr) - - def hit(self, pos): - ''' Is (x, y) on top of the sprite? ''' - x, y = pos - if x < self.rect[0]: - return False - if x > self.rect[0] + self.rect[2]: - return False - if y < self.rect[1]: - return False - if y > self.rect[1] + self.rect[3]: - return False - return True - - def draw_label(self, cr): - ''' Draw the label based on its attributes ''' - my_width = self.rect[2] - self._margins[0] - self._margins[2] - if my_width < 0: - my_width = 0 - my_height = self.rect[3] - self._margins[1] - self._margins[3] - for i in range(len(self.labels)): - pl = PangoCairo.create_layout(cr) - pl.set_text(str(self.labels[i]), -1) - 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 > my_width: - if self._rescale[i]: - self._fd.set_size( - int(self._scale[i] * Pango.SCALE * my_width / w)) - pl.set_font_description(self._fd) - w = pl.get_size()[0] / Pango.SCALE - else: - j = len(self.labels[i]) - 1 - while(w > my_width and j > 0): - pl.set_text( - "…" + self.labels[i][len(self.labels[i]) - j:], -1) - 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[i] == "center": - x = int(self.rect[0] + self._margins[0] + (my_width - w) / 2) - elif self._horiz_align[i] == 'left': - x = int(self.rect[0] + self._margins[0]) - else: # right - x = int(self.rect[0] + self.rect[2] - w - self._margins[2]) - h = pl.get_size()[1] / Pango.SCALE - if self._vert_align[i] == "middle": - y = int(self.rect[1] + self._margins[1] + (my_height - h) / 2) - elif self._vert_align[i] == "top": - y = int(self.rect[1] + self._margins[1]) - else: # bottom - y = int(self.rect[1] + self.rect[3] - h - self._margins[3]) - cr.save() - cr.translate(x, y) - cr.set_source_rgb(self._color[0], self._color[1], self._color[2]) - PangoCairo.update_layout(cr, pl) - PangoCairo.show_layout(cr, pl) - cr.restore() - - 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[2] - self._margins[0] - self._margins[2] - - def label_safe_height(self): - ''' Return maximum height for a label ''' - return self.rect[3] - 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): - ''' Return the pixl at (x, y) ''' - x, y = pos - x = x - self.rect[0] - y = y - self.rect[1] - if y > self.images[i].get_height() - 1: - return(-1, -1, -1, -1) - try: - array = self.images[i].get_pixels() - if array is not None: - offset = (y * self.images[i].get_width() + x) * 4 - r, g, b, a = ord(array[offset]), ord(array[offset + 1]),\ - ord(array[offset + 2]), ord(array[offset + 3]) - return(r, g, b, a) - else: - return(-1, -1, -1, -1) - except IndexError: - print "Index Error: %d %d" % (len(array), offset) - return(-1, -1, -1, -1) diff --git a/utils.pyo b/utils.pyo Binary files differdeleted file mode 100644 index 94b1def..0000000 --- a/utils.pyo +++ /dev/null |