class Tab(): """ Create tabs for the slide; include a TextView for OSK input """ def __init__(self, sprites, path, name, x, y, w, h): self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) self.spr.label = "1.0" self.spr.type = name self.name = name self.width = w self.textview = None self.textbuffer = None self.fixed = None self.textview_y_offset = 0 def label(self, label): if self.textbuffer is not None: self.textbuffer.set_text(label) def _move_textview(self, x, y): y += self.textview_y_offset if self.textview is not None: if x > 0 and x < Gdk.Screen.width() - self.width and y > 0: self.fixed.move(self.textview, x, y) self.textview.show() else: self.textview.hide() def move(self, x, y): self.spr.move((x, y)) self._move_textview(x, y) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) x, y = self.spr.get_xy() self._move_textview(x, y) def draw(self, layer=100): self.spr.set_layer(layer) self.spr.draw() x, y = self.spr.get_xy() self._move_textview(x, y) def hide(self): self.spr.hide()
class Tab(): """ Create tabs for the slide; include a TextView for OSK input """ def __init__(self, sprites, path, name, x, y, w, h): self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h)) self.spr.label = "1.0" self.spr.type = name self.name = name self.width = w self.textview = None self.textbuffer = None self.fixed = None self.textview_y_offset = 0 def label(self, label): if self.textbuffer is not None: self.textbuffer.set_text(label) def _move_textview(self, x, y): y += self.textview_y_offset if self.textview is not None: if x > 0 and x < Gdk.Screen.width() - self.width and y > 0: self.fixed.move(self.textview, x, y) self.textview.show() else: self.textview.hide() def move(self, x, y): self.spr.move((x, y)) self._move_textview(x, y) def move_relative(self, dx, dy): self.spr.move_relative((dx, dy)) x, y = self.spr.get_xy() self._move_textview(x, y) def draw(self, layer=100): self.spr.set_layer(layer) self.spr.draw() x, y = self.spr.get_xy() self._move_textview(x, y) def hide(self): self.spr.hide()
class Ball(): ''' The Bounce class is used to define the ball and the user interaction. ''' def __init__(self, sprites, filename): self.current_frame = 0 self.frames = [] # Easter Egg animation self.sprites = sprites self.ball = Sprite(self.sprites, 0, 0, svg_str_to_pixbuf( svg_from_file(filename))) self.ball.set_layer(1) self.ball.set_label_attributes(24) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self.frames.append(Sprite( self.sprites, 0, 0, svg_str_to_pixbuf( svg_header(SIZE, SIZE, 1.0) + TRANSFORMS[i] + \ ball + PUNCTURE + AIR + '</g>' + svg_footer()))) for frame in self.frames: frame.set_layer(1) frame.move((0, -SIZE)) # move animation frames off screen def new_ball(self, filename): ''' Create a ball object and Easter Egg animation from an SVG file. ''' self.ball.images[0] = svg_str_to_pixbuf(svg_from_file(filename)) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self.frames[i].images[0] = svg_str_to_pixbuf( svg_header(SIZE, SIZE, 1.0) + TRANSFORMS[i] + \ ball + PUNCTURE + AIR + '</g>' + svg_footer()) def new_ball_from_image(self, filename): ''' Just create a ball object from an image file ''' if filename == '': _logger.debug('Image file not found.') return try: self.ball.images[0] = gtk.gdk.pixbuf_new_from_file_at_size( filename, SIZE, SIZE) except: _logger.debug('Could not load image from %s.', filename) def ball_x(self): return self.ball.get_xy()[0] def ball_y(self): return self.ball.get_xy()[1] def frame_x(self, i): return self.frames[i].get_xy()[0] def frame_y(self, i): return self.frames[i].get_xy()[1] def width(self): return self.ball.rect[2] def height(self): return self.ball.rect[3] def move_ball(self, pos): self.ball.move(pos) def move_ball_relative(self, pos): self.ball.move_relative(pos) def move_frame(self, i, pos): self.frames[i].move(pos) def move_frame_relative(self, i, pos): self.frames[i].move_relative(pos) def hide_frames(self): for frame in self.frames: frame.move((0, -SIZE)) # hide the animation frames def next_frame(self, frame_counter): if frame_counter in ANIMATION: self._switch_frames(ANIMATION[frame_counter]) return self.current_frame def _switch_frames(self, frames): ''' Switch between frames in the animation ''' self.move_frame(frames[1], (self.frame_x(frames[0]), self.frame_y(frames[0]))) self.move_frame(frames[0], ((0, -SIZE))) # hide the frame self.current_frame = frames[1]
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = colors self._canvas = canvas parent.show_all() 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._height / (14.0 * DOT_SIZE * 1.2) self._scale_gameover = self._height / (4.0 * DOT_SIZE_GAMEOVER * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._dot_size_gameover = int(DOT_SIZE_GAMEOVER * self._scale) self._turtle_offset = 0 self._space = int(self._dot_size / 5.) self._space_gameover = int(self._dot_size_gameover / 5.) self._orientation = 0 self.level = 0 self.custom_strategy = None self.strategies = [ BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY, self.custom_strategy ] self.strategy = self.strategies[self.level] self._timeout_id = None self.best_time = self.load_best_time() # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] self._gameover = [] self._your_time = [] self._best_time = [] self._win_lose = [] for y in range(THIRTEEN): for x in range(THIRTEEN): offset_x = int((self._width - THIRTEEN * (self._dot_size + \ self._space) - self._space) / 2.) if y % 2 == 1: offset_x += int((self._dot_size + self._space) / 2.) if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1: self._dots.append( Sprite(self._sprites, offset_x + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot('#B0B0B0', self._dot_size))) else: self._dots.append( Sprite( self._sprites, offset_x + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[FILL], self._dot_size))) self._dots[-1].type = False # not set # Put a turtle at the center of the screen... self._turtle_images = [] self._rotate_turtle(self._new_turtle()) self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0]) self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) # ...and initialize. self._all_clear() def _move_turtle(self, pos): ''' Move turtle and add its offset ''' self._turtle.move(pos) self._turtle.move_relative( (-self._turtle_offset, -self._turtle_offset)) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' # Clear dots for gameover_shape in self._gameover: gameover_shape.hide() for win_lose_shape in self._win_lose: win_lose_shape.hide() for your_time_shape in self._your_time: your_time_shape.hide() for highscore_shape in self._best_time: highscore_shape.hide() for dot in self._dots: if dot.type: dot.type = False dot.set_shape(self._new_dot(self._colors[FILL], self._dot_size)) dot.set_label('') dot.set_layer(100) self._turtle.set_layer(100) # Recenter the turtle self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) self._turtle.set_shape(self._turtle_images[0]) self._set_label('') if self._timeout_id is not None: GLib.source_remove(self._timeout_id) self._timeout_id = None def new_game(self, saved_state=None): ''' Start a new game. ''' self.gameover_flag = False self.game_lost = False self._all_clear() # Fill in a few dots to start for i in range(15): n = int(uniform(0, THIRTEEN * THIRTEEN)) if self._dots[n].type is not None: self._dots[n].type = True self._dots[n].set_shape( self._new_dot(self._colors[STROKE], self._dot_size)) # Calculate the distances to the edge self._initialize_weights() self.game_start_time = time.time() self.strategy = self.strategies[self.level] self._timeout_id = None def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._activity.status.set_label(string) 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), inverse=True) if spr == None: return if spr.type is not None and not spr.type: spr.type = True spr.set_shape(self._new_dot(self._colors[STROKE], self._dot_size)) self._weights[self._dots.index(spr)] = 1000 self._test_game_over(self._move_the_turtle()) return True def _find_the_turtle(self): turtle_pos = self._turtle.get_xy() turtle_dot = None for dot in self._dots: pos = dot.get_xy() # Turtle is offset if pos[0] == turtle_pos[0] + self._turtle_offset and \ pos[1] == turtle_pos[1] + self._turtle_offset: turtle_dot = self._dots.index(dot) break if turtle_dot is None: _logger.debug('Cannot find the turtle...') return None return turtle_dot def _move_the_turtle(self): ''' Move the turtle after each click ''' self._turtle_dot = self._find_the_turtle() if self._turtle_dot is None: return # Given the col and row of the turtle, do something new_dot = self._grid_to_dot( self._my_strategy_import(self.strategy, self._dot_to_grid(self._turtle_dot))) self._move_turtle(self._dots[new_dot].get_xy()) # And set the orientation self._turtle.set_shape(self._turtle_images[self._orientation]) return new_dot def _test_game_over(self, new_dot): ''' Check to see if game is over ''' if new_dot is None: return if self._dots[new_dot].type is None: # Game-over feedback self._once_around = False self.game_stop_time = time.time() self.gameover_flag = True self._happy_turtle_dance() self._timeout_id = GLib.timeout_add(10000, self._game_over) return True c = int(self._turtle_dot / THIRTEEN) % 2 if self._dots[ new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \ self._dots[ new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \ self._dots[ new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \ self._dots[ new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \ self._dots[ new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \ self._dots[ new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type: # Game-over feedback for dot in self._dots: dot.set_label(':)') self.game_stop_time = time.time() self.gameover_flag = True self._timeout_id = GLib.timeout_add(4000, self._game_over) return True return False def _game_over(self): best_seconds = self.best_time % 60 best_minutes = self.best_time // 60 self.elapsed_time = int(self.game_stop_time - self.game_start_time) second = self.elapsed_time % 60 minute = self.elapsed_time // 60 for dot in self._dots: dot.hide() self._turtle.hide() offset_y = int(self._space_gameover / 4.) offset_x = int((self._width - 6 * self._dot_size_gameover - 5 * self._space_gameover) / 2.) y = 1.5 for x in range(2, 6): self._gameover.append( Sprite( self._sprites, offset_x + (x - 0.50) * self._dot_size_gameover, y * (self._dot_size + self._space) + offset_y, self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._gameover[-1].type = -1 # No image self._gameover[-1].set_label_attributes(72) text = ["☻", " Game ", " Over ", "☻"] self.rings(len(text), text, self._gameover) y = 4.5 for x in range(2, 6): self._win_lose.append( Sprite( self._sprites, offset_x + (x - 0.50) * self._dot_size_gameover, y * (self._dot_size + self._space) + offset_y, self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._win_lose[-1].type = -1 # No image self._win_lose[-1].set_label_attributes(72) text_win_best_time = ["☻", " YOU ", " WON ", "☻"] text_lose = ["☹", " YOU ", " LOST ", "☹"] text_win = ["☻", " GOOD ", " JOB ", "☻"] if self.game_lost: self.rings(len(text_lose), text_lose, self._win_lose) elif self.elapsed_time <= self.best_time: self.rings(len(text_win_best_time), text_win_best_time, self._win_lose) else: self.rings(len(text_win), text_win, self._win_lose) y = 7.5 for x in range(2, 5): self._your_time.append( Sprite( self._sprites, offset_x + x * self._dot_size_gameover, y * (self._dot_size + self._space), self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._your_time[-1].type = -1 # No image self._your_time[-1].set_label_attributes(72) text = [ " your ", " time: ", (' {:02d}:{:02d} '.format(minute, second)) ] self.rings(len(text), text, self._your_time) y = 10.5 for x in range(2, 5): self._best_time.append( Sprite( self._sprites, offset_x + x * self._dot_size_gameover, y * (self._dot_size + self._space), self._new_dot(self._colors[FILL], self._dot_size_gameover))) self._best_time[-1].type = -1 # No image self._best_time[-1].set_label_attributes(72) if self.elapsed_time <= self.best_time and not self.game_lost: self.best_time = self.elapsed_time best_seconds = second best_minutes = minute text = [ " best ", " time: ", (' {:02d}:{:02d} '.format(best_minutes, best_seconds)) ] self.rings(len(text), text, self._best_time) self.save_best_time() self._timeout_id = GLib.timeout_add(7000, self.new_game) def rings(self, num, text, shape): i = 0 for x in range(num): shape[x].type = -1 shape[x].set_shape( self._new_dot(self._colors[FILL], self._dot_size_gameover)) shape[x].set_label(text[i]) shape[x].set_layer(100) i += 1 def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * THIRTEEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % THIRTEEN, int(dot / THIRTEEN)] def _happy_turtle_dance(self): ''' Turtle dances along the edge ''' self.game_lost = True i = self._find_the_turtle() if i == 0: if self._once_around: return else: self._once_around = True _logger.debug(i) x, y = self._dot_to_grid(i) if y == 0: x += 1 if x == 0: y -= 1 if x == THIRTEEN - 1: y += 1 if y == THIRTEEN - 1: x -= 1 i = self._grid_to_dot((x, y)) self._dots[i].set_label(':)') self._move_turtle(self._dots[i].get_xy()) self._orientation += 1 self._orientation %= 6 self._turtle.set_shape(self._turtle_images[self._orientation]) self._timeout_id = GLib.timeout_add(250, self._happy_turtle_dance) def _ordered_weights(self, pos): ''' Returns the list of surrounding points sorted by their distance to the edge ''' dots = self._surrounding_dots(pos) dots_and_weights = [] for dot in dots: dots_and_weights.append((dot, self._weights[dot])) sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1]) for i in range(6): dots[i] = sorted_dots[i][0] return dots def _daylight_ahead(self, pos): ''' Returns true if there is a straight path to the edge from the current position/orientation ''' dots = self._surrounding_dots(pos) while True: dot_type = self._dots[dots[self._orientation]].type if dot_type is None: return True elif dot_type: return False else: # keep looking pos = self._dot_to_grid(dots[self._orientation]) dots = self._surrounding_dots(pos) def _surrounding_dots(self, pos): ''' Returns dots surrounding a position in the grid ''' dots = [] evenodd = pos[1] % 2 for i in range(6): col = pos[0] + CIRCLE[evenodd][i][0] row = pos[1] + CIRCLE[evenodd][i][1] dots.append(self._grid_to_dot((col, row))) return dots def _initialize_weights(self): ''' How many steps to an edge? ''' self._weights = [] for d, dot in enumerate(self._dots): if dot.type is None: self._weights.append(0) elif dot.type: self._weights.append(1000) else: pos = self._dot_to_grid(d) pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1]) self._weights.append( min(min(pos[0], pos2[0]), min(pos[1], pos2[1]))) def _my_strategy_import(self, f, arg): ''' Run Python code passed as argument ''' userdefined = {} try: exec(f, globals(), userdefined) return userdefined['_turtle_strategy'](self, arg) except ZeroDivisionError as e: self._set_label('Python zero-divide error: {}'.format(e)) except ValueError as e: self._set_label('Python value error: {}'.format(e)) except SyntaxError as e: self._set_label('Python syntax error: {}'.format(e)) except NameError as e: self._set_label('Python name error: {}'.format(e)) except OverflowError as e: self._set_label('Python overflow error: {}'.format(e)) except TypeError as e: self._set_label('Python type error: {}'.format(e)) except: self._set_label('Python error') traceback.print_exc() return None 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, dot_size): ''' generate a dot of a color color ''' self._stroke = color self._fill = color self._svg_width = dot_size self._svg_height = dot_size return svg_str_to_pixbuf( self._header() + \ self._circle(dot_size / 2., dot_size / 2., dot_size / 2.) + \ self._footer()) def _new_turtle(self): ''' generate a turtle ''' self._svg_width = self._dot_size * 2 self._svg_height = self._dot_size * 2 self._stroke = '#101010' self._fill = '#404040' return svg_str_to_pixbuf( self._header() + \ self._turtle() + \ self._footer()) def _rotate_turtle(self, image): w, h = image.get_width(), image.get_height() nw = nh = int(sqrt(w * w + h * h)) for i in range(6): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh) context = cairo.Context(surface) context.translate(w / 2., h / 2.) context.rotate((30 + i * 60) * pi / 180.) context.translate(-w / 2., -h / 2.) Gdk.cairo_set_source_pixbuf(context, image, 0, 0) context.rectangle(0, 0, nw, nh) context.fill() self._turtle_images.append(surface) self._turtle_offset = int(self._dot_size / 2.) 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 _circle(self, r, cx, cy): return '<circle style="fill:' + str(self._fill) + ';stroke:' + \ str(self._stroke) + ';" r="' + str(r - 0.5) + '" cx="' + \ str(cx) + '" cy="' + str(cy) + '" />\n' def _footer(self): return '</svg>\n' def _turtle(self): svg = '<g\ntransform="scale(%.1f, %.1f)">\n' % (self._svg_width / 60., self._svg_height / 60.) svg += '%s%s%s%s%s%s%s%s' % ( ' <path d="M 27.5 48.3 ', 'C 26.9 48.3 26.4 48.2 25.9 48.2 L 27.2 50.5 L 28.6 48.2 ', 'C 28.2 48.2 27.9 48.3 27.5 48.3 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 40.2 11.7 ', 'C 38.0 11.7 36.2 13.3 35.8 15.3 ', 'C 37.7 16.7 39.3 18.4 40.5 20.5 ', 'C 42.8 20.4 44.6 18.5 44.6 16.2 ', 'C 44.6 13.7 42.6 11.7 40.2 11.7 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 40.7 39.9 ', 'C 39.5 42.1 37.9 44.0 35.9 45.4 ', 'C 36.4 47.3 38.1 48.7 40.2 48.7 ', 'C 42.6 48.7 44.6 46.7 44.6 44.3 ', 'C 44.6 42.0 42.9 40.2 40.7 39.9 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 14.3 39.9 ', 'C 12.0 40.1 10.2 42.0 10.2 44.3 ', 'C 10.2 46.7 12.2 48.7 14.7 48.7 ', 'C 16.7 48.7 18.5 47.3 18.9 45.4 ', 'C 17.1 43.9 15.5 42.1 14.3 39.9 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 19.0 15.4 ', 'C 18.7 13.3 16.9 11.7 14.7 11.7 ', 'C 12.2 11.7 10.2 13.7 10.2 16.2 ', 'C 10.2 18.5 12.1 20.5 14.5 20.6 ', 'C 15.7 18.5 17.2 16.8 19.0 15.4 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 27.5 12.6 ', 'C 29.4 12.6 31.2 13.0 32.9 13.7 ', 'C 33.7 12.6 34.1 11.3 34.1 9.9 ', 'C 34.1 6.2 31.1 3.2 27.4 3.2 ', 'C 23.7 3.2 20.7 6.2 20.7 9.9 ', 'C 20.7 11.3 21.2 12.7 22.0 13.7 ', 'C 23.7 13.0 25.5 12.6 27.5 12.6 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s%s%s%s%s%s%s%s' % ( ' <path d="M 43.1 30.4 ', 'C 43.1 35.2 41.5 39.7 38.5 43.0 ', 'C 35.6 46.4 31.6 48.3 27.5 48.3 ', 'C 23.4 48.3 19.4 46.4 16.5 43.0 ', 'C 13.5 39.7 11.9 35.2 11.9 30.4 ', 'C 11.9 20.6 18.9 12.6 27.5 12.6 ', 'C 36.1 12.6 43.1 20.6 43.1 30.4 Z" stroke_width="3.5" ', 'fill="', self._fill, ';" stroke="', self._stroke, '" />\n') svg += '%s%s%s%s%s' % ( ' <path d="M 25.9 33.8 L 24.3 29.1 ', 'L 27.5 26.5 L 31.1 29.2 L 29.6 33.8 Z" stroke_width="3.5" ', 'fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 27.5 41.6 ', 'C 23.5 41.4 22.0 39.5 22.0 39.5 L 25.5 35.4 L 30.0 35.5 ', 'L 33.1 39.7 C 33.1 39.7 30.2 41.7 27.5 41.6 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 18.5 33.8 ', 'C 17.6 30.9 18.6 27.0 18.6 27.0 L 22.6 29.1 L 24.1 33.8 ', 'L 20.5 38.0 C 20.5 38.0 19.1 36.0 18.4 33.8 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 19.5 25.1 ', 'C 19.5 25.1 20.0 23.2 22.5 21.3 ', 'C 24.7 19.7 27.0 19.6 27.0 19.6 L 26.9 24.6 L 23.4 27.3 ', 'L 19.5 25.1 Z" stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 32.1 27.8 L 28.6 25.0 ', 'L 29 19.8 C 29 19.8 30.8 19.7 33.0 21.4 ', 'C 35.2 23.2 36.3 26.4 36.3 26.4 L 32.1 27.8 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '%s%s%s%s%s%s' % ( ' <path d="M 31.3 34.0 L 32.6 29.6 ', 'L 36.8 28.0 C 36.8 28.0 37.5 30.7 36.8 33.7 ', 'C 36.2 36.0 34.7 38.1 34.7 38.1 L 31.3 34.0 Z" ', 'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n') svg += '</g>\n' return svg def save_best_time(self): file_path = os.path.join(get_activity_root(), 'data', 'best-time') best_time = [180] if os.path.exists(file_path): with open(file_path, "r") as fp: best_time = fp.readlines() int_best_time = int(best_time[0]) if not int_best_time <= self.elapsed_time and not self.game_lost: int_best_time = self.elapsed_time with open(file_path, "w") as fp: fp.write(str(int_best_time)) def load_best_time(self): file_path = os.path.join(get_activity_root(), 'data', 'best-time') if os.path.exists(file_path): with open(file_path, "r") as fp: highscore = fp.readlines() try: return int(highscore[0]) except (ValueError, IndexError) as e: logging.exception(e) return 0 return 0
class Ball(): ''' The Bounce class is used to define the ball and the user interaction. ''' def __init__(self, sprites, filename): self._current_frame = 0 self._frames = [] # Easter Egg animation self._sprites = sprites self.ball = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf(svg_from_file(filename))) self.ball.set_layer(3) self.ball.set_label_attributes(24, vert_align='top') ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames.append( Sprite( self._sprites, 0, 0, svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer()))) for frame in self._frames: frame.set_layer(3) frame.move((0, -SIZE[1])) # move animation frames off screen def new_ball(self, filename): ''' Create a ball object and Easter Egg animation from an SVG file. ''' self.ball.set_shape(svg_str_to_pixbuf(svg_from_file(filename))) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames[i].set_shape( svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer())) def new_ball_from_image(self, filename, save_path): ''' Just create a ball object from an image file ''' if filename == '': _logger.debug('Image file not found.') return try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) if pixbuf.get_width() > pixbuf.get_height(): size = pixbuf.get_height() x = int((pixbuf.get_width() - size) / 2) else: size = pixbuf.get_width() x = int((pixbuf.get_height() - size) / 2) crop = GdkPixbuf.Pixbuf.new(0, True, 8, size, size) pixbuf.copy_area(x, 0, size, size, crop, 0, 0) scale = crop.scale_simple(85, 85, GdkPixbuf.InterpType.BILINEAR) scale.savev(save_path, 'png', [], []) self.ball.set_shape(svg_str_to_pixbuf( generate_ball_svg(save_path))) except Exception as e: _logger.error('Could not load image from %s: %s' % (filename, e)) def new_ball_from_fraction(self, fraction): ''' Create a ball with a section of size fraction. ''' r = SIZE[0] / 2.0 self.ball.set_shape( svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + svg_sector(r, r + BOX[1], r - 1, 1.999 * pi, COLORS[0], COLORS[1]) + svg_sector(r, r + BOX[1], r - 1, fraction * 2 * pi, COLORS[1], COLORS[0]) + svg_rect(BOX[0], BOX[1], 4, 4, 0, 0, '#FFFFFF', 'none') + svg_footer())) def ball_x(self): return self.ball.get_xy()[0] def ball_y(self): return self.ball.get_xy()[1] def frame_x(self, i): return self._frames[i].get_xy()[0] def frame_y(self, i): return self._frames[i].get_xy()[1] def width(self): return self.ball.rect[2] def height(self): return self.ball.rect[3] def move_ball(self, pos): self.ball.move(pos) def move_ball_relative(self, pos): self.ball.move_relative(pos) def move_frame(self, i, pos): self._frames[i].move(pos) def move_frame_relative(self, i, pos): self._frames[i].move_relative(pos) def hide_frames(self): for frame in self._frames: frame.move((0, -SIZE[1])) # hide the animation frames def next_frame(self, frame_counter): if frame_counter in ANIMATION: self._switch_frames(ANIMATION[frame_counter]) return self._current_frame def _switch_frames(self, frames): ''' Switch between frames in the animation ''' self.move_frame(frames[1], (self.frame_x(frames[0]), self.frame_y(frames[0]))) self.move_frame(frames[0], ((0, -SIZE[1]))) # hide the frame self._current_frame = frames[1]
class TurtleGraphics: """ A class for the Turtle graphics canvas """ def __init__(self, tw, width, height): """ Create a sprite to hold the canvas. """ self.tw = tw self.width = width self.height = height if self.tw.interactive_mode: self.canvas = Sprite(tw.sprite_list, 0, 0, gtk.gdk.Pixmap(self.tw.area, self.width * 2, self.height * 2, -1)) else: self.canvas = Sprite(None, 0, 0, self.tw.window) self.canvas.set_layer(CANVAS_LAYER) (self.cx, self.cy) = self.canvas.get_xy() self.canvas.type = 'canvas' self.gc = self.canvas.images[0].new_gc() self.cm = self.gc.get_colormap() self.fgrgb = [255, 0, 0] self.fgcolor = self.cm.alloc_color('red') self.bgrgb = [255, 248, 222] self.bgcolor = self.cm.alloc_color('#fff8de') self.textsize = 48 # depreciated self.textcolor = self.cm.alloc_color('blue') self.tw.active_turtle.show() self.shade = 0 self.pendown = False self.xcor = 0 self.ycor = 0 self.heading = 0 self.pensize = 5 self.tcolor = 0 self.color = 0 self.gray = 100 self.fill = False self.poly_points = [] self.svg = SVG() self.svg.set_fill_color('none') self.tw.svg_string = '' self.clearscreen(False) def start_fill(self): """ Start accumulating points of a polygon to fill. """ self.fill = True self.poly_points = [] def stop_fill(self): """ Fill the polygon. """ self.fill = False if len(self.poly_points) == 0: return minx = self.poly_points[0][0] miny = self.poly_points[0][1] maxx = minx maxy = miny for p in self.poly_points: if p[0] < minx: minx = p[0] elif p[0] > maxx: maxx = p[0] if p[1] < miny: miny = p[1] elif p[1] > maxy: maxy = p[1] w = maxx - minx h = maxy - miny self.canvas.images[0].draw_polygon(self.gc, True, self.poly_points) self.invalt(minx - self.pensize * self.tw.coord_scale / 2 - 3, miny - self.pensize * self.tw.coord_scale / 2 - 3, w + self.pensize * self.tw.coord_scale + 6, h + self.pensize * self.tw.coord_scale + 6) self.poly_points = [] def clearscreen(self, share=True): """Clear the canvas and reset most graphics attributes to defaults.""" rect = gtk.gdk.Rectangle(0, 0, self.width, self.height) self.gc.set_foreground(self.bgcolor) self.canvas.images[0].draw_rectangle(self.gc, True, *rect) self.invalt(0, 0, self.width, self.height) self.setpensize(5, share) self.setgray(100, share) self.setcolor(0, share) self.settextcolor(70) self.setshade(50, share) for turtle_key in iter(self.tw.turtles.dict): self.set_turtle(turtle_key) self.tw.active_turtle.set_color(0) self.tw.active_turtle.set_shade(50) self.tw.active_turtle.set_gray(100) self.tw.active_turtle.set_pen_size(5) self.tw.active_turtle.reset_shapes() self.seth(0, share) self.setpen(False, share) self.setxy(0, 0, share) self.setpen(True, share) self.tw.active_turtle.hide() self.set_turtle(self.tw.default_turtle_name) self.tw.svg_string = '' self.svg.reset_min_max() self.fill = False self.poly_points = [] def forward(self, n, share=True): """ Move the turtle forward.""" nn = n * self.tw.coord_scale self.gc.set_foreground(self.fgcolor) oldx, oldy = self.xcor, self.ycor try: self.xcor += nn * sin(self.heading * DEGTOR) self.ycor += nn * cos(self.heading * DEGTOR) except TypeError, ValueError: _logger.debug("bad value sent to %s" % (__name__)) return if self.pendown: self.draw_line(oldx, oldy, self.xcor, self.ycor) self.move_turtle() if self.tw.saving_svg and self.pendown: self.tw.svg_string += self.svg.new_path(oldx, self.height / 2 - oldy) self.tw.svg_string += self.svg.line_to(self.xcor, self.height / 2 - self.ycor) self.tw.svg_string += "\"\n" self.tw.svg_string += self.svg.style() if self.tw.sharing() and share: self.tw.activity.send_event("f|%s" % \ (data_to_string([self.tw.nick, int(n)])))
class Game(): ''' OLPC XO man color changer designed in memory of Nat Jacobson ''' def __init__(self, canvas, parent=None, mycolors=['#A0FFA0', '#FF8080']): self._activity = parent self.colors = [mycolors[0]] self.colors.append(mycolors[1]) self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent 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.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - GRID_CELL_SIZE self._scale = self._width / 1200. self.press = None self.dragpos = [0, 0] self.startpos = [0, 0] self._dot_cache = {} self._xo_cache = {} self._radius = 22.5 self._stroke_width = 9.5 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._sprites.set_delay(True) self._dots = [] self._xo_man = None self._generate_bg('#FFF') # First dot, starting angle self._cxy = [self._width / 2, self._height / 2] self._xy = [self._width / 2 + 120 * self._scale, self._height / 2 - self._radius * self._scale] self._angle = 0 self._dot_size_plus = self._radius * 3 * self._scale self._min = -self._dot_size_plus / 3 self._max = self._height - (self._dot_size_plus / 2.2) self._zones = [] self._calc_zones() self._generate_spiral() self._sprites.draw_all() def _calc_zones(self): for color in colors: rgb1 = _from_hex(color[0]) rgb2 = _from_hex(color[1]) dv = _contrast(rgb1, rgb2) dh = _delta_hue(rgb1, rgb2) self._zones.append(_zone(dv, dh)) def _calc_next_dot_position(self): ''' calculate spiral coordinates ''' dx = self._xy[0] - self._cxy[0] dy = self._xy[1] - self._cxy[1] r = sqrt(dx * dx + dy * dy) c = 2 * r * pi a = atan2(dy, dx) da = (self._dot_size_plus / c) * 2 * pi a += da r += self._dot_size_plus / (c / self._dot_size_plus) self._xy[0] = r * cos(a) + self._cxy[0] self._xy[1] = r * sin(a) + self._cxy[1] if self._xy[1] < self._min or self._xy[1] > self._max: self._calc_next_dot_position() def _generate_spiral(self): ''' Make a new set of dots for a sprial ''' for z in range(4): for i in range(len(colors)): if self._zones[i] == z: self._dots.append( Sprite(self._sprites, self._xy[0], self._xy[1], self._new_dot(colors[i]))) self._dots[-1].type = i self._calc_next_dot_position() if self._xo_man is None: x = 510 * self._scale y = 280 * self._scale self._xo_man = Sprite(self._sprites, x, y, self._new_xo_man(self.colors)) self._xo_man.type = None def move_dot(self, i, x, y): self._dots[i].move((x, y)) def get_dot_xy(self, i): return self._dots[i].get_xy() def move_xo_man(self, x, y): self._xo_man.move((x, y)) def get_xo_man_xy(self): return self._xo_man.get_xy() def rotate(self): x, y = self._dots[0].get_xy() for i in range(len(colors) - 1): self._dots[i].move(self._dots[i + 1].get_xy()) self._dots[-1].move((x, y)) def _generate_bg(self, color): ''' a background color ''' self._bg = Sprite(self._sprites, 0, 0, self._new_background(color)) self._bg.set_layer(0) self._bg.type = None def adj_background(self, color): ''' Change background ''' self._bg.set_image(self._new_background(color)) self._bg.set_layer(0) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) self.dragpos = [x, y] spr = self._sprites.find_sprite((x, y)) if spr == None or spr == self._bg: return self.startpos = spr.get_xy() self.press = spr def _mouse_move_cb(self, win, event): """ Drag a rule with the mouse. """ if self.press is None: self.dragpos = [0, 0] return True win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self.dragpos[0] dy = y - self.dragpos[1] self.press.move_relative((dx, dy)) self.dragpos = [x, y] def _button_release_cb(self, win, event): if self.press == None: return True if _distance(self.press.get_xy(), self.startpos) < 20: if type(self.press.type) == int: self.i = self.press.type self._new_surface() self.press.move(self.startpos) self.press = None def _new_surface(self): self.colors[0] = colors[self.i][0] self.colors[1] = colors[self.i][1] self._xo_man.set_image(self._new_xo_man(colors[self.i])) self._xo_man.set_layer(100) 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 ''' if True: # not color in self._dot_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(60 * self._scale) self._svg_height = int(60 * self._scale) pixbuf = svg_str_to_pixbuf( self._header() + \ '<circle cx="%f" cy="%f" r="%f" stroke="%s" fill="%s" \ stroke-width="%f" visibility="visible" />' % ( 30 * self._scale, 30 * self._scale, self._radius * self._scale, self._stroke, self._fill, self._stroke_width * self._scale) + \ 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 surface # self._dot_cache[color] def _new_background(self, color): ''' Background color ''' self._svg_width = int(self._width) self._svg_height = int(self._height) string = \ self._header() + \ '<rect width="%f" height="%f" x="%f" \ y="%f" fill="%s" stroke="none" visibility="visible" />' % ( self._width, self._height, 0, 0, color) + \ self._footer() pixbuf = svg_str_to_pixbuf(string) 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() return surface def _new_xo_man(self, color): ''' generate a xo-man of a color color ''' if True: # not color in self._xo_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(240. * self._scale) self._svg_height = int(260. * self._scale) string = \ self._header() + \ '<g>' + \ '<g id="XO">' + \ '<path id="Line1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Line2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Fill1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._fill, 17 * self._scale) + \ '<path id="Fill2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._fill, 17 * self._scale) + \ '<circle id="Circle" cx="%f" cy="%f" r="%f" \ fill="%s" stroke="%s" stroke-width="%f" visibility="visible" />' % ( 120 * self._scale, 61.5 * self._scale, 27.5 * self._scale, self._fill, self._stroke, 11 * self._scale) + \ '</g></g>' + \ self._footer() pixbuf = svg_str_to_pixbuf(string) 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._xo_cache[color] = surface return surface # self._xo_cache[color] 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 _footer(self): return '</svg>\n'
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = colors self._canvas = canvas parent.show_all() 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._height / (14.0 * DOT_SIZE * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._turtle_offset = 0 self._space = int(self._dot_size / 5.) self._orientation = 0 self.level = 0 self.custom_strategy = None self.strategies = [ BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY, self.custom_strategy ] self.strategy = self.strategies[self.level] self._timeout_id = None # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] for y in range(THIRTEEN): for x in range(THIRTEEN): xoffset = int((self._width - THIRTEEN * (self._dot_size + \ self._space) - self._space) / 2.) if y % 2 == 1: xoffset += int((self._dot_size + self._space) / 2.) if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot('#B0B0B0'))) else: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[FILL]))) self._dots[-1].type = False # not set # Put a turtle at the center of the screen... self._turtle_images = [] self._rotate_turtle(self._new_turtle()) self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0]) self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) # ...and initialize. self._all_clear() def _move_turtle(self, pos): ''' Move turtle and add its offset ''' self._turtle.move(pos) self._turtle.move_relative( (-self._turtle_offset, -self._turtle_offset)) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' # Clear dots for dot in self._dots: if dot.type: dot.type = False dot.set_shape(self._new_dot(self._colors[FILL])) dot.set_label('') # Recenter the turtle self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) self._turtle.set_shape(self._turtle_images[0]) self._set_label('') if self._timeout_id is not None: GObject.source_remove(self._timeout_id) def new_game(self, saved_state=None): ''' Start a new game. ''' self._all_clear() # Fill in a few dots to start for i in range(15): n = int(uniform(0, THIRTEEN * THIRTEEN)) if self._dots[n].type is not None: self._dots[n].type = True self._dots[n].set_shape(self._new_dot(self._colors[STROKE])) # Calculate the distances to the edge self._initialize_weights() self.strategy = self.strategies[self.level] def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' 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), inverse=True) if spr == None: return if spr.type is not None and not spr.type: spr.type = True spr.set_shape(self._new_dot(self._colors[STROKE])) self._weights[self._dots.index(spr)] = 1000 self._test_game_over(self._move_the_turtle()) return True def _find_the_turtle(self): turtle_pos = self._turtle.get_xy() turtle_dot = None for dot in self._dots: pos = dot.get_xy() # Turtle is offset if pos[0] == turtle_pos[0] + self._turtle_offset and \ pos[1] == turtle_pos[1] + self._turtle_offset: turtle_dot = self._dots.index(dot) break if turtle_dot is None: _logger.debug('Cannot find the turtle...') return None return turtle_dot def _move_the_turtle(self): ''' Move the turtle after each click ''' self._turtle_dot = self._find_the_turtle() if self._turtle_dot is None: return # Given the col and row of the turtle, do something new_dot = self._grid_to_dot( self._my_strategy_import(self.strategy, self._dot_to_grid(self._turtle_dot))) self._move_turtle(self._dots[new_dot].get_xy()) # And set the orientation self._turtle.set_shape(self._turtle_images[self._orientation]) return new_dot def _test_game_over(self, new_dot): ''' Check to see if game is over ''' if new_dot is None: return if self._dots[new_dot].type is None: # Game-over feedback self._once_around = False self._happy_turtle_dance() return True c = int(self._turtle_dot / THIRTEEN) % 2 if self._dots[ new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \ self._dots[ new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \ self._dots[ new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \ self._dots[ new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \ self._dots[ new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \ self._dots[ new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type: # Game-over feedback for dot in self._dots: dot.set_label(':)') return True return False def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * THIRTEEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % THIRTEEN, int(dot / THIRTEEN)] def _happy_turtle_dance(self): ''' Turtle dances along the edge ''' i = self._find_the_turtle() if i == 0: if self._once_around: return else: self._once_around = True _logger.debug(i) x, y = self._dot_to_grid(i) if y == 0: x += 1 if x == 0: y -= 1 if x == THIRTEEN - 1: y += 1 if y == THIRTEEN - 1: x -= 1 i = self._grid_to_dot((x, y)) self._dots[i].set_label(':)') self._move_turtle(self._dots[i].get_xy()) self._orientation += 1 self._orientation %= 6 self._turtle.set_shape(self._turtle_images[self._orientation]) self._timeout_id = GObject.timeout_add(250, self._happy_turtle_dance) def _ordered_weights(self, pos): ''' Returns the list of surrounding points sorted by their distance to the edge ''' dots = self._surrounding_dots(pos) dots_and_weights = [] for dot in dots: dots_and_weights.append((dot, self._weights[dot])) sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1]) for i in range(6): dots[i] = sorted_dots[i][0] return dots def _daylight_ahead(self, pos): ''' Returns true if there is a straight path to the edge from the current position/orientation ''' dots = self._surrounding_dots(pos) while True: dot_type = self._dots[dots[self._orientation]].type if dot_type is None: return True elif dot_type: return False else: # keep looking pos = self._dot_to_grid(dots[self._orientation]) dots = self._surrounding_dots(pos) def _surrounding_dots(self, pos): ''' Returns dots surrounding a position in the grid ''' dots = [] evenodd = pos[1] % 2 for i in range(6): col = pos[0] + CIRCLE[evenodd][i][0] row = pos[1] + CIRCLE[evenodd][i][1] dots.append(self._grid_to_dot((col, row))) return dots def _initialize_weights(self): ''' How many steps to an edge? ''' self._weights = [] for d, dot in enumerate(self._dots): if dot.type is None: self._weights.append(0) elif dot.type: self._weights.append(1000) else: pos = self._dot_to_grid(d) pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1]) self._weights.append( min(min(pos[0], pos2[0]), min(pos[1], pos2[1]))) def _my_strategy_import(self, f, arg): ''' Run Python code passed as argument ''' userdefined = {} try: exec f in globals(), userdefined return userdefined['_turtle_strategy'](self, arg) except ZeroDivisionError, e: self._set_label('Python zero-divide error: %s' % (str(e))) except ValueError, e: self._set_label('Python value error: %s' % (str(e)))
class Ball(): ''' The Bounce class is used to define the ball and the user interaction. ''' def __init__(self, sprites, filename): self._current_frame = 0 self._frames = [] # Easter Egg animation self._sprites = sprites self.ball = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf( svg_from_file(filename))) self.ball.set_layer(3) self.ball.set_label_attributes(24, vert_align='top') ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames.append(Sprite( self._sprites, 0, 0, svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer()))) for frame in self._frames: frame.set_layer(3) frame.move((0, -SIZE[1])) # move animation frames off screen def new_ball(self, filename): ''' Create a ball object and Easter Egg animation from an SVG file. ''' self.ball.set_shape(svg_str_to_pixbuf(svg_from_file(filename))) ball = extract_svg_payload(file(filename, 'r')) for i in range(8): self._frames[i].set_shape(svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball + PUNCTURE + AIR + '</g>' + svg_footer())) def new_ball_from_image(self, filename, save_path): ''' Just create a ball object from an image file ''' if filename == '': _logger.debug('Image file not found.') return try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) if pixbuf.get_width() > pixbuf.get_height(): size = pixbuf.get_height() x = int((pixbuf.get_width() - size) / 2) else: size = pixbuf.get_width() x = int((pixbuf.get_height() - size) / 2) crop = GdkPixbuf.Pixbuf.new(0, True, 8, size, size) pixbuf.copy_area(x, 0, size, size, crop, 0, 0) scale = crop.scale_simple(85, 85, GdkPixbuf.InterpType.BILINEAR) scale.savev(save_path, 'png', [], []) self.ball.set_shape( svg_str_to_pixbuf(generate_ball_svg(save_path))) except Exception as e: _logger.error('Could not load image from %s: %s' % (filename, e)) def new_ball_from_fraction(self, fraction): ''' Create a ball with a section of size fraction. ''' r = SIZE[0] / 2.0 self.ball.set_shape(svg_str_to_pixbuf( svg_header(SIZE[0], SIZE[1], 1.0) + svg_sector(r, r + BOX[1], r - 1, 1.999 * pi, COLORS[0], COLORS[1]) + svg_sector(r, r + BOX[1], r - 1, fraction * 2 * pi, COLORS[1], COLORS[0]) + svg_rect(BOX[0], BOX[1], 4, 4, 0, 0, '#FFFFFF', 'none') + svg_footer())) def ball_x(self): return self.ball.get_xy()[0] def ball_y(self): return self.ball.get_xy()[1] def frame_x(self, i): return self._frames[i].get_xy()[0] def frame_y(self, i): return self._frames[i].get_xy()[1] def width(self): return self.ball.rect[2] def height(self): return self.ball.rect[3] def move_ball(self, pos): self.ball.move(pos) def move_ball_relative(self, pos): self.ball.move_relative(pos) def move_frame(self, i, pos): self._frames[i].move(pos) def move_frame_relative(self, i, pos): self._frames[i].move_relative(pos) def hide_frames(self): for frame in self._frames: frame.move((0, -SIZE[1])) # hide the animation frames def next_frame(self, frame_counter): if frame_counter in ANIMATION: self._switch_frames(ANIMATION[frame_counter]) return self._current_frame def _switch_frames(self, frames): ''' Switch between frames in the animation ''' self.move_frame(frames[1], (self.frame_x(frames[0]), self.frame_y(frames[0]))) self.move_frame(frames[0], ((0, -SIZE[1]))) # hide the frame self._current_frame = frames[1]
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = colors self._canvas = canvas parent.show_all() 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._height / (14.0 * DOT_SIZE * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._turtle_offset = 0 self._space = int(self._dot_size / 5.) self._orientation = 0 self.level = 0 self.custom_strategy = None self.strategies = [BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY, self.custom_strategy] self.strategy = self.strategies[self.level] self._timeout_id = None # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] for y in range(THIRTEEN): for x in range(THIRTEEN): xoffset = int((self._width - THIRTEEN * (self._dot_size + \ self._space) - self._space) / 2.) if y % 2 == 1: xoffset += int((self._dot_size + self._space) / 2.) if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot('#B0B0B0'))) else: self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[FILL]))) self._dots[-1].type = False # not set # Put a turtle at the center of the screen... self._turtle_images = [] self._rotate_turtle(self._new_turtle()) self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0]) self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) # ...and initialize. self._all_clear() def _move_turtle(self, pos): ''' Move turtle and add its offset ''' self._turtle.move(pos) self._turtle.move_relative( (-self._turtle_offset, -self._turtle_offset)) def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' # Clear dots for dot in self._dots: if dot.type: dot.type = False dot.set_shape(self._new_dot(self._colors[FILL])) dot.set_label('') # Recenter the turtle self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy()) self._turtle.set_shape(self._turtle_images[0]) self._set_label('') if self._timeout_id is not None: GObject.source_remove(self._timeout_id) def new_game(self, saved_state=None): ''' Start a new game. ''' self._all_clear() # Fill in a few dots to start for i in range(15): n = int(uniform(0, THIRTEEN * THIRTEEN)) if self._dots[n].type is not None: self._dots[n].type = True self._dots[n].set_shape(self._new_dot(self._colors[STROKE])) # Calculate the distances to the edge self._initialize_weights() self.strategy = self.strategies[self.level] def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' 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), inverse=True) if spr == None: return if spr.type is not None and not spr.type: spr.type = True spr.set_shape(self._new_dot(self._colors[STROKE])) self._weights[self._dots.index(spr)] = 1000 self._test_game_over(self._move_the_turtle()) return True def _find_the_turtle(self): turtle_pos = self._turtle.get_xy() turtle_dot = None for dot in self._dots: pos = dot.get_xy() # Turtle is offset if pos[0] == turtle_pos[0] + self._turtle_offset and \ pos[1] == turtle_pos[1] + self._turtle_offset: turtle_dot = self._dots.index(dot) break if turtle_dot is None: _logger.debug('Cannot find the turtle...') return None return turtle_dot def _move_the_turtle(self): ''' Move the turtle after each click ''' self._turtle_dot = self._find_the_turtle() if self._turtle_dot is None: return # Given the col and row of the turtle, do something new_dot = self._grid_to_dot( self._my_strategy_import(self.strategy, self._dot_to_grid(self._turtle_dot))) self._move_turtle(self._dots[new_dot].get_xy()) # And set the orientation self._turtle.set_shape(self._turtle_images[self._orientation]) return new_dot def _test_game_over(self, new_dot): ''' Check to see if game is over ''' if new_dot is None: return if self._dots[new_dot].type is None: # Game-over feedback self._once_around = False self._happy_turtle_dance() return True c = int(self._turtle_dot / THIRTEEN) % 2 if self._dots[ new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \ self._dots[ new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \ self._dots[ new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \ self._dots[ new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \ self._dots[ new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \ self._dots[ new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type: # Game-over feedback for dot in self._dots: dot.set_label(':)') return True return False def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * THIRTEEN def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % THIRTEEN, int(dot / THIRTEEN)] def _happy_turtle_dance(self): ''' Turtle dances along the edge ''' i = self._find_the_turtle() if i == 0: if self._once_around: return else: self._once_around = True _logger.debug(i) x, y = self._dot_to_grid(i) if y == 0: x += 1 if x == 0: y -= 1 if x == THIRTEEN - 1: y += 1 if y == THIRTEEN - 1: x -= 1 i = self._grid_to_dot((x, y)) self._dots[i].set_label(':)') self._move_turtle(self._dots[i].get_xy()) self._orientation += 1 self._orientation %= 6 self._turtle.set_shape(self._turtle_images[self._orientation]) self._timeout_id = GObject.timeout_add(250, self._happy_turtle_dance) def _ordered_weights(self, pos): ''' Returns the list of surrounding points sorted by their distance to the edge ''' dots = self._surrounding_dots(pos) dots_and_weights = [] for dot in dots: dots_and_weights.append((dot, self._weights[dot])) sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1]) for i in range(6): dots[i] = sorted_dots[i][0] return dots def _daylight_ahead(self, pos): ''' Returns true if there is a straight path to the edge from the current position/orientation ''' dots = self._surrounding_dots(pos) while True: dot_type = self._dots[dots[self._orientation]].type if dot_type is None: return True elif dot_type: return False else: # keep looking pos = self._dot_to_grid(dots[self._orientation]) dots = self._surrounding_dots(pos) def _surrounding_dots(self, pos): ''' Returns dots surrounding a position in the grid ''' dots = [] evenodd = pos[1] % 2 for i in range(6): col = pos[0] + CIRCLE[evenodd][i][0] row = pos[1] + CIRCLE[evenodd][i][1] dots.append(self._grid_to_dot((col, row))) return dots def _initialize_weights(self): ''' How many steps to an edge? ''' self._weights = [] for d, dot in enumerate(self._dots): if dot.type is None: self._weights.append(0) elif dot.type: self._weights.append(1000) else: pos = self._dot_to_grid(d) pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1]) self._weights.append(min(min(pos[0], pos2[0]), min(pos[1], pos2[1]))) def _my_strategy_import(self, f, arg): ''' Run Python code passed as argument ''' userdefined = {} try: exec f in globals(), userdefined return userdefined['_turtle_strategy'](self, arg) except ZeroDivisionError, e: self._set_label('Python zero-divide error: %s' % (str(e))) except ValueError, e: self._set_label('Python value error: %s' % (str(e)))
class Game(): ''' OLPC XO man color changer designed in memory of Nat Jacobson ''' def __init__(self, canvas, parent=None, mycolors=['#A0FFA0', '#FF8080']): self._activity = parent self.colors = [mycolors[0]] self.colors.append(mycolors[1]) self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent 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.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - GRID_CELL_SIZE self._scale = self._width / 1200. self.press = None self.dragpos = [0, 0] self.startpos = [0, 0] self._dot_cache = {} self._xo_cache = {} self._radius = 22.5 self._stroke_width = 9.5 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._dots = [] self._xo_man = None self._generate_bg('#FFF') # First dot, starting angle self._cxy = [self._width / 2, self._height / 2] self._xy = [ self._width / 2 + 120 * self._scale, self._height / 2 - self._radius * self._scale ] self._angle = 0 self._dot_size_plus = self._radius * 3 * self._scale self._min = -self._dot_size_plus / 3 self._max = self._height - (self._dot_size_plus / 2.2) self._zones = [] self._calc_zones() self._generate_spiral() def _calc_zones(self): for color in colors: rgb1 = _from_hex(color[0]) rgb2 = _from_hex(color[1]) dv = _contrast(rgb1, rgb2) dh = _delta_hue(rgb1, rgb2) self._zones.append(_zone(dv, dh)) def _calc_next_dot_position(self): ''' calculate spiral coordinates ''' dx = self._xy[0] - self._cxy[0] dy = self._xy[1] - self._cxy[1] r = sqrt(dx * dx + dy * dy) c = 2 * r * pi a = atan2(dy, dx) da = (self._dot_size_plus / c) * 2 * pi a += da r += self._dot_size_plus / (c / self._dot_size_plus) self._xy[0] = r * cos(a) + self._cxy[0] self._xy[1] = r * sin(a) + self._cxy[1] if self._xy[1] < self._min or self._xy[1] > self._max: self._calc_next_dot_position() def _generate_spiral(self): ''' Make a new set of dots for a sprial ''' for z in range(4): for i in range(len(colors)): if self._zones[i] == z: self._dots.append( Sprite(self._sprites, self._xy[0], self._xy[1], self._new_dot(colors[i]))) self._dots[-1].type = i self._calc_next_dot_position() if self._xo_man is None: x = 510 * self._scale y = 280 * self._scale self._xo_man = Sprite(self._sprites, x, y, self._new_xo_man(self.colors)) self._xo_man.type = None def move_dot(self, i, x, y): self._dots[i].move((x, y)) def get_dot_xy(self, i): return self._dots[i].get_xy() def move_xo_man(self, x, y): self._xo_man.move((x, y)) def get_xo_man_xy(self): return self._xo_man.get_xy() def rotate(self): x, y = self._dots[0].get_xy() for i in range(len(colors) - 1): self._dots[i].move(self._dots[i + 1].get_xy()) self._dots[-1].move((x, y)) def _generate_bg(self, color): ''' a background color ''' self._bg = Sprite(self._sprites, 0, 0, self._new_background(color)) self._bg.set_layer(0) self._bg.type = None def adj_background(self, color): ''' Change background ''' self._bg.set_image(self._new_background(color)) self._bg.set_layer(0) def _button_press_cb(self, win, event): win.grab_focus() x, y = map(int, event.get_coords()) self.dragpos = [x, y] spr = self._sprites.find_sprite((x, y)) if spr == None or spr == self._bg: return self.startpos = spr.get_xy() self.press = spr def _mouse_move_cb(self, win, event): """ Drag a rule with the mouse. """ if self.press is None: self.dragpos = [0, 0] return True win.grab_focus() x, y = map(int, event.get_coords()) dx = x - self.dragpos[0] dy = y - self.dragpos[1] self.press.move_relative((dx, dy)) self.dragpos = [x, y] def _button_release_cb(self, win, event): if self.press == None: return True if _distance(self.press.get_xy(), self.startpos) < 20: if type(self.press.type) == int: self.i = self.press.type self._new_surface() self.press.move(self.startpos) self.press = None def _new_surface(self): self.colors[0] = colors[self.i][0] self.colors[1] = colors[self.i][1] self._xo_man.set_image(self._new_xo_man(colors[self.i])) self._xo_man.set_layer(100) 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 ''' if True: # not color in self._dot_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(60 * self._scale) self._svg_height = int(60 * self._scale) pixbuf = svg_str_to_pixbuf( self._header() + \ '<circle cx="%f" cy="%f" r="%f" stroke="%s" fill="%s" \ stroke-width="%f" visibility="visible" />' % ( 30 * self._scale, 30 * self._scale, self._radius * self._scale, self._stroke, self._fill, self._stroke_width * self._scale) + \ 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 surface # self._dot_cache[color] def _new_background(self, color): ''' Background color ''' self._svg_width = int(self._width) self._svg_height = int(self._height) string = \ self._header() + \ '<rect width="%f" height="%f" x="%f" \ y="%f" fill="%s" stroke="none" visibility="visible" />' % ( self._width, self._height, 0, 0, color) + \ self._footer() pixbuf = svg_str_to_pixbuf(string) 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() return surface def _new_xo_man(self, color): ''' generate a xo-man of a color color ''' if True: # not color in self._xo_cache: self._stroke = color[0] self._fill = color[1] self._svg_width = int(240. * self._scale) self._svg_height = int(260. * self._scale) string = \ self._header() + \ '<g>' + \ '<g id="XO">' + \ '<path id="Line1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Line2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._stroke, 37 * self._scale) + \ '<path id="Fill1" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 97 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 188 * self._scale, self._fill, 17 * self._scale) + \ '<path id="Fill2" d="M%f,%f C%f,%f %f,%f %f,%f" stroke="%s" \ stroke-width="%f" stroke-linecap="round" fill="none" visibility="visible" />' \ % ( 165.5 * self._scale, 188 * self._scale, 120 * self._scale, 140.5 * self._scale, 120 * self._scale, 140.5 * self._scale, 74.5 * self._scale, 97 * self._scale, self._fill, 17 * self._scale) + \ '<circle id="Circle" cx="%f" cy="%f" r="%f" \ fill="%s" stroke="%s" stroke-width="%f" visibility="visible" />' % ( 120 * self._scale, 61.5 * self._scale, 27.5 * self._scale, self._fill, self._stroke, 11 * self._scale) + \ '</g></g>' + \ self._footer() pixbuf = svg_str_to_pixbuf(string) 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._xo_cache[color] = surface return surface # self._xo_cache[color] 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 _footer(self): return '</svg>\n'