[gnome-games/applygsoc2009: 33/76] XXX: Towards split view and model
- From: Pablo Castellano <pablog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/applygsoc2009: 33/76] XXX: Towards split view and model
- Date: Mon, 6 Sep 2010 02:49:59 +0000 (UTC)
commit a2480e430936ec05db1e30410f4a3369b1bf1bdf
Author: Pablo Castellano <pablog src gnome org>
Date: Thu Aug 26 15:12:03 2010 +0200
XXX: Towards split view and model
* SudokuGameDisplay is splitted into SudokuView and SudokuModel.
* Much work is still needed, but the rough shape should be seen. Now the game
can be launched.
gnome-sudoku/src/lib/gsudoku.py | 303 ++++++++++++++++++++++-----------------
gnome-sudoku/src/lib/main.py | 33 +++--
2 files changed, 187 insertions(+), 149 deletions(-)
---
diff --git a/gnome-sudoku/src/lib/gsudoku.py b/gnome-sudoku/src/lib/gsudoku.py
index 391cc6c..040c445 100644
--- a/gnome-sudoku/src/lib/gsudoku.py
+++ b/gnome-sudoku/src/lib/gsudoku.py
@@ -71,29 +71,18 @@ class SudokuNumberGrid (gtk.AspectFrame):
return self.table.get_focus_child()
-class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
+class SudokuView (SudokuNumberGrid, gobject.GObject):
- __gsignals__ = {
- 'focus-changed':(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
- 'puzzle-finished':(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
- }
-
- do_highlight_cells = False
-
- def __init__ (self, grid = None, group_size = 9,
- show_impossible_implications = False):
- group_size = int(group_size)
- self.hints = 0
- self.hint_square = None
- self.always_show_hints = False
- self.show_impossible_implications = show_impossible_implications
- self.impossible_hints = 0
- self.impossibilities = []
- self.trackers = {}
- self.tinfo = tracker_info.TrackerInfo()
+ def __init__(self, group_size):
+ SudokuNumberGrid.__init__(self, group_size)
+# self.hint_square = None
+# self.tinfo = tracker_info.TrackerInfo()
gobject.GObject.__init__(self)
SudokuNumberGrid.__init__(self, group_size = group_size)
- self._setup_grid(grid, group_size)
+
+ self._model = None
+ self._do_highlight_cells = False
+
for e in self.__entries__.values():
e.show()
e.connect('number-changed', self._number_changed_cb)
@@ -102,8 +91,49 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
e.connect('key-press-event', self._key_press_cb)
self.connect('focus-changed', self._highlight_cells)
- def _notes_changed_cb(self, box, top_note, bottom_note):
- box.set_notes((top_note, bottom_note))
+ def connect_to_model(self, model):
+ if not model:
+ return
+ self._model = model
+ model.add_observer(self)
+ for x in range(self.group_size):
+ for y in range(self.group_size):
+ val = model.grid._get_(x, y)
+ if val:
+ self.set_readonly_appearance(x, y, True)
+ self.set_value(x, y, val)
+
+ def update(self, values):
+ """Update the GUI, called by a Model
+
+ values should be a list of NumberBoxModel"""
+ for box in values:
+ x, y = box.x, box.y,
+ if box.value is not None:
+ self.set_value(x, y, box.value)
+ if box.top_note is not None or box.bottom_note is not None:
+ self._set_notes(x, y, box.top_note, box.bottom_note)
+ if box.conflict is not None:
+ self._show_conflict(x, y, box.conflict)
+
+ def _show_conflict(self, x, y, conflict):
+ self.__entries__[(x, y)].set_error_highlight(conflict)
+
+ def _set_notes(self, x, y, top_note, bottom_note):
+ self.__entries__[(x, y)].set_notes((top_note, bottom_note))
+
+ def _number_changed_cb(self, widget, new_number):
+ print "user input: number", (widget.x, widget.y, new_number)
+ self._model.set_value(widget.x, widget.y, new_number)
+
+ def _notes_changed_cb(self, widget, top_note, bottom_note):
+ print "user input: notes", (widget.x, widget.y, top_note, bottom_note)
+ self._model.set_notes(widget.x, widget.y, top_note, bottom_note)
+
+ def _focus_callback(self, widget, event):
+ if self._do_highlight_cells:
+ self._highlight_cells()
+# self.emit('focus-changed')
def _key_press_cb (self, widget, event):
key = gtk.gdk.keyval_name(event.keyval)
@@ -111,47 +141,8 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
if dest:
self.table.set_focus_child(self.__entries__[dest])
- def _go_around (self, x, y, direction):
- '''return the coordinate if we should go to the other side of the grid.
- Or else return None.'''
- (limit_min, limit_max) = (0, self.group_size -1)
- if (y, direction) == (limit_min, 'Up'):
- dest = (x, limit_max)
- elif (y, direction) == (limit_max, 'Down'):
- dest = (x, limit_min)
- elif (x, direction) == (limit_min, 'Left'):
- dest = (limit_max, y)
- elif (x, direction) == (limit_max, 'Right'):
- dest = (limit_min, y)
- else:
- return None
- return dest
-
- def focus_callback (self, e, event):
- self.emit('focus-changed')
-
- def _get_highlight_colors (self):
- entry = self.__entries__.values()[0]
- default_color = gtkcolor_to_rgb(entry.style.bg[gtk.STATE_SELECTED])
- hsv = colors.rgb_to_hsv(*default_color)
- box_s = hsv[1]
- box_v = hsv[2]
- if box_v < 0.5:
- box_v = box_v * 2
- if box_s > 0.75:
- box_s = box_s * 0.5
- else:
- box_s = box_s * 1.5
- if box_s > 1:
- box_s = 1.0
- self.box_color = colors.hsv_to_rgb(hsv[0], box_s, box_v)
- self.box_and_row_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.33 / 2})
- self.row_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.33})
- self.col_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.66})
- self.box_and_col_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 1.0 - (0.33 / 2)})
-
def toggle_highlight (self, val):
- self.do_highlight_cells = val
+ self._do_highlight_cells = val
if val:
self._unhighlight_cells()
else:
@@ -172,17 +163,14 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
self._get_highlight_colors()
my_x, my_y = focused.x, focused.y
- # col_coords can sometimes be null.
- if not hasattr(self.grid, 'col_coords'):
- return
-
- for x, y in self.grid.col_coords[my_x]:
+ grid = self._model.grid
+ for x, y in grid.col_coords[my_x]:
if (x, y) != (my_x, my_y):
self.__entries__[(x, y)].set_background_color(self.col_color)
- for x, y in self.grid.row_coords[my_y]:
+ for x, y in grid.row_coords[my_y]:
if (x, y) != (my_x, my_y):
self.__entries__[(x, y)].set_background_color(self.row_color)
- for x, y in self.grid.box_coords[self.grid.box_by_coords[(my_x, my_y)]]:
+ for x, y in grid.box_coords[grid.box_by_coords[(my_x, my_y)]]:
if (x, y) != (my_x, my_y):
e = self.__entries__[(x, y)]
if x == my_x:
@@ -192,6 +180,81 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
else:
e.set_background_color(self.box_color)
+ def _get_highlight_colors(self):
+ entry = self.__entries__.values()[0]
+ default_color = gtkcolor_to_rgb(entry.style.bg[gtk.STATE_SELECTED])
+ hsv = colors.rgb_to_hsv(*default_color)
+ box_s = hsv[1]
+ box_v = hsv[2]
+ if box_v < 0.5:
+ box_v = box_v * 2
+ if box_s > 0.75:
+ box_s = box_s * 0.5
+ else:
+ box_s = box_s * 1.5
+ if box_s > 1:
+ box_s = 1.0
+ self.box_color = colors.hsv_to_rgb(hsv[0], box_s, box_v)
+ self.box_and_row_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.33 / 2})
+ self.row_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.33})
+ self.col_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 0.66})
+ self.box_and_col_color = colors.rotate_hue_rgb(*self.box_color, **{'rotate_by': 1.0 - (0.33 / 2)})
+
+ def _go_around(self, x, y, direction):
+ '''return the coordinate if we should go to the other side of the grid.
+ Or else return None.'''
+ limit_min, limit_max = 0, self.group_size - 1
+ y_edge = [(limit_min, 'Up'), (limit_max, 'Down')]
+ x_edge = [(limit_min, 'Left'), (limit_max, 'Right')]
+ opposite_edge = {limit_min: limit_max, limit_max: limit_min}
+
+ if (y, direction) in y_edge:
+ return x, opposite_edge[y]
+ elif (x, direction) in x_edge:
+ return opposite_edge[x], y
+ else:
+ return None, None
+
+
+class NumberBoxModel:
+ def __init__(self, x, y, value=None, top_note=None, bottom_note=None,
+ conflict=None):
+ self.x = x
+ self.y = y
+ self.value = value
+ self.top_note = top_note
+ self.bottom_note = bottom_note
+ self.conflict = conflict
+
+
+class SudokuModel(gobject.GObject):
+
+ __gsignals__ = {
+ 'puzzle-finished':(gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
+ }
+
+ def __init__ (self, grid, group_size):
+ group_size = int(group_size)
+ self.hints = 0
+ self.always_show_hints = False
+ self.auto_fills = 0
+ self.show_impossible_implications = False
+ self.impossible_hints = 0
+ self.impossibilities = []
+ self.trackers = {}
+ self.__trackers_tracking__ = {}
+ gobject.GObject.__init__(self)
+ self._setup_grid(grid, group_size)
+
+ self._observers = []
+
+ def add_observer(self, observer):
+ self._observers.append(observer)
+
+ def _signal_observers(self, values):
+ for observer in self._observers:
+ observer.update(values)
+
def animate_hint (self):
if self.hint_animate_count % 2 == 0:
color = (1.0, 0.0, 0.0)
@@ -409,17 +472,10 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
self.add_value(col, row, values[index])
def _setup_grid (self, grid, group_size):
- self.doing_initial_setup = True
if isinstance(grid, sudoku.SudokuGrid):
self.grid = sudoku.InteractiveSudoku(grid.grid, group_size = grid.group_size)
else:
self.grid = sudoku.InteractiveSudoku(grid, group_size = group_size)
- for x in range(group_size):
- for y in range(group_size):
- val = self.grid._get_(x, y)
- if val:
- self.add_value(x, y, val)
- self.doing_initial_setup = False
def _number_changed_cb(self, widget, new_value):
# TODO
@@ -475,12 +531,42 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
for coord in self.grid.conflicts[(x, y)]:
self.__entries__[coord].set_error_highlight(True)
- def set_value(self, x, y, val):
- '''Sets value for position x, y to val.
-
- Calls set_text_interactive so the history list is updated.
- '''
- self.__entries__[(x, y)].set_text_interactive(str(val))
+ def set_value(self, x, y, value):
+ result = []
+ # first remove conflicts; grid.add should return conflicts together
+ # with ConflictError
+ old_conflicts = self._remove_related_conflicts(x, y)
+ new_conflicts = []
+ for entry in old_conflicts:
+ change = NumberBoxModel(x=entry[0], y=entry[1], conflict=False)
+ result.append(change)
+
+ try:
+ self.grid.add(x, y, value) # force=True???
+ except sudoku.ConflictError, err:
+ new_conflicts = self.grid.find_conflicts(x, y, value)
+ has_conflict = True if new_conflicts else False
+ result.append(NumberBoxModel(x, y, value, conflict=has_conflict))
+ if new_conflicts:
+ self._record_conflicts(x, y, new_conflicts)
+ for entry in new_conflicts:
+ change = NumberBoxModel(x=entry[0], y=entry[1], conflict=True)
+ result.append(change)
+ self._signal_observers(result)
+
+ def _remove_related_conflicts(self, x, y):
+ result = []
+ if self.__error_pairs__.has_key((x, y)):
+ errors_removed = self.__error_pairs__[(x, y)]
+ del self.__error_pairs__[(x, y)]
+ for coord in errors_removed:
+ # If we're not an error by some other pairing...
+ if not self.__error_pairs__.has_key(coord):
+ result.append(coord)
+ return result
+
+ def _record_conflicts(self, x, y, new_conflicts):
+ self.__error_pairs__[(x, y)] = new_conflicts
def add_value (self, x, y, val, tracker = None):
"""Add value val at position x, y.
@@ -548,19 +634,6 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
if not self.doing_initial_setup:
self._mark_impossible_implications(x, y)
- def _remove_error_highlight (self):
- '''remove error highlight from [x, y] and also all errors caused by it
-
- Conflict resolution is now handled within the InteractiveSudoku class.
- If any conflicts were cleared on the last remove() then they are
- stored in grid.cleared_conflicts
- '''
- if not self.grid.cleared_conflicts:
- return
- for coord in self.grid.cleared_conflicts:
- linked_entry = self.__entries__[coord]
- linked_entry.set_error_highlight(False)
-
def auto_fill (self):
changed = self.grid.auto_fill()
retval = []
@@ -725,43 +798,3 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
if self.always_show_hints and not self.doing_initial_setup:
self.update_all_hints()
-if __name__ == '__main__':
- window = gtk.Window()
- window.connect('delete-event', gtk.main_quit)
-
- def test_number_grid ():
- t = SudokuNumberGrid(4)
- window.add(t)
- t.__entries__[(0, 1)].set_color((0.0, 1.0, 0.0))
- t.__entries__[(0, 1)].set_value(4)
- t.__entries__[(1, 1)].set_error_highlight(True)
- t.__entries__[(1, 1)].set_value(1)
- t.__entries__[(2, 1)].set_color((0.0, 0.0, 1.0))
- t.__entries__[(2, 1)].set_error_highlight(True)
- t.__entries__[(2, 1)].set_value(2)
- t.__entries__[(3, 1)].set_color((0.0, 0.0, 1.0))
- t.__entries__[(3, 1)].set_error_highlight(True)
- t.__entries__[(3, 1)].set_error_highlight(False)
- t.__entries__[(3, 1)].set_value(3)
- t.__entries__[(3, 1)].set_note_text('234', '12')
-
- def test_sudoku_game ():
- game = '''1 8 4 2 0 0 0 0 0
- 0 6 0 0 0 9 1 2 0
- 0 2 0 0 8 0 0 0 0
- 0 1 8 0 5 0 0 0 0
- 9 0 0 0 0 0 0 0 3
- 0 0 0 0 1 0 6 5 0
- 0 0 0 0 9 0 0 8 0
- 0 5 7 1 0 0 0 9 0
- 0 0 0 0 0 3 5 4 7'''
- sgd = SudokuGameDisplay(game)
- sgd.set_bg_color('black')
- window.add(sgd)
- window.show_all()
-
-# test_number_grid()
-# reproduce_foobared_rendering()
- test_sudoku_game()
- window.show_all()
- gtk.main()
diff --git a/gnome-sudoku/src/lib/main.py b/gnome-sudoku/src/lib/main.py
index cf25f32..7aa7e7c 100644
--- a/gnome-sudoku/src/lib/main.py
+++ b/gnome-sudoku/src/lib/main.py
@@ -58,6 +58,8 @@ class UI (gconf_wrapper.GConfWrapper):
)
self.sudoku_maker = sudoku_maker.SudokuMaker()
self.sudoku_tracker = saver.SudokuTracker()
+ self._main_model = None
+ self._main_grid_vew = None
self.setup_gui()
self.timer = timer.ActiveTimer(self.w)
self.gsd.set_timer(self.timer)
@@ -106,10 +108,13 @@ class UI (gconf_wrapper.GConfWrapper):
logger.warning("failed to genereate new puzzle")
return None
- def _open_game(self, type, puzzle):
+ def _open_game(self, game_type, puzzle):
"""Finally enter the puzzle"""
+ self._puzzle = puzzle
if type == game_selector.NewOrSavedGameSelector.NEW_GAME:
- self.gsd.change_grid(puzzle, 9)
+ self._main_model = gsudoku.SudokuModel(puzzle, 9)
+ self._main_grid_vew.connect_to_model(self._main_model)
+# self._main_grid_vew.connect('puzzle-finished', self.you_win_callback)
elif type == game_selector.NewOrSavedGameSelector.SAVED_GAME:
saver.open_game(self, puzzle)
self._post_open_setup()
@@ -155,6 +160,7 @@ class UI (gconf_wrapper.GConfWrapper):
self.gsd.connect('puzzle-finished', self.you_win_callback)
self.setup_color()
self._setup_actions()
+ return
self.setup_undo()
self.setup_autosave()
#TODO
@@ -185,9 +191,8 @@ class UI (gconf_wrapper.GConfWrapper):
def _setup_main_boxes(self):
main_area = self.builder.get_object("main_area")
- self.gsd = gsudoku.SudokuGameDisplay()
- self.gsd.connect('puzzle-finished', self.you_win_callback)
- main_area.pack_start(self.gsd, padding = 6)
+ self._main_grid_vew = gsudoku.SudokuView(group_size=9)
+ main_grid_container.pack_start(self._main_grid_vew, padding=6)
self.tracker_ui = tracker_box.TrackerBox(self)
self.tracker_ui.show_all()
@@ -198,7 +203,7 @@ class UI (gconf_wrapper.GConfWrapper):
# setup background colors
bgcol = self.gconf['bg_color']
if bgcol != '':
- self.gsd.set_bg_color(bgcol)
+ self._main_grid_vew.set_bg_color(bgcol)
def _setup_actions (self):
toggle_actions = [
@@ -340,7 +345,7 @@ class UI (gconf_wrapper.GConfWrapper):
"""Close current running game
Return True if game is closed, or else return False"""
- if (self.gsd.grid and self.gsd.grid.is_changed() and not self.won):
+ if (self._main_model.grid and self._main_model.grid.is_changed() and not self.won):
try:
if dialog_extras.getBoolean(
label = _("Save current game?"),
@@ -357,7 +362,7 @@ class UI (gconf_wrapper.GConfWrapper):
def do_stop (self):
self.stop_dancer()
- self.gsd.grid = None
+ self._main_modelgsd.grid = None
self.tracker_ui.reset()
self.history.clear()
self.won = False
@@ -369,8 +374,8 @@ class UI (gconf_wrapper.GConfWrapper):
def quit_cb (self, *args):
self.w.hide()
- if (self.gsd.grid
- and self.gsd.grid.is_changed()
+ if (self._main_model.grid
+ and self._main_model.grid.is_changed()
and (not self.won)):
self.save_game(self)
# make sure we really go away before doing our saving --
@@ -468,7 +473,7 @@ class UI (gconf_wrapper.GConfWrapper):
# Redraw the notes
self.gsd.update_all_notes()
# Make sure we're still dancing if we undo after win
- if self.gsd.grid.check_for_completeness():
+ if self._main_model.grid.check_for_completeness():
self.start_dancer()
def show_hint_cb (self, *args):
@@ -521,13 +526,13 @@ class UI (gconf_wrapper.GConfWrapper):
self.gsd.toggle_highlight(False)
def show_info_cb (self, *args):
- if not self.gsd.grid:
+ if not self._main_model.grid:
dialog_extras.show_message(parent = self.w,
title = _("Puzzle Information"),
label = _("There is no current puzzle.")
)
return
- puzzle = self.gsd.grid.virgin.to_string()
+ puzzle = self._main_model.grid.virgin.to_string()
diff = self.sudoku_maker.get_difficulty(puzzle)
information = _("Calculated difficulty: ")
information += diff.value_string()
@@ -549,7 +554,7 @@ class UI (gconf_wrapper.GConfWrapper):
def autosave (self):
# this is called on a regular loop and will autosave if we
# have reason to...
- if self.gsd.grid and self.gsd.grid.is_changed() and not self.won:
+ if self._main_model.grid and self._main_model.grid.is_changed() and not self.won:
self.sudoku_tracker.save_game(self)
return True
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]