From 91dac4aa39452fd648887056c9184d3be374ff20 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 22 Aug 2013 21:11:49 +0000 Subject: Add [read|write]_file() support The activity now has full support for saving games in progress. --- diff --git a/PascalTriangle.activity/pascaltriangle.py b/PascalTriangle.activity/pascaltriangle.py index 083693d..95d422d 100644 --- a/PascalTriangle.activity/pascaltriangle.py +++ b/PascalTriangle.activity/pascaltriangle.py @@ -27,6 +27,8 @@ import random from gi.repository import Gtk, Gdk import cairo from gettext import gettext as _ +import pickle +import logging class PascalTriangleActivity(activity.Activity): @@ -44,6 +46,9 @@ class PascalTriangleActivity(activity.Activity): self.max_participants = 1 # No sharing + # Set up logging first. + self._logger = logging.getLogger('pascal-triangle-activity') + # Create the standard activity toolbox. toolbar_box = ToolbarBox() self.set_toolbar_box(toolbar_box) @@ -62,6 +67,7 @@ class PascalTriangleActivity(activity.Activity): hint_button = HintButton(self) hint_button.show() main_toolbar.insert(hint_button, -1) + self._hint_button = hint_button separator = Gtk.SeparatorToolItem() separator.props.draw = True @@ -114,6 +120,7 @@ class PascalTriangleActivity(activity.Activity): self._triangle_size = int(slider.get_value()) slider.connect('value-changed', self.__slider_value_changed_cb, None) + self._slider = slider # Parent and show the drawing area. self.set_canvas(overlay) @@ -153,6 +160,70 @@ class PascalTriangleActivity(activity.Activity): self._drawing_area.queue_draw() + def read_file(self, file_path): + """Read a saved game from the journal. + + If this fails, the current game state will be left untouched. If extra + state over what was expected is present in the save file, it will be + ignored. For documentation on the save format, see write_file(). + """ + obj = None + try: + with open(file_path, 'rb') as file_fd: + obj = pickle.load(file_fd) + if len(obj) < 5: + raise pickle.UnpicklingError, 'Invalid tuple.' + except EnvironmentError as err: + self._logger.warning('Error reading save file ā€˜%sā€™: %s' % + (file_path, err)) + return + except pickle.UnpicklingError as err: + self._logger.warning('Malformed save file ā€˜%sā€™: %s' % + (file_path, err)) + return + + # Restore the UI state. Setting the triangle size will start a new + # game, so we must restore other state afterwards. From this point + # onwards, the code can't fail. + triangle_size = obj[0] + self._slider.set_value(triangle_size) + + show_hints = obj[4] + self._hint_button.set_active(show_hints) + + # Access the obj elements by index so that we don't fail if obj has + # more than the expected number of elements (e.g. if we've somehow + # got a save file from a newer version of the activity which saves + # more state). + self._blank_cells = obj[1] + self._current_cell = obj[2] + self._current_cell_text = obj[3] + + # Redraw everything. + self._drawing_area.queue_draw() + + def write_file(self, file_path): + """Write a game to the journal. + + The game state is pickled as a tuple of at least 5 elements. This + format may be extended in future by appending elements to the tuple; + read_file() is guaranteed to ignore extra tuple elements. The existing + tuple elements must be stable, though. + """ + obj = ( + self._triangle_size, + self._blank_cells, + self._current_cell, + self._current_cell_text, + self._show_hints, + ) + + try: + with open(file_path, 'wb') as file_fd: + pickle.dump(obj, file_fd, pickle.HIGHEST_PROTOCOL) + except EnvironmentError as err: + self._logger.warning('Error saving game: %s' % err) + def _update_current_cell(self, index): """Change position of the currently selected cell and clear any pending text edits.""" -- cgit v0.9.1