gnome-games r7128 - trunk/gnome-sudoku/src/lib
- From: andreasr svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-games r7128 - trunk/gnome-sudoku/src/lib
- Date: Thu, 10 Jan 2008 23:24:18 +0000 (GMT)
Author: andreasr
Date: Thu Jan 10 23:24:18 2008
New Revision: 7128
URL: http://svn.gnome.org/viewvc/gnome-games?rev=7128&view=rev
Log:
Make gnome-sudoku startup process more consistent with glChess.
Added:
trunk/gnome-sudoku/src/lib/main.py
Modified:
trunk/gnome-sudoku/src/lib/Makefile.am
trunk/gnome-sudoku/src/lib/defaults.py.in
trunk/gnome-sudoku/src/lib/gnome_sudoku.py
Modified: trunk/gnome-sudoku/src/lib/Makefile.am
==============================================================================
--- trunk/gnome-sudoku/src/lib/Makefile.am (original)
+++ trunk/gnome-sudoku/src/lib/Makefile.am Thu Jan 10 23:24:18 2008
@@ -9,6 +9,7 @@
gnome_sudoku.py \
gsudoku.py \
__init__.py \
+ main.py \
pausable.py \
printing.py \
saver.py \
Modified: trunk/gnome-sudoku/src/lib/defaults.py.in
==============================================================================
--- trunk/gnome-sudoku/src/lib/defaults.py.in (original)
+++ trunk/gnome-sudoku/src/lib/defaults.py.in Thu Jan 10 23:24:18 2008
@@ -1,8 +1,8 @@
-import os, os.path
+import sys, os, os.path
import errno
import gettext
-if sys.modules["glchess"].installed_mode:
+if sys.modules["gnome_sudoku"].installed_mode:
APP_DATA_DIR = os.path.join('@prefix@', 'share')
IMAGE_DIR = os.path.join(APP_DATA_DIR, 'pixmaps', 'gnome-sudoku')
LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
@@ -10,7 +10,7 @@
BASE_DIR = os.path.join(APP_DATA_DIR,'gnome-sudoku')
else:
APP_DATA_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'data')
- IMAGE_DIR = os.path.join('@abs_top_builddir@', 'glchess', 'images')
+ IMAGE_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'images')
LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
GLADE_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'glade')
BASE_DIR = os.path.join('@abs_top_builddir@', 'gnome-sudoku', 'data')
Modified: trunk/gnome-sudoku/src/lib/gnome_sudoku.py
==============================================================================
--- trunk/gnome-sudoku/src/lib/gnome_sudoku.py (original)
+++ trunk/gnome-sudoku/src/lib/gnome_sudoku.py Thu Jan 10 23:24:18 2008
@@ -1,1034 +1,21 @@
-try:
- import pygtk
- pygtk.require('2.0')
-except ImportError, err:
- print ("PyGTK not found. Please make sure it is installed properly and referenced in your PYTHONPATH environment variable.")
+import sys
-import gtk, gobject, gtk.glade
-import gnome, gnome.ui, pango
-import os, os.path
-from gtk_goodies import gconf_wrapper, Undo, dialog_extras, image_extras
-import gsudoku, sudoku, saver, sudoku_maker, printing, sudoku_generator_gui
-import game_selector
-import time, threading
-from gettext import gettext as _
-from gettext import ngettext
-from defaults import *
-from timer import ActiveTimer
-from simple_debug import simple_debug,options
-from dialog_swallower import SwappableArea
-
-icon_factory = gtk.IconFactory()
-STOCK_PIXBUFS = {}
-for filename,stock_id in [('footprints.png','tracks'),]:
- pb = gtk.gdk.pixbuf_new_from_file(os.path.join(IMAGE_DIR,filename))
- STOCK_PIXBUFS[stock_id]=pb
- iconset = gtk.IconSet(pb)
- icon_factory.add(stock_id,iconset)
- icon_factory.add_default()
-
-gtk.stock_add([('tracks',
- _('Track moves'),
- 0,0,""),])
-
-try:
- STOCK_FULLSCREEN = gtk.STOCK_FULLSCREEN
-except:
- STOCK_FULLSCREEN = _('Full Screen')
-
-def inactivate_new_game_etc (fun):
- def _ (ui, *args, **kwargs):
- paths = [
- '/MenuBar/Game/New',
- #'/MenuBar/Game/Open',
- #'/MenuBar/Game/ByHand',
- '/MenuBar/Game/Print',
- '/MenuBar/Edit/Undo',
- '/MenuBar/Edit/Redo',
- '/MenuBar/Edit/Clear',
- '/MenuBar/Edit/ClearNotes',
- '/MenuBar/Tools/ShowPossible',
- '/MenuBar/Tools/AutofillCurrentSquare',
- '/MenuBar/Tools/Autofill',
- '/MenuBar/Tools/AlwaysShowPossible',
- '/MenuBar/Tools/ShowImpossibleImplications',
- '/MenuBar/Tools/Tracker',
- '/MenuBar/Game/PuzzleInfo',
- #'/MenuBar/Game/HighScores',
- ]
- for p in paths:
- action = ui.uimanager.get_action(p)
- if not action: action = ui.uimanager.get_widget(p)
- if not action: print 'No action at path',p
- else: action.set_sensitive(False)
- ret = fun(ui,*args,**kwargs)
- for p in paths:
- action = ui.uimanager.get_action(p)
- if not action: action = ui.uimanager.get_widget(p)
- if not action: print 'No action at path',p
- else: action.set_sensitive(True)
- return ret
- return _
-
-class UI (gconf_wrapper.GConfWrapper):
- ui='''<ui>
- <menubar name="MenuBar">
- <menu name="Game" action="Game">
- <menuitem action="New"/>
- <!--<menuitem action="ByHand"/>-->
- <!--<menuitem action="Open"/>-->
- <!--<separator/>
- <menuitem action="Save"/>-->
- <separator/>
- <menuitem action="PuzzleInfo"/>
- <!--<menuitem action="HighScores"/>-->
- <separator/>
- <menuitem action="Print"/>
- <menuitem action="PrintMany"/>
- <separator/>
- <menuitem action="Close"/>
- <!--<menuitem action="Quit"/>-->
- </menu>
- <menu action="Edit">
- <menuitem action="Undo"/>
- <menuitem action="Redo"/>
- <separator/>
- <menuitem action="Clear"/>
- <menuitem action="ClearNotes"/>
- </menu>
- <menu action="View">
- <menuitem action="FullScreen"/>
- <separator/>
- <menuitem action="ToggleToolbar"/>
- <menuitem action="ToggleHighlight"/>
- </menu>
- <menu action="Tools">
- <menuitem action="ShowPossible"/>
- <menuitem action="AutofillCurrentSquare"/>
- <menuitem action="Autofill"/>
- <separator/>
- <menuitem action="AlwaysShowPossible"/>
- <menuitem action="ShowImpossibleImplications"/>
-
- <separator/>
- <menuitem action="Generator"/>
- <menuitem action="BackgroundGenerator"/>
- <separator/>
- <menuitem action="Tracker"/>
- </menu>
- <menu action="Help">
- <menuitem action="ShowHelp"/>
- <menuitem action="About"/>
- </menu>
- </menubar>
- <toolbar name="Toolbar">
- <!--<toolitem action="Quit"/>-->
- <toolitem action="New"/>
- <!--<toolitem action="Open"/>-->
- <toolitem action="Print"/>
- <!--<toolitem action="Save"/>-->
- <!--<separator/>-->
- <!--<toolitem action="Clear"/> -->
- <!--<toolitem action="ClearNotes"/>-->
- <separator/>
- <toolitem action="Undo"/>
- <toolitem action="Redo"/>
- <separator/>
- <toolitem action="ShowPossible"/>
- <!--<toolitem action="AlwaysShowPossible"/>-->
- <toolitem action="AutofillCurrentSquare"/>
- <separator/>
- <toolitem action="ToggleHighlight"/>
- <!--<toolitem action="AlwaysShowPossible"/>-->
-
- <toolitem action="Tracker"/>
- </toolbar>
- </ui>'''
-
- initial_prefs = {'group_size':9,
- 'font_zoom':0,
- 'zoom_on_resize':1,
- 'always_show_hints':0,
- 'player':os.environ.get('USERNAME',''),
- 'difficulty':0.0,
- 'minimum_number_of_new_puzzles':MIN_NEW_PUZZLES,
- 'highlight':0,
- 'bg_black':1,
- 'bg_custom_color':'',
- 'show_tracker':False,
- 'width': 700,
- 'height': 675,
- 'auto_save_interval':60 # auto-save interval in seconds...
- #'show_notes':0
- }
-
- @simple_debug
- def __init__ (self):
- gconf_wrapper.GConfWrapper.__init__(self,
- gconf_wrapper.GConf('gnome-sudoku')
- )
- self.setup_gui()
- self.timer = ActiveTimer(self.w)
- self.won = False
- # add the accelerator group to our toplevel window
- self.worker_connections=[]
- self.timer.start_timing()
- # setup sudoku maker...
- self.sudoku_maker = sudoku_maker.SudokuMaker()
- self.sudoku_tracker = saver.SudokuTracker()
- #if not self.sudoku_tracker.playing:
- # self.main_actions.get_action('Open').set_sensitive(False)
- #else:
- #self.main_actions.get_action('Open').set_sensitive(True)
- #if not self.sudoku_tracker.are_finished_games():
- # self.main_actions.get_action('HighScores').set_sensitive(False)
- # select an easy puzzle...
- #puz,d=self.sudoku_maker.get_new_puzzle(self.gconf['difficulty'])
- #print 'Default to ',puz
- #self.gsd.change_grid(puz, 9)
- # generate puzzles while our use is working...
- self.show()
- #print 'Select game!'
- if self.select_game():
- # If this return True, the user closed...
- #print 'select game returned True - exit'
- self.quit = True
- else:
- self.quit = False
- # Generate puzzles in background...
- if self.gconf['generate_puzzles_in_background']:
- gobject.timeout_add(1000,lambda *args: self.start_worker_thread() and True)
-
-
- @inactivate_new_game_etc
- def select_game (self):
- self.tb.hide()
- choice = game_selector.NewOrSavedGameSelector().run_swallowed_dialog(self.swallower)
- #print "The user's choice is ",choice
- if not choice:
- return True
- #print 'choice is ',choice
- if choice[0] == game_selector.NewOrSavedGameSelector.NEW_GAME:
- self.gsd.change_grid(choice[1],9)
- if choice[0] == game_selector.NewOrSavedGameSelector.SAVED_GAME:
- saver.open_game(self,choice[1])
- if self.gconf['show_toolbar']: self.tb.show()
-
- def show (self):
- self.gsd.show()
- self.w.show()
-
- def setup_gui (self):
- gnome.program_init('gnome-sudoku',VERSION, properties={gnome.PARAM_APP_DATADIR:APP_DATA_DIR})
- self.initialize_prefs()
- self.setup_main_window()
- self.gsd = gsudoku.SudokuGameDisplay()
- self.gsd.connect('puzzle-finished',self.you_win_callback)
- self.setup_color()
- self.setup_actions()
- self.setup_undo()
- self.setup_autosave()
- self.w.add_accel_group(self.uimanager.get_accel_group())
- self.setup_main_boxes()
- self.setup_tracker_interface()
- self.setup_toggles()
-
- def setup_main_window (self):
- gtk.window_set_default_icon_name('gnome-sudoku')
- self.w = gtk.Window()
- self.w.set_default_size(self.gconf['width'], self.gconf['height'])
- self.w.set_title(APPNAME_SHORT)
- self.w.connect('configure-event',self.resize_cb)
- self.w.connect('delete-event',self.quit_cb)
- self.uimanager = gtk.UIManager()
-
- def setup_actions (self):
- self.main_actions = gtk.ActionGroup('MainActions')
- self.main_actions.add_actions([
- ('Game',None,_('_Game')),
- ('New',gtk.STOCK_NEW,None,
- '<Control>n',_('New game'),self.new_cb),
- ('Print',gtk.STOCK_PRINT,None,
- None,_('Print current game'),self.print_game),
- ('PrintMany',gtk.STOCK_PRINT,_('Print _Multiple Sudokus'),
- None,_('Print more than one sudoku at a time.'),self.print_multiple_games),
- #('Quit',gtk.STOCK_QUIT,None,'<Control>q',
- # 'Quit Sudoku game',self.quit_cb),
- ('Close',gtk.STOCK_CLOSE,None,'<Control>w',
- _('Close Sudoku'),self.quit_cb),
- #('Save',gtk.STOCK_SAVE,_('_Save'),
- # '<Control>s','Save game to play later.',
- # self.save_game_cb),
- #('ByHand',gtk.STOCK_EDIT,_('_Enter custom game'),
- # None,_('Enter new puzzle by hand (use this to copy a puzzle from another source).'),
- # self.enter_game_by_hand),
- #('Open',gtk.STOCK_OPEN,_('Open game'),
- # '<Control>o',_('Open a saved game from file.'),
- # self.open_game),
- ('Tools',None,_('_Tools')),
- ('View',None,_('_View')),
- ('ShowPossible',gtk.STOCK_DIALOG_INFO,_('_Hint'),
- '<Control>h',
- _('Show which numbers could go in the current square.'),
- self.show_hint_cb),
- ('AutofillCurrentSquare',gtk.STOCK_APPLY,_('_Fill'),'<Control>f',
- _('Automatically fill in the current square if possible.'),
- self.auto_fill_current_square_cb),
- ('Autofill',gtk.STOCK_REFRESH,_('Fill _all squares'),'<Control>a',
- _('Automatically fill in all squares for which there is only one valid value.'),
- self.auto_fill_cb),
- ('FullScreen',STOCK_FULLSCREEN,None,
- 'F11',None,self.full_screen_cb),
- ('PuzzleInfo',gtk.STOCK_ABOUT,_('Puzzle _Statistics'),
- None,_('Show statistics about current puzzle'),
- self.show_info_cb),
- ('Help',None,_('_Help'),
- None,None,None),
- ('About',gtk.STOCK_ABOUT,None,
- None,None,self.show_about),
- ('ShowHelp',gtk.STOCK_HELP, _('_Contents'),
- 'F1',None,self.show_help),
- #('HighScores',None,_('High _Scores'),
- # None,_('Show high scores or replay old games.'),
- # self.show_high_scores_cb),
- ])
- self.main_actions.add_toggle_actions([
- ('AlwaysShowPossible',
- None,
- _('_Always show hint'),
- None,
- _('Always show possible numbers in a square'),
- self.auto_hint_cb),
- ('ShowImpossibleImplications',
- None,
- _('Warn about _unfillable squares'),
- None,
- _('Warn about squares made unfillable by a move'),
- self.impossible_implication_cb),
- ('Tracker','tracks',_('_Track additions'),
- '<Control>T',
- _('Mark new additions in a separate color so you can keep track of them.'),
- self.tracker_toggle_cb,False),
- ('ToggleToolbar',None,_('Show _Toolbar'),None,None,self.toggle_toolbar_cb,True),
- ('ToggleHighlight',gtk.STOCK_SELECT_COLOR,_('_Highlighter'),
- None,_('Highlight the current row, column and box'),self.toggle_highlight_cb,False),
- ('BackgroundGenerator',None,_('Generate new puzzles _while you play'),
- None,
- _('Generate new puzzles in the background while you play. This will automatically pause when the game goes into the background.'),
- self.toggle_generator_cb, True),
- ])
-
- self.edit_actions = gtk.ActionGroup('EditActions')
- self.edit_actions.add_actions(
- [('Edit',None,_('_Edit')),
- ('Undo',gtk.STOCK_UNDO,_('_Undo'),'<Control>z',_('Undo last action')),
- ('Redo',gtk.STOCK_REDO,_('_Redo'),'<Shift><Control>z',_('Redo last action')),
- ('Clear',gtk.STOCK_CLEAR,_('_Clear'),'<Control>b',_("Clear entries you've filled in"),self.clear_cb),
- ('ClearNotes',None,_('Clear _Notes'),None,_("Clear notes and hints"),self.clear_notes_cb),
- # Trackers...
- ('Tracker%s',None,_('No Tracker'),'<Control>0',None,lambda *args: self.set_tracker(-1)),
- ('Generator',None,_('_Generate new puzzles'),None,_('Generate new puzzles.'),
- self.generate_puzzle_gui,),
- ])
- self.edit_actions.add_actions(
- [('Tracker%s'%n,None,'Tracker _%s'%n,'<Control>%s'%n,None,lambda *args: self.set_tracker(n-1)) for
- n in range(1,9)])
- self.uimanager.insert_action_group(self.main_actions,0)
- self.uimanager.insert_action_group(self.edit_actions,0)
- self.uimanager.add_ui_from_string(self.ui)
-
- def setup_undo (self):
- self.cleared = [] # used for Undo memory
- self.cleared_notes = [] # used for Undo memory
- # Set up our UNDO stuff
- undo_widg = self.edit_actions.get_action('Undo')
- redo_widg = self.edit_actions.get_action('Redo')
- self.history = Undo.UndoHistoryList(undo_widg,redo_widg)
- for e in self.gsd.__entries__.values():
- Undo.UndoableGenericWidget(e,self.history,
- set_method='set_value_from_undo',
- pre_change_signal='value-about-to-change'
- )
- Undo.UndoableGenericWidget(e,self.history,
- set_method='set_notes',
- get_method='get_note_text',
- signal='notes-changed',
- pre_change_signal='value-about-to-change',
- )
-
- def setup_color (self):
- # setup background colors
- if self.gconf['bg_custom_color']:
- bgcol = self.gconf['bg_custom_color']
- elif self.gconf['bg_black']:
- bgcol = 'black'
- else:
- bgcol = None
- if bgcol: self.gsd.set_bg_color(bgcol)
-
- def setup_autosave (self):
- gobject.timeout_add(1000*(self.gconf['auto_save_interval'] or 60), # in seconds...
- self.autosave)
-
- def setup_main_boxes (self):
- self.vb = gtk.VBox()
- # Add menu bar and toolbar...
- mb = self.uimanager.get_widget('/MenuBar'); mb.show()
- self.vb.pack_start(mb,fill=False,expand=False)
- self.tb = self.uimanager.get_widget('/Toolbar')
- self.vb.pack_start(self.tb,fill=False,expand=False)
- self.main_area = gtk.HBox()
- self.swallower = SwappableArea(self.main_area)
- self.swallower.show()
- self.vb.pack_start(self.swallower,True,padding=12)
- self.main_area.pack_start(self.gsd,padding=6)
- self.main_actions.set_visible(True)
- self.game_box = gtk.VBox()
- self.main_area.show()
- self.vb.show()
- self.game_box.show()
- self.main_area.pack_start(self.game_box,False,padding=12)
- self.statusbar = gtk.Statusbar(); self.statusbar.show()
- gobject.timeout_add(500,self.update_statusbar_cb)
- self.vb.pack_end(self.statusbar,fill=False,expand=False)
- self.w.add(self.vb)
-
- def setup_by_hand_area (self):
- # Set up area for by-hand editing...
- self.by_hand_label = gtk.Label()
- self.by_hand_label.set_alignment(0,0)
- self.by_hand_label.set_markup('<i>%s</i>'%_('Entering custom grid...'))
- self.game_box.pack_start(self.by_hand_label,False,)#padding=12)
- self.by_hand_buttonbox = gtk.HButtonBox()
- self.by_hand_buttonbox.set_spacing(12)
- self.by_hand_save_button = gtk.Button(_('_Play game'))
- self.by_hand_save_button.connect('clicked',self.save_handmade_grid)
- self.by_hand_cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
- self.by_hand_cancel_button.connect('clicked',self.cancel_handmade_grid)
- self.by_hand_buttonbox.add(self.by_hand_cancel_button)
- self.by_hand_buttonbox.add(self.by_hand_save_button)
- self.game_box.pack_start(self.by_hand_buttonbox,False,padding=18)
- self.by_hand_widgets = [self.by_hand_label,self.by_hand_buttonbox]
-
- def setup_toggles (self):
- # sync up toggles with gconf values...
- map(lambda tpl: self.gconf_wrap_toggle(*tpl),
- [('always_show_hints',
- self.main_actions.get_action('AlwaysShowPossible')),
- ('show_impossible_implications',
- self.main_actions.get_action('ShowImpossibleImplications')),
- ('generate_puzzles_in_background',
- self.main_actions.get_action('BackgroundGenerator')),
- ('show_toolbar',
- self.main_actions.get_action('ToggleToolbar')),
- ('highlight',
- self.main_actions.get_action('ToggleHighlight')),
- ('show_tracker',
- self.main_actions.get_action('Tracker')),
- ])
-
- @simple_debug
- def start_worker_thread (self, *args):
- n_new_puzzles = self.sudoku_maker.n_puzzles(new=True)
- if n_new_puzzles < self.gconf['minimum_number_of_new_puzzles']:
- #print 'Generate puzzles'
- self.worker = threading.Thread(target=lambda *args: self.sudoku_maker.work(limit=5))
- self.worker_connections = [
- self.timer.connect('timing-started',self.sudoku_maker.resume),
- self.timer.connect('timing-stopped',self.sudoku_maker.pause)
- ]
- self.worker.start()
- #else:
- # print "Don't generate...",'We already have ',n_new_puzzles,'!'
-
- @simple_debug
- def stop_worker_thread (self, *args):
- if hasattr(self,'worker'):
- self.sudoku_maker.stop()
- for c in self.worker_connections:
- self.timer.disconnect(c)
-
- @simple_debug
- def you_win_callback (self,grid):
- self.won = True
- # increase difficulty for next time.
- self.gconf['difficulty']=self.gconf['difficulty']+0.1
- self.timer.finish_timing()
- self.sudoku_tracker.finish_game(self)
- sublabel = _("You completed the puzzle in %(totalTime)s (%(activeTime)s active)")%{'totalTime': self.timer.total_time_string(),
- 'activeTime': self.timer.active_time_string()
- }
- sublabel += "\n"
- sublabel += ngettext("You got %(n)s hint","You got %(n)s hints",self.gsd.hints)%{'n':self.gsd.hints}
- sublabel += "\n"
- if self.gsd.impossible_hints:
- sublabel += ngettext("You had %(n)s impossibility pointed out.",
- "You had %(n)s impossibilities pointed out.",
- self.gsd.impossible_hints)%{'n':self.gsd.impossible_hints}
- sublabel += "\n"
- if self.gsd.auto_fills:
- sublabel += ngettext("You used the auto-fill %(n)s time",
- "You used the auto-fill %(n)s times",
- self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
- dialog_extras.show_message(_("You win!"),label=_("You win!"),
- #icon=os.path.join(IMAGE_DIR,'winner2.png'),
- sublabel=sublabel
- )
- # High scores is complicated and kind of unnecessary for now...
- # If we reimplement, we should use the proper gnome-games system.
- #hs = game_selector.HighScores(self.sudoku_tracker)
- #hs.highlight_newest=True
- #hs.run_swallowed_dialog(self.swallower)
- #print 'Run high scores dialog!'
- #hs.run_dialog()
- #self.main_actions.get_action('HighScores').set_sensitive(True)
- #self.gsd.blank_grid()
- self.new_cb()
-
- @simple_debug
- def initialize_prefs (self):
- for k,v in self.initial_prefs.items():
- try:
- self.gconf[k]
- except:
- self.gconf[k]=v
- self.player = self.gconf['player']
-
- @simple_debug
- @inactivate_new_game_etc
- def new_cb (self,*args):
- if (self.gsd.grid and self.gsd.grid.is_changed() and not self.won):
- try:
- if dialog_extras.getBoolean(
- label=_("Save this game before starting new one?"),
- custom_yes=_("_Save game for later"),
- custom_no=_("_Abandon game"),
- ):
- self.save_game()
- else:
- self.sudoku_tracker.abandon_game(self)
- except dialog_extras.UserCancelledError:
- # User cancelled new game
- return
- self.do_stop()
- self.select_game()
-
-
- @simple_debug
- def stop_game (self):
- if (self.gsd.grid
- and self.gsd.grid.is_changed()
- and (not self.won)):
- try:
- if dialog_extras.getBoolean(label=_("Save game before closing?")):
- self.save_game(self)
- except dialog_extras.UserCancelledError:
- return
- self.do_stop()
-
- def do_stop (self):
- self.gsd.grid = None
- self.tracker_ui.reset()
- self.timer.reset_timer()
- self.timer.start_timing()
- self.history.clear()
- self.won = False
-
- @simple_debug
- def resize_cb (self, widget, event):
- self.gconf['width'] = event.width
- self.gconf['height'] = event.height
-
- @simple_debug
- def quit_cb (self, *args):
- # Offer a save...
- #try:
- if (self.gsd.grid
- and self.gsd.grid.is_changed()
- and (not self.won)):
- #and dialog_extras.getBoolean(label=_("Save game before closing?"))):
- self.save_game(self) # autosave...
- #except dialog_extras.UserCancelledError:
- # return
- if gtk.main_level() > 1:
- # If we are in an embedded mainloop, that means that one
- # of our "swallowed" dialogs is active, in which case we
- # have to quit that mainloop before we can quit
- # properly.
- if self.swallower.running:
- d = self.swallower.running
- d.response(gtk.RESPONSE_DELETE_EVENT)
- gtk.main_quit() # Quit the embedded mainloop
- gobject.idle_add(self.quit_cb,100) # Call ourselves again
- # to quit the main
- # mainloop
- return
- #buttons = d.action_area.get_children()
- #for b in buttons:
- # if d.get_response_for_widget(b) in [gtk.RESPONSE_CLOSE,gtk.RESPONSE_CANCEL]:
- # print 'clicking button',b
- # b.emit('clicked')
- # while gtk.events_pending():
- # print 'Take care of iters...'
- # gtk.main_iteration()
- # break
- self.w.hide()
- # make sure we really go away before doing our saving --
- # otherwise we appear sluggish.
- while gtk.events_pending():
- gtk.main_iteration()
- if self.won:
- self.gconf['current_game']=''
- if not self.won:
- if not self.gsd.grid:
- self.gconf['current_game']=''
- self.stop_worker_thread()
- # allow KeyboardInterrupts, which calls quit_cb outside the main loop
- try:
- gtk.main_quit()
- except RuntimeError, e:
- pass
-
- @simple_debug
- @inactivate_new_game_etc
- def enter_game_by_hand (self, *args):
- self.stop_game()
- self.gsd.change_grid(sudoku.InteractiveSudoku(),9)
- for w in self.by_hand_widgets: w.show_all()
-
- @simple_debug
- def save_handmade_grid (self, *args):
- for w in self.by_hand_widgets: w.hide()
- # this should make our active grid into our virgin grid
- self.won = False
- self.gsd.change_grid(self.gsd.grid,9)
- self.sudoku_maker.names[self.gsd.grid.to_string()]=self.sudoku_maker.get_puzzle_name('Custom Puzzle')
- self.history.clear()
-
- @simple_debug
- def cancel_handmade_grid (self, *args):
- for w in self.by_hand_widgets: w.hide()
-
- #def save_game_cb (self, *args):
- # try:
- # self.save_game(*args)
- # except dialog_extras.UserCancelledError:
- # pass
-
- @simple_debug
- def save_game (self, *args):
- self.sudoku_tracker.save_game(self)
-
- @simple_debug
- def zoom_in_cb (self,*args):
- self.gh.change_font_size(multiplier=1.1)
- self.zoom = self.zoom * 1.1
-
- @simple_debug
- def zoom_out_cb (self,*args):
- self.gh.change_font_size(multiplier=0.9)
- self.zoom = self.zoom * 0.9
-
- def full_screen_cb (self, *args):
- if not hasattr(self,'is_fullscreen'): self.is_fullscreen = False
- if self.is_fullscreen:
- self.w.unfullscreen()
- self.is_fullscreen = False
- else:
- self.w.fullscreen()
- self.is_fullscreen = True
-
- @simple_debug
- def clear_cb (self,*args):
- clearer=Undo.UndoableObject(
- lambda *args: self.cleared.append(self.gsd.reset_grid()), #action
- lambda *args: [self.gsd.add_value_to_ui(*entry) for entry in self.cleared.pop()], #inverse
- self.history #history
- )
- clearer.perform()
-
- def clear_notes_cb (self, *args):
- clearer = Undo.UndoableObject(
- lambda *args: self.cleared_notes.append(self.gsd.clear_notes()), #action
- # clear_notes returns a list of tuples indicating the cleared notes...
- # (x,y,(top,bottom)) -- this is what we need for undoing
- lambda *args: [self.gsd.__entries__[t[0],t[1]].set_notes(t[2]) for t in self.cleared_notes.pop()], #inverse
- self.history
- )
- clearer.perform()
-
- @simple_debug
- def show_hint_cb (self, *args):
- self.gsd.show_hint()
-
- @simple_debug
- def auto_hint_cb (self, action):
- if action.get_active():
- self.gsd.always_show_hints = True
- self.gsd.update_all_hints()
- else:
- self.gsd.always_show_hints = False
- self.gsd.clear_hints()
-
- @simple_debug
- def impossible_implication_cb (self, action):
- if action.get_active():
- self.gsd.show_impossible_implications = True
- else:
- self.gsd.show_impossible_implications = False
-
- @simple_debug
- def auto_fill_cb (self, *args):
- if not hasattr(self,'autofilled'): self.autofilled=[]
- if not hasattr(self,'autofiller'):
- self.autofiller = Undo.UndoableObject(
- lambda *args: self.autofilled.append(self.gsd.auto_fill()),
- lambda *args: [self.gsd.remove(entry[0],entry[1],do_removal=True) for entry in self.autofilled.pop()],
- self.history
- )
- self.autofiller.perform()
-
- @simple_debug
- def auto_fill_current_square_cb (self, *args):
- self.gsd.auto_fill_current_entry()
-
- @simple_debug
- def setup_tracker_interface (self):
- self.trackers = {}
- self.tracker_ui = TrackerBox(self)
- self.tracker_ui.show_all()
- self.tracker_ui.hide()
- self.game_box.add(self.tracker_ui)
-
- @simple_debug
- def set_tracker (self, n):
- if self.gsd.trackers.has_key(n):
- self.tracker_ui.select_tracker(n)
- e = self.gsd.get_focused_entry()
- if e:
- if n==-1:
- for tid in self.gsd.trackers_for_point(e.x,e.y):
- self.gsd.remove_tracker(e.x,e.y,tid)
- else:
- self.gsd.add_tracker(e.x,e.y,n)
- else:
- print 'No tracker ',n,'yet'
-
- @simple_debug
- def tracker_toggle_cb (self, widg):
- if widg.get_active():
- #if len(self.tracker_ui.tracker_model)<=1:
- # self.tracker_ui.add_tracker()
- self.tracker_ui.show_all()
- else:
- self.tracker_ui.hide()
-
- @simple_debug
- def toggle_toolbar_cb (self, widg):
- if widg.get_active(): self.tb.show()
- else: self.tb.hide()
-
- def update_statusbar_cb (self, *args):
- if not self.gsd.grid: return True
- puzz = self.gsd.grid.virgin.to_string()
- if (not hasattr(self,'current_puzzle_string') or
- self.current_puzzle_string != puzz):
- self.current_puzzle_diff = self.sudoku_maker.get_difficulty(puzz)
- tot_string = _("Playing %(difficulty)s puzzle.")%{'difficulty':self.current_puzzle_diff.value_string()}
- tot_string += " " + "(%1.2f)"%self.current_puzzle_diff.value
- #if self.timer.tot_time or self.timer.tot_time_complete:
- # time_string = _("%s (%s active)")%(
- # self.timer.total_time_string(),
- # self.timer.active_time_string()
- # )
- # if not self.timer.__timing__:
- # time_string += " %s"%_('paused')
- # tot_string += " - " + time_string
- #if self.gsd.hints and not self.gconf['always_show_hints']:
- # tot_string += " - " +ngettext("%(n)s hint","%(n)s hints",
- # self.gsd.hints)%{'n':self.gsd.hints}
- #if self.gsd.auto_fills:
- # tot_string += " " +ngettext("%(n)s auto-fill","%(n)s auto-fills",
- # self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
- if not hasattr(self,'sbid'):
- self.sbid = self.statusbar.get_context_id('game_info')
- self.statusbar.pop(self.sbid)
- self.statusbar.push(self.sbid,
- tot_string)
- return True
-
- def toggle_highlight_cb (self, widg):
- if widg.get_active():
- self.gsd.toggle_highlight(True)
- else:
- self.gsd.toggle_highlight(False)
-
- @simple_debug
- def show_info_cb (self, *args):
- if not self.gsd.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()
- diff = self.sudoku_maker.get_difficulty(puzzle)
- information = _("Calculated difficulty: ")
- information += diff.value_string()
- information += " (%1.2f)"%diff.value
- information += "\n"
- information += _("Number of moves instantly fillable by elimination: ")
- information += str(int(diff.instant_elimination_fillable))
- information += "\n"
- information += _("Number of moves instantly fillable by filling: ")
- information += str(int(diff.instant_fill_fillable))
- information += "\n"
- information += _("Amount of trial-and-error required to solve: ")
- information += str(len(diff.guesses))
- dialog_extras.show_message(parent=self.w,
- title=_("Puzzle Statistics"),
- label=_("Puzzle Statistics"),
- sublabel=information)
-
- @simple_debug
- def toggle_generator_cb (self, toggle):
- if toggle.get_active():
- self.start_worker_thread()
- else:
- self.stop_worker_thread()
-
- @simple_debug
- def show_high_scores_cb (self, *args):
- hs=game_selector.HighScores(self.sudoku_tracker)
- replay_game = hs.run_dialog()
- if replay_game:
- self.stop_game()
- self.gsd.change_grid(replay_game,9)
-
- @simple_debug
- 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:
- self.sudoku_tracker.save_game(self)
+# Ignore any exceptions writing to stdout using print statements
+class SafeStdout:
+ def __init__(self):
+ self.stdout = sys.stdout
- def offer_to_load_autosaved_file (self):
- pass
-
- @simple_debug
- def load_autosave (self, filename):
- saver.unpickle_game(self,filename)
- self.history.clear()
-
- #def toggle_autosave ():
+ def fileno(self):
+ return self.stdout.fileno()
- @simple_debug
- def show_about (self, *args):
- about = gtk.AboutDialog()
- about.set_name(APPNAME)
- about.set_version(VERSION)
- about.set_copyright(COPYRIGHT)
- about.set_license(LICENSE[0] + '\n\n' + LICENSE[1] + '\n\n' +LICENSE[2])
- about.set_wrap_license(True)
- about.set_comments(DESCRIPTION)
- about.set_authors(AUTHORS)
- about.set_website(WEBSITE)
- about.set_website_label(WEBSITE_LABEL)
- about.set_logo_icon_name("gnome-sudoku")
- about.set_translator_credits(_("translator-credits"))
- about.connect("response", lambda d, r: d.destroy())
- about.show()
-
- @simple_debug
- def show_help (self, *args):
- #dialog_extras.show_faq(faq_file=os.path.join(BASE_DIR,_('FAQ')))
+ def write(self, data):
try:
- gnome.help_display('gnome-sudoku')
- except gobject.GError, e:
- # FIXME: This should create a pop-up dialog
- print _('Unable to display help: %s') % str(e)
-
- @simple_debug
- def print_game (self, *args):
- printing.print_sudokus([self.gsd])
-
- @simple_debug
- def print_multiple_games (self, *args):
- gp=game_selector.GamePrinter(self.sudoku_tracker, self.gconf)
- gp.run_dialog()
-
- @simple_debug
- def generate_puzzle_gui (self, *args):
- sudoku_generator_gui.GameGenerator(self,self.gconf)
-
-class TrackerBox (gtk.VBox):
-
- @simple_debug
- def __init__ (self, main_ui):
-
- gtk.VBox.__init__(self)
- self.glade = gtk.glade.XML(os.path.join(GLADE_DIR,'tracker.glade'))
- self.main_ui = main_ui
- self.vb = self.glade.get_widget('vbox1')
- self.vb.unparent()
- self.pack_start(self.vb,expand=True,fill=True)
- self.setup_actions()
- self.setup_tree()
- self.show_all()
-
- @simple_debug
- def reset (self):
-
- for tree in self.tracker_model:
- if tree[0]>-1:
- self.tracker_model.remove(tree.iter)
-
- @simple_debug
- def setup_tree (self):
- self.tracker_tree = self.glade.get_widget('treeview1')
- self.tracker_model = gtk.ListStore(int,gtk.gdk.Pixbuf,str)
- self.tracker_tree.set_model(self.tracker_model)
- col1 = gtk.TreeViewColumn("",gtk.CellRendererPixbuf(),pixbuf=1)
- col2 = gtk.TreeViewColumn("",gtk.CellRendererText(),text=2)
- self.tracker_tree.append_column(col2)
- self.tracker_tree.append_column(col1)
- # Our initial row...
- self.tracker_model.append([-1,None,_('No Tracker')])
- self.tracker_tree.get_selection().connect('changed',self.selection_changed_cb)
-
- @simple_debug
- def setup_actions (self):
- self.tracker_actions = gtk.ActionGroup('tracker_actions')
- self.tracker_actions.add_actions(
- [('Clear',
- gtk.STOCK_CLEAR,
- _('_Clear Tracker'),
- None,_('Clear all moves tracked by selected tracker.'),
- self.clear_cb
- ),
- ('Keep',None,
- _('_Clear Others'),
- None,
- _('Clear all moves not tracked by selected tracker.'),
- self.keep_cb),
- ]
- )
- for action,widget_name in [('Clear','ClearTrackerButton'),
- ('Keep','KeepTrackerButton'),
- ]:
- a=self.tracker_actions.get_action(action)
- a.connect_proxy(self.glade.get_widget(widget_name))
- self.glade.get_widget('AddTrackerButton').connect('clicked',
- self.add_tracker)
- # Default to insensitive (they only become sensitive once a tracker is added)
- self.tracker_actions.set_sensitive(False)
-
- @simple_debug
- def add_tracker (self,*args):
- #print 'Adding tracker!'
- tracker_id = self.main_ui.gsd.create_tracker()
- #print 'tracker_id = ',tracker_id
- pb=image_extras.pixbuf_transform_color(
- STOCK_PIXBUFS['tracks'],
- (0,0,0),#white
- self.main_ui.gsd.get_tracker_color(tracker_id),
- )
- # select our new tracker
- self.tracker_tree.get_selection().select_iter(
- self.tracker_model.append([tracker_id,
- pb,
- _("Tracker %s")%(tracker_id+1)]
- )
- )
-
- @simple_debug
- def select_tracker (self, tracker_id):
- for row in self.tracker_model:
- if row[0]==tracker_id:
- self.tracker_tree.get_selection().select_iter(row.iter)
-
- @simple_debug
- def selection_changed_cb (self, selection):
- mod,itr = selection.get_selected()
- if itr: selected_tracker_id = mod.get_value(itr,0)
- else: selected_tracker_id=-1
- # This should be cheap since we don't expect many trackers...
- # We cycle through each row and toggle it off if it's not
- # selected; on if it is selected
- for row in self.tracker_model:
- tid = row[0]
- if tid != -1: # -1 == no tracker
- self.main_ui.gsd.toggle_tracker(tid,tid==selected_tracker_id)
- self.tracker_actions.set_sensitive(selected_tracker_id != -1)
-
- @simple_debug
- def clear_cb (self, action):
- mod,itr=self.tracker_tree.get_selection().get_selected()
- # This should only be called if there is an itr, but we'll
- # double-check just in case.
- if itr:
- selected_tracker_id=mod.get_value(itr,0)
- self.tracker_delete_tracks(selected_tracker_id)
-
- @simple_debug
- def keep_cb (self, action):
- mod,itr=self.tracker_tree.get_selection().get_selected()
- selected_tracker_id=mod.get_value(itr,0)
- self.tracker_keep_tracks(selected_tracker_id)
-
- @simple_debug
- def tracker_delete_tracks (self, tracker_id):
- clearer=Undo.UndoableObject(
- lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_by_tracker(tracker_id)),
- lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
- self.main_ui.history)
- clearer.perform()
-
- @simple_debug
- def tracker_keep_tracks (self, tracker_id):
- clearer=Undo.UndoableObject(
- lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_except_for_tracker(tracker_id)),
- lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
- self.main_ui.history)
- clearer.perform()
+ self.stdout.write(data)
+ except:
+ pass
+sys.stdout = SafeStdout()
def start_game ():
- if options.debug: print 'Starting GNOME Sudoku in debug mode'
-
- ## You must call g_thread_init() before executing any other GLib
- ## functions in a threaded GLib program.
- gobject.threads_init()
-
- if options.profile:
- options.profile = False
- profile_me()
- return
-
- u = UI()
- if not u.quit:
- try:
- gtk.main()
- except KeyboardInterrupt:
- # properly quit on a keyboard interrupt...
- u.quit_cb()
-
-def profile_me ():
- print 'Profiling GNOME Sudoku'
- import tempfile,os.path
- import hotshot, hotshot.stats
- pname = os.path.join(tempfile.gettempdir(),'GNOME_SUDOKU_HOTSHOT_PROFILE')
- prof = hotshot.Profile(pname)
- prof.runcall(start_game)
- stats = hotshot.stats.load(pname)
- stats.strip_dirs()
- stats.sort_stats('time','calls').print_stats()
-
-
-
-
-if __name__ == '__main__':
- import defaults
- defaults.DATA_DIR == '/tmp/'; DATA_DIR=='/tmp/'
-
-
+ import main
+ main.start_game()
Added: trunk/gnome-sudoku/src/lib/main.py
==============================================================================
--- (empty file)
+++ trunk/gnome-sudoku/src/lib/main.py Thu Jan 10 23:24:18 2008
@@ -0,0 +1,1034 @@
+try:
+ import pygtk
+ pygtk.require('2.0')
+except ImportError, err:
+ print ("PyGTK not found. Please make sure it is installed properly and referenced in your PYTHONPATH environment variable.")
+
+import gtk, gobject, gtk.glade
+import gnome, gnome.ui, pango
+import os, os.path
+from gtk_goodies import gconf_wrapper, Undo, dialog_extras, image_extras
+import gsudoku, sudoku, saver, sudoku_maker, printing, sudoku_generator_gui
+import game_selector
+import time, threading
+from gettext import gettext as _
+from gettext import ngettext
+from defaults import *
+from timer import ActiveTimer
+from simple_debug import simple_debug,options
+from dialog_swallower import SwappableArea
+
+icon_factory = gtk.IconFactory()
+STOCK_PIXBUFS = {}
+for filename,stock_id in [('footprints.png','tracks'),]:
+ pb = gtk.gdk.pixbuf_new_from_file(os.path.join(IMAGE_DIR,filename))
+ STOCK_PIXBUFS[stock_id]=pb
+ iconset = gtk.IconSet(pb)
+ icon_factory.add(stock_id,iconset)
+ icon_factory.add_default()
+
+gtk.stock_add([('tracks',
+ _('Track moves'),
+ 0,0,""),])
+
+try:
+ STOCK_FULLSCREEN = gtk.STOCK_FULLSCREEN
+except:
+ STOCK_FULLSCREEN = _('Full Screen')
+
+def inactivate_new_game_etc (fun):
+ def _ (ui, *args, **kwargs):
+ paths = [
+ '/MenuBar/Game/New',
+ #'/MenuBar/Game/Open',
+ #'/MenuBar/Game/ByHand',
+ '/MenuBar/Game/Print',
+ '/MenuBar/Edit/Undo',
+ '/MenuBar/Edit/Redo',
+ '/MenuBar/Edit/Clear',
+ '/MenuBar/Edit/ClearNotes',
+ '/MenuBar/Tools/ShowPossible',
+ '/MenuBar/Tools/AutofillCurrentSquare',
+ '/MenuBar/Tools/Autofill',
+ '/MenuBar/Tools/AlwaysShowPossible',
+ '/MenuBar/Tools/ShowImpossibleImplications',
+ '/MenuBar/Tools/Tracker',
+ '/MenuBar/Game/PuzzleInfo',
+ #'/MenuBar/Game/HighScores',
+ ]
+ for p in paths:
+ action = ui.uimanager.get_action(p)
+ if not action: action = ui.uimanager.get_widget(p)
+ if not action: print 'No action at path',p
+ else: action.set_sensitive(False)
+ ret = fun(ui,*args,**kwargs)
+ for p in paths:
+ action = ui.uimanager.get_action(p)
+ if not action: action = ui.uimanager.get_widget(p)
+ if not action: print 'No action at path',p
+ else: action.set_sensitive(True)
+ return ret
+ return _
+
+class UI (gconf_wrapper.GConfWrapper):
+ ui='''<ui>
+ <menubar name="MenuBar">
+ <menu name="Game" action="Game">
+ <menuitem action="New"/>
+ <!--<menuitem action="ByHand"/>-->
+ <!--<menuitem action="Open"/>-->
+ <!--<separator/>
+ <menuitem action="Save"/>-->
+ <separator/>
+ <menuitem action="PuzzleInfo"/>
+ <!--<menuitem action="HighScores"/>-->
+ <separator/>
+ <menuitem action="Print"/>
+ <menuitem action="PrintMany"/>
+ <separator/>
+ <menuitem action="Close"/>
+ <!--<menuitem action="Quit"/>-->
+ </menu>
+ <menu action="Edit">
+ <menuitem action="Undo"/>
+ <menuitem action="Redo"/>
+ <separator/>
+ <menuitem action="Clear"/>
+ <menuitem action="ClearNotes"/>
+ </menu>
+ <menu action="View">
+ <menuitem action="FullScreen"/>
+ <separator/>
+ <menuitem action="ToggleToolbar"/>
+ <menuitem action="ToggleHighlight"/>
+ </menu>
+ <menu action="Tools">
+ <menuitem action="ShowPossible"/>
+ <menuitem action="AutofillCurrentSquare"/>
+ <menuitem action="Autofill"/>
+ <separator/>
+ <menuitem action="AlwaysShowPossible"/>
+ <menuitem action="ShowImpossibleImplications"/>
+
+ <separator/>
+ <menuitem action="Generator"/>
+ <menuitem action="BackgroundGenerator"/>
+ <separator/>
+ <menuitem action="Tracker"/>
+ </menu>
+ <menu action="Help">
+ <menuitem action="ShowHelp"/>
+ <menuitem action="About"/>
+ </menu>
+ </menubar>
+ <toolbar name="Toolbar">
+ <!--<toolitem action="Quit"/>-->
+ <toolitem action="New"/>
+ <!--<toolitem action="Open"/>-->
+ <toolitem action="Print"/>
+ <!--<toolitem action="Save"/>-->
+ <!--<separator/>-->
+ <!--<toolitem action="Clear"/> -->
+ <!--<toolitem action="ClearNotes"/>-->
+ <separator/>
+ <toolitem action="Undo"/>
+ <toolitem action="Redo"/>
+ <separator/>
+ <toolitem action="ShowPossible"/>
+ <!--<toolitem action="AlwaysShowPossible"/>-->
+ <toolitem action="AutofillCurrentSquare"/>
+ <separator/>
+ <toolitem action="ToggleHighlight"/>
+ <!--<toolitem action="AlwaysShowPossible"/>-->
+
+ <toolitem action="Tracker"/>
+ </toolbar>
+ </ui>'''
+
+ initial_prefs = {'group_size':9,
+ 'font_zoom':0,
+ 'zoom_on_resize':1,
+ 'always_show_hints':0,
+ 'player':os.environ.get('USERNAME',''),
+ 'difficulty':0.0,
+ 'minimum_number_of_new_puzzles':MIN_NEW_PUZZLES,
+ 'highlight':0,
+ 'bg_black':1,
+ 'bg_custom_color':'',
+ 'show_tracker':False,
+ 'width': 700,
+ 'height': 675,
+ 'auto_save_interval':60 # auto-save interval in seconds...
+ #'show_notes':0
+ }
+
+ @simple_debug
+ def __init__ (self):
+ gconf_wrapper.GConfWrapper.__init__(self,
+ gconf_wrapper.GConf('gnome-sudoku')
+ )
+ self.setup_gui()
+ self.timer = ActiveTimer(self.w)
+ self.won = False
+ # add the accelerator group to our toplevel window
+ self.worker_connections=[]
+ self.timer.start_timing()
+ # setup sudoku maker...
+ self.sudoku_maker = sudoku_maker.SudokuMaker()
+ self.sudoku_tracker = saver.SudokuTracker()
+ #if not self.sudoku_tracker.playing:
+ # self.main_actions.get_action('Open').set_sensitive(False)
+ #else:
+ #self.main_actions.get_action('Open').set_sensitive(True)
+ #if not self.sudoku_tracker.are_finished_games():
+ # self.main_actions.get_action('HighScores').set_sensitive(False)
+ # select an easy puzzle...
+ #puz,d=self.sudoku_maker.get_new_puzzle(self.gconf['difficulty'])
+ #print 'Default to ',puz
+ #self.gsd.change_grid(puz, 9)
+ # generate puzzles while our use is working...
+ self.show()
+ #print 'Select game!'
+ if self.select_game():
+ # If this return True, the user closed...
+ #print 'select game returned True - exit'
+ self.quit = True
+ else:
+ self.quit = False
+ # Generate puzzles in background...
+ if self.gconf['generate_puzzles_in_background']:
+ gobject.timeout_add(1000,lambda *args: self.start_worker_thread() and True)
+
+
+ @inactivate_new_game_etc
+ def select_game (self):
+ self.tb.hide()
+ choice = game_selector.NewOrSavedGameSelector().run_swallowed_dialog(self.swallower)
+ #print "The user's choice is ",choice
+ if not choice:
+ return True
+ #print 'choice is ',choice
+ if choice[0] == game_selector.NewOrSavedGameSelector.NEW_GAME:
+ self.gsd.change_grid(choice[1],9)
+ if choice[0] == game_selector.NewOrSavedGameSelector.SAVED_GAME:
+ saver.open_game(self,choice[1])
+ if self.gconf['show_toolbar']: self.tb.show()
+
+ def show (self):
+ self.gsd.show()
+ self.w.show()
+
+ def setup_gui (self):
+ gnome.program_init('gnome-sudoku',VERSION, properties={gnome.PARAM_APP_DATADIR:APP_DATA_DIR})
+ self.initialize_prefs()
+ self.setup_main_window()
+ self.gsd = gsudoku.SudokuGameDisplay()
+ self.gsd.connect('puzzle-finished',self.you_win_callback)
+ self.setup_color()
+ self.setup_actions()
+ self.setup_undo()
+ self.setup_autosave()
+ self.w.add_accel_group(self.uimanager.get_accel_group())
+ self.setup_main_boxes()
+ self.setup_tracker_interface()
+ self.setup_toggles()
+
+ def setup_main_window (self):
+ gtk.window_set_default_icon_name('gnome-sudoku')
+ self.w = gtk.Window()
+ self.w.set_default_size(self.gconf['width'], self.gconf['height'])
+ self.w.set_title(APPNAME_SHORT)
+ self.w.connect('configure-event',self.resize_cb)
+ self.w.connect('delete-event',self.quit_cb)
+ self.uimanager = gtk.UIManager()
+
+ def setup_actions (self):
+ self.main_actions = gtk.ActionGroup('MainActions')
+ self.main_actions.add_actions([
+ ('Game',None,_('_Game')),
+ ('New',gtk.STOCK_NEW,None,
+ '<Control>n',_('New game'),self.new_cb),
+ ('Print',gtk.STOCK_PRINT,None,
+ None,_('Print current game'),self.print_game),
+ ('PrintMany',gtk.STOCK_PRINT,_('Print _Multiple Sudokus'),
+ None,_('Print more than one sudoku at a time.'),self.print_multiple_games),
+ #('Quit',gtk.STOCK_QUIT,None,'<Control>q',
+ # 'Quit Sudoku game',self.quit_cb),
+ ('Close',gtk.STOCK_CLOSE,None,'<Control>w',
+ _('Close Sudoku'),self.quit_cb),
+ #('Save',gtk.STOCK_SAVE,_('_Save'),
+ # '<Control>s','Save game to play later.',
+ # self.save_game_cb),
+ #('ByHand',gtk.STOCK_EDIT,_('_Enter custom game'),
+ # None,_('Enter new puzzle by hand (use this to copy a puzzle from another source).'),
+ # self.enter_game_by_hand),
+ #('Open',gtk.STOCK_OPEN,_('Open game'),
+ # '<Control>o',_('Open a saved game from file.'),
+ # self.open_game),
+ ('Tools',None,_('_Tools')),
+ ('View',None,_('_View')),
+ ('ShowPossible',gtk.STOCK_DIALOG_INFO,_('_Hint'),
+ '<Control>h',
+ _('Show which numbers could go in the current square.'),
+ self.show_hint_cb),
+ ('AutofillCurrentSquare',gtk.STOCK_APPLY,_('_Fill'),'<Control>f',
+ _('Automatically fill in the current square if possible.'),
+ self.auto_fill_current_square_cb),
+ ('Autofill',gtk.STOCK_REFRESH,_('Fill _all squares'),'<Control>a',
+ _('Automatically fill in all squares for which there is only one valid value.'),
+ self.auto_fill_cb),
+ ('FullScreen',STOCK_FULLSCREEN,None,
+ 'F11',None,self.full_screen_cb),
+ ('PuzzleInfo',gtk.STOCK_ABOUT,_('Puzzle _Statistics'),
+ None,_('Show statistics about current puzzle'),
+ self.show_info_cb),
+ ('Help',None,_('_Help'),
+ None,None,None),
+ ('About',gtk.STOCK_ABOUT,None,
+ None,None,self.show_about),
+ ('ShowHelp',gtk.STOCK_HELP, _('_Contents'),
+ 'F1',None,self.show_help),
+ #('HighScores',None,_('High _Scores'),
+ # None,_('Show high scores or replay old games.'),
+ # self.show_high_scores_cb),
+ ])
+ self.main_actions.add_toggle_actions([
+ ('AlwaysShowPossible',
+ None,
+ _('_Always show hint'),
+ None,
+ _('Always show possible numbers in a square'),
+ self.auto_hint_cb),
+ ('ShowImpossibleImplications',
+ None,
+ _('Warn about _unfillable squares'),
+ None,
+ _('Warn about squares made unfillable by a move'),
+ self.impossible_implication_cb),
+ ('Tracker','tracks',_('_Track additions'),
+ '<Control>T',
+ _('Mark new additions in a separate color so you can keep track of them.'),
+ self.tracker_toggle_cb,False),
+ ('ToggleToolbar',None,_('Show _Toolbar'),None,None,self.toggle_toolbar_cb,True),
+ ('ToggleHighlight',gtk.STOCK_SELECT_COLOR,_('_Highlighter'),
+ None,_('Highlight the current row, column and box'),self.toggle_highlight_cb,False),
+ ('BackgroundGenerator',None,_('Generate new puzzles _while you play'),
+ None,
+ _('Generate new puzzles in the background while you play. This will automatically pause when the game goes into the background.'),
+ self.toggle_generator_cb, True),
+ ])
+
+ self.edit_actions = gtk.ActionGroup('EditActions')
+ self.edit_actions.add_actions(
+ [('Edit',None,_('_Edit')),
+ ('Undo',gtk.STOCK_UNDO,_('_Undo'),'<Control>z',_('Undo last action')),
+ ('Redo',gtk.STOCK_REDO,_('_Redo'),'<Shift><Control>z',_('Redo last action')),
+ ('Clear',gtk.STOCK_CLEAR,_('_Clear'),'<Control>b',_("Clear entries you've filled in"),self.clear_cb),
+ ('ClearNotes',None,_('Clear _Notes'),None,_("Clear notes and hints"),self.clear_notes_cb),
+ # Trackers...
+ ('Tracker%s',None,_('No Tracker'),'<Control>0',None,lambda *args: self.set_tracker(-1)),
+ ('Generator',None,_('_Generate new puzzles'),None,_('Generate new puzzles.'),
+ self.generate_puzzle_gui,),
+ ])
+ self.edit_actions.add_actions(
+ [('Tracker%s'%n,None,'Tracker _%s'%n,'<Control>%s'%n,None,lambda *args: self.set_tracker(n-1)) for
+ n in range(1,9)])
+ self.uimanager.insert_action_group(self.main_actions,0)
+ self.uimanager.insert_action_group(self.edit_actions,0)
+ self.uimanager.add_ui_from_string(self.ui)
+
+ def setup_undo (self):
+ self.cleared = [] # used for Undo memory
+ self.cleared_notes = [] # used for Undo memory
+ # Set up our UNDO stuff
+ undo_widg = self.edit_actions.get_action('Undo')
+ redo_widg = self.edit_actions.get_action('Redo')
+ self.history = Undo.UndoHistoryList(undo_widg,redo_widg)
+ for e in self.gsd.__entries__.values():
+ Undo.UndoableGenericWidget(e,self.history,
+ set_method='set_value_from_undo',
+ pre_change_signal='value-about-to-change'
+ )
+ Undo.UndoableGenericWidget(e,self.history,
+ set_method='set_notes',
+ get_method='get_note_text',
+ signal='notes-changed',
+ pre_change_signal='value-about-to-change',
+ )
+
+ def setup_color (self):
+ # setup background colors
+ if self.gconf['bg_custom_color']:
+ bgcol = self.gconf['bg_custom_color']
+ elif self.gconf['bg_black']:
+ bgcol = 'black'
+ else:
+ bgcol = None
+ if bgcol: self.gsd.set_bg_color(bgcol)
+
+ def setup_autosave (self):
+ gobject.timeout_add(1000*(self.gconf['auto_save_interval'] or 60), # in seconds...
+ self.autosave)
+
+ def setup_main_boxes (self):
+ self.vb = gtk.VBox()
+ # Add menu bar and toolbar...
+ mb = self.uimanager.get_widget('/MenuBar'); mb.show()
+ self.vb.pack_start(mb,fill=False,expand=False)
+ self.tb = self.uimanager.get_widget('/Toolbar')
+ self.vb.pack_start(self.tb,fill=False,expand=False)
+ self.main_area = gtk.HBox()
+ self.swallower = SwappableArea(self.main_area)
+ self.swallower.show()
+ self.vb.pack_start(self.swallower,True,padding=12)
+ self.main_area.pack_start(self.gsd,padding=6)
+ self.main_actions.set_visible(True)
+ self.game_box = gtk.VBox()
+ self.main_area.show()
+ self.vb.show()
+ self.game_box.show()
+ self.main_area.pack_start(self.game_box,False,padding=12)
+ self.statusbar = gtk.Statusbar(); self.statusbar.show()
+ gobject.timeout_add(500,self.update_statusbar_cb)
+ self.vb.pack_end(self.statusbar,fill=False,expand=False)
+ self.w.add(self.vb)
+
+ def setup_by_hand_area (self):
+ # Set up area for by-hand editing...
+ self.by_hand_label = gtk.Label()
+ self.by_hand_label.set_alignment(0,0)
+ self.by_hand_label.set_markup('<i>%s</i>'%_('Entering custom grid...'))
+ self.game_box.pack_start(self.by_hand_label,False,)#padding=12)
+ self.by_hand_buttonbox = gtk.HButtonBox()
+ self.by_hand_buttonbox.set_spacing(12)
+ self.by_hand_save_button = gtk.Button(_('_Play game'))
+ self.by_hand_save_button.connect('clicked',self.save_handmade_grid)
+ self.by_hand_cancel_button = gtk.Button(stock=gtk.STOCK_CANCEL)
+ self.by_hand_cancel_button.connect('clicked',self.cancel_handmade_grid)
+ self.by_hand_buttonbox.add(self.by_hand_cancel_button)
+ self.by_hand_buttonbox.add(self.by_hand_save_button)
+ self.game_box.pack_start(self.by_hand_buttonbox,False,padding=18)
+ self.by_hand_widgets = [self.by_hand_label,self.by_hand_buttonbox]
+
+ def setup_toggles (self):
+ # sync up toggles with gconf values...
+ map(lambda tpl: self.gconf_wrap_toggle(*tpl),
+ [('always_show_hints',
+ self.main_actions.get_action('AlwaysShowPossible')),
+ ('show_impossible_implications',
+ self.main_actions.get_action('ShowImpossibleImplications')),
+ ('generate_puzzles_in_background',
+ self.main_actions.get_action('BackgroundGenerator')),
+ ('show_toolbar',
+ self.main_actions.get_action('ToggleToolbar')),
+ ('highlight',
+ self.main_actions.get_action('ToggleHighlight')),
+ ('show_tracker',
+ self.main_actions.get_action('Tracker')),
+ ])
+
+ @simple_debug
+ def start_worker_thread (self, *args):
+ n_new_puzzles = self.sudoku_maker.n_puzzles(new=True)
+ if n_new_puzzles < self.gconf['minimum_number_of_new_puzzles']:
+ #print 'Generate puzzles'
+ self.worker = threading.Thread(target=lambda *args: self.sudoku_maker.work(limit=5))
+ self.worker_connections = [
+ self.timer.connect('timing-started',self.sudoku_maker.resume),
+ self.timer.connect('timing-stopped',self.sudoku_maker.pause)
+ ]
+ self.worker.start()
+ #else:
+ # print "Don't generate...",'We already have ',n_new_puzzles,'!'
+
+ @simple_debug
+ def stop_worker_thread (self, *args):
+ if hasattr(self,'worker'):
+ self.sudoku_maker.stop()
+ for c in self.worker_connections:
+ self.timer.disconnect(c)
+
+ @simple_debug
+ def you_win_callback (self,grid):
+ self.won = True
+ # increase difficulty for next time.
+ self.gconf['difficulty']=self.gconf['difficulty']+0.1
+ self.timer.finish_timing()
+ self.sudoku_tracker.finish_game(self)
+ sublabel = _("You completed the puzzle in %(totalTime)s (%(activeTime)s active)")%{'totalTime': self.timer.total_time_string(),
+ 'activeTime': self.timer.active_time_string()
+ }
+ sublabel += "\n"
+ sublabel += ngettext("You got %(n)s hint","You got %(n)s hints",self.gsd.hints)%{'n':self.gsd.hints}
+ sublabel += "\n"
+ if self.gsd.impossible_hints:
+ sublabel += ngettext("You had %(n)s impossibility pointed out.",
+ "You had %(n)s impossibilities pointed out.",
+ self.gsd.impossible_hints)%{'n':self.gsd.impossible_hints}
+ sublabel += "\n"
+ if self.gsd.auto_fills:
+ sublabel += ngettext("You used the auto-fill %(n)s time",
+ "You used the auto-fill %(n)s times",
+ self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
+ dialog_extras.show_message(_("You win!"),label=_("You win!"),
+ #icon=os.path.join(IMAGE_DIR,'winner2.png'),
+ sublabel=sublabel
+ )
+ # High scores is complicated and kind of unnecessary for now...
+ # If we reimplement, we should use the proper gnome-games system.
+ #hs = game_selector.HighScores(self.sudoku_tracker)
+ #hs.highlight_newest=True
+ #hs.run_swallowed_dialog(self.swallower)
+ #print 'Run high scores dialog!'
+ #hs.run_dialog()
+ #self.main_actions.get_action('HighScores').set_sensitive(True)
+ #self.gsd.blank_grid()
+ self.new_cb()
+
+ @simple_debug
+ def initialize_prefs (self):
+ for k,v in self.initial_prefs.items():
+ try:
+ self.gconf[k]
+ except:
+ self.gconf[k]=v
+ self.player = self.gconf['player']
+
+ @simple_debug
+ @inactivate_new_game_etc
+ def new_cb (self,*args):
+ if (self.gsd.grid and self.gsd.grid.is_changed() and not self.won):
+ try:
+ if dialog_extras.getBoolean(
+ label=_("Save this game before starting new one?"),
+ custom_yes=_("_Save game for later"),
+ custom_no=_("_Abandon game"),
+ ):
+ self.save_game()
+ else:
+ self.sudoku_tracker.abandon_game(self)
+ except dialog_extras.UserCancelledError:
+ # User cancelled new game
+ return
+ self.do_stop()
+ self.select_game()
+
+
+ @simple_debug
+ def stop_game (self):
+ if (self.gsd.grid
+ and self.gsd.grid.is_changed()
+ and (not self.won)):
+ try:
+ if dialog_extras.getBoolean(label=_("Save game before closing?")):
+ self.save_game(self)
+ except dialog_extras.UserCancelledError:
+ return
+ self.do_stop()
+
+ def do_stop (self):
+ self.gsd.grid = None
+ self.tracker_ui.reset()
+ self.timer.reset_timer()
+ self.timer.start_timing()
+ self.history.clear()
+ self.won = False
+
+ @simple_debug
+ def resize_cb (self, widget, event):
+ self.gconf['width'] = event.width
+ self.gconf['height'] = event.height
+
+ @simple_debug
+ def quit_cb (self, *args):
+ # Offer a save...
+ #try:
+ if (self.gsd.grid
+ and self.gsd.grid.is_changed()
+ and (not self.won)):
+ #and dialog_extras.getBoolean(label=_("Save game before closing?"))):
+ self.save_game(self) # autosave...
+ #except dialog_extras.UserCancelledError:
+ # return
+ if gtk.main_level() > 1:
+ # If we are in an embedded mainloop, that means that one
+ # of our "swallowed" dialogs is active, in which case we
+ # have to quit that mainloop before we can quit
+ # properly.
+ if self.swallower.running:
+ d = self.swallower.running
+ d.response(gtk.RESPONSE_DELETE_EVENT)
+ gtk.main_quit() # Quit the embedded mainloop
+ gobject.idle_add(self.quit_cb,100) # Call ourselves again
+ # to quit the main
+ # mainloop
+ return
+ #buttons = d.action_area.get_children()
+ #for b in buttons:
+ # if d.get_response_for_widget(b) in [gtk.RESPONSE_CLOSE,gtk.RESPONSE_CANCEL]:
+ # print 'clicking button',b
+ # b.emit('clicked')
+ # while gtk.events_pending():
+ # print 'Take care of iters...'
+ # gtk.main_iteration()
+ # break
+ self.w.hide()
+ # make sure we really go away before doing our saving --
+ # otherwise we appear sluggish.
+ while gtk.events_pending():
+ gtk.main_iteration()
+ if self.won:
+ self.gconf['current_game']=''
+ if not self.won:
+ if not self.gsd.grid:
+ self.gconf['current_game']=''
+ self.stop_worker_thread()
+ # allow KeyboardInterrupts, which calls quit_cb outside the main loop
+ try:
+ gtk.main_quit()
+ except RuntimeError, e:
+ pass
+
+ @simple_debug
+ @inactivate_new_game_etc
+ def enter_game_by_hand (self, *args):
+ self.stop_game()
+ self.gsd.change_grid(sudoku.InteractiveSudoku(),9)
+ for w in self.by_hand_widgets: w.show_all()
+
+ @simple_debug
+ def save_handmade_grid (self, *args):
+ for w in self.by_hand_widgets: w.hide()
+ # this should make our active grid into our virgin grid
+ self.won = False
+ self.gsd.change_grid(self.gsd.grid,9)
+ self.sudoku_maker.names[self.gsd.grid.to_string()]=self.sudoku_maker.get_puzzle_name('Custom Puzzle')
+ self.history.clear()
+
+ @simple_debug
+ def cancel_handmade_grid (self, *args):
+ for w in self.by_hand_widgets: w.hide()
+
+ #def save_game_cb (self, *args):
+ # try:
+ # self.save_game(*args)
+ # except dialog_extras.UserCancelledError:
+ # pass
+
+ @simple_debug
+ def save_game (self, *args):
+ self.sudoku_tracker.save_game(self)
+
+ @simple_debug
+ def zoom_in_cb (self,*args):
+ self.gh.change_font_size(multiplier=1.1)
+ self.zoom = self.zoom * 1.1
+
+ @simple_debug
+ def zoom_out_cb (self,*args):
+ self.gh.change_font_size(multiplier=0.9)
+ self.zoom = self.zoom * 0.9
+
+ def full_screen_cb (self, *args):
+ if not hasattr(self,'is_fullscreen'): self.is_fullscreen = False
+ if self.is_fullscreen:
+ self.w.unfullscreen()
+ self.is_fullscreen = False
+ else:
+ self.w.fullscreen()
+ self.is_fullscreen = True
+
+ @simple_debug
+ def clear_cb (self,*args):
+ clearer=Undo.UndoableObject(
+ lambda *args: self.cleared.append(self.gsd.reset_grid()), #action
+ lambda *args: [self.gsd.add_value_to_ui(*entry) for entry in self.cleared.pop()], #inverse
+ self.history #history
+ )
+ clearer.perform()
+
+ def clear_notes_cb (self, *args):
+ clearer = Undo.UndoableObject(
+ lambda *args: self.cleared_notes.append(self.gsd.clear_notes()), #action
+ # clear_notes returns a list of tuples indicating the cleared notes...
+ # (x,y,(top,bottom)) -- this is what we need for undoing
+ lambda *args: [self.gsd.__entries__[t[0],t[1]].set_notes(t[2]) for t in self.cleared_notes.pop()], #inverse
+ self.history
+ )
+ clearer.perform()
+
+ @simple_debug
+ def show_hint_cb (self, *args):
+ self.gsd.show_hint()
+
+ @simple_debug
+ def auto_hint_cb (self, action):
+ if action.get_active():
+ self.gsd.always_show_hints = True
+ self.gsd.update_all_hints()
+ else:
+ self.gsd.always_show_hints = False
+ self.gsd.clear_hints()
+
+ @simple_debug
+ def impossible_implication_cb (self, action):
+ if action.get_active():
+ self.gsd.show_impossible_implications = True
+ else:
+ self.gsd.show_impossible_implications = False
+
+ @simple_debug
+ def auto_fill_cb (self, *args):
+ if not hasattr(self,'autofilled'): self.autofilled=[]
+ if not hasattr(self,'autofiller'):
+ self.autofiller = Undo.UndoableObject(
+ lambda *args: self.autofilled.append(self.gsd.auto_fill()),
+ lambda *args: [self.gsd.remove(entry[0],entry[1],do_removal=True) for entry in self.autofilled.pop()],
+ self.history
+ )
+ self.autofiller.perform()
+
+ @simple_debug
+ def auto_fill_current_square_cb (self, *args):
+ self.gsd.auto_fill_current_entry()
+
+ @simple_debug
+ def setup_tracker_interface (self):
+ self.trackers = {}
+ self.tracker_ui = TrackerBox(self)
+ self.tracker_ui.show_all()
+ self.tracker_ui.hide()
+ self.game_box.add(self.tracker_ui)
+
+ @simple_debug
+ def set_tracker (self, n):
+ if self.gsd.trackers.has_key(n):
+ self.tracker_ui.select_tracker(n)
+ e = self.gsd.get_focused_entry()
+ if e:
+ if n==-1:
+ for tid in self.gsd.trackers_for_point(e.x,e.y):
+ self.gsd.remove_tracker(e.x,e.y,tid)
+ else:
+ self.gsd.add_tracker(e.x,e.y,n)
+ else:
+ print 'No tracker ',n,'yet'
+
+ @simple_debug
+ def tracker_toggle_cb (self, widg):
+ if widg.get_active():
+ #if len(self.tracker_ui.tracker_model)<=1:
+ # self.tracker_ui.add_tracker()
+ self.tracker_ui.show_all()
+ else:
+ self.tracker_ui.hide()
+
+ @simple_debug
+ def toggle_toolbar_cb (self, widg):
+ if widg.get_active(): self.tb.show()
+ else: self.tb.hide()
+
+ def update_statusbar_cb (self, *args):
+ if not self.gsd.grid: return True
+ puzz = self.gsd.grid.virgin.to_string()
+ if (not hasattr(self,'current_puzzle_string') or
+ self.current_puzzle_string != puzz):
+ self.current_puzzle_diff = self.sudoku_maker.get_difficulty(puzz)
+ tot_string = _("Playing %(difficulty)s puzzle.")%{'difficulty':self.current_puzzle_diff.value_string()}
+ tot_string += " " + "(%1.2f)"%self.current_puzzle_diff.value
+ #if self.timer.tot_time or self.timer.tot_time_complete:
+ # time_string = _("%s (%s active)")%(
+ # self.timer.total_time_string(),
+ # self.timer.active_time_string()
+ # )
+ # if not self.timer.__timing__:
+ # time_string += " %s"%_('paused')
+ # tot_string += " - " + time_string
+ #if self.gsd.hints and not self.gconf['always_show_hints']:
+ # tot_string += " - " +ngettext("%(n)s hint","%(n)s hints",
+ # self.gsd.hints)%{'n':self.gsd.hints}
+ #if self.gsd.auto_fills:
+ # tot_string += " " +ngettext("%(n)s auto-fill","%(n)s auto-fills",
+ # self.gsd.auto_fills)%{'n':self.gsd.auto_fills}
+ if not hasattr(self,'sbid'):
+ self.sbid = self.statusbar.get_context_id('game_info')
+ self.statusbar.pop(self.sbid)
+ self.statusbar.push(self.sbid,
+ tot_string)
+ return True
+
+ def toggle_highlight_cb (self, widg):
+ if widg.get_active():
+ self.gsd.toggle_highlight(True)
+ else:
+ self.gsd.toggle_highlight(False)
+
+ @simple_debug
+ def show_info_cb (self, *args):
+ if not self.gsd.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()
+ diff = self.sudoku_maker.get_difficulty(puzzle)
+ information = _("Calculated difficulty: ")
+ information += diff.value_string()
+ information += " (%1.2f)"%diff.value
+ information += "\n"
+ information += _("Number of moves instantly fillable by elimination: ")
+ information += str(int(diff.instant_elimination_fillable))
+ information += "\n"
+ information += _("Number of moves instantly fillable by filling: ")
+ information += str(int(diff.instant_fill_fillable))
+ information += "\n"
+ information += _("Amount of trial-and-error required to solve: ")
+ information += str(len(diff.guesses))
+ dialog_extras.show_message(parent=self.w,
+ title=_("Puzzle Statistics"),
+ label=_("Puzzle Statistics"),
+ sublabel=information)
+
+ @simple_debug
+ def toggle_generator_cb (self, toggle):
+ if toggle.get_active():
+ self.start_worker_thread()
+ else:
+ self.stop_worker_thread()
+
+ @simple_debug
+ def show_high_scores_cb (self, *args):
+ hs=game_selector.HighScores(self.sudoku_tracker)
+ replay_game = hs.run_dialog()
+ if replay_game:
+ self.stop_game()
+ self.gsd.change_grid(replay_game,9)
+
+ @simple_debug
+ 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:
+ self.sudoku_tracker.save_game(self)
+
+ def offer_to_load_autosaved_file (self):
+ pass
+
+ @simple_debug
+ def load_autosave (self, filename):
+ saver.unpickle_game(self,filename)
+ self.history.clear()
+
+ #def toggle_autosave ():
+
+ @simple_debug
+ def show_about (self, *args):
+ about = gtk.AboutDialog()
+ about.set_name(APPNAME)
+ about.set_version(VERSION)
+ about.set_copyright(COPYRIGHT)
+ about.set_license(LICENSE[0] + '\n\n' + LICENSE[1] + '\n\n' +LICENSE[2])
+ about.set_wrap_license(True)
+ about.set_comments(DESCRIPTION)
+ about.set_authors(AUTHORS)
+ about.set_website(WEBSITE)
+ about.set_website_label(WEBSITE_LABEL)
+ about.set_logo_icon_name("gnome-sudoku")
+ about.set_translator_credits(_("translator-credits"))
+ about.connect("response", lambda d, r: d.destroy())
+ about.show()
+
+ @simple_debug
+ def show_help (self, *args):
+ #dialog_extras.show_faq(faq_file=os.path.join(BASE_DIR,_('FAQ')))
+ try:
+ gnome.help_display('gnome-sudoku')
+ except gobject.GError, e:
+ # FIXME: This should create a pop-up dialog
+ print _('Unable to display help: %s') % str(e)
+
+ @simple_debug
+ def print_game (self, *args):
+ printing.print_sudokus([self.gsd])
+
+ @simple_debug
+ def print_multiple_games (self, *args):
+ gp=game_selector.GamePrinter(self.sudoku_tracker, self.gconf)
+ gp.run_dialog()
+
+ @simple_debug
+ def generate_puzzle_gui (self, *args):
+ sudoku_generator_gui.GameGenerator(self,self.gconf)
+
+class TrackerBox (gtk.VBox):
+
+ @simple_debug
+ def __init__ (self, main_ui):
+
+ gtk.VBox.__init__(self)
+ self.glade = gtk.glade.XML(os.path.join(GLADE_DIR,'tracker.glade'))
+ self.main_ui = main_ui
+ self.vb = self.glade.get_widget('vbox1')
+ self.vb.unparent()
+ self.pack_start(self.vb,expand=True,fill=True)
+ self.setup_actions()
+ self.setup_tree()
+ self.show_all()
+
+ @simple_debug
+ def reset (self):
+
+ for tree in self.tracker_model:
+ if tree[0]>-1:
+ self.tracker_model.remove(tree.iter)
+
+ @simple_debug
+ def setup_tree (self):
+ self.tracker_tree = self.glade.get_widget('treeview1')
+ self.tracker_model = gtk.ListStore(int,gtk.gdk.Pixbuf,str)
+ self.tracker_tree.set_model(self.tracker_model)
+ col1 = gtk.TreeViewColumn("",gtk.CellRendererPixbuf(),pixbuf=1)
+ col2 = gtk.TreeViewColumn("",gtk.CellRendererText(),text=2)
+ self.tracker_tree.append_column(col2)
+ self.tracker_tree.append_column(col1)
+ # Our initial row...
+ self.tracker_model.append([-1,None,_('No Tracker')])
+ self.tracker_tree.get_selection().connect('changed',self.selection_changed_cb)
+
+ @simple_debug
+ def setup_actions (self):
+ self.tracker_actions = gtk.ActionGroup('tracker_actions')
+ self.tracker_actions.add_actions(
+ [('Clear',
+ gtk.STOCK_CLEAR,
+ _('_Clear Tracker'),
+ None,_('Clear all moves tracked by selected tracker.'),
+ self.clear_cb
+ ),
+ ('Keep',None,
+ _('_Clear Others'),
+ None,
+ _('Clear all moves not tracked by selected tracker.'),
+ self.keep_cb),
+ ]
+ )
+ for action,widget_name in [('Clear','ClearTrackerButton'),
+ ('Keep','KeepTrackerButton'),
+ ]:
+ a=self.tracker_actions.get_action(action)
+ a.connect_proxy(self.glade.get_widget(widget_name))
+ self.glade.get_widget('AddTrackerButton').connect('clicked',
+ self.add_tracker)
+ # Default to insensitive (they only become sensitive once a tracker is added)
+ self.tracker_actions.set_sensitive(False)
+
+ @simple_debug
+ def add_tracker (self,*args):
+ #print 'Adding tracker!'
+ tracker_id = self.main_ui.gsd.create_tracker()
+ #print 'tracker_id = ',tracker_id
+ pb=image_extras.pixbuf_transform_color(
+ STOCK_PIXBUFS['tracks'],
+ (0,0,0),#white
+ self.main_ui.gsd.get_tracker_color(tracker_id),
+ )
+ # select our new tracker
+ self.tracker_tree.get_selection().select_iter(
+ self.tracker_model.append([tracker_id,
+ pb,
+ _("Tracker %s")%(tracker_id+1)]
+ )
+ )
+
+ @simple_debug
+ def select_tracker (self, tracker_id):
+ for row in self.tracker_model:
+ if row[0]==tracker_id:
+ self.tracker_tree.get_selection().select_iter(row.iter)
+
+ @simple_debug
+ def selection_changed_cb (self, selection):
+ mod,itr = selection.get_selected()
+ if itr: selected_tracker_id = mod.get_value(itr,0)
+ else: selected_tracker_id=-1
+ # This should be cheap since we don't expect many trackers...
+ # We cycle through each row and toggle it off if it's not
+ # selected; on if it is selected
+ for row in self.tracker_model:
+ tid = row[0]
+ if tid != -1: # -1 == no tracker
+ self.main_ui.gsd.toggle_tracker(tid,tid==selected_tracker_id)
+ self.tracker_actions.set_sensitive(selected_tracker_id != -1)
+
+ @simple_debug
+ def clear_cb (self, action):
+ mod,itr=self.tracker_tree.get_selection().get_selected()
+ # This should only be called if there is an itr, but we'll
+ # double-check just in case.
+ if itr:
+ selected_tracker_id=mod.get_value(itr,0)
+ self.tracker_delete_tracks(selected_tracker_id)
+
+ @simple_debug
+ def keep_cb (self, action):
+ mod,itr=self.tracker_tree.get_selection().get_selected()
+ selected_tracker_id=mod.get_value(itr,0)
+ self.tracker_keep_tracks(selected_tracker_id)
+
+ @simple_debug
+ def tracker_delete_tracks (self, tracker_id):
+ clearer=Undo.UndoableObject(
+ lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_by_tracker(tracker_id)),
+ lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
+ self.main_ui.history)
+ clearer.perform()
+
+ @simple_debug
+ def tracker_keep_tracks (self, tracker_id):
+ clearer=Undo.UndoableObject(
+ lambda *args: self.main_ui.cleared.append(self.main_ui.gsd.delete_except_for_tracker(tracker_id)),
+ lambda *args: [self.main_ui.gsd.add_value_to_ui(*entry) for entry in self.main_ui.cleared.pop()],
+ self.main_ui.history)
+ clearer.perform()
+
+
+def start_game ():
+ if options.debug: print 'Starting GNOME Sudoku in debug mode'
+
+ ## You must call g_thread_init() before executing any other GLib
+ ## functions in a threaded GLib program.
+ gobject.threads_init()
+
+ if options.profile:
+ options.profile = False
+ profile_me()
+ return
+
+ u = UI()
+ if not u.quit:
+ try:
+ gtk.main()
+ except KeyboardInterrupt:
+ # properly quit on a keyboard interrupt...
+ u.quit_cb()
+
+def profile_me ():
+ print 'Profiling GNOME Sudoku'
+ import tempfile,os.path
+ import hotshot, hotshot.stats
+ pname = os.path.join(tempfile.gettempdir(),'GNOME_SUDOKU_HOTSHOT_PROFILE')
+ prof = hotshot.Profile(pname)
+ prof.runcall(start_game)
+ stats = hotshot.stats.load(pname)
+ stats.strip_dirs()
+ stats.sort_stats('time','calls').print_stats()
+
+
+
+
+if __name__ == '__main__':
+ import defaults
+ defaults.DATA_DIR == '/tmp/'; DATA_DIR=='/tmp/'
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]