class Card: ''' Individual cards ''' def __init__(self, scale=1.0): ''' Create the card and store its attributes ''' self.spr = None self.index = None # Calculated index self._scale = scale def create(self, string, attributes=None, sprites=None, file_path=None): if attributes is None: if self.spr is None: self.spr = Sprite(sprites, 0, 0, svg_str_to_pixbuf(string)) else: self.spr.set_image(svg_str_to_pixbuf(string)) self.index = None else: self.shape = attributes[0] self.color = attributes[1] self.num = attributes[2] self.fill = attributes[3] self.index = self.shape * COLORS * NUMBER * FILLS + \ self.color * NUMBER * FILLS + \ self.num * FILLS + \ self.fill if self.spr is None: self.spr = Sprite(sprites, 0, 0, svg_str_to_pixbuf(string)) else: self.spr.set_image(svg_str_to_pixbuf(string, True)) if file_path is not None: self.spr.set_image(load_image(file_path, self._scale), i=1, dx=int(self._scale * CARD_WIDTH * .125), dy=int(self._scale * CARD_HEIGHT * .125)) self.spr.set_label_attributes(self._scale * 24) self.spr.set_label('') def show_card(self, layer=2000): ''' Show the card ''' if self.spr is not None: self.spr.set_layer(layer) self.spr.draw() def hide_card(self): ''' Hide a card ''' if self.spr is not None: self.spr.hide()
class Card: ''' Individual cards ''' def __init__(self, scale=1.0): ''' Create the card and store its attributes ''' self.spr = None self.index = None # Calculated index self._scale = scale def create(self, string, attributes=None, sprites=None, file_path=None): if attributes is None: if self.spr is None: self.spr = Sprite(sprites, 0, 0, svg_str_to_pixbuf(string)) else: self.spr.set_image(svg_str_to_pixbuf(string)) self.index = None else: self.shape = attributes[0] self.color = attributes[1] self.num = attributes[2] self.fill = attributes[3] self.index = self.shape * COLORS * NUMBER * FILLS + \ self.color * NUMBER * FILLS + \ self.num * FILLS + \ self.fill if self.spr is None: self.spr = Sprite(sprites, 0, 0, svg_str_to_pixbuf(string)) else: self.spr.set_image(svg_str_to_pixbuf(string)) if file_path is not None: self.spr.set_image(load_image(file_path, self._scale), i=1, dx=int(self._scale * CARD_WIDTH * .125), dy=int(self._scale * CARD_HEIGHT * .125)) self.spr.set_label_attributes(self._scale * 24) self.spr.set_label('') def show_card(self, layer=2000): ''' Show the card ''' if self.spr is not None: self.spr.set_layer(layer) self.spr.draw() def hide_card(self): ''' Hide a card ''' if self.spr is not None: self.spr.hide()
class Card: # Spade = 1,-1 # Heart = 2,-2 # Club = 3,-3 # Diamond = 4,-4 def __init__(self, game, c, i, x, y): self.north = c[0] self.east = c[1] self.south = c[2] self.west = c[3] self.orientation = 0 self.images = [] self.images.append(load_image( os.path.join(game.path, 'card%d.svg' % (i)), game.card_dim * game.scale, game.card_dim * game.scale)) for j in range(3): self.images.append(self.images[j].rotate_simple(90)) # create sprite from svg file self.spr = Sprite(game.sprites, x, y, self.images[0]) self.spr.set_label(i) def reset_image(self, game, i): while self.orientation != 0: self.rotate_ccw() def set_orientation(self, r, rotate_spr=True): while r != self.orientation: self.rotate_ccw(rotate_spr) def rotate_ccw(self, rotate_spr=True): # print "rotating card " + str(self.spr.label) tmp = self.north self.north = self.east self.east = self.south self.south = self.west self.west = tmp self.orientation += 90 if self.orientation == 360: self.orientation = 0 if rotate_spr is True: self.spr.set_shape(self.images[int(self.orientation / 90)]) def print_card(self): print "(" + str(self.north) + "," + str(self.east) + \ "," + str(self.south) + "," + str(self.west) + \ ") " + str(self.rotate) + "ccw" + \ " x:" + str(self.spr.x) + " y:" + str(self.spr.y)
class Card: # Spade = 1,-1 # Heart = 2,-2 # Club = 3,-3 # Diamond = 4,-4 def __init__(self, game, c, i, x, y): self.north = c[0] self.east = c[1] self.south = c[2] self.west = c[3] self.orientation = 0 self.images = [] self.images.append( load_image(os.path.join(game.path, 'card%d.svg' % (i)), game.card_dim * game.scale, game.card_dim * game.scale)) for j in range(3): self.images.append(self.images[j].rotate_simple(90)) # create sprite from svg file self.spr = Sprite(game.sprites, x, y, self.images[0]) self.spr.set_label(i) def reset_image(self, game, i): while self.orientation != 0: self.rotate_ccw() def set_orientation(self, r, rotate_spr=True): while r != self.orientation: self.rotate_ccw(rotate_spr) def rotate_ccw(self, rotate_spr=True): # print "rotating card " + str(self.spr.label) tmp = self.north self.north = self.east self.east = self.south self.south = self.west self.west = tmp self.orientation += 90 if self.orientation == 360: self.orientation = 0 if rotate_spr is True: self.spr.set_shape(self.images[int(self.orientation / 90)]) def print_card(self): print "(" + str(self.north) + "," + str(self.east) + \ "," + str(self.south) + "," + str(self.west) + \ ") " + str(self.rotate) + "ccw" + \ " x:" + str(self.spr.x) + " y:" + str(self.spr.y)
def _test(self, easter_egg=False): ''' Test to see if we estimated correctly ''' self._timeout = None if self._expert: delta = self.ball.width() / 6 else: delta = self.ball.width() / 3 x = self.ball.ball_x() + self.ball.width() / 2 f = int(self._fraction * self.bar.width()) self.bar.mark.move((int(f - self.bar.mark_width() / 2), int(self.bar.bar_y() + self._mark_offset(f)))) if self._challenges[self._n][2] == 0: # label the column spr = Sprite(self._sprites, 0, 0, self.blank_graphic) spr.set_label(self._label) spr.move((int(self._n * 27), 0)) spr.set_layer(-1) self._challenges[self._n][2] += 1 if x > f - delta and x < f + delta: spr = Sprite(self._sprites, 0, 0, self.smiley_graphic) self._correct += 1 GObject.idle_add(play_audio_from_file, self, self._path_to_success) else: spr = Sprite(self._sprites, 0, 0, self.frown_graphic) GObject.idle_add(play_audio_from_file, self, self._path_to_failure) spr.move((int(self._n * 27), int(self._challenges[self._n][2] * 27))) spr.set_layer(-1) # after enough correct answers, up the difficulty if self._correct == len(self._challenges) * 2: self._challenge += 1 if self._challenge < len(CHALLENGES): for challenge in CHALLENGES[self._challenge]: self._challenges.append(challenge) else: self._expert = True self.count += 1 self._dx = 0. # stop horizontal movement between bounces
def _test(self, easter_egg=False): ''' Test to see if we estimated correctly ''' self.timeout = None delta = self.ball.width() / 4 x = self.ball.ball_x() + self.ball.width() / 2 f = self.ball.width() / 2 + int(self.fraction * self.bar.width()) self.bar.mark.move( (int(f - self.bar.mark_width() / 2), self.bar.bar_y() - 2)) if self.challenges[self.n][2] == 0: # label the column spr = Sprite(self.sprites, 0, 0, self.blank_graphic) spr.set_label(self.label) spr.move((int(self.n * 25), 0)) spr.set_layer(-1) self.challenges[self.n][2] += 1 if x > f - delta and x < f + delta: if not easter_egg: spr = Sprite(self.sprites, 0, 0, self.smiley_graphic) self.correct += 1 gobject.idle_add(play_audio_from_file, self, self.path_to_success) else: if not easter_egg: spr = Sprite(self.sprites, 0, 0, self.frown_graphic) gobject.idle_add(play_audio_from_file, self, self.path_to_failure) if easter_egg: spr = Sprite(self.sprites, 0, 0, self.egg_graphic) spr.move((int(self.n * 25), int(self.challenges[self.n][2] * 25))) spr.set_layer(-1) # after enough correct answers, up the difficulty if self.correct == len(self.challenges) * 2: self.challenge += 1 if self.challenge < len(CHALLENGES): for challenge in CHALLENGES[self.challenge]: self.challenges.append(challenge) else: self.expert = True self.count += 1 self.dx = 0. # stop horizontal movement between bounces
def _test(self, easter_egg=False): ''' Test to see if we estimated correctly ''' if self._expert: delta = self.ball.width() / 6 else: delta = self.ball.width() / 3 x = self.ball.ball_x() + self.ball.width() / 2 f = int(self._fraction * self.bar.width()) self.bar.mark.move((int(f - self.bar.mark_width() / 2), int(self.bar.bar_y() + self._mark_offset(f)))) if self._challenges[self._n][2] == 0: # label the column spr = Sprite(self._sprites, 0, 0, self.blank_graphic) spr.set_label(self._label) spr.move((int(self._n * 27), 0)) spr.set_layer(-1) self._challenges[self._n][2] += 1 if x > f - delta and x < f + delta: spr = Sprite(self._sprites, 0, 0, self.smiley_graphic) self._correct += 1 aplay.play(self._path_to_success) else: spr = Sprite(self._sprites, 0, 0, self.frown_graphic) aplay.play(self._path_to_failure) spr.move((int(self._n * 27), int(self._challenges[self._n][2] * 27))) spr.set_layer(-1) # after enough correct answers, up the difficulty if self._correct == len(self._challenges) * 2: self._challenge += 1 if self._challenge < len(CHALLENGES): for challenge in CHALLENGES[self._challenge]: self._challenges.append(challenge) else: self._expert = True self.count += 1 self._dx = 0. # stop horizontal movement between bounces
class PortfolioActivity(activity.Activity): ''' Make a slideshow from starred Journal entries. ''' def __init__(self, handle): ''' Initialize the toolbars and the work surface ''' super(PortfolioActivity, self).__init__(handle) self._tmp_path = get_path(activity, 'instance') self._hw = get_hardware() self._setup_toolbars() self._setup_canvas() self._setup_workspace() self._thumbs = [] self._thumbnail_mode = False def _setup_canvas(self): ''' Create a canvas ''' self._canvas = gtk.DrawingArea() self._canvas.set_size_request(gtk.gdk.screen_width(), gtk.gdk.screen_height()) self.set_canvas(self._canvas) self._canvas.show() self.show_all() self._canvas.set_flags(gtk.CAN_FOCUS) self._canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK) self._canvas.add_events(gtk.gdk.POINTER_MOTION_MASK) self._canvas.add_events(gtk.gdk.BUTTON_RELEASE_MASK) self._canvas.add_events(gtk.gdk.KEY_PRESS_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("motion-notify-event", self._mouse_move_cb) def _setup_workspace(self): ''' Prepare to render the datastore entries. ''' self._colors = profile.get_color().to_string().split(',') # Use the lighter color for the text background if lighter_color(self._colors) == 0: tmp = self._colors[0] self._colors[0] = self._colors[1] self._colors[1] = tmp self._width = gtk.gdk.screen_width() self._height = gtk.gdk.screen_height() self._scale = gtk.gdk.screen_height() / 900. if self._hw[0:2] == 'xo': titlef = 18 descriptionf = 12 else: titlef = 36 descriptionf = 24 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._title = Sprite( self._sprites, 0, 0, svg_str_to_pixbuf( genblank(self._width, int(TITLEH * self._scale), self._colors))) self._title.set_label_attributes(int(titlef * self._scale), rescale=False) self._preview = Sprite( self._sprites, int( (self._width - int(PREVIEWW * self._scale)) / 2), int(PREVIEWY * self._scale), svg_str_to_pixbuf( genblank(int(PREVIEWW * self._scale), int(PREVIEWH * self._scale), self._colors))) self._full_screen = Sprite( self._sprites, int((self._width - int(FULLW * self._scale)) / 2), int(PREVIEWY * self._scale), svg_str_to_pixbuf( genblank(int(FULLW * self._scale), int(FULLH * self._scale), self._colors))) self._description = Sprite( self._sprites, int(DESCRIPTIONX * self._scale), int(DESCRIPTIONY * self._scale), svg_str_to_pixbuf( genblank(int(self._width - (2 * DESCRIPTIONX * self._scale)), int(DESCRIPTIONH * self._scale), self._colors))) self._description.set_label_attributes(int(descriptionf * self._scale)) self._description2 = Sprite( self._sprites, int(SHORTX * self._scale), int(SHORTY * self._scale), svg_str_to_pixbuf( genblank(int(self._width - (2 * SHORTX * self._scale)), int(SHORTH * self._scale), self._colors))) self._description2.set_label_attributes(int(descriptionf * self._scale)) self._my_canvas = Sprite( self._sprites, 0, 0, gtk.gdk.Pixmap(self._canvas.window, self._width, self._height, -1)) self._my_gc = self._my_canvas.images[0].new_gc() self._my_canvas.set_layer(BOTTOM) self._clear_screen() self._find_starred() self.i = 0 self._show_slide() self._playing = False self._rate = 10 def _setup_toolbars(self): ''' Setup the toolbars. ''' self.max_participants = 1 # no sharing 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 adjust_toolbar = gtk.Toolbar() adjust_toolbar_button = ToolbarButton( label=_('Adjust'), page=adjust_toolbar, icon_name='preferences-system') adjust_toolbar.show_all() adjust_toolbar_button.show() else: # Use pre-0.86 toolbar design primary_toolbar = gtk.Toolbar() toolbox = activity.ActivityToolbox(self) self.set_toolbox(toolbox) toolbox.add_toolbar(_('Page'), primary_toolbar) adjust_toolbar = gtk.Toolbar() toolbox.add_toolbar(_('Adjust'), adjust_toolbar) toolbox.show() toolbox.set_current_toolbar(1) self.toolbar = primary_toolbar self._prev_button = button_factory('go-previous-inactive', _('Prev slide'), self._prev_cb, self.toolbar, accelerator='<Ctrl>P') self._next_button = button_factory('go-next', _('Next slide'), self._next_cb, self.toolbar, accelerator='<Ctrl>N') separator_factory(self.toolbar) self._auto_button = button_factory('media-playlist-repeat', _('Autoplay'), self._autoplay_cb, self.toolbar) if HAVE_TOOLBOX: toolbox.toolbar.insert(adjust_toolbar_button, -1) label = label_factory(_('Adjust playback speed'), adjust_toolbar) label.show() self._unit_combo = combo_factory(UNITS, TEN, _('Adjust playback speed'), self._unit_combo_cb, adjust_toolbar) self._unit_combo.show() separator_factory(self.toolbar) self._thumb_button = button_factory('thumbs-view', _('Thumbnail view'), self._thumbs_cb, self.toolbar) button_factory('view-fullscreen', _('Fullscreen'), self.do_fullscreen_cb, self.toolbar, accelerator='<Alt>Return') separator_factory(self.toolbar) self._save_button = button_factory('save-as-html', _('Save as HTML'), self._save_as_html_cb, self.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 _expose_cb(self, win, event): ''' Have to refresh after a change in window status. ''' self._sprites.redraw_sprites() return True def _destroy_cb(self, win, event): ''' Clean up on the way out. ''' gtk.main_quit() def _find_starred(self): ''' Find all the favorites in the Journal. ''' self._dsobjects, self._nobjects = datastore.find({'keep': '1'}) return def _prev_cb(self, button=None): ''' The previous button has been clicked; goto previous slide. ''' if self.i > 0: self.i -= 1 self._show_slide() def _next_cb(self, button=None): ''' The next button has been clicked; goto next slide. ''' if self.i < self._nobjects - 1: self.i += 1 self._show_slide() def _autoplay_cb(self, button=None): ''' The autoplay button has been clicked; step through slides. ''' if self._playing: self._stop_autoplay() else: if self._thumbnail_mode: self._set_view_mode(self._current_slide) self._playing = True self._auto_button.set_icon('media-playback-pause') self._loop() def _stop_autoplay(self): ''' Stop autoplaying. ''' self._playing = False self._auto_button.set_icon('media-playlist-repeat') if hasattr(self, '_timeout_id') and self._timeout_id is not None: gobject.source_remove(self._timeout_id) def _loop(self): ''' Show a slide and then call oneself with a timeout. ''' self.i += 1 if self.i == self._nobjects: self.i = 0 self._show_slide() self._timeout_id = gobject.timeout_add(int(self._rate * 1000), self._loop) def _bump_test(self): ''' Test for accelerometer event (XO 1.75 only). ''' fh = open('/sys/devices/platform/lis3lv02d/position') string = fh.read() xyz = string[1:-2].split(',') dx = int(xyz[0]) fh.close() if dx > 250: self.i += 1 if self.i == self._nobjects: self.i = 0 self._show_slide() elif dx < -250: self.i -= 1 if self.i < 0: self.i = self._nobjects - 1 self._show_slide() elif not self._thumbnail_mode: self._bump_id = gobject.timeout_add(int(100), self._bump_test) def _save_as_html_cb(self, button=None): ''' Export an HTML version of the slideshow to the Journal. ''' self._save_button.set_icon('save-in-progress') results = save_html(self._dsobjects, profile.get_nick_name(), self._colors, self._tmp_path) html_file = os.path.join(self._tmp_path, 'tmp.html') tmp_file = open(html_file, 'w') tmp_file.write(results) tmp_file.close() dsobject = datastore.create() dsobject.metadata['title'] = profile.get_nick_name() + ' ' + \ _('Portfolio') dsobject.metadata['icon-color'] = profile.get_color().to_string() dsobject.metadata['mime_type'] = 'text/html' dsobject.set_file_path(html_file) dsobject.metadata['activity'] = 'org.laptop.WebActivity' datastore.write(dsobject) dsobject.destroy() gobject.timeout_add(250, self._save_button.set_icon, 'save-as-html') return def _clear_screen(self): ''' Clear the screen to the darker of the two XO colors. ''' self._my_gc.set_foreground(self._my_gc.get_colormap().alloc_color( self._colors[0])) self._my_canvas.images[0].draw_rectangle(self._my_gc, True, 0, 0, self._width, self._height) self._title.hide() self._full_screen.hide() self._preview.hide() self._description.hide() if hasattr(self, '_thumbs'): for thumbnail in self._thumbs: thumbnail[0].hide() self.invalt(0, 0, self._width, self._height) # Reset drag settings self._press = None self._release = None self._dragpos = [0, 0] self._total_drag = [0, 0] self.last_spr_moved = None def _show_slide(self): ''' Display a title, preview image, and decription for slide i. ''' self._clear_screen() if self._nobjects == 0: self._prev_button.set_icon('go-previous-inactive') self._next_button.set_icon('go-next-inactive') self._description.set_label( _('Do you have any items in your Journal starred?')) self._description.set_layer(MIDDLE) return if self.i == 0: self._prev_button.set_icon('go-previous-inactive') else: self._prev_button.set_icon('go-previous') if self.i == self._nobjects - 1: self._next_button.set_icon('go-next-inactive') else: self._next_button.set_icon('go-next') pixbuf = None media_object = False try: pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( self._dsobjects[self.i].file_path, int(PREVIEWW * self._scale), int(PREVIEWH * self._scale)) media_object = True except: pixbuf = get_pixbuf_from_journal(self._dsobjects[self.i], 300, 225) if pixbuf is not None: if not media_object: self._preview.images[0] = pixbuf.scale_simple( int(PREVIEWW * self._scale), int(PREVIEWH * self._scale), gtk.gdk.INTERP_TILES) self._full_screen.hide() self._preview.set_layer(MIDDLE) else: self._full_screen.images[0] = pixbuf.scale_simple( int(FULLW * self._scale), int(FULLH * self._scale), gtk.gdk.INTERP_TILES) self._full_screen.set_layer(MIDDLE) self._preview.hide() else: if self._preview is not None: self._preview.hide() self._full_screen.hide() self._title.set_label(self._dsobjects[self.i].metadata['title']) self._title.set_layer(MIDDLE) if 'description' in self._dsobjects[self.i].metadata: if media_object: self._description2.set_label( self._dsobjects[self.i].metadata['description']) self._description2.set_layer(MIDDLE) self._description.set_label('') self._description.hide() else: self._description.set_label( self._dsobjects[self.i].metadata['description']) self._description.set_layer(MIDDLE) self._description2.set_label('') self._description2.hide() else: self._description.set_label('') self._description.hide() self._description2.set_label('') self._description2.hide() if self._hw == XO175: self._bump_id = gobject.timeout_add(int(500), self._bump_test) def _thumbs_cb(self, button=None): ''' Toggle between thumbnail view and slideshow view. ''' if self._thumbnail_mode: self._set_view_mode(self._current_slide) self._show_slide() else: self._stop_autoplay() self._current_slide = self.i self._thumbnail_mode = True self._clear_screen() self._prev_button.set_icon('go-previous-inactive') self._next_button.set_icon('go-next-inactive') self._thumb_button.set_icon('slide-view') self._thumb_button.set_tooltip(_('Slide view')) n = int(sqrt(self._nobjects) + 0.5) w = int(self._width / n) h = int(w * 0.75) # maintain 4:3 aspect ratio x_off = int((self._width - n * w) / 2) x = x_off y = 0 for i in range(self._nobjects): self.i = i self._show_thumb(x, y, w, h) x += w if x + w > self._width: x = x_off y += h self.i = 0 # Reset position in slideshow to the beginning return False def _show_thumb(self, x, y, w, h): ''' Display a preview image and title as a thumbnail. ''' if len(self._thumbs) < self.i + 1: # Create a Sprite for this thumbnail pixbuf = None try: pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( self._dsobjects[self.i].file_path, int(w), int(h)) except: pixbuf = get_pixbuf_from_journal(self._dsobjects[self.i], int(w), int(h)) pixbuf_thumb = pixbuf.scale_simple(int(w), int(h), gtk.gdk.INTERP_TILES) self._thumbs.append( [Sprite(self._sprites, x, y, pixbuf_thumb), x, y, self.i]) self._thumbs[-1][0].set_label(str(self.i + 1)) self._thumbs[self.i][0].set_layer(TOP) def do_fullscreen_cb(self, button): ''' Hide the Sugar toolbars. ''' self.fullscreen() 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) def _spr_to_thumb(self, spr): ''' Find which entry in the thumbnails table matches spr. ''' for i, thumb in enumerate(self._thumbs): if spr == thumb[0]: return i return -1 def _spr_is_thumbnail(self, spr): ''' Does spr match an entry in the thumbnails table? ''' if self._spr_to_thumb(spr) == -1: return False else: return True def _button_press_cb(self, win, event): ''' The mouse button was pressed. Is it on a thumbnail sprite? ''' win.grab_focus() x, y = map(int, event.get_coords()) self._dragpos = [x, y] self._total_drag = [0, 0] spr = self._sprites.find_sprite((x, y)) self._press = None self._release = None # Are we clicking on a thumbnail? if not self._spr_is_thumbnail(spr): return False _logger.debug('found a thumbnail') self.last_spr_moved = spr self._press = spr self._press.set_layer(DRAG) return False def _mouse_move_cb(self, win, event): """ Drag a thumbnail with the mouse. """ spr = self._press if spr is None: self._dragpos = [0, 0] return False win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self._dragpos[0] dy = y - self._dragpos[1] spr.move_relative([dx, dy]) self._dragpos = [x, y] self._total_drag[0] += dx self._total_drag[1] += dy return False def _button_release_cb(self, win, event): ''' Button event is used to swap slides or goto next slide. ''' win.grab_focus() self._dragpos = [0, 0] x, y = map(int, event.get_coords()) if self._thumbnail_mode: # Drop the dragged thumbnail below the other thumbnails so # that you can find the thumbnail beneath it. self._press.set_layer(UNDRAG) i = self._spr_to_thumb(self._press) spr = self._sprites.find_sprite((x, y)) if self._spr_is_thumbnail(spr): self._release = spr # If we found a thumbnail and it is not the one we # dragged, swap their positions. if not self._press == self._release: j = self._spr_to_thumb(self._release) self._thumbs[i][0] = self._release self._thumbs[j][0] = self._press tmp = self._dsobjects[i] self._dsobjects[i] = self._dsobjects[j] self._dsobjects[j] = tmp self._thumbs[j][0].move( (self._thumbs[j][1], self._thumbs[j][2])) self._thumbs[i][0].move((self._thumbs[i][1], self._thumbs[i][2])) self._press.set_layer(TOP) self._press = None self._release = None else: self._next_cb() return False def _set_view_mode(self, i): ''' Switch to slide-viewing mode. ''' self._thumbnail_mode = False self.i = i self._thumb_button.set_icon('thumbs-view') self._thumb_button.set_tooltip(_('Thumbnail view')) def _unit_combo_cb(self, arg=None): ''' Read value of predefined conversion factors from combo box ''' if hasattr(self, '_unit_combo'): active = self._unit_combo.get_active() if active in UNIT_DICTIONARY: self._rate = UNIT_DICTIONARY[active][1]
class Yupana(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = ['#FFFFFF'] self._colors.append(colors[0]) self._colors.append(colors[1]) self._colors.append('#000000') self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._width / (20 * DOT_SIZE * 1.1) self._dot_size = int(DOT_SIZE * self._scale) self._space = int(self._dot_size / 5.) self.we_are_sharing = False self._sum = 0 self._mode = 'ten' self.custom = [1, 1, 1, 1, 10] # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) Sprite(self._sprites, 0, 0, self._box(self._width, self._height, color=colors[1])) self._number_box = Sprite( self._sprites, 0, 0, self._box(self._width, 2 * self._dot_size, color=colors[1])) self._number_box.set_label_attributes(48) self._dots = [] for p in range(SIX): y = self._height - self._space Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int(p * self._width / 6) + self._space y -= self._dot_size for d in range(3): # bottom of fives row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space x = int((p * self._width / 6.) + self._dot_size / 2.) + self._space y -= self._dot_size + self._space for d in range(2): # top of fives row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int((p * self._width / 6.) + self._dot_size / 2.) + self._space y -= self._dot_size for d in range(2): # bottom of threes row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space x = int((p * self._width / 6.) + self._dot_size) + self._space y -= self._dot_size + self._space for d in range(1): # top of threes row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int((p * self._width / 6.) + self._dot_size / 2.) + self._space y -= self._dot_size for d in range(2): # twos row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int((p * self._width / 6.) + self._dot_size) + self._space y -= self._dot_size for d in range(1): # ones row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) for p in range(SIX - 1): x = int((p + 1) * self._width / 6) Sprite(self._sprites, x - 1, y, self._line(vertical=True)) # and initialize a few variables we'll need. self._all_clear() def _all_clear(self): ''' Things to reinitialize when starting up a new yupana. ''' self._sum = 0 for dot in self._dots: if dot.type > 0: dot.type = 0 dot.set_shape(self._new_dot(self._colors[0])) dot.set_label('') self.set_label(str(self._sum)) if self.we_are_sharing: _logger.debug('sending new label to the share') self._parent.send_label(str(self._sum)) def _initiating(self): return self._activity._collab.props.leader def new_yupana(self, mode=None): ''' Create a new yupana. ''' self._all_clear() if mode is not None: self._mode = mode o = (SIX - 1) * (TEN + 1) # only label units if mode == 'ten': for i in range(TEN + 1): self._dots[o + i].set_label('1') self._dots[o - 1].set_label('10') elif mode == 'twenty': for i in range(TEN + 1): if i in [7, 10]: self._dots[o + i].set_label('1') else: self._dots[o + i].set_label('2') self._dots[o - 1].set_label('20') elif mode == 'factor': for i in range(TEN + 1): if i in [10]: self._dots[o + i].set_label('1') elif i in [8, 9]: self._dots[o + i].set_label('2') elif i in [5, 6, 7]: self._dots[o + i].set_label('3') else: self._dots[o + i].set_label('5') self._dots[o - 1].set_label('10') elif mode == 'fibonacci': for i in range(TEN + 1): if i in [10]: self._dots[o + i].set_label('1') elif i in [8, 9]: self._dots[o + i].set_label('2') elif i in [5, 6, 7]: self._dots[o + i].set_label('5') else: self._dots[o + i].set_label('20') self._dots[o - 1].set_label('60') else: # custom for i in range(TEN + 1): if i in [10]: self._dots[o + i].set_label(str(self.custom[0])) elif i in [8, 9]: self._dots[o + i].set_label(str(self.custom[1])) elif i in [5, 6, 7]: self._dots[o + i].set_label(str(self.custom[2])) else: self._dots[o + i].set_label(str(self.custom[3])) self._dots[o - 1].set_label(str(self.custom[4])) if self.we_are_sharing: _logger.debug('sending a new yupana') self._parent.send_new_yupana() def restore_yupana(self, mode, dot_list): ''' Restore a yumpana 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(self._colors[self._dots[i].type])) if self._dots[i].type == 1: self._sum += self._calc_bead_value(i) self._mode = mode self.set_label(str(self._sum)) if self.we_are_sharing: _logger.debug('sending new label to the share') self._parent.send_label(str(self._sum)) def save_yupana(self): ''' Return dot list and orientation for saving to Journal or sharing ''' dot_list = [] for dot in self._dots: dot_list.append(dot.type) return [self._mode, dot_list] def set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._number_box.set_label(string) def get_label(self): return self._number_box.get_label() def _button_press_cb(self, win, event): win.grab_focus() x, y = list(map(int, event.get_coords())) spr = self._sprites.find_sprite((x, y)) if spr == None: return if spr.type is not None: spr.type += 1 spr.type %= 2 spr.set_shape(self._new_dot(self._colors[spr.type])) if self.we_are_sharing: _logger.debug('sending a click to the share') self._parent.send_dot_click(self._dots.index(spr), spr.type) if spr.type == 1: self._sum += self._calc_bead_value(self._dots.index(spr)) else: self._sum -= self._calc_bead_value(self._dots.index(spr)) self.set_label(str(self._sum)) if self.we_are_sharing: _logger.debug('sending new label to the share') self._parent.send_label(str(self._sum)) return True def _calc_bead_value(self, i): ''' Calculate a bead value based on the index and the mode ''' e = 5 - i / (TEN + 1) m = i % 11 if self._mode == 'ten': return 10**e elif self._mode == 'twenty': if m in [7, 10]: return 20**e else: return (20**e) * 2 elif self._mode == 'factor': if m in [10]: return 10**e elif m in [8, 9]: return (10**e) * 2 elif m in [5, 6, 7]: return (10**e) * 3 else: return (10**e) * 5 elif self._mode == 'fibonacci': if m in [10]: return 60**e elif m in [8, 9]: return (60**e) * 2 elif m in [5, 6, 7]: return (60**e) * 5 else: return (60**e) * 20 else: # custom if m in [10]: return (self.custom[4]**e) * self.custom[0] elif m in [8, 9]: return (self.custom[4]**e) * self.custom[1] elif m in [5, 6, 7]: return (self.custom[4]**e) * self.custom[2] else: return (self.custom[4]**e) * self.custom[3] def remote_button_press(self, dot, color): ''' Receive a button press from a sharer ''' self._dots[dot].type = color self._dots[dot].set_shape(self._new_dot(self._colors[color])) 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] * TEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % TEN, int(dot / TEN)] def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) 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 _new_dot(self, color): ''' generate a dot of a color color ''' def darken(color): ''' return a darker color than color ''' gdk_fill_color = Gdk.color_parse(self._fill) gdk_fill_dark_color = Gdk.Color(int(gdk_fill_color.red * 0.5), int(gdk_fill_color.green * 0.5), int(gdk_fill_color.blue * 0.5)).to_string() return str(gdk_fill_dark_color) self._dot_cache = {} if not color in self._dot_cache: self._stroke = color self._fill = color self._fill_dark = darken(color) self._svg_width = self._dot_size self._svg_height = self._dot_size if color in ['#FFFFFF', '#000000']: pixbuf = svg_str_to_pixbuf( self._header() + \ self._circle(self._dot_size / 2., self._dot_size / 2., self._dot_size / 2.) + \ self._footer()) else: pixbuf = svg_str_to_pixbuf( self._header() + \ self._def(self._dot_size) + \ self._gradient(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) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() self._dot_cache[color] = surface return self._dot_cache[color] def _line(self, vertical=True): ''' Generate a center line ''' if vertical: self._svg_width = 3 self._svg_height = self._dot_size * 10 + self._space * 2 return svg_str_to_pixbuf( self._header() + \ self._rect(3, self._dot_size * 10 + self._space * 2, 0, 0) + \ self._footer()) else: self._svg_width = self._width self._svg_height = 3 return svg_str_to_pixbuf( self._header() + \ self._rect(self._width, 3, 0, 0) + \ self._footer()) def _box(self, w, h, color='white'): ''' Generate a box ''' self._svg_width = w self._svg_height = h return svg_str_to_pixbuf( self._header() + \ self._rect(self._svg_width, self._svg_height, 0, 0, color=color) + \ self._footer()) 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, color='black'): 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) if color == 'black': svg_string += 'style="fill:#000000;stroke:#000000;"/>\n' elif color == 'white': svg_string += 'style="fill:#ffffff;stroke:#ffffff;"/>\n' else: svg_string += 'style="fill:%s;stroke:%s;"/>\n' % (color, color) return svg_string def _circle(self, r, cx, cy): scale = (DOT_SIZE * self._scale) / 55. return '\ <g transform="matrix(%f,0,0,%f,0,0)">\ <path\ d="m 35.798426,4.2187227 c -2.210658,0.9528967 -4.993612,-0.9110169 -7.221856,0 C 23.805784,6.1692574 20.658687,10.945585 17.543179,15.051507 13.020442,21.012013 7.910957,27.325787 6.7103942,34.711004 6.0558895,38.737163 6.434461,43.510925 8.917073,46.747431 c 3.604523,4.699107 15.24614,7.62307 16.048569,7.62307 0.802429,0 8.366957,0.46766 12.036427,-1.203642 2.841316,-1.294111 5.173945,-3.766846 6.820641,-6.419428 2.543728,-4.097563 3.563068,-9.062928 4.21275,-13.841891 C 49.107723,25.018147 48.401726,15.967648 47.433639,9.0332932 47.09109,6.5796321 43.508442,7.2266282 42.329009,5.7211058 41.256823,4.3524824 42.197481,1.860825 40.813604,0.80840168 40.384481,0.48205899 39.716131,0.42556727 39.208747,0.60779459 37.650593,1.1674066 37.318797,3.5633724 35.798426,4.2187227 z"\ style="fill:#be9e00;fill-opacity:1;stroke:%s;stroke-width:3.0" />\ </g>' % (scale, scale, self._colors[1]) def _gradient(self, r, cx, cy): scale = (DOT_SIZE * self._scale) / 55. return '\ <defs>\ <linearGradient\ id="linearGradient3769">\ <stop\ id="stop3771"\ style="stop-color:#ffff00;stop-opacity:1"\ offset="0" />\ <stop\ id="stop3773"\ style="stop-color:#ffff00;stop-opacity:0"\ offset="1" />\ </linearGradient>\ <linearGradient\ x1="10.761448"\ y1="41.003559"\ x2="56.70686"\ y2="41.003559"\ id="linearGradient2999"\ xlink:href="#linearGradient3769"\ gradientUnits="userSpaceOnUse"\ gradientTransform="matrix(0.93094239,0,0,0.93094239,-3.9217825,-2.4013121)" />\ </defs>\ <g transform="matrix(%f,0,0,%f,0,0)">\ <path\ d="m 35.798426,4.2187227 c -2.210658,0.9528967 -4.993612,-0.9110169 -7.221856,0 C 23.805784,6.1692574 20.658687,10.945585 17.543179,15.051507 13.020442,21.012013 7.910957,27.325787 6.7103942,34.711004 6.0558895,38.737163 6.434461,43.510925 8.917073,46.747431 c 3.604523,4.699107 15.24614,7.62307 16.048569,7.62307 0.802429,0 8.366957,0.46766 12.036427,-1.203642 2.841316,-1.294111 5.173945,-3.766846 6.820641,-6.419428 2.543728,-4.097563 3.563068,-9.062928 4.21275,-13.841891 C 49.107723,25.018147 48.401726,15.967648 47.433639,9.0332932 47.09109,6.5796321 43.508442,7.2266282 42.329009,5.7211058 41.256823,4.3524824 42.197481,1.860825 40.813604,0.80840168 40.384481,0.48205899 39.716131,0.42556727 39.208747,0.60779459 37.650593,1.1674066 37.318797,3.5633724 35.798426,4.2187227 z"\ style="fill:#fffec2;fill-opacity:1;stroke:#878600;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />\ <path\ d="m 15.11608,18.808876 c 1.271657,-1.444003 4.153991,-3.145785 5.495465,-1.7664 2.950062,3.033434 -6.07961,8.17155 -4.219732,11.972265 0.545606,1.114961 2.322391,1.452799 3.532799,1.177599 5.458966,-1.241154 6.490591,-12.132334 12.070397,-11.677864 1.584527,0.129058 2.526156,2.269906 2.845867,3.827199 0.453143,2.207236 -1.962667,6.182399 -1.570133,6.574932 0.392533,0.392533 2.371401,0.909584 3.140266,0.196266 1.91857,-1.779962 -0.490667,-7.752531 0.09813,-7.850664 0.5888,-0.09813 4.421663,2.851694 5.789865,5.004799 0.583188,0.917747 -0.188581,2.956817 0.8832,3.140266 2.128963,0.364398 1.601562,-5.672021 3.729066,-5.299199 1.836829,0.321884 1.450925,3.532631 1.471999,5.397332 0.06743,5.965698 -0.565586,12.731224 -4.317865,17.369596 -3.846028,4.75426 -10.320976,8.31978 -16.388263,7.556266 C 22.030921,53.720741 16.615679,52.58734 11.485147,49.131043 7.9833717,46.771994 6.8028191,42.063042 6.5784815,37.846738 6.3607378,33.754359 8.3381535,29.765466 10.111281,26.070741 c 1.271951,-2.650408 2.940517,-4.917813 5.004799,-7.261865 z"\ style="fill:url(#linearGradient2999);fill-opacity:1;stroke:none" />\ <path\ d="m 32.382709,4.7758124 c -0.123616,1.0811396 1.753928,2.8458658 2.728329,2.9439992 0.974405,0.098134 6.718874,0.7298319 9.159392,-0.1962668 0.820281,-0.3112699 0.968884,-0.9547989 0.974407,-1.4719993 0.02053,-1.9240971 0.03247,-4.7715376 -3.507853,-5.49546551 C 39.556079,0.11012647 37.217081,1.4131653 35.500801,2.2243463 34.054814,2.9077752 32.496703,3.7788369 32.382709,4.7758124 z"\ style="fill:#b69556;fill-opacity:1;stroke:#b69556;stroke-width:1.31189477px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></g>' % ( scale, scale) def _def(self, r): return ' <defs>\ <linearGradient\ id="linearGradient3755">\ <stop\ id="stop3757"\ style="stop-color:%s;stop-opacity:1"\ offset="0" />\ <stop\ id="stop3759"\ style="stop-color:%s;stop-opacity:1"\ offset="1" />\ </linearGradient>\ <radialGradient\ cx="0"\ cy="0"\ r="%f"\ fx="%f"\ fy="%f"\ id="radialGradient3761"\ xlink:href="#linearGradient3755"\ gradientUnits="userSpaceOnUse" />\ </defs>\ ' % (self._fill, self._fill_dark, r, r / 3, r / 3) def _footer(self): return '</svg>\n'
class Game(): def __init__(self, canvas, parent=None, path=None): self._canvas = canvas self._parent = parent self._parent.show_all() self._path = path self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() self._scale = self._width / 1200. self._target = 0 self._tries = 0 self.level = 0 self._picture_cards = [] self._small_picture_cards = [] self.food_cards = [] self._group_cards = [] self._quantity_cards = [] self._balance_cards = [] self._last_twenty = [] self._background = None # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._background = Sprite( self._sprites, 0, 0, GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images','background.png'), self._width, self._height)) self._background.set_layer(0) self._background.type = None self._background.hide() self.pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'word-box.png'), int(350 * self._scale), int(100 * self._scale)) for i in range(len(FOOD_DATA) / 4): FOOD.append([FOOD_DATA[i * 4 + NAME], FOOD_DATA[i * 4 + CALS], FOOD_DATA[i * 4 + GROUP], FOOD_DATA[i * 4 + IMAGE]]) self.food_cards.append(None) self._picture_cards.append(None) for j in range(6): self._small_picture_cards.append(None) self.allocate_food(0) x = 10 dx, dy = self.food_cards[0].get_dimensions() y = 10 for i in range(len(MYPLATE)): self.word_card_append(self._group_cards, self.pixbuf) self._group_cards[-1].type = i self._group_cards[-1].set_label(MYPLATE[i][0]) self._group_cards[-1].move((x, y)) y += int(dy * 1.25) y = 10 for i in range(len(QUANTITIES)): self.word_card_append(self._quantity_cards, self.pixbuf) self._quantity_cards[-1].type = i self._quantity_cards[-1].set_label(QUANTITIES[i]) self._quantity_cards[-1].move((x, y)) y += int(dy * 1.25) y = 10 for i in range(len(BALANCE)): self.word_card_append(self._balance_cards, self.pixbuf) self._balance_cards[-1].type = i self._balance_cards[-1].set_label(BALANCE[i]) self._balance_cards[-1].move((x, y)) y += int(dy * 1.25) self._smile = Sprite(self._sprites, int(self._width / 4), int(self._height / 4), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'correct.png'), int(self._width / 2), int(self._height / 2))) self._smile.set_label_attributes(36) self._smile.set_margins(10, 0, 10, 0) self._frown = Sprite(self._sprites, int(self._width / 4), int(self._height / 4), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'wrong.png'), int(self._width / 2), int(self._height / 2))) self._frown.set_label_attributes(36) self._frown.set_margins(10, 0, 10, 0) self.build_food_groups() self._all_clear() def allocate_food(self, i): self.picture_append(os.path.join(self._path, 'images', FOOD_DATA[i * 4 + IMAGE]), i) self.small_picture_append(os.path.join(self._path, 'images', FOOD_DATA[i * 4 + IMAGE]), i) self.word_card_append(self.food_cards, self.pixbuf, i) self.food_cards[i].type = i self.food_cards[i].set_label(FOOD_DATA[i * 4 + NAME]) def word_card_append(self, card_list, pixbuf, i=-1): if i == -1: card_list.append(Sprite(self._sprites, 10, 10, pixbuf)) else: card_list[i] = Sprite(self._sprites, 10, 10, pixbuf) card_list[i].set_label_attributes(36) card_list[i].set_margins(10, 0, 10, 0) card_list[i].hide() def picture_append(self, path, i=-1): spr = Sprite( self._sprites, int(self._width / 2.), int(self._height / 4.), GdkPixbuf.Pixbuf.new_from_file_at_size( path, int(self._width / 3.), int(9 * self._width / 12.))) if i == -1: self._picture_cards.append(spr) else: self._picture_cards[i] = spr self._picture_cards[i].type = 'picture' self._picture_cards[i].hide() def small_picture_append(self, path, i=-1): x = int(self._width / 3.) y = int(self._height / 6.) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( path, int(self._width / 6.), int(3 * self._width / 8.)) for j in range(6): # up to 6 of each card if i == -1: self._small_picture_cards.append(Sprite( self._sprites, x, y, pixbuf)) self._small_picture_cards[-1].type = 'picture' self._small_picture_cards[-1].hide() else: self._small_picture_cards[i * 6 + j] = Sprite( self._sprites, x, y, pixbuf) self._small_picture_cards[i * 6 + j].type = 'picture' self._small_picture_cards[i * 6 + j].hide() x += int(self._width / 6.) if j == 2: x = int(self._width / 3.) y += int(3 * self._width / 16.) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' for p in self._picture_cards: if p is not None: p.hide() for p in self._small_picture_cards: if p is not None: p.hide() for i, w in enumerate(self.food_cards): if w is not None: w.set_label_color('black') w.set_label(FOOD[i][NAME]) w.hide() for i, w in enumerate(self._group_cards): w.set_label_color('black') w.set_label(MYPLATE[i][0]) w.hide() for i, w in enumerate(self._quantity_cards): w.set_label_color('black') w.set_label(QUANTITIES[i]) w.hide() for i, w in enumerate(self._balance_cards): w.set_label_color('black') w.set_label(BALANCE[i]) w.hide() self._smile.hide() self._frown.hide() self._background.set_layer(1) def build_food_groups(self): self._my_plate = [[], [], [], []] for i, food in enumerate(FOOD): self._my_plate[MYPLATE[food[GROUP]][QUANT]].append(i) def new_game(self): ''' Start a new game. ''' games = {0: self._name_that_food, 1: self._name_that_food_group, 2: self._compare_calories, 3: self._how_much_to_eat, 4: self._balanced_meal} self._all_clear() games[self.level]() self._frown.set_label('') self._smile.set_label('') self._tries = 0 def _name_that_food(self): ''' Choose food cards and one matching food picture ''' x = 10 y = 10 dx, dy = self.food_cards[0].get_dimensions() # Select some cards word_list = [] for i in range(NCARDS): j = int(uniform(0, len(FOOD))) while j in word_list: j = int(uniform(0, len(FOOD))) word_list.append(j) # Show the word cards from the list for i in word_list: if self.food_cards[i] is None: self.allocate_food(i) self.food_cards[i].set_layer(100) self.food_cards[i].move((x, y)) y += int(dy * 1.25) # Choose a random food image from the list and show it. self._target = self.food_cards[ word_list[int(uniform(0, NCARDS))]].type while self._target in self._last_twenty: self._target = self.food_cards[ word_list[int(uniform(0, NCARDS))]].type self._last_twenty.append(self._target) if len(self._last_twenty) > 20: self._last_twenty.remove(self._last_twenty[0]) self._picture_cards[self._target].set_layer(100) def _name_that_food_group(self): ''' Show group cards and one food picture ''' for i in range(len(MYPLATE)): self._group_cards[i].set_layer(100) # Choose a random food image and show it. self._target = int(uniform(0, len(FOOD))) if self.food_cards[self._target] is None: self.allocate_food(self._target) self._picture_cards[self._target].set_layer(100) def _compare_calories(self): ''' Choose food cards and compare the calories ''' x = 10 y = 10 dx, dy = self.food_cards[0].get_dimensions() # Select some cards word_list = [] for i in range(6): j = int(uniform(0, len(FOOD))) while j in word_list: j = int(uniform(0, len(FOOD))) word_list.append(j) if self.food_cards[j] is None: self.allocate_food(j) # Show the word cards from the list for i in word_list: self.food_cards[i].set_layer(100) self.food_cards[i].move((x, y)) y += int(dy * 1.25) # Show food images self._target = word_list[0] for i in range(5): if FOOD[word_list[i + 1]][CALS] > FOOD[self._target][CALS]: self._target = word_list[i + 1] self._small_picture_cards[word_list[0] * 6].set_layer(100) self._small_picture_cards[word_list[1] * 6 + 1].set_layer(100) self._small_picture_cards[word_list[2] * 6 + 2].set_layer(100) self._small_picture_cards[word_list[3] * 6 + 3].set_layer(100) self._small_picture_cards[word_list[4] * 6 + 4].set_layer(100) self._small_picture_cards[word_list[5] * 6 + 5].set_layer(100) def _how_much_to_eat(self): ''' Show quantity cards and one food picture ''' for i in range(len(QUANTITIES)): self._quantity_cards[i].set_layer(100) # Choose a random image from the list and show it. self._target = int(uniform(0, len(FOOD))) if self.food_cards[self._target] is None: self.allocate_food(self._target) self._picture_cards[self._target].set_layer(100) def _balanced_meal(self): ''' A well-balanced meal ''' for i in range(2): self._balance_cards[i].set_layer(100) # Determine how many foods from each group n = [0, 0, 0, 0] n[0] = int(uniform(0, 2.5)) n[1] = int(uniform(0, 3 - n[0])) n[2] = 3 - n[0] - n[1] n[3] = 6 - n[0] - n[1] - n[2] # Fill a plate with foods from different groups meal = [] for i in range(n[0]): # Sweets j = int(uniform(0, len(self._my_plate[0]))) meal.append(self._my_plate[0][j]) for i in range(n[1]): # Dairy j = int(uniform(0, len(self._my_plate[1]))) meal.append(self._my_plate[1][j]) for i in range(n[2]): # Protein and Fruits j = int(uniform(0, len(self._my_plate[2]))) meal.append(self._my_plate[2][j]) for i in range(n[3]): # Veggies and Grains j = int(uniform(0, len(self._my_plate[3]))) meal.append(self._my_plate[3][j]) if n[0] < 2 and n[1] < 2 and n[2] < n[3]: self._target = 0 # Balanced meal else: self._target = 1 for i in range(6): if self.food_cards[meal[i]] is None: self.allocate_food(meal[i]) # Randomly position small cards self._small_picture_cards[meal[3] * 6].set_layer(100) self._small_picture_cards[meal[4] * 6 + 1].set_layer(100) self._small_picture_cards[meal[1] * 6 + 2].set_layer(100) self._small_picture_cards[meal[2] * 6 + 3].set_layer(100) self._small_picture_cards[meal[5] * 6 + 4].set_layer(100) self._small_picture_cards[meal[0] * 6 + 5].set_layer(100) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) spr = self._sprites.find_sprite((x, y)) if spr == None: return # We only care about clicks on word cards if type(spr.type) != int: return # Which card was clicked? Set its label to red. spr.set_label_color('red') label = spr.labels[0] spr.set_label(label) if self.level == 0: if spr.type == self._target: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self.food_cards[self._target].set_label_color('blue') label = self.food_cards[self._target].labels[0] self.food_cards[self._target].set_label(label) elif self.level == 1: i = FOOD[self._target][GROUP] if spr.type == i: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self._group_cards[i].set_label_color('blue') label = self._group_cards[i].labels[0] self._group_cards[i].set_label(label) elif self.level == 2: if spr.type == self._target: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self.food_cards[self._target].set_label_color('blue') label = self.food_cards[self._target].labels[0] self.food_cards[self._target].set_label(label) elif self.level == 3: i = MYPLATE[FOOD[self._target][GROUP]][QUANT] if spr.type == i: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self._quantity_cards[i].set_label_color('blue') label = self._quantity_cards[i].labels[0] self._quantity_cards[i].set_label(label) elif self.level == 4: if self._target == spr.type: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self._balance_cards[self._target].set_label_color('blue') label = self._balance_cards[self._target].labels[0] self._balance_cards[self._target].set_label(label) else: _logger.debug('unknown play level %d' % (self.level)) # Play again if self._tries == 3: GObject.timeout_add(2000, self.new_game) else: GObject.timeout_add(1000, self._reset_game) return True def _reset_game(self): self._frown.hide() if self.level in [0, 2]: for i, w in enumerate(self.food_cards): w.set_label_color('black') w.set_label(FOOD[i][NAME]) elif self.level == 1: for i, w in enumerate(self._group_cards): w.set_label_color('black') w.set_label(MYPLATE[i][0]) elif self.level == 3: for i, w in enumerate(self._quantity_cards): w.set_label_color('black') w.set_label(QUANTITIES[i]) elif self.level == 4: for i, w in enumerate(self._balance_cards): w.set_label_color('black') w.set_label(BALANCE[i]) def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) 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()
class Game(): def __init__(self, canvas, parent=None, path=None): self._canvas = canvas self._parent = parent self._parent.show_all() self._path = path self._canvas.connect("draw", self.__draw_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self._canvas.connect('key-press-event', self._keypress_cb) self._canvas.set_can_focus(True) self._canvas.grab_focus() self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() self._scale_x = self._width / 1200.0 self._scale_y = self._height / 900.0 self._first_time = True self._loco_pos = (0, 0) self._loco_dim = (0, 0) self._loco_quadrant = 3 self._drag_pos = [0, 0] self._counter = 0 self._correct = 0 self._timeout_id = None self._pause = 200 self._press = None self._clicked = False self._dead_key = None self._waiting_for_delete = False self._waiting_for_enter = False self._seconds = 0 self._timer_id = None self.level = 0 self.score = 0 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._BG = [ 'background0.jpg', 'background0.jpg', 'background0.jpg', 'background1.jpg', 'background2.jpg', 'background2.jpg', 'background2.jpg' ] self._backgrounds = [] for bg in self._BG: pixbuf = GdkPixbuf.Pixbuf.new_from_file( os.path.join(self._path, 'images', bg)) pixbuf = pixbuf.scale_simple(self._width, self._height, GdkPixbuf.InterpType.BILINEAR) self._backgrounds.append(Sprite(self._sprites, 0, 0, pixbuf)) self._backgrounds[-1].type = 'background' self._backgrounds[-1].hide() self._panel = Sprite( self._sprites, int(400 * self._scale_x), int(400 * self._scale_y), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'ventana.png'), int(720 * self._scale_x), int(370 * self._scale_y))) self._panel.type = 'panel' self._panel.set_label(LABELS[0]) self._panel.set_label_attributes(20) self._panel.hide() self._LOCOS = glob.glob(os.path.join(self._path, 'images', 'loco*.png')) self._loco_cards = [] for loco in self._LOCOS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._loco_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._loco_cards[-1].type = 'loco' self._loco_dim = (int(150 * self._scale_x), int(208 * self._scale_y)) self._MEN = glob.glob(os.path.join(self._path, 'images', 'man*.png')) self._man_cards = [] for loco in self._MEN: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._man_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._man_cards[-1].type = 'loco' self._TAUNTS = glob.glob( os.path.join(self._path, 'images', 'taunt*.png')) self._taunt_cards = [] for loco in self._TAUNTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._taunt_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._taunt_cards[-1].type = 'loco' self._GHOSTS = glob.glob( os.path.join(self._path, 'images', 'ghost*.png')) self._ghost_cards = [] for loco in self._GHOSTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._ghost_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._ghost_cards[-1].type = 'loco' self._sticky_cards = [] self._loco_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._LOCOS[0], int(150 * self._scale_x), int(208 * self._scale_y)) self._man_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._MEN[0], int(150 * self._scale_x), int(208 * self._scale_y)) self._ghost_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._GHOSTS[0], int(150 * self._scale_x), int(208 * self._scale_y)) for i in range(len(MSGS[1])): # Check re i18n self._sticky_cards.append( Sprite(self._sprites, 0, 0, self._loco_pixbuf)) self._sticky_cards[-1].type = 'loco' self._sticky_cards[-1].set_label_attributes(24, vert_align='bottom') self._all_clear() def _time_increment(self): ''' Track seconds since start_time. ''' self._seconds = int(GLib.get_current_time() - self._start_time) self.timer_id = GLib.timeout_add(1000, self._time_increment) def _timer_reset(self): ''' Reset the timer for each level ''' self._start_time = GLib.get_current_time() if self._timer_id is not None: GLib.source_remove(self._timer_id) self._timer_id = None self.score += self._seconds self._time_increment() def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' for p in self._loco_cards: p.hide() for p in self._man_cards: p.hide() for p in self._taunt_cards: p.hide() for p in self._ghost_cards: p.hide() for p in self._sticky_cards: p.set_shape(self._loco_pixbuf) p.set_label('') p.set_label_color('white') p.hide() self._backgrounds[self.level].set_layer(BG_LAYER) def _show_time(self): self.level = 0 self._all_clear() x = int(self._width / 4.) y = int(self._height / 8.) for i in range(len(str(self.score))): self._sticky_cards[i].move((x, y)) self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(str(self.score)[i]) x += int(self._loco_dim[0] / 2.) self.score = 0 self._parent.fullscreen() aplay.play(os.path.join(self._path, 'sounds', 'sonar.ogg')) GLib.timeout_add(5000, self.new_game, True) def new_game(self, first_time): ''' Start a new game at the current level. ''' self._first_time = first_time self._clicked = False # It may be time to advance to the next level. if (self.level == 6 and self._counter == len(MSGS)) or \ self._counter > 4: self._first_time = True self.level += 1 self._counter = 0 self._correct = 0 self._pause = 200 if self.level == len(self._backgrounds): self._show_time() return self._all_clear() if self._first_time: # Every game starts by putting up a panel with instructions # The panel disappears on mouse movement self._panel.set_label(LABELS[self.level]) self._panel.set_layer(PANEL_LAYER) aplay.play(os.path.join(self._path, 'sounds', 'drip.ogg')) self._timer_reset() if self.level == 0: # Choose a random location for the Loco self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) aplay.play(os.path.join(self._path, 'sounds', 'bark.ogg')) self._loco_cards[0].move((x, y)) self._loco_pos = (x, y) elif self.level == 1: aplay.play(os.path.join(self._path, 'sounds', 'glass.ogg')) elif self.level == 2: aplay.play(os.path.join(self._path, 'sounds', 'glass.ogg')) # Place some Locos on the canvas for i in range(self._counter + 1): self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 3: aplay.play(os.path.join(self._path, 'sounds', 'bark.ogg')) # Place some Locos on the left-side of the canvas for i in range(self._counter + 1): self._loco_quadrant = int(uniform(2, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 4: # Place some Locos on the canvas with letters as labels # Just lowercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(ALPHABETLC[int( uniform(0, len(ALPHABETLC)))]) elif self.level == 5: # Place some Locos on the canvas with letters as labels # Uppercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(ALPHABETUC[int( uniform(0, len(ALPHABETUC)))]) elif self.level == 6: x = 0 y = 0 c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': y += self._loco_dim[1] x = 0 else: self._sticky_cards[c].move((x, y)) self._sticky_cards[c].type = i self._sticky_cards[c].set_layer(LOCO_LAYER) self._sticky_cards[c].set_label(MSGS[self._counter][i]) c += 1 x += int(self._loco_dim[0] / 2.) if self.level in [0, 1]: self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) if self.level == 0: self._move_loco(x, y, 0) else: self._taunt(x, y, 0) def _quad_to_xy(self, q): x = int(max(0, (self._width / 2.) * uniform(0, 1) - self._loco_dim[0])) if q in [0, 1]: x += int(self._width / 2.) y = int(max(0, (self._height / 2.) * uniform(0, 1) - self._loco_dim[1])) if q in [1, 2]: y += int(self._height / 2.) return x, y def _taunt(self, x, y, i): n = len(self._taunt_cards) self._taunt_cards[(i + 1) % n].hide() if self._clicked: self._timeout_id = None return True else: self._taunt_cards[i % n].move((x, y)) self._taunt_cards[i % n].set_layer(LOCO_LAYER) self._timeout_id = GLib.timeout_add(200, self._taunt, x, y, i + 1) def _move_loco(self, x, y, i): j = (i + 1) % len(self._loco_cards) cx, cy = self._loco_cards[i].get_xy() dx = cx - x dy = cy - y if dx * dx + dy * dy < 100: self._loco_cards[j].move((x, y)) self._loco_pos = (x, y) self._loco_cards[j].hide() self._loco_cards[i].hide() self._man_cards[0].move((x, y)) self._man_cards[0].set_layer(LOCO_LAYER) self._timeout_id = None if self._pause > 50: self._pause -= 10 return True else: if dx > 0: cx -= 5 elif dx < 0: cx += 5 if dy > 0: cy -= 5 elif dy < 0: cy += 5 self._loco_cards[j].move((cx, cy)) self._loco_pos = (cx, cy) self._loco_cards[j].set_layer(LOCO_LAYER) self._loco_cards[i].hide() self._timeout_id = GLib.timeout_add(self._pause, self._move_loco, x, y, j) def _keypress_cb(self, area, event): ''' Keypress ''' # Games 4, 5, and 6 use the keyboard if self.level not in [4, 5, 6]: return True k = Gdk.keyval_name(event.keyval) if self._waiting_for_enter: if k == 'Return': self._waiting_for_enter = False self._panel.hide() self._counter += 1 self._correct = 0 GLib.timeout_add(1000, self.new_game, False) return if k in NOISE_KEYS or k in WHITE_SPACE: return True if self.level == 6 and self._waiting_for_delete: if k in ['BackSpace', 'Delete']: self._waiting_for_delete = False self._sticky_cards[self._correct].set_label_color('white') self._sticky_cards[self._correct].set_label(MSGS[ self._counter][self._sticky_cards[self._correct].type]) self._panel.hide() self._panel.set_label_color('black') return if k[0:5] == 'dead_': self._dead_key = k[5:] return if self.level == 6: n = len(MSGS[self._counter]) else: n = self._counter + 1 if self.level == 6: i = self._correct if self._dead_key is not None: k = DEAD_DICTS[DEAD_KEYS.index(self._dead_key)][k] self._dead_key = None elif k in PUNCTUATION: k = PUNCTUATION[k] elif k in SPECIAL: k = SPECIAL[k] elif len(k) > 1: return True if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label_color('blue') self._sticky_cards[i].set_label(k) self._correct += 1 else: self._sticky_cards[i].set_label_color('red') self._sticky_cards[i].set_label(k) self._panel.set_label_color('red') self._panel.set_label(ALERTS[1]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_delete = True aplay.play(os.path.join(self._path, 'sounds', 'glass.ogg')) else: for i in range(n): if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label('') self._sticky_cards[i].hide() break # Test for end condition if self.level == 6 and \ self._correct == len(MSGS[self._counter]) - \ MSGS[self._counter].count(' '): c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': continue elif MSGS[self._counter][i] != self._sticky_cards[c].labels[0]: return True c += 1 self._panel.set_label(ALERTS[0]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_enter = True aplay.play(os.path.join(self._path, 'sounds', 'drip.ogg')) return else: for i in range(n): if len(self._sticky_cards[i].labels[0]) > 0: return True self._counter += 1 self._correct = 0 GLib.timeout_add(1000, self.new_game, False) def _mouse_move_cb(self, win, event): ''' Move the mouse. ''' # Games 0, 3, 4, and 5 use move events x, y = list(map(int, event.get_coords())) if self._seconds > 1: self._panel.hide() if not self._clicked and self.level == 0: # For Game 0, see if the mouse is on the Loco dx = x - self._loco_pos[0] - self._loco_dim[0] / 2. dy = y - self._loco_pos[1] - self._loco_dim[1] / 2. if dx * dx + dy * dy < 200: self._clicked = True if self._timeout_id is not None: GLib.source_remove(self._timeout_id) # Play again self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._correct += 1 self._counter += 1 GLib.timeout_add(1000, self.new_game, False) elif self.level in [4, 5]: # For Game 4 and 5, we allow dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] elif self.level == 3: # For Game 3, we are dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] if x > self._width / 2.: self._press.set_shape(self._man_pixbuf) if self._press.type == 'loco': self._correct += 1 self._press.type = 'man' return True def _button_release_cb(self, win, event): # Game 3 uses release if self.level == 3: # Move to release if self._correct == self._counter + 1: self._counter += 1 self._correct = 0 GLib.timeout_add(2000, self.new_game, False) self._press = None self._drag_pos = [0, 0] return True def _button_press_cb(self, win, event): self._press = None x, y = list(map(int, event.get_coords())) if self.level == 0: return spr = self._sprites.find_sprite((x, y)) if spr is None: return if spr.type != 'loco': return if self.level < 2 and self._timeout_id is None: return if self._clicked: return # Games 1, 2, and 3 involve clicks; Games 4 and 5 allow click to drag if self.level == 1: self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._clicked = True self._counter += 1 self._correct += 1 if self._timeout_id is not None: GLib.source_remove(self._timeout_id) GLib.timeout_add(2000, self.new_game, False) elif self.level == 2: spr.set_shape(self._ghost_pixbuf) spr.type = 'ghost' if self._correct == self._counter: self._counter += 1 self._correct = 0 GLib.timeout_add(2000, self.new_game, False) else: self._correct += 1 elif self.level in [3, 4, 5]: # In Games 4 and 5, dragging is used to remove overlaps self._press = spr self._drag_pos = [x, y] return True def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) 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()
class BBoardActivity(activity.Activity): ''' Make a slideshow from starred Journal entries. ''' def __init__(self, handle): ''' Initialize the toolbars and the work surface ''' super(BBoardActivity, self).__init__(handle) self.datapath = get_path(activity, 'instance') self._hw = get_hardware() self._playback_buttons = {} self._audio_recordings = {} self.colors = profile.get_color().to_string().split(',') self._setup_toolbars() self._setup_canvas() self.slides = [] self._setup_workspace() self._buddies = [profile.get_nick_name()] self._setup_presence_service() self._thumbs = [] self._thumbnail_mode = False self._recording = False self._grecord = None self._alert = None self._dirty = False def _setup_canvas(self): ''' Create a canvas ''' self._canvas = gtk.DrawingArea() self._canvas.set_size_request(int(gtk.gdk.screen_width()), int(gtk.gdk.screen_height())) self._canvas.show() self.set_canvas(self._canvas) self.show_all() self._canvas.set_flags(gtk.CAN_FOCUS) self._canvas.add_events(gtk.gdk.BUTTON_PRESS_MASK) self._canvas.add_events(gtk.gdk.POINTER_MOTION_MASK) self._canvas.add_events(gtk.gdk.BUTTON_RELEASE_MASK) self._canvas.add_events(gtk.gdk.KEY_PRESS_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("motion-notify-event", self._mouse_move_cb) def _setup_workspace(self): ''' Prepare to render the datastore entries. ''' # Use the lighter color for the text background if lighter_color(self.colors) == 0: tmp = self.colors[0] self.colors[0] = self.colors[1] self.colors[1] = tmp self._width = gtk.gdk.screen_width() self._height = gtk.gdk.screen_height() self._scale = gtk.gdk.screen_height() / 900. if not HAVE_TOOLBOX and self._hw[0:2] == 'xo': titlef = 18 descriptionf = 12 else: titlef = 36 descriptionf = 24 self._find_starred() for ds in self.dsobjects: if 'title' in ds.metadata: title = ds.metadata['title'] else: title = None pixbuf = None media_object = False mimetype = None if 'mime_type' in ds.metadata: mimetype = ds.metadata['mime_type'] if mimetype[0:5] == 'image': pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( ds.file_path, MAXX, MAXY) # ds.file_path, 300, 225) media_object = True else: pixbuf = get_pixbuf_from_journal(ds, MAXX, MAXY) # 300, 225) if 'description' in ds.metadata: desc = ds.metadata['description'] else: desc = None self.slides.append(Slide(True, ds.object_id, self.colors, title, pixbuf, desc)) # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._help = Sprite( self._sprites, int((self._width - int(PREVIEWW * self._scale)) / 2), int(PREVIEWY * self._scale), gtk.gdk.pixbuf_new_from_file_at_size( os.path.join(activity.get_bundle_path(), 'help.png'), int(PREVIEWW * self._scale), int(PREVIEWH * self._scale))) self._help.hide() self._genblanks(self.colors) self._title = Sprite(self._sprites, 0, 0, self._title_pixbuf) self._title.set_label_attributes(int(titlef * self._scale), rescale=False) self._preview = Sprite(self._sprites, int((self._width - int(PREVIEWW * self._scale)) / 2), int(PREVIEWY * self._scale), self._preview_pixbuf) self._description = Sprite(self._sprites, int(DESCRIPTIONX * self._scale), int(DESCRIPTIONY * self._scale), self._desc_pixbuf) self._description.set_label_attributes(int(descriptionf * self._scale)) self._my_canvas = Sprite(self._sprites, 0, 0, self._canvas_pixbuf) self._my_canvas.set_layer(BOTTOM) self._clear_screen() self.i = 0 self._show_slide() self._playing = False self._rate = 10 def _genblanks(self, colors): ''' Need to cache these ''' self._title_pixbuf = svg_str_to_pixbuf( genblank(self._width, int(TITLEH * self._scale), colors)) self._preview_pixbuf = svg_str_to_pixbuf( genblank(int(PREVIEWW * self._scale), int(PREVIEWH * self._scale), colors)) self._desc_pixbuf = svg_str_to_pixbuf( genblank(int(self._width - (2 * DESCRIPTIONX * self._scale)), int(DESCRIPTIONH * self._scale), colors)) self._canvas_pixbuf = svg_str_to_pixbuf( genblank(self._width, self._height, (colors[0], colors[0]))) def _setup_toolbars(self): ''' Setup the toolbars. ''' self.max_participants = 6 if HAVE_TOOLBOX: toolbox = ToolbarBox() # Activity toolbar activity_button_toolbar = ActivityToolbarButton(self) toolbox.toolbar.insert(activity_button_toolbar, 0) activity_button_toolbar.show() self.set_toolbar_box(toolbox) toolbox.show() self.toolbar = toolbox.toolbar self.record_toolbar = gtk.Toolbar() record_toolbar_button = ToolbarButton( label=_('Record a sound'), page=self.record_toolbar, icon_name='media-audio') self.record_toolbar.show_all() record_toolbar_button.show() toolbox.toolbar.insert(record_toolbar_button, -1) else: # Use pre-0.86 toolbar design primary_toolbar = gtk.Toolbar() toolbox = activity.ActivityToolbox(self) self.set_toolbox(toolbox) toolbox.add_toolbar(_('Page'), primary_toolbar) self.record_toolbar = gtk.Toolbar() toolbox.add_toolbar(_('Record'), self.record_toolbar) toolbox.show() toolbox.set_current_toolbar(1) self.toolbar = primary_toolbar self._prev_button = button_factory( 'go-previous-inactive', self.toolbar, self._prev_cb, tooltip=_('Prev slide'), accelerator='<Ctrl>P') self._next_button = button_factory( 'go-next', self.toolbar, self._next_cb, tooltip=_('Next slide'), accelerator='<Ctrl>N') separator_factory(self.toolbar) slide_button = radio_factory('slide-view', self.toolbar, self._slides_cb, group=None, tooltip=_('Slide view')) radio_factory('thumbs-view', self.toolbar, self._thumbs_cb, tooltip=_('Thumbnail view'), group=slide_button) button_factory('view-fullscreen', self.toolbar, self.do_fullscreen_cb, tooltip=_('Fullscreen'), accelerator='<Alt>Return') separator_factory(self.toolbar) journal_button = button_factory( 'write-journal', self.toolbar, self._do_journal_cb, tooltip=_('Update description')) self._palette = journal_button.get_palette() msg_box = gtk.HBox() sw = gtk.ScrolledWindow() sw.set_size_request(int(gtk.gdk.screen_width() / 2), 2 * style.GRID_CELL_SIZE) sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self._text_view = gtk.TextView() self._text_view.set_left_margin(style.DEFAULT_PADDING) self._text_view.set_right_margin(style.DEFAULT_PADDING) self._text_view.set_wrap_mode(gtk.WRAP_WORD_CHAR) self._text_view.connect('focus-out-event', self._text_view_focus_out_event_cb) sw.add(self._text_view) sw.show() msg_box.pack_start(sw, expand=False) msg_box.show_all() self._palette.set_content(msg_box) label_factory(self.record_toolbar, _('Record a sound') + ':') self._record_button = button_factory( 'media-record', self.record_toolbar, self._record_cb, tooltip=_('Start recording')) separator_factory(self.record_toolbar) # Look to see if we have audio previously recorded obj_id = self._get_audio_obj_id() dsobject = self._search_for_audio_note(obj_id) if dsobject is not None: _logger.debug('Found previously recorded audio') self._add_playback_button(profile.get_nick_name(), self.colors, dsobject.file_path) if HAVE_TOOLBOX: button_factory('system-restart', activity_button_toolbar, self._resend_cb, tooltip=_('Refresh')) separator_factory(activity_button_toolbar) self._save_pdf = button_factory( 'save-as-pdf', activity_button_toolbar, self._save_as_pdf_cb, tooltip=_('Save as PDF')) else: separator_factory(self.toolbar) self._save_pdf = button_factory( 'save-as-pdf', self.toolbar, self._save_as_pdf_cb, tooltip=_('Save as PDF')) if HAVE_TOOLBOX: separator_factory(toolbox.toolbar, True, False) stop_button = StopButton(self) stop_button.props.accelerator = '<Ctrl>q' toolbox.toolbar.insert(stop_button, -1) stop_button.show() def _do_journal_cb(self, button): self._dirty = True if self._palette: if not self._palette.is_up(): self._palette.popup(immediate=True, state=self._palette.SECONDARY) else: self._palette.popdown(immediate=True) return def _text_view_focus_out_event_cb(self, widget, event): buffer = self._text_view.get_buffer() start_iter = buffer.get_start_iter() end_iter = buffer.get_end_iter() self.slides[self.i].desc = buffer.get_text(start_iter, end_iter) self._show_slide() def _destroy_cb(self, win, event): ''' Clean up on the way out. ''' gtk.main_quit() def _find_starred(self): ''' Find all the favorites in the Journal. ''' self.dsobjects, nobjects = datastore.find({'keep': '1'}) _logger.debug('found %d starred items', nobjects) def _prev_cb(self, button=None): ''' The previous button has been clicked; goto previous slide. ''' if self.i > 0: self.i -= 1 self._show_slide(direction=-1) def _next_cb(self, button=None): ''' The next button has been clicked; goto next slide. ''' if self.i < len(self.slides) - 1: self.i += 1 self._show_slide() def _save_as_pdf_cb(self, button=None): ''' Export an PDF version of the slideshow to the Journal. ''' _logger.debug('saving to PDF...') if 'description' in self.metadata: tmp_file = save_pdf(self, self._buddies, description=self.metadata['description']) else: tmp_file = save_pdf(self, self._buddies) _logger.debug('copying PDF file to Journal...') dsobject = datastore.create() dsobject.metadata['title'] = profile.get_nick_name() + ' ' + \ _('Bboard') dsobject.metadata['icon-color'] = profile.get_color().to_string() dsobject.metadata['mime_type'] = 'application/pdf' dsobject.set_file_path(tmp_file) dsobject.metadata['activity'] = 'org.laptop.sugar.ReadActivity' datastore.write(dsobject) dsobject.destroy() return def _clear_screen(self): ''' Clear the screen to the darker of the two user colors. ''' self._title.hide() self._preview.hide() self._description.hide() if hasattr(self, '_thumbs'): for thumbnail in self._thumbs: thumbnail[0].hide() self.invalt(0, 0, self._width, self._height) # Reset drag settings self._press = None self._release = None self._dragpos = [0, 0] self._total_drag = [0, 0] self.last_spr_moved = None def _update_colors(self): ''' Match the colors to those of the slide originator. ''' if len(self.slides) == 0: return self._genblanks(self.slides[self.i].colors) self._title.set_image(self._title_pixbuf) self._preview.set_image(self._preview_pixbuf) self._description.set_image(self._desc_pixbuf) self._my_canvas.set_image(self._canvas_pixbuf) def _show_slide(self, direction=1): ''' Display a title, preview image, and decription for slide. ''' self._clear_screen() self._update_colors() if len(self.slides) == 0: self._prev_button.set_icon('go-previous-inactive') self._next_button.set_icon('go-next-inactive') self._description.set_label( _('Do you have any items in your Journal starred?')) self._help.set_layer(TOP) self._description.set_layer(MIDDLE) return if self.i == 0: self._prev_button.set_icon('go-previous-inactive') else: self._prev_button.set_icon('go-previous') if self.i == len(self.slides) - 1: self._next_button.set_icon('go-next-inactive') else: self._next_button.set_icon('go-next') pixbuf = self.slides[self.i].pixbuf if pixbuf is not None: self._preview.set_shape(pixbuf.scale_simple( int(PREVIEWW * self._scale), int(PREVIEWH * self._scale), gtk.gdk.INTERP_NEAREST)) self._preview.set_layer(MIDDLE) else: if self._preview is not None: self._preview.hide() self._title.set_label(self.slides[self.i].title) self._title.set_layer(MIDDLE) if self.slides[self.i].desc is not None: self._description.set_label(self.slides[self.i].desc) self._description.set_layer(MIDDLE) text_buffer = gtk.TextBuffer() text_buffer.set_text(self.slides[self.i].desc) self._text_view.set_buffer(text_buffer) else: self._description.set_label('') self._description.hide() def _add_playback_button(self, nick, colors, audio_file): ''' Add a toolbar button for this audio recording ''' if nick not in self._playback_buttons: self._playback_buttons[nick] = button_factory( 'xo-chat', self.record_toolbar, self._playback_recording_cb, cb_arg=nick, tooltip=_('Audio recording by %s') % (nick)) xocolor = XoColor('%s,%s' % (colors[0], colors[1])) icon = Icon(icon_name='xo-chat', xo_color=xocolor) icon.show() self._playback_buttons[nick].set_icon_widget(icon) self._playback_buttons[nick].show() self._audio_recordings[nick] = audio_file def _slides_cb(self, button=None): if self._thumbnail_mode: self._thumbnail_mode = False self.i = self._current_slide self._show_slide() def _thumbs_cb(self, button=None): ''' Toggle between thumbnail view and slideshow view. ''' if not self._thumbnail_mode: self._current_slide = self.i self._thumbnail_mode = True self._clear_screen() self._prev_button.set_icon('go-previous-inactive') self._next_button.set_icon('go-next-inactive') n = int(ceil(sqrt(len(self.slides)))) if n > 0: w = int(self._width / n) else: w = self._width h = int(w * 0.75) # maintain 4:3 aspect ratio x_off = int((self._width - n * w) / 2) x = x_off y = 0 self._thumbs = [] for i in range(len(self.slides)): self._show_thumb(i, x, y, w, h) x += w if x + w > self._width: x = x_off y += h self.i = 0 # Reset position in slideshow to the beginning return False def _show_thumb(self, i, x, y, w, h): ''' Display a preview image and title as a thumbnail. ''' pixbuf = self.slides[i].pixbuf if pixbuf is not None: pixbuf_thumb = pixbuf.scale_simple( int(w), int(h), gtk.gdk.INTERP_TILES) else: pixbuf_thumb = svg_str_to_pixbuf( genblank(int(w), int(h), self.slides[i].colors)) # Create a Sprite for this thumbnail self._thumbs.append([Sprite(self._sprites, x, y, pixbuf_thumb), x, y, i]) self._thumbs[i][0].set_image( svg_str_to_pixbuf(svg_rectangle(int(w), int(h), self.slides[i].colors)), i=1) self._thumbs[i][0].set_layer(TOP) def _expose_cb(self, win, event): ''' Callback to handle window expose events ''' self.do_expose_event(event) return True # Handle the expose-event by drawing def do_expose_event(self, event): # Create the cairo context cr = self.canvas.window.cairo_create() # Restrict Cairo to the exposed area; avoid extra work 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 write_file(self, file_path): ''' Clean up ''' if self._dirty: self._save_descriptions_cb() self._dirty = False if os.path.exists(os.path.join(self.datapath, 'output.ogg')): os.remove(os.path.join(self.datapath, 'output.ogg')) def do_fullscreen_cb(self, button): ''' Hide the Sugar toolbars. ''' self.fullscreen() 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) def _spr_to_thumb(self, spr): ''' Find which entry in the thumbnails table matches spr. ''' for i, thumb in enumerate(self._thumbs): if spr == thumb[0]: return i return -1 def _spr_is_thumbnail(self, spr): ''' Does spr match an entry in the thumbnails table? ''' if self._spr_to_thumb(spr) == -1: return False else: return True def _button_press_cb(self, win, event): ''' The mouse button was pressed. Is it on a thumbnail sprite? ''' win.grab_focus() x, y = map(int, event.get_coords()) self._dragpos = [x, y] self._total_drag = [0, 0] spr = self._sprites.find_sprite((x, y)) self._press = None self._release = None # Are we clicking on a thumbnail? if not self._spr_is_thumbnail(spr): return False self.last_spr_moved = spr self._press = spr self._press.set_layer(DRAG) return False def _mouse_move_cb(self, win, event): """ Drag a thumbnail with the mouse. """ spr = self._press if spr is None: self._dragpos = [0, 0] return False win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self._dragpos[0] dy = y - self._dragpos[1] spr.move_relative([dx, dy]) # Also move the star self._dragpos = [x, y] self._total_drag[0] += dx self._total_drag[1] += dy return False def _button_release_cb(self, win, event): ''' Button event is used to swap slides or goto next slide. ''' win.grab_focus() self._dragpos = [0, 0] x, y = map(int, event.get_coords()) if self._thumbnail_mode: if self._press is None: return # Drop the dragged thumbnail below the other thumbnails so # that you can find the thumbnail beneath it. self._press.set_layer(UNDRAG) i = self._spr_to_thumb(self._press) spr = self._sprites.find_sprite((x, y)) if self._spr_is_thumbnail(spr): self._release = spr # If we found a thumbnail and it is not the one we # dragged, swap their positions. if not self._press == self._release: j = self._spr_to_thumb(self._release) self._thumbs[i][0] = self._release self._thumbs[j][0] = self._press tmp = self.slides[i] self.slides[i] = self.slides[j] self.slides[j] = tmp self._thumbs[j][0].move((self._thumbs[j][1], self._thumbs[j][2])) self._thumbs[i][0].move((self._thumbs[i][1], self._thumbs[i][2])) self._press.set_layer(TOP) self._press = None self._release = None else: self._next_cb() return False def _unit_combo_cb(self, arg=None): ''' Read value of predefined conversion factors from combo box ''' if hasattr(self, '_unit_combo'): active = self._unit_combo.get_active() if active in UNIT_DICTIONARY: self._rate = UNIT_DICTIONARY[active][1] 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')) _logger.debug('Autosaving recording') self._notify(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, nick=profile.get_nick_name()): ''' Play back current recording ''' _logger.debug('Playback current recording from %s...' % (nick)) if nick in self._audio_recordings: play_audio_from_file(self._audio_recordings[nick]) return def _get_audio_obj_id(self): ''' Find unique name for audio object ''' if 'activity_id' in self.metadata: obj_id = self.metadata['activity_id'] else: obj_id = _('Bulletin Board') _logger.debug(obj_id) return obj_id def _save_recording(self): if os.path.exists(os.path.join(self.datapath, 'output.ogg')): _logger.debug('Saving recording to Journal...') obj_id = self._get_audio_obj_id() copyfile(os.path.join(self.datapath, 'output.ogg'), os.path.join(self.datapath, '%s.ogg' % (obj_id))) dsobject = self._search_for_audio_note(obj_id) if dsobject is None: dsobject = datastore.create() if dsobject is not None: _logger.debug(self.dsobjects[self.i].metadata['title']) dsobject.metadata['title'] = _('Audio recording by %s') % \ (self.metadata['title']) dsobject.metadata['icon-color'] = \ profile.get_color().to_string() dsobject.metadata['tags'] = obj_id dsobject.metadata['mime_type'] = 'audio/ogg' dsobject.set_file_path( os.path.join(self.datapath, '%s.ogg' % (obj_id))) datastore.write(dsobject) dsobject.destroy() self._add_playback_button( profile.get_nick_name(), self.colors, os.path.join(self.datapath, '%s.ogg' % (obj_id))) if hasattr(self, 'chattube') and self.chattube is not None: self._share_audio() else: _logger.debug('Nothing to save...') return def _search_for_audio_note(self, obj_id): ''' Look to see if there is already a sound recorded for this dsobject ''' dsobjects, nobjects = datastore.find({'mime_type': ['audio/ogg']}) # Look for tag that matches the target object id for dsobject in dsobjects: if 'tags' in dsobject.metadata and \ obj_id in dsobject.metadata['tags']: _logger.debug('Found audio note') return dsobject return None def _save_descriptions_cb(self, button=None): ''' Find the object in the datastore and write out the changes to the decriptions. ''' for s in self.slides: if not s.owner: continue jobject = datastore.get(s.uid) jobject.metadata['description'] = s.desc datastore.write(jobject, update_mtime=False, reply_handler=self.datastore_write_cb, error_handler=self.datastore_write_error_cb) def datastore_write_cb(self): pass def datastore_write_error_cb(self, error): _logger.error('datastore_write_error_cb: %r' % error) def _notify(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() def _resend_cb(self, button=None): ''' Resend slides, but only of sharing ''' if hasattr(self, 'chattube') and self.chattube is not None: self._share_slides() self._share_audio() # Serialize def _dump(self, slide): ''' Dump data for sharing.''' _logger.debug('dumping %s' % (slide.uid)) data = [slide.uid, slide.colors, slide.title, pixbuf_to_base64(activity, slide.pixbuf), slide.desc] return self._data_dumper(data) def _data_dumper(self, data): if _OLD_SUGAR_SYSTEM: return json.write(data) else: io = StringIO() jdump(data, io) return io.getvalue() def _load(self, data): ''' Load game data from the journal. ''' slide = self._data_loader(data) if len(slide) == 5: if not self._slide_search(slide[0]): _logger.debug('loading %s' % (slide[0])) self.slides.append(Slide( False, slide[0], slide[1], slide[2], base64_to_pixbuf(activity, slide[3]), slide[4])) def _slide_search(self, uid): ''' Is this slide in the list already? ''' for slide in self.slides: if slide.uid == uid: _logger.debug('skipping %s' % (slide.uid)) return True return False def _data_loader(self, data): if _OLD_SUGAR_SYSTEM: return json.read(data) else: io = StringIO(data) return jload(io) # Sharing-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.buddies = [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...''' if self._shared_activity is None: _logger.error('Failed to share or join activity ... \ _shared_activity is null in _shared_cb()') return self.initiating = True self.waiting = False _logger.debug('I am sharing...') 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) _logger.debug('This is my activity: making a tube...') id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) def _joined_cb(self, activity): ''' ...or join an exisiting share. ''' if self._shared_activity is None: _logger.error('Failed to share or join activity ... \ _shared_activity is null in _shared_cb()') return self.initiating = False _logger.debug('I joined a shared activity.') 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) _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.waiting = 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.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) if self.waiting: self._send_event('j:%s' % (profile.get_nick_name())) def event_received_cb(self, text): ''' Data is passed as tuples: cmd:text ''' _logger.debug('<<< %s' % (text[0])) if text[0] == 's': # shared journal objects e, data = text.split(':') self._load(data) elif text[0] == 'j': # Someone new has joined e, buddy = text.split(':') _logger.debug('%s has joined' % (buddy)) if buddy not in self._buddies: self._buddies.append(buddy) if self.initiating: self._send_event('J:%s' % (profile.get_nick_name())) self._share_slides() self._share_audio() elif text[0] == 'J': # Everyone must share e, buddy = text.split(':') self.waiting = False if buddy not in self._buddies: self._buddies.append(buddy) _logger.debug('%s has joined' % (buddy)) self._share_slides() self._share_audio() elif text[0] == 'a': # audio recording e, data = text.split(':') nick, colors, base64 = self._data_loader(data) path = os.path.join(activity.get_activity_root(), 'instance', 'nick.ogg') base64_to_file(activity, base64, path) self._add_playback_button(nick, colors, path) def _share_audio(self): if profile.get_nick_name() in self._audio_recordings: base64 = file_to_base64( activity, self._audio_recordings[profile.get_nick_name()]) gobject.idle_add(self._send_event, 'a:' + str( self._data_dumper([profile.get_nick_name(), self.colors, base64]))) def _share_slides(self): for s in self.slides: if s.owner: # Maybe stagger the timing of the sends? gobject.idle_add(self._send_event, 's:' + str(self._dump(s))) _logger.debug('finished sharing') def _send_event(self, text): ''' Send event through the tube. ''' if hasattr(self, 'chattube') and self.chattube is not None: _logger.debug('>>> %s' % (text[0])) self.chattube.SendText(text)
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = [colors[0]] self._colors.append(colors[1]) self._colors.append('#FFFFFF') self._colors.append('#000000') self._colors.append('#FF0000') self._colors.append('#FF8080') self._colors.append('#FFa0a0') self._colors.append('#FFc0c0') self._colors.append('#FFFF00') self._colors.append('#FFFF80') self._colors.append('#FFFFa0') self._colors.append('#FFFFe0') self._colors.append('#0000FF') self._colors.append('#8080FF') self._colors.append('#80a0FF') self._colors.append('#c0c0FF') self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.connect("button-release-event", self._button_release_cb) self._global_scale = 1 self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._width / (10 * DOT_SIZE * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._space = int(self._dot_size / 5.) self._press = False self._release = None self._rubbing = False self._tapped = None self._pausing = False self._count = 0 self._targets = None # click target self._shake = None # accelerometer target self._next = None self.last_spr = None self._timer = None self.roygbiv = False # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._svg_width = self._width self._svg_height = self._height self._lightbg = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf( self._header() + \ self._rect(self._width, self._height, 0, 0) + \ self._footer())) self._lightbg.set_label_attributes(24) self._lightbg._vert_align = ["bottom"] self._darkbg = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf( self._header() + \ self._rect(self._width, self._height, 0, 0, color='#000000') + \ self._footer())) self._darkbg.set_label_attributes(24) self._darkbg._vert_align = ["bottom"] self._darkbg.set_label_color('yellow') self._darkbg.set_layer(0) self._dots = [] for y in range(FIVE): for x in range(NINE): xoffset = int((self._width - NINE * self._dot_size - \ (NINE - 1) * self._space) / 2.) self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[WHITE]))) self._dots[-1].type = DOT self._dots[-1].set_label_attributes(40) n = FIVE / 2. # and initialize a few variables we'll need. self._yellow_dot() def _clear_pause(self): self._pausing = False if self._rubbing and self._release is not None: if self._next is not None: self._next() def _yellow_dot(self): for y in range(FIVE): for x in range(NINE): xoffset = int((self._width - NINE * self._dot_size - \ (NINE - 1) * self._space) / 2.) self._dots[x + y * NINE].move( (xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space))) self._dots[x + y * NINE].set_shape( self._new_dot(self._colors[WHITE])) self._dots[x + y * NINE].type = DOT self._dots[x + y * NINE].set_layer(100) self._lightbg.set_label(_('Tap on the yellow dot.')) self._targets = [int(uniform(0, NINE * FIVE))] self._next = self._yellow_dot_too self._dots[self._targets[0]].set_shape( self._new_dot(self._colors[YELLOW])) self._dots[self._targets[0]].type = YELLOW self._rubbing = False self._shake = None def _yellow_dot_too(self, append=True): ''' Things to reinitialize when starting up a new game. ''' if append: i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._lightbg.set_label( _('Well done! \ Now tap on the other yellow dot.')) self._next = self._yellow_dots_three self._dots[self._targets[1]].set_shape( self._new_dot(self._colors[YELLOW])) self._dots[self._targets[1]].type = YELLOW self._rubbing = False def _yellow_dots_three(self): ''' Things to reinitialize when starting up a new game. ''' if self._release == self._targets[0]: self._yellow_dot_too(append=False) self._lightbg.set_label(_('The other yellow dot!')) return i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._lightbg.set_label(_('Great! Now rub on one of the yellow dots.')) self._next = self._red_dot self._dots[self._targets[2]].set_shape( self._new_dot(self._colors[YELLOW])) self._dots[self._targets[2]].type = YELLOW self._rubbing = True def _red_dot(self): if self._release is None: return self._lightbg.set_label( _('Good job! \ Now rub on another one of the yellow dots.')) self._next = self._blue_dot self._dots[self._release].set_shape(self._new_dot(self._colors[RED])) self._dots[self._release].type = RED self._rubbing = True def _blue_dot(self): if self._release is None: return if self._dots[self._release].type != YELLOW: return self._lightbg.set_label( _('Now gently tap on the yellow dot five times.')) self._next = self._yellow_tap self._dots[self._release].set_shape(self._new_dot(self._colors[BLUE])) self._dots[self._release].type = BLUE self._rubbing = False self._count = 0 def _yellow_tap(self): if self._dots[self._release].type != YELLOW: if self._count == 0: self._lightbg.set_label( _('Now gently tap on the yellow dot five times.')) else: self._lightbg.set_label(_('Tap on a yellow dot.')) return self._count += 1 if self._count > 4: self._count = 0 self._next = self._red_tap self._lightbg.set_label( _('Now gently tap on the red dot five times.')) else: self._lightbg.set_label(_('Keep tapping.')) i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._dots[i].set_shape(self._new_dot(self._colors[YELLOW])) self._dots[i].type = YELLOW def _red_tap(self): if self._dots[self._release].type != RED: if self._count == 0: self._lightbg.set_label( _('Now gently tap on the red dot five times.')) else: self._lightbg.set_label(_('Tap on a red dot.')) return self._count += 1 if self._count > 4: self._count = 0 self._next = self._blue_tap self._lightbg.set_label( _('Now gently tap on the blue dot five times.')) else: self._lightbg.set_label(_('Keep tapping.')) i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._dots[i].set_shape(self._new_dot(self._colors[RED])) self._dots[i].type = RED def _blue_tap(self): if self._dots[self._release].type != BLUE: if self._count == 0: self._lightbg.set_label( _('Now gently tap on the blue dot five times.')) else: self._lightbg.set_label(_('Tap on a blue dot.')) return self._count += 1 if self._count > 4: self._count = 0 self._next = self._shake_it self._lightbg.set_label('') # Since we don't end up in the button press GObject.timeout_add(500, self._next) else: self._lightbg.set_label(_('Keep tapping.')) i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._dots[i].set_shape(self._new_dot(self._colors[BLUE])) self._dots[i].type = BLUE def _shake_it(self): self._lightbg.set_label(_('OK. Now, shake the computer!!')) for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: dot.set_layer(200) self._next = self._shake_it_more self._shake = 'random' self._pausing = True GObject.timeout_add(5000, self._clear_pause) GObject.timeout_add(100, read_accelerometer, self) def _shake_it_more(self): self._lightbg.set_label(_('Shake it harder!!')) self._next = self._turn_left self._shake = 'random2' self._pausing = True GObject.timeout_add(5000, self._clear_pause) def _turn_left(self): self._lightbg.set_label( _('See what happens if you turn it to the left.')) self._next = self._turn_right self._shake = 'left' self._pausing = True def _turn_right(self): self._lightbg.set_label(_('Now turn it to the right.')) self._next = self._align self._pausing = True self._shake = 'right' def _align(self): self._lightbg.set_label(_('Shake it some more.')) self._next = self._tap_six self._pausing = True self._shake = 'align' def _tap_six(self): self._shake = None self._lightbg.set_label(_('OK. Now press each of the yellow dots.')) if self._tapped == None: self._tapped = [] if self._dots[self._release].type != YELLOW: self._lightbg.set_label(_('Press the yellow dots.')) return else: if not self._release in self._tapped: self._tapped.append(self._release) self._dots[self._release].set_label(':)') if len(self._tapped) == 6: self._darkbg.set_layer(100) self._lightbg.set_layer(0) for dot in self._dots: if dot.type != YELLOW: dot.set_layer(0) self._darkbg.set_label(_('Press all of the yellow dots again!')) self._tapped = None self._next = self._tap_six_too def _tap_six_too(self): self._shake = None if self._tapped == None: self._tapped = [] if self._dots[self._release].type != YELLOW: return else: if not self._release in self._tapped: self._tapped.append(self._release) self._dots[self._release].set_label('') if len(self._tapped) == 6: self._lightbg.set_layer(100) self._darkbg.set_layer(0) for dot in self._dots: if dot.type in [RED, BLUE]: dot.set_layer(100) pos1 = self._dots[self._targets[1]].get_xy() pos2 = self._dots[self._targets[2]].get_xy() self._dots[self._targets[1]].move(pos2) self._dots[self._targets[2]].move(pos1) self._lightbg.set_label( _('Tap on the two dots that switched positions.')) self._tapped = None self._next = self._tap_two def _tap_two(self): self._shake = None if self._tapped == None: self._tapped = [] if not self._release in [self._targets[1], self._targets[2]]: self._lightbg.set_label(_('Keep trying.')) return else: if not self._release in self._tapped: self._tapped.append(self._release) self._dots[self._release].set_label(':)') if len(self._tapped) == 2: pos1 = self._dots[self._targets[1]].get_xy() pos2 = self._dots[self._targets[2]].get_xy() self._dots[self._targets[1]].move(pos2) self._dots[self._targets[2]].move(pos1) self._lightbg.set_label(_("Good job! Now let's shake again.")) self._shake = 'random2' self._next = self._shake_three # Since we don't end up in the button press GObject.timeout_add(500, self._next) for i in self._tapped: self._dots[i].set_label('') elif len(self._tapped) == 1: self._lightbg.set_label( _('You found one. Now find the other one.')) def _shake_three(self): self._next = self._fade_it self._shake = 'random2' GObject.timeout_add(100, read_accelerometer, self) self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _fade_it(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 1) self._lightbg.set_label(_('Going')) self._shake = 'random2' self._next = self._fade_it_again self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _fade_it_again(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 2) self._lightbg.set_label(_('Going') + '..') self._shake = 'random2' self._next = self._and_again self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _and_again(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 3) self._lightbg.set_label(_('Going') + '...') self._shake = 'random2' self._next = self._one_last_time self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _one_last_time(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 4) self._lightbg.set_label(_('Gone!')) self._shake = None self._next = self._yellow_dot GObject.timeout_add(500, self._next) def _fade_dot(self, dot, i): if i == 4: dot.set_shape(self._new_dot(self._colors[WHITE])) else: dot.set_shape(self._new_dot(self._colors[dot.type + i])) def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._activity.status.set_label(string) def motion_cb(self, x, y, z): if read_accelerometer.device_path is None: jiggle_factor = 5 else: jiggle_factor = 3 if self._shake is None: return elif self._shake in ['random', 'random2']: if self._shake == 'random2': jiggle_factor *= 2 for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: x += int(uniform(-jiggle_factor, jiggle_factor)) z += int(uniform(-jiggle_factor, jiggle_factor)) # Randomize z drift, which tends toward up... if int(uniform(0, 2)) == 0: z = -z dot.move_relative((x, z)) elif self._shake == 'align': docked = True yellow = 0 red = 0 blue = 0 for dot in self._dots: if dot.type == YELLOW: docked = self._dock_dot(dot, yellow + 1, 1, jiggle_factor, docked) yellow += 1 elif dot.type == RED: docked = self._dock_dot(dot, red + 2, 2, jiggle_factor, docked) red += 1 elif dot.type == BLUE: docked = self._dock_dot(dot, blue + 3, 3, jiggle_factor, docked) blue += 1 if docked: self._lightbg.set_label(_('Interesting.')) self._pausing = False elif self._shake == 'left': right = False for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: pos = dot.get_xy() if pos[0] < 0: if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) x = int(uniform(0, 10)) dot.move_relative((x, z)) elif x < 0: x += int(uniform(-10, 0)) if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) if pos[0] > -x: dot.move_relative((x, z)) pos = dot.get_xy() if pos[0] > 100: right = True if not right: self._lightbg.set_label(_('Hmm')) self._pausing = False elif self._shake == 'right': left = False for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: pos = dot.get_xy() if pos[0] > self._width - self._dot_size: if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) x = int(uniform(-10, 0)) dot.move_relative((x, z)) elif x < self._width - self._dot_size: x += int(uniform(0, 10)) if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) if pos[0] < self._width - x - self._dot_size: dot.move_relative((x, z)) pos = dot.get_xy() if pos[0] < self._width - self._dot_size - 100: left = True if not left: self._lightbg.set_label(_('Hmm')) self._pausing = False if not self._pausing: if self._next is not None: GObject.timeout_add(1000, self._next) else: self._lightbg.set_label('') self._shake = None GObject.timeout_add(100, read_accelerometer, self) return def _dock_dot(self, dot, n, m, jiggle_factor, docked): x = (self._dot_size + self._space) * n y = (self._dot_size + self._space) * m pos = dot.get_xy() dx = x - pos[0] dy = y - pos[1] if abs(dx) < 11 and abs(dy) < 11: dot.move((x, y)) return docked else: if dx < 0: dx = max(-10, dx) elif dx > 0: dx = min(10, dx) if dy < 0: dy = max(-10, dy) elif dy > 0: dy = min(10, dy) dx += int(uniform(-jiggle_factor, jiggle_factor)) dy += int(uniform(-jiggle_factor, jiggle_factor)) dot.move_relative((dx, dy)) return False def _button_press_cb(self, win, event): if self._shake is not None: return True win.grab_focus() x, y = map(int, event.get_coords()) self._press = True self._release = None spr = self._sprites.find_sprite((x, y)) if spr == None: return True self.last_spr = spr if self._rubbing: self._pausing = True if spr in self._dots: for target in self._targets: if self._dots.index(spr) == target: self._release = target GObject.timeout_add(1000, self._clear_pause) return True def _button_release_cb(self, win, event): if self._shake is not None: return True self._press = False self._release = None if self._pausing: self._lightbg.set_label(_('Rub a little longer.')) return True x, y = map(int, event.get_coords()) spr = self._sprites.find_sprite((x, y)) if spr.type is not None: if spr in self._dots: for target in self._targets: if self._dots.index(spr) == target: self._release = target if self._next is not None: GObject.timeout_add(200, self._next) def _smile(self): for dot in self._dots: dot.set_label(':)') def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * NINE def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % NINE, int(dot / NINE)] def _destroy_cb(self, win, event): Gtk.main_quit() def _new_dot(self, color): ''' generate a dot of a color color ''' self._dot_cache = {} if not color in self._dot_cache: self._stroke = color self._fill = color self._svg_width = self._dot_size self._svg_height = self._dot_size 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) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() self._dot_cache[color] = surface return self._dot_cache[color] def _line(self, vertical=True): ''' Generate a center line ''' if vertical: self._svg_width = 3 self._svg_height = self._height return svg_str_to_pixbuf( self._header() + \ self._rect(3, self._height, 0, 0) + \ self._footer()) else: self._svg_width = self._width self._svg_height = 3 return svg_str_to_pixbuf( self._header() + \ self._rect(self._width, 3, 0, 0) + \ self._footer()) 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, color='#ffffff'): 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:%s;stroke:none;"/>\n' % (color) 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'
class AbacusGeneric(): """ A generic abacus: a frame, rods, and beads. """ def __init__(self, abacus): """ Specify parameters that define the abacus """ self.abacus = abacus self.set_parameters() self.create() def set_parameters(self): """ Define the physical paramters. """ self.name = "suanpan" self.num_rods = 15 self.bot_beads = 5 self.top_beads = 2 self.base = 10 self.top_factor = 5 def create(self): """ Create and position the sprites that compose the abacus """ # Width is a function of the number of rods self.frame_width = self.num_rods*(BWIDTH+BOFFSET)+FSTROKE*2 # Height is a function of the number of beads if self.top_beads > 0: self.frame_height = (self.bot_beads+self.top_beads+5)*BHEIGHT +\ FSTROKE*2 else: self.frame_height = (self.bot_beads+2)*BHEIGHT + FSTROKE*2 # Draw the frame... x = (self.abacus.width-(self.frame_width*self.abacus.scale))/2 y = (self.abacus.height-(self.frame_height*self.abacus.scale))/2 _frame = _svg_header(self.frame_width, self.frame_height, self.abacus.scale) +\ _svg_rect(self.frame_width, self.frame_height, FSTROKE/2, FSTROKE/2, 0, 0, "#000000", "#000000") +\ _svg_rect(self.frame_width-(FSTROKE*2), self.frame_height-(FSTROKE*2), 0, 0, FSTROKE, FSTROKE, "#808080", "#000000") +\ _svg_footer() self.frame = Sprite(self.abacus.sprites, x, y, _svg_str_to_pixbuf(_frame)) self.frame.type = 'frame' # and then the rods and beads. self.rods = [] self.beads = [] x += FSTROKE*self.abacus.scale y += FSTROKE*self.abacus.scale self.draw_rods_and_beads(x, y) # Draw the dividing bar... _bar = _svg_header(self.frame_width-(FSTROKE*2), BHEIGHT, self.abacus.scale) +\ _svg_rect(self.frame_width-(FSTROKE*2), BHEIGHT, 0, 0, 0, 0, "#000000", "#000000") +\ _svg_footer() if self.top_beads > 0: self.bar = Sprite(self.abacus.sprites, x, y+(self.top_beads+2)*BHEIGHT*self.abacus.scale, _svg_str_to_pixbuf(_bar)) else: self.bar = Sprite(self.abacus.sprites, x, y-FSTROKE*self.abacus.scale, _svg_str_to_pixbuf(_bar)) self.bar.type = 'frame' self.bar.set_label_color('white') # and finally, the mark. _mark = _svg_header(20, 15, self.abacus.scale) +\ _svg_indicator() +\ _svg_footer() dx = (BWIDTH+BOFFSET)*self.abacus.scale self.mark = Sprite(self.abacus.sprites, x+(self.num_rods-1)*dx, y-((FSTROKE/2)*self.abacus.scale), _svg_str_to_pixbuf(_mark)) self.mark.type = 'mark' def draw_rods_and_beads(self, x, y): """ Draw the rods and beads """ _white = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffffff", "#000000") +\ _svg_footer() _yellow1 = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffffcc", "#000000") +\ _svg_footer() _yellow2 = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffff88", "#000000") +\ _svg_footer() _yellow3 = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffff00", "#000000") +\ _svg_footer() self.colors = [_svg_str_to_pixbuf(_white), _svg_str_to_pixbuf(_yellow1), _svg_str_to_pixbuf(_yellow2), _svg_str_to_pixbuf(_yellow3)] dx = (BWIDTH+BOFFSET)*self.abacus.scale bo = (BWIDTH-BOFFSET)*self.abacus.scale/4 ro = (BWIDTH+5)*self.abacus.scale/2 for i in range(self.num_rods): _rod = _svg_header(10, self.frame_height-(FSTROKE*2), self.abacus.scale) +\ _svg_rect(10, self.frame_height-(FSTROKE*2), 0, 0, 0, 0, ROD_COLORS[i%len(ROD_COLORS)], "#404040") +\ _svg_footer() self.rods.append(Sprite(self.abacus.sprites, x+i*dx+ro, y, _svg_str_to_pixbuf(_rod))) for b in range(self.top_beads): self.beads.append(Bead(Sprite(self.abacus.sprites, x+i*dx+bo, y+b*BHEIGHT*self.abacus.scale, self.colors[0]), 2*BHEIGHT*self.abacus.scale, self.top_factor*(pow(self.base, self.num_rods-i-1)))) for b in range(self.bot_beads): if self.top_beads > 0: self.beads.append(Bead(Sprite(self.abacus.sprites, x+i*dx+bo, y+(self.top_beads+5+b)*\ BHEIGHT*self.abacus.scale, self.colors[0]), 2*BHEIGHT*self.abacus.scale, pow(self.base,self.num_rods-i-1))) else: self.beads.append(Bead(Sprite(self.abacus.sprites, x+i*dx+bo, y+(2+b)*BHEIGHT\ *self.abacus.scale, self.colors[0]), 2*BHEIGHT*self.abacus.scale, pow(self.base,self.num_rods-i-1))) for rod in self.rods: rod.type = "frame" def hide(self): """ Hide the rod, beads, mark, and frame. """ for rod in self.rods: rod.hide() for bead in self.beads: bead.hide() self.bar.hide() self.frame.hide() self.mark.hide() def show(self): """ Show the rod, beads, mark, and frame. """ self.frame.set_layer(FRAME_LAYER) for rod in self.rods: rod.set_layer(ROD_LAYER) for bead in self.beads: bead.show() self.bar.set_layer(BAR_LAYER) self.mark.set_layer(MARK_LAYER) def set_value(self, string): """ Set abacus to value in string """ _logger.debug("restoring %s: %s" % (self.name, string)) # String has two bytes per column. v = [] for r in range(self.num_rods): v.append(0) # Convert string to column values. if len(string) == 2*self.num_rods: for i in range(self.num_rods): v[self.num_rods-i-1] = int( string[2*self.num_rods-i*2-1:2*self.num_rods-i*2]) else: _logger.debug("bad saved string %s (%d != 2*%d)" % (string, len(string), self.num_rods)) # Move the beads to correspond to column values. for r in range(self.num_rods): self.set_rod_value(r, v[r]) def set_rod_value(self, r, v): """ Move beads on rod r to represent value v """ bot = v % self.top_factor top = (v-bot)/self.top_factor top_bead_index = r*(self.top_beads+self.bot_beads) bot_bead_index = r*(self.top_beads+self.bot_beads)+self.top_beads # Clear the top. for i in range(self.top_beads): if self.beads[top_bead_index+i].get_state() == 1: self.beads[top_bead_index+i].move_up() # Clear the bottom. for i in range(self.bot_beads): if self.beads[bot_bead_index+i].get_state() == 1: self.beads[bot_bead_index+i].move_down() # Set the top. for i in range(top): self.beads[top_bead_index+self.top_beads-i-1].move_down() # Set the bottom for i in range(bot): self.beads[bot_bead_index+i].move_up() def value(self, count_beads=False): """ Return a string representing the value of each rod. """ string = '' v = [] for r in range(self.num_rods+1): # +1 for overflow v.append(0) # Tally the values on each rod. for i, bead in enumerate(self.beads): r = i/(self.top_beads+self.bot_beads) j = i % (self.top_beads+self.bot_beads) if bead.get_state() == 1: if j < self.top_beads: v[r+1] += self.top_factor else: v[r+1] += 1 if count_beads: # Save the value associated with each rod as a 2-byte integer. for j in v[1:]: string += "%2d" % (j) else: sum = 0 for bead in self.beads: sum += bead.get_value() string = str(sum) return(string) def label(self, string): """ Label the crossbar with the string. (Used with self.value) """ self.bar.set_label(string) def move_mark(self, dx): """ Move indicator horizontally across the top of the frame. """ self.mark.move_relative((dx, 0)) def fade_colors(self): """ Reduce the saturation level of every bead. """ for bead in self.beads: if bead.get_level() > 0: bead.set_color(self.colors[bead.get_level()-1]) bead.set_level(bead.get_level()-1) def move_bead(self, sprite, dy): """ Move a bead (or beads) up or down a rod. """ # find the bead associated with the sprite i = -1 for bead in self.beads: if sprite == bead.spr: i = self.beads.index(bead) break if i == -1: print "bead not found" return b = i % (self.top_beads+self.bot_beads) if b < self.top_beads: if dy > 0 and bead.get_state() == 0: self.fade_colors() bead.set_color(self.colors[3]) bead.move_down() # Make sure beads below this bead are also moved. for ii in range(self.top_beads-b): if self.beads[i+ii].state == 0: self.beads[i+ii].set_color(self.colors[3]) self.beads[i+ii].move_down() elif dy < 0 and bead.state == 1: self.fade_colors() bead.set_color(self.colors[3]) bead.move_up() # Make sure beads above this bead are also moved. for ii in range(b+1): if self.beads[i-ii].state == 1: self.beads[i-ii].set_color(self.colors[3]) self.beads[i-ii].move_up() else: if dy < 0 and bead.state == 0: self.fade_colors() bead.set_color(self.colors[3]) bead.move_up() # Make sure beads above this bead are also moved. for ii in range(b-self.top_beads+1): if self.beads[i-ii].state == 0: self.beads[i-ii].set_color(self.colors[3]) self.beads[i-ii].move_up() elif dy > 0 and bead.state == 1: self.fade_colors() bead.set_color(self.colors[3]) bead.move_down() # Make sure beads below this bead are also moved. for ii in range(self.top_beads+self.bot_beads-b): if self.beads[i+ii].state == 1: self.beads[i+ii].set_color(self.colors[3]) self.beads[i+ii].move_down()
class Game(): def __init__(self, canvas, parent=None, path=None): self._canvas = canvas self._parent = parent self._parent.show_all() self._path = path self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() self._scale = self._width / 1200. self._target = 0 self._tries = 0 self.level = 0 self._picture_cards = [] self._small_picture_cards = [] self.food_cards = [] self._group_cards = [] self._quantity_cards = [] self._balance_cards = [] self._last_twenty = [] self._background = None # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._background = Sprite( self._sprites, 0, 0, GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'background.png'), self._width, self._height)) self._background.set_layer(0) self._background.type = None self._background.hide() self.pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'word-box.png'), int(350 * self._scale), int(100 * self._scale)) for i in range(len(FOOD_DATA) / 4): FOOD.append([ FOOD_DATA[i * 4 + NAME], FOOD_DATA[i * 4 + CALS], FOOD_DATA[i * 4 + GROUP], FOOD_DATA[i * 4 + IMAGE] ]) self.food_cards.append(None) self._picture_cards.append(None) for j in range(6): self._small_picture_cards.append(None) self.allocate_food(0) x = 10 dx, dy = self.food_cards[0].get_dimensions() y = 10 for i in range(len(MYPLATE)): self.word_card_append(self._group_cards, self.pixbuf) self._group_cards[-1].type = i self._group_cards[-1].set_label(MYPLATE[i][0]) self._group_cards[-1].move((x, y)) y += int(dy * 1.25) y = 10 for i in range(len(QUANTITIES)): self.word_card_append(self._quantity_cards, self.pixbuf) self._quantity_cards[-1].type = i self._quantity_cards[-1].set_label(QUANTITIES[i]) self._quantity_cards[-1].move((x, y)) y += int(dy * 1.25) y = 10 for i in range(len(BALANCE)): self.word_card_append(self._balance_cards, self.pixbuf) self._balance_cards[-1].type = i self._balance_cards[-1].set_label(BALANCE[i]) self._balance_cards[-1].move((x, y)) y += int(dy * 1.25) self._smile = Sprite( self._sprites, int(self._width / 4), int(self._height / 4), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'correct.png'), int(self._width / 2), int(self._height / 2))) self._smile.set_label_attributes(36) self._smile.set_margins(10, 0, 10, 0) self._frown = Sprite( self._sprites, int(self._width / 4), int(self._height / 4), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'wrong.png'), int(self._width / 2), int(self._height / 2))) self._frown.set_label_attributes(36) self._frown.set_margins(10, 0, 10, 0) self.build_food_groups() self._all_clear() def allocate_food(self, i): self.picture_append( os.path.join(self._path, 'images', FOOD_DATA[i * 4 + IMAGE]), i) self.small_picture_append( os.path.join(self._path, 'images', FOOD_DATA[i * 4 + IMAGE]), i) self.word_card_append(self.food_cards, self.pixbuf, i) self.food_cards[i].type = i self.food_cards[i].set_label(FOOD_DATA[i * 4 + NAME]) def word_card_append(self, card_list, pixbuf, i=-1): if i == -1: card_list.append(Sprite(self._sprites, 10, 10, pixbuf)) else: card_list[i] = Sprite(self._sprites, 10, 10, pixbuf) card_list[i].set_label_attributes(36) card_list[i].set_margins(10, 0, 10, 0) card_list[i].hide() def picture_append(self, path, i=-1): spr = Sprite( self._sprites, int(self._width / 2.), int(self._height / 4.), GdkPixbuf.Pixbuf.new_from_file_at_size(path, int(self._width / 3.), int(9 * self._width / 12.))) if i == -1: self._picture_cards.append(spr) else: self._picture_cards[i] = spr self._picture_cards[i].type = 'picture' self._picture_cards[i].hide() def small_picture_append(self, path, i=-1): x = int(self._width / 3.) y = int(self._height / 6.) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( path, int(self._width / 6.), int(3 * self._width / 8.)) for j in range(6): # up to 6 of each card if i == -1: self._small_picture_cards.append( Sprite(self._sprites, x, y, pixbuf)) self._small_picture_cards[-1].type = 'picture' self._small_picture_cards[-1].hide() else: self._small_picture_cards[i * 6 + j] = Sprite( self._sprites, x, y, pixbuf) self._small_picture_cards[i * 6 + j].type = 'picture' self._small_picture_cards[i * 6 + j].hide() x += int(self._width / 6.) if j == 2: x = int(self._width / 3.) y += int(3 * self._width / 16.) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' for p in self._picture_cards: if p is not None: p.hide() for p in self._small_picture_cards: if p is not None: p.hide() for i, w in enumerate(self.food_cards): if w is not None: w.set_label_color('black') w.set_label(FOOD[i][NAME]) w.hide() for i, w in enumerate(self._group_cards): w.set_label_color('black') w.set_label(MYPLATE[i][0]) w.hide() for i, w in enumerate(self._quantity_cards): w.set_label_color('black') w.set_label(QUANTITIES[i]) w.hide() for i, w in enumerate(self._balance_cards): w.set_label_color('black') w.set_label(BALANCE[i]) w.hide() self._smile.hide() self._frown.hide() self._background.set_layer(1) def build_food_groups(self): self._my_plate = [[], [], [], []] for i, food in enumerate(FOOD): self._my_plate[MYPLATE[food[GROUP]][QUANT]].append(i) def new_game(self): ''' Start a new game. ''' games = { 0: self._name_that_food, 1: self._name_that_food_group, 2: self._compare_calories, 3: self._how_much_to_eat, 4: self._balanced_meal } self._all_clear() games[self.level]() self._frown.set_label('') self._smile.set_label('') self._tries = 0 def _name_that_food(self): ''' Choose food cards and one matching food picture ''' x = 10 y = 10 dx, dy = self.food_cards[0].get_dimensions() # Select some cards word_list = [] for i in range(NCARDS): j = int(uniform(0, len(FOOD))) while j in word_list: j = int(uniform(0, len(FOOD))) word_list.append(j) # Show the word cards from the list for i in word_list: if self.food_cards[i] is None: self.allocate_food(i) self.food_cards[i].set_layer(100) self.food_cards[i].move((x, y)) y += int(dy * 1.25) # Choose a random food image from the list and show it. self._target = self.food_cards[word_list[int(uniform(0, NCARDS))]].type while self._target in self._last_twenty: self._target = self.food_cards[word_list[int(uniform( 0, NCARDS))]].type self._last_twenty.append(self._target) if len(self._last_twenty) > 20: self._last_twenty.remove(self._last_twenty[0]) self._picture_cards[self._target].set_layer(100) def _name_that_food_group(self): ''' Show group cards and one food picture ''' for i in range(len(MYPLATE)): self._group_cards[i].set_layer(100) # Choose a random food image and show it. self._target = int(uniform(0, len(FOOD))) if self.food_cards[self._target] is None: self.allocate_food(self._target) self._picture_cards[self._target].set_layer(100) def _compare_calories(self): ''' Choose food cards and compare the calories ''' x = 10 y = 10 dx, dy = self.food_cards[0].get_dimensions() # Select some cards word_list = [] for i in range(6): j = int(uniform(0, len(FOOD))) while j in word_list: j = int(uniform(0, len(FOOD))) word_list.append(j) if self.food_cards[j] is None: self.allocate_food(j) # Show the word cards from the list for i in word_list: self.food_cards[i].set_layer(100) self.food_cards[i].move((x, y)) y += int(dy * 1.25) # Show food images self._target = word_list[0] for i in range(5): if FOOD[word_list[i + 1]][CALS] > FOOD[self._target][CALS]: self._target = word_list[i + 1] self._small_picture_cards[word_list[0] * 6].set_layer(100) self._small_picture_cards[word_list[1] * 6 + 1].set_layer(100) self._small_picture_cards[word_list[2] * 6 + 2].set_layer(100) self._small_picture_cards[word_list[3] * 6 + 3].set_layer(100) self._small_picture_cards[word_list[4] * 6 + 4].set_layer(100) self._small_picture_cards[word_list[5] * 6 + 5].set_layer(100) def _how_much_to_eat(self): ''' Show quantity cards and one food picture ''' for i in range(len(QUANTITIES)): self._quantity_cards[i].set_layer(100) # Choose a random image from the list and show it. self._target = int(uniform(0, len(FOOD))) if self.food_cards[self._target] is None: self.allocate_food(self._target) self._picture_cards[self._target].set_layer(100) def _balanced_meal(self): ''' A well-balanced meal ''' for i in range(2): self._balance_cards[i].set_layer(100) # Determine how many foods from each group n = [0, 0, 0, 0] n[0] = int(uniform(0, 2.5)) n[1] = int(uniform(0, 3 - n[0])) n[2] = 3 - n[0] - n[1] n[3] = 6 - n[0] - n[1] - n[2] # Fill a plate with foods from different groups meal = [] for i in range(n[0]): # Sweets j = int(uniform(0, len(self._my_plate[0]))) meal.append(self._my_plate[0][j]) for i in range(n[1]): # Dairy j = int(uniform(0, len(self._my_plate[1]))) meal.append(self._my_plate[1][j]) for i in range(n[2]): # Protein and Fruits j = int(uniform(0, len(self._my_plate[2]))) meal.append(self._my_plate[2][j]) for i in range(n[3]): # Veggies and Grains j = int(uniform(0, len(self._my_plate[3]))) meal.append(self._my_plate[3][j]) if n[0] < 2 and n[1] < 2 and n[2] < n[3]: self._target = 0 # Balanced meal else: self._target = 1 for i in range(6): if self.food_cards[meal[i]] is None: self.allocate_food(meal[i]) # Randomly position small cards self._small_picture_cards[meal[3] * 6].set_layer(100) self._small_picture_cards[meal[4] * 6 + 1].set_layer(100) self._small_picture_cards[meal[1] * 6 + 2].set_layer(100) self._small_picture_cards[meal[2] * 6 + 3].set_layer(100) self._small_picture_cards[meal[5] * 6 + 4].set_layer(100) self._small_picture_cards[meal[0] * 6 + 5].set_layer(100) def _button_press_cb(self, win, event): win.grab_focus() x, y = list(map(int, event.get_coords())) spr = self._sprites.find_sprite((x, y)) if spr == None: return # We only care about clicks on word cards if type(spr.type) != int: return # Which card was clicked? Set its label to red. spr.set_label_color('red') label = spr.labels[0] spr.set_label(label) if self.level == 0: if spr.type == self._target: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self.food_cards[self._target].set_label_color('blue') label = self.food_cards[self._target].labels[0] self.food_cards[self._target].set_label(label) elif self.level == 1: i = FOOD[self._target][GROUP] if spr.type == i: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self._group_cards[i].set_label_color('blue') label = self._group_cards[i].labels[0] self._group_cards[i].set_label(label) elif self.level == 2: if spr.type == self._target: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self.food_cards[self._target].set_label_color('blue') label = self.food_cards[self._target].labels[0] self.food_cards[self._target].set_label(label) elif self.level == 3: i = MYPLATE[FOOD[self._target][GROUP]][QUANT] if spr.type == i: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self._quantity_cards[i].set_label_color('blue') label = self._quantity_cards[i].labels[0] self._quantity_cards[i].set_label(label) elif self.level == 4: if self._target == spr.type: self._smile.set_layer(200) self._tries = 3 else: self._frown.set_layer(200) self._tries += 1 if self._tries == 3: self._balance_cards[self._target].set_label_color('blue') label = self._balance_cards[self._target].labels[0] self._balance_cards[self._target].set_label(label) else: _logger.debug('unknown play level %d' % (self.level)) # Play again if self._tries == 3: GObject.timeout_add(2000, self.new_game) else: GObject.timeout_add(1000, self._reset_game) return True def _reset_game(self): self._frown.hide() if self.level in [0, 2]: for i, w in enumerate(self.food_cards): w.set_label_color('black') w.set_label(FOOD[i][NAME]) elif self.level == 1: for i, w in enumerate(self._group_cards): w.set_label_color('black') w.set_label(MYPLATE[i][0]) elif self.level == 3: for i, w in enumerate(self._quantity_cards): w.set_label_color('black') w.set_label(QUANTITIES[i]) elif self.level == 4: for i, w in enumerate(self._balance_cards): w.set_label_color('black') w.set_label(BALANCE[i]) def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) 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()
class Game(): def __init__(self, canvas, parent=None, path=None): self._canvas = canvas self._parent = parent self._parent.show_all() self._path = path self._canvas.connect("draw", self.__draw_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self._canvas.connect('key-press-event', self._keypress_cb) self._canvas.set_can_focus(True) self._canvas.grab_focus() self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() self._scale = self._width / 1200. self._first_time = True self._loco_pos = (0, 0) self._loco_dim = (0, 0) self._loco_quadrant = 3 self._drag_pos = [0, 0] self._counter = 0 self._correct = 0 self._timeout_id = None self._pause = 200 self._press = None self._clicked = False self._dead_key = None self._waiting_for_delete = False self._waiting_for_enter = False self._seconds = 0 self._timer_id = None self.level = 0 self.score = 0 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._BG = ['background0.jpg', 'background0.jpg', 'background0.jpg', 'background1.jpg', 'background2.jpg', 'background2.jpg', 'background2.jpg'] self._backgrounds = [] for bg in self._BG: self._backgrounds.append(Sprite( self._sprites, 0, 0, GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', bg), self._width, self._height))) self._backgrounds[-1].type = 'background' self._backgrounds[-1].hide() self._panel = Sprite( self._sprites, int(400 * self._scale), int(400 * self._scale), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'ventana.png'), int(720 * self._scale), int(370 * self._scale))) self._panel.type = 'panel' self._panel.set_label(LABELS[0]) self._panel.set_label_attributes(20) self._panel.hide() self._LOCOS = glob.glob( os.path.join(self._path, 'images', 'loco*.png')) self._loco_cards = [] for loco in self._LOCOS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._loco_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._loco_cards[-1].type = 'loco' self._loco_dim = (int(150 * self._scale), int(208 * self._scale)) self._MEN = glob.glob( os.path.join(self._path, 'images', 'man*.png')) self._man_cards = [] for loco in self._MEN: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._man_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._man_cards[-1].type = 'loco' self._TAUNTS = glob.glob( os.path.join(self._path, 'images', 'taunt*.png')) self._taunt_cards = [] for loco in self._TAUNTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._taunt_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._taunt_cards[-1].type = 'loco' self._GHOSTS = glob.glob( os.path.join(self._path, 'images', 'ghost*.png')) self._ghost_cards = [] for loco in self._GHOSTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._ghost_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._ghost_cards[-1].type = 'loco' self._sticky_cards = [] self._loco_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._LOCOS[0], int(150 * self._scale), int(208 * self._scale)) self._man_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._MEN[0], int(150 * self._scale), int(208 * self._scale)) self._ghost_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._GHOSTS[0], int(150 * self._scale), int(208 * self._scale)) for i in range(len(MSGS[1])): # Check re i18n self._sticky_cards.append(Sprite(self._sprites, 0, 0, self._loco_pixbuf)) self._sticky_cards[-1].type = 'loco' self._sticky_cards[-1].set_label_attributes(24, vert_align='bottom') self._all_clear() def _time_increment(self): ''' Track seconds since start_time. ''' self._seconds = int(GObject.get_current_time() - self._start_time) self.timer_id = GObject.timeout_add(1000, self._time_increment) def _timer_reset(self): ''' Reset the timer for each level ''' self._start_time = GObject.get_current_time() if self._timer_id is not None: GObject.source_remove(self._timer_id) self._timer_id = None self.score += self._seconds self._time_increment() def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' for p in self._loco_cards: p.hide() for p in self._man_cards: p.hide() for p in self._taunt_cards: p.hide() for p in self._ghost_cards: p.hide() for p in self._sticky_cards: p.set_shape(self._loco_pixbuf) p.set_label('') p.set_label_color('white') p.hide() self._backgrounds[self.level].set_layer(BG_LAYER) def _show_time(self): self.level = 0 self._all_clear() x = int(self._width / 4.) y = int(self._height / 8.) for i in range(len(str(self.score))): self._sticky_cards[i].move((x, y)) self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(str(self.score)[i]) x += int(self._loco_dim[0] / 2.) self.score = 0 self._parent.unfullscreen() GObject.idle_add(play_audio_from_file, self, os.path.join( self._path, 'sounds', 'sonar.ogg')) GObject.timeout_add(5000, self.new_game, True) def new_game(self, first_time): ''' Start a new game at the current level. ''' self._first_time = first_time self._clicked = False # It may be time to advance to the next level. if (self.level == 6 and self._counter == len(MSGS)) or \ self._counter > 4: self._first_time = True self.level += 1 self._counter = 0 self._correct = 0 self._pause = 200 if self.level == len(self._backgrounds): self._show_time() return self._all_clear() if self._first_time: # Every game starts by putting up a panel with instructions # The panel disappears on mouse movement self._panel.set_label(LABELS[self.level]) self._panel.set_layer(PANEL_LAYER) play_audio_from_file(self, os.path.join( self._path, 'sounds', 'drip.ogg')) self._timer_reset() if self.level == 0: # Choose a random location for the Loco self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) play_audio_from_file(self, os.path.join( self._path, 'sounds', 'bark.ogg')) self._loco_cards[0].move((x, y)) self._loco_pos = (x, y) elif self.level == 1: play_audio_from_file(self, os.path.join( self._path, 'sounds', 'glass.ogg')) elif self.level == 2: play_audio_from_file(self, os.path.join( self._path, 'sounds', 'glass.ogg')) # Place some Locos on the canvas for i in range(self._counter + 1): self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 3: play_audio_from_file(self, os.path.join( self._path, 'sounds', 'bark.ogg')) # Place some Locos on the left-side of the canvas for i in range(self._counter + 1): self._loco_quadrant = int(uniform(2, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 4: # Place some Locos on the canvas with letters as labels # Just lowercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label( ALPHABETLC[int(uniform(0, len(ALPHABETLC)))]) elif self.level == 5: # Place some Locos on the canvas with letters as labels # Uppercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label( ALPHABETUC[int(uniform(0, len(ALPHABETUC)))]) elif self.level == 6: x = 0 y = 0 c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': y += self._loco_dim[1] x = 0 else: self._sticky_cards[c].move((x, y)) self._sticky_cards[c].type = i self._sticky_cards[c].set_layer(LOCO_LAYER) self._sticky_cards[c].set_label(MSGS[self._counter][i]) c += 1 x += int(self._loco_dim[0] / 2.) if self.level in [0, 1]: self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) if self.level == 0: self._move_loco(x, y, 0) else: self._taunt(x, y, 0) def _quad_to_xy(self, q): x = int(max(0, (self._width / 2.) * uniform(0, 1) - self._loco_dim[0])) if q in [0, 1]: x += int(self._width / 2.) y = int(max(0, (self._height / 2.) * uniform(0, 1) - self._loco_dim[1])) if q in [1, 2]: y += int(self._height / 2.) return x, y def _taunt(self, x, y, i): n = len(self._taunt_cards) self._taunt_cards[(i + 1) % n].hide() if self._clicked: self._timeout_id = None return True else: self._taunt_cards[i % n].move((x, y)) self._taunt_cards[i % n].set_layer(LOCO_LAYER) self._timeout_id = GObject.timeout_add( 200, self._taunt, x, y, i + 1) def _move_loco(self, x, y, i): j = (i + 1) % len(self._loco_cards) cx, cy = self._loco_cards[i].get_xy() dx = cx - x dy = cy - y if dx * dx + dy * dy < 100: self._loco_cards[j].move((x, y)) self._loco_pos = (x, y) self._loco_cards[j].hide() self._loco_cards[i].hide() self._man_cards[0].move((x, y)) self._man_cards[0].set_layer(LOCO_LAYER) self._timeout_id = None if self._pause > 50: self._pause -= 10 return True else: if dx > 0: cx -= 5 elif dx < 0: cx += 5 if dy > 0: cy -= 5 elif dy < 0: cy += 5 self._loco_cards[j].move((cx, cy)) self._loco_pos = (cx, cy) self._loco_cards[j].set_layer(LOCO_LAYER) self._loco_cards[i].hide() self._timeout_id = GObject.timeout_add( self._pause, self._move_loco, x, y, j) def _keypress_cb(self, area, event): ''' Keypress ''' # Games 4, 5, and 6 use the keyboard print 'keypress event' if self.level not in [4, 5, 6]: return True k = Gdk.keyval_name(event.keyval) u = Gdk.keyval_to_unicode(event.keyval) if self._waiting_for_enter: if k == 'Return': self._waiting_for_enter = False self._panel.hide() self._counter += 1 self._correct = 0 GObject.timeout_add(1000, self.new_game, False) return if k in NOISE_KEYS or k in WHITE_SPACE: return True if self.level == 6 and self._waiting_for_delete: if k in ['BackSpace', 'Delete']: self._waiting_for_delete = False self._sticky_cards[self._correct].set_label_color('white') self._sticky_cards[self._correct].set_label( MSGS[self._counter][ self._sticky_cards[self._correct].type]) self._panel.hide() self._panel.set_label_color('black') return if k[0:5] == 'dead_': self._dead_key = k[5:] return if self.level == 6: n = len(MSGS[self._counter]) else: n = self._counter + 1 if self.level == 6: i = self._correct if self._dead_key is not None: k = DEAD_DICTS[DEAD_KEYS.index(self._dead_key)][k] self._dead_key = None elif k in PUNCTUATION: k = PUNCTUATION[k] elif k in SPECIAL: k = SPECIAL[k] elif len(k) > 1: return True if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label_color('blue') self._sticky_cards[i].set_label(k) self._correct += 1 else: self._sticky_cards[i].set_label_color('red') self._sticky_cards[i].set_label(k) self._panel.set_label_color('red') self._panel.set_label(ALERTS[1]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_delete = True play_audio_from_file(self, os.path.join( self._path, 'sounds', 'glass.ogg')) else: for i in range(n): if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label('') self._sticky_cards[i].hide() break # Test for end condition if self.level == 6 and \ self._correct == len(MSGS[self._counter]) - \ MSGS[self._counter].count(' '): c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': continue elif MSGS[self._counter][i] != self._sticky_cards[c].labels[0]: return True c += 1 self._panel.set_label(ALERTS[0]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_enter = True GObject.idle_add(play_audio_from_file, self, os.path.join( self._path, 'sounds', 'drip.ogg')) return else: for i in range(n): if len(self._sticky_cards[i].labels[0]) > 0: return True self._counter += 1 self._correct = 0 GObject.timeout_add(1000, self.new_game, False) def _mouse_move_cb(self, win, event): ''' Move the mouse. ''' # Games 0, 3, 4, and 5 use move events x, y = map(int, event.get_coords()) if self._seconds > 1: self._panel.hide() if not self._clicked and self.level == 0: # For Game 0, see if the mouse is on the Loco dx = x - self._loco_pos[0] - self._loco_dim[0] / 2. dy = y - self._loco_pos[1] - self._loco_dim[1] / 2. if dx * dx + dy * dy < 200: self._clicked = True if self._timeout_id is not None: GObject.source_remove(self._timeout_id) # Play again self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._correct += 1 self._counter += 1 GObject.timeout_add(1000, self.new_game, False) elif self.level in [4, 5]: # For Game 4 and 5, we allow dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] elif self.level == 3: # For Game 3, we are dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] if x > self._width / 2.: self._press.set_shape(self._man_pixbuf) if self._press.type == 'loco': self._correct += 1 self._press.type = 'man' return True def _button_release_cb(self, win, event): # Game 3 uses release if self.level == 3: # Move to release if self._correct == self._counter + 1: self._counter += 1 self._correct = 0 GObject.timeout_add(2000, self.new_game, False) self._press = None self._drag_pos = [0, 0] return True def _button_press_cb(self, win, event): self._press = None x, y = map(int, event.get_coords()) if self.level == 0: return spr = self._sprites.find_sprite((x, y)) if spr is None: return if spr.type != 'loco': return if self.level < 2 and self._timeout_id is None: return if self._clicked: return # Games 1, 2, and 3 involve clicks; Games 4 and 5 allow click to drag if self.level == 1: self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._clicked = True self._counter += 1 self._correct += 1 if self._timeout_id is not None: GObject.source_remove(self._timeout_id) GObject.timeout_add(2000, self.new_game, False) elif self.level == 2: spr.set_shape(self._ghost_pixbuf) spr.type = 'ghost' if self._correct == self._counter: self._counter += 1 self._correct = 0 GObject.timeout_add(2000, self.new_game, False) else: self._correct += 1 elif self.level in [3, 4, 5]: # In Games 4 and 5, dragging is used to remove overlaps self._press = spr self._drag_pos = [x, y] return True def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) 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()
class Yupana(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = ['#FFFFFF'] self._colors.append(colors[0]) self._colors.append(colors[1]) self._colors.append('#000000') self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._width / (20 * DOT_SIZE * 1.1) self._dot_size = int(DOT_SIZE * self._scale) self._space = int(self._dot_size / 5.) self.we_are_sharing = False self._sum = 0 self._mode = 'ten' self.custom = [1, 1, 1, 1, 10] # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) Sprite(self._sprites, 0, 0, self._box(self._width, self._height, color=colors[1])) self._number_box = Sprite(self._sprites, 0, 0, self._box( self._width, 2 * self._dot_size, color=colors[1])) self._number_box.set_label_attributes(48) self._dots = [] for p in range(SIX): y = self._height - self._space Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int(p * self._width / 6) + self._space y -= self._dot_size for d in range(3): # bottom of fives row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space x = int((p * self._width / 6.) + self._dot_size / 2.) + self._space y -= self._dot_size + self._space for d in range(2): # top of fives row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int((p * self._width / 6.) + self._dot_size / 2.) + self._space y -= self._dot_size for d in range(2): # bottom of threes row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space x = int((p * self._width / 6.) + self._dot_size) + self._space y -= self._dot_size + self._space for d in range(1): # top of threes row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int((p * self._width / 6.) + self._dot_size / 2.) + self._space y -= self._dot_size for d in range(2): # twos row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) x = int((p * self._width / 6.) + self._dot_size) + self._space y -= self._dot_size for d in range(1): # ones row self._dots.append( Sprite(self._sprites, x, y, self._new_dot(self._colors[0]))) self._dots[-1].type = 0 # not set # self._dots[-1].set_label_color('white') x += self._dot_size + self._space y -= self._dot_size Sprite(self._sprites, 0, y, self._line(vertical=False)) for p in range(SIX - 1): x = int((p + 1) * self._width / 6) Sprite(self._sprites, x - 1, y, self._line(vertical=True)) # and initialize a few variables we'll need. self._all_clear() def _all_clear(self): ''' Things to reinitialize when starting up a new yupana. ''' self._sum = 0 for dot in self._dots: if dot.type > 0: dot.type = 0 dot.set_shape(self._new_dot(self._colors[0])) dot.set_label('') self._set_label(str(self._sum)) def _initiating(self): return self._activity.initiating def new_yupana(self, mode=None): ''' Create a new yupana. ''' self._all_clear() if mode is not None: self._mode = mode o = (SIX - 1) * (TEN + 1) # only label units if mode == 'ten': for i in range(TEN + 1): self._dots[o + i].set_label('1') self._dots[o - 1].set_label('10') elif mode == 'twenty': for i in range(TEN + 1): if i in [7, 10]: self._dots[o + i].set_label('1') else: self._dots[o + i].set_label('2') self._dots[o - 1].set_label('20') elif mode == 'factor': for i in range(TEN + 1): if i in [10]: self._dots[o + i].set_label('1') elif i in [8, 9]: self._dots[o + i].set_label('2') elif i in [5, 6, 7]: self._dots[o + i].set_label('3') else: self._dots[o + i].set_label('5') self._dots[o - 1].set_label('10') elif mode == 'fibonacci': for i in range(TEN + 1): if i in [10]: self._dots[o + i].set_label('1') elif i in [8, 9]: self._dots[o + i].set_label('2') elif i in [5, 6, 7]: self._dots[o + i].set_label('5') else: self._dots[o + i].set_label('20') self._dots[o - 1].set_label('60') else: # custom for i in range(TEN + 1): if i in [10]: self._dots[o + i].set_label(str(self.custom[0])) elif i in [8, 9]: self._dots[o + i].set_label(str(self.custom[1])) elif i in [5, 6, 7]: self._dots[o + i].set_label(str(self.custom[2])) else: self._dots[o + i].set_label(str(self.custom[3])) self._dots[o - 1].set_label(str(self.custom[4])) if self.we_are_sharing: _logger.debug('sending a new yupana') self._parent.send_new_yupana() def restore_yupana(self, dot_list): ''' Restore a yumpana 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( self._colors[self._dots[i].type])) if self._dots[i].type == 1: self._sum += self._calc_bead_value(i) self._set_label(str(self._sum)) def save_yupana(self): ''' Return dot list and orientation for saving to Journal or sharing ''' dot_list = [] for dot in self._dots: dot_list.append(dot.type) return [self._mode, dot_list] def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._number_box.set_label(string) # self._activity.status.set_label(string) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) spr = self._sprites.find_sprite((x, y)) if spr == None: return if spr.type is not None: spr.type += 1 spr.type %= 2 spr.set_shape(self._new_dot(self._colors[spr.type])) if self.we_are_sharing: _logger.debug('sending a click to the share') self._parent.send_dot_click(self._dots.index(spr), spr.type) if spr.type == 1: self._sum += self._calc_bead_value(self._dots.index(spr)) else: self._sum -= self._calc_bead_value(self._dots.index(spr)) self._set_label(str(self._sum)) return True def _calc_bead_value(self, i): ''' Calculate a bead value based on the index and the mode ''' e = 5 - i / (TEN + 1) m = i % 11 if self._mode == 'ten': return 10 ** e elif self._mode == 'twenty': if m in [7, 10]: return 20 ** e else: return (20 ** e) * 2 elif self._mode == 'factor': if m in [10]: return 10 ** e elif m in [8, 9]: return (10 ** e) * 2 elif m in [5, 6, 7]: return (10 ** e) * 3 else: return (10 ** e) * 5 elif self._mode == 'fibonacci': if m in [10]: return 60 ** e elif m in [8, 9]: return (60 ** e) * 2 elif m in [5, 6, 7]: return (60 ** e) * 5 else: return (60 ** e) * 20 else: # custom if m in [10]: return (self.custom[4] ** e) * self.custom[0] elif m in [8, 9]: return (self.custom[4] ** e) * self.custom[1] elif m in [5, 6, 7]: return (self.custom[4] ** e) * self.custom[2] else: return (self.custom[4] ** e) * self.custom[3] def remote_button_press(self, dot, color): ''' Receive a button press from a sharer ''' self._dots[dot].type = color self._dots[dot].set_shape(self._new_dot(self._colors[color])) 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] * TEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % TEN, int(dot / TEN)] def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) 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 _new_dot(self, color): ''' generate a dot of a color color ''' def darken(color): ''' return a darker color than color ''' gdk_fill_color = Gdk.color_parse(self._fill) gdk_fill_dark_color = Gdk.Color( int(gdk_fill_color.red * 0.5), int(gdk_fill_color.green * 0.5), int(gdk_fill_color.blue * 0.5)).to_string() return str(gdk_fill_dark_color) self._dot_cache = {} if not color in self._dot_cache: self._stroke = color self._fill = color self._fill_dark = darken(color) self._svg_width = self._dot_size self._svg_height = self._dot_size if color in ['#FFFFFF', '#000000']: pixbuf = svg_str_to_pixbuf( self._header() + \ self._circle(self._dot_size / 2., self._dot_size / 2., self._dot_size / 2.) + \ self._footer()) else: pixbuf = svg_str_to_pixbuf( self._header() + \ self._def(self._dot_size) + \ self._gradient(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) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() self._dot_cache[color] = surface return self._dot_cache[color] def _line(self, vertical=True): ''' Generate a center line ''' if vertical: self._svg_width = 3 self._svg_height = self._dot_size * 10 + self._space * 2 return svg_str_to_pixbuf( self._header() + \ self._rect(3, self._dot_size * 10 + self._space * 2, 0, 0) + \ self._footer()) else: self._svg_width = self._width self._svg_height = 3 return svg_str_to_pixbuf( self._header() + \ self._rect(self._width, 3, 0, 0) + \ self._footer()) def _box(self, w, h, color='white'): ''' Generate a box ''' self._svg_width = w self._svg_height = h return svg_str_to_pixbuf( self._header() + \ self._rect(self._svg_width, self._svg_height, 0, 0, color=color) + \ self._footer()) 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, color='black'): 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) if color == 'black': svg_string += 'style="fill:#000000;stroke:#000000;"/>\n' elif color == 'white': svg_string += 'style="fill:#ffffff;stroke:#ffffff;"/>\n' else: svg_string += 'style="fill:%s;stroke:%s;"/>\n' % (color, color) return svg_string def _circle(self, r, cx, cy): scale = (DOT_SIZE * self._scale) / 55. return '\ <g transform="matrix(%f,0,0,%f,0,0)">\ <path\ d="m 35.798426,4.2187227 c -2.210658,0.9528967 -4.993612,-0.9110169 -7.221856,0 C 23.805784,6.1692574 20.658687,10.945585 17.543179,15.051507 13.020442,21.012013 7.910957,27.325787 6.7103942,34.711004 6.0558895,38.737163 6.434461,43.510925 8.917073,46.747431 c 3.604523,4.699107 15.24614,7.62307 16.048569,7.62307 0.802429,0 8.366957,0.46766 12.036427,-1.203642 2.841316,-1.294111 5.173945,-3.766846 6.820641,-6.419428 2.543728,-4.097563 3.563068,-9.062928 4.21275,-13.841891 C 49.107723,25.018147 48.401726,15.967648 47.433639,9.0332932 47.09109,6.5796321 43.508442,7.2266282 42.329009,5.7211058 41.256823,4.3524824 42.197481,1.860825 40.813604,0.80840168 40.384481,0.48205899 39.716131,0.42556727 39.208747,0.60779459 37.650593,1.1674066 37.318797,3.5633724 35.798426,4.2187227 z"\ style="fill:none;fill-opacity:1;stroke:%s;stroke-width:3.0" />\ </g>' % ( scale, scale, self._colors[1]) def _gradient(self, r, cx, cy): scale = (DOT_SIZE * self._scale) / 55. return '\ <defs>\ <linearGradient\ id="linearGradient3769">\ <stop\ id="stop3771"\ style="stop-color:#ffff00;stop-opacity:1"\ offset="0" />\ <stop\ id="stop3773"\ style="stop-color:#ffff00;stop-opacity:0"\ offset="1" />\ </linearGradient>\ <linearGradient\ x1="10.761448"\ y1="41.003559"\ x2="56.70686"\ y2="41.003559"\ id="linearGradient2999"\ xlink:href="#linearGradient3769"\ gradientUnits="userSpaceOnUse"\ gradientTransform="matrix(0.93094239,0,0,0.93094239,-3.9217825,-2.4013121)" />\ </defs>\ <g transform="matrix(%f,0,0,%f,0,0)">\ <path\ d="m 35.798426,4.2187227 c -2.210658,0.9528967 -4.993612,-0.9110169 -7.221856,0 C 23.805784,6.1692574 20.658687,10.945585 17.543179,15.051507 13.020442,21.012013 7.910957,27.325787 6.7103942,34.711004 6.0558895,38.737163 6.434461,43.510925 8.917073,46.747431 c 3.604523,4.699107 15.24614,7.62307 16.048569,7.62307 0.802429,0 8.366957,0.46766 12.036427,-1.203642 2.841316,-1.294111 5.173945,-3.766846 6.820641,-6.419428 2.543728,-4.097563 3.563068,-9.062928 4.21275,-13.841891 C 49.107723,25.018147 48.401726,15.967648 47.433639,9.0332932 47.09109,6.5796321 43.508442,7.2266282 42.329009,5.7211058 41.256823,4.3524824 42.197481,1.860825 40.813604,0.80840168 40.384481,0.48205899 39.716131,0.42556727 39.208747,0.60779459 37.650593,1.1674066 37.318797,3.5633724 35.798426,4.2187227 z"\ style="fill:#fffec2;fill-opacity:1;stroke:#878600;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />\ <path\ d="m 15.11608,18.808876 c 1.271657,-1.444003 4.153991,-3.145785 5.495465,-1.7664 2.950062,3.033434 -6.07961,8.17155 -4.219732,11.972265 0.545606,1.114961 2.322391,1.452799 3.532799,1.177599 5.458966,-1.241154 6.490591,-12.132334 12.070397,-11.677864 1.584527,0.129058 2.526156,2.269906 2.845867,3.827199 0.453143,2.207236 -1.962667,6.182399 -1.570133,6.574932 0.392533,0.392533 2.371401,0.909584 3.140266,0.196266 1.91857,-1.779962 -0.490667,-7.752531 0.09813,-7.850664 0.5888,-0.09813 4.421663,2.851694 5.789865,5.004799 0.583188,0.917747 -0.188581,2.956817 0.8832,3.140266 2.128963,0.364398 1.601562,-5.672021 3.729066,-5.299199 1.836829,0.321884 1.450925,3.532631 1.471999,5.397332 0.06743,5.965698 -0.565586,12.731224 -4.317865,17.369596 -3.846028,4.75426 -10.320976,8.31978 -16.388263,7.556266 C 22.030921,53.720741 16.615679,52.58734 11.485147,49.131043 7.9833717,46.771994 6.8028191,42.063042 6.5784815,37.846738 6.3607378,33.754359 8.3381535,29.765466 10.111281,26.070741 c 1.271951,-2.650408 2.940517,-4.917813 5.004799,-7.261865 z"\ style="fill:url(#linearGradient2999);fill-opacity:1;stroke:none" />\ <path\ d="m 32.382709,4.7758124 c -0.123616,1.0811396 1.753928,2.8458658 2.728329,2.9439992 0.974405,0.098134 6.718874,0.7298319 9.159392,-0.1962668 0.820281,-0.3112699 0.968884,-0.9547989 0.974407,-1.4719993 0.02053,-1.9240971 0.03247,-4.7715376 -3.507853,-5.49546551 C 39.556079,0.11012647 37.217081,1.4131653 35.500801,2.2243463 34.054814,2.9077752 32.496703,3.7788369 32.382709,4.7758124 z"\ style="fill:#b69556;fill-opacity:1;stroke:#b69556;stroke-width:1.31189477px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></g>' % ( scale, scale) def _def(self, r): return ' <defs>\ <linearGradient\ id="linearGradient3755">\ <stop\ id="stop3757"\ style="stop-color:%s;stop-opacity:1"\ offset="0" />\ <stop\ id="stop3759"\ style="stop-color:%s;stop-opacity:1"\ offset="1" />\ </linearGradient>\ <radialGradient\ cx="0"\ cy="0"\ r="%f"\ fx="%f"\ fy="%f"\ id="radialGradient3761"\ xlink:href="#linearGradient3755"\ gradientUnits="userSpaceOnUse" />\ </defs>\ ' % (self._fill, self._fill_dark, r, r / 3, r / 3) def _footer(self): return '</svg>\n'
class Bar(): ''' The Bar class is used to define the bars at the bottom of the screen ''' def __init__(self, sprites, width, height, scale, size): ''' Initialize the 2-segment bar, labels, and mark ''' self.sprites = sprites self.bars = {} self.screen_width = width self.screen_height = height self.scale = scale self.ball_size = size self.make_bar(2) self.make_mark() self.make_labels() def make_mark(self): ''' Make a mark to show the fraction position on the bar. ''' mark = svg_header(self.ball_size / 2., BAR_HEIGHT * self.scale + 4, 1.0) + \ svg_rect(self.ball_size / 2., BAR_HEIGHT * self.scale + 4, 0, 0, 0, 0, '#FF0000', '#FF0000') + \ svg_rect(1, BAR_HEIGHT * self.scale + 4, 0, 0, self.ball_size / 4., 0, '#000000', '#000000') + \ svg_footer() self.mark = Sprite( self.sprites, 0, self.screen_height, # hide off bottom of screen svg_str_to_pixbuf(mark)) self.mark.set_layer(2) def mark_width(self): return self.mark.rect[2] def bar_x(self): return self.bars[2].get_xy()[0] def bar_y(self): return self.bars[2].get_xy()[1] def width(self): return self.bars[2].rect[2] def height(self): return self.bars[2].rect[3] def hide_bars(self): ''' Hide all of the bars ''' for bar in self.bars: self.bars[bar].set_layer(-1) def make_labels(self): ''' Label the bar ''' num = svg_header(BAR_HEIGHT * self.scale, BAR_HEIGHT * self.scale, 1.0) + \ svg_rect(BAR_HEIGHT * self.scale, BAR_HEIGHT * self.scale, 0, 0, 0, 0, 'none', 'none') + \ svg_footer() self.left = Sprite(self.sprites, int(self.ball_size / 4), self.bar_y(), svg_str_to_pixbuf(num)) self.left.set_label(_('0')) self.right = Sprite(self.sprites, self.screen_width - int(self.ball_size / 2), self.bar_y(), svg_str_to_pixbuf(num)) self.right.set_label(_('1')) def get_bar(self, nsegments): ''' Return a bar with n segments ''' if nsegments not in self.bars: self.make_bar(nsegments) return self.bars[nsegments] def make_bar(self, nsegments): ''' Create a bar with n segments ''' svg = svg_header(self.screen_width - self.ball_size, BAR_HEIGHT, 1.0) dx = (self.screen_width - self.ball_size) / float(nsegments) for i in range(int(nsegments) / 2): svg += svg_rect(dx, BAR_HEIGHT * self.scale, 0, 0, i * 2 * dx, 0, '#FFFFFF', '#FFFFFF') svg += svg_rect(dx, BAR_HEIGHT * self.scale, 0, 0, (i * 2 + 1) * dx, 0, '#AAAAAA', '#AAAAAA') if int(nsegments) % 2 == 1: # odd svg += svg_rect(dx, BAR_HEIGHT * self.scale, 0, 0, (i * 2 + 2) * dx, 0, '#FFFFFF', '#FFFFFF') svg += svg_footer() self.bars[nsegments] = Sprite(self.sprites, 0, 0, svg_str_to_pixbuf(svg)) self.bars[nsegments].move( (int(self.ball_size / 2), self.screen_height - \ int((self.ball_size + self.height()) / 2)))