Viewing file: main.py (34.4 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- coding: utf-8 -*- 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 os.path import threading
import gobject import gtk from gettext import gettext as _ from gettext import ngettext
import dialog_swallower import game_selector import gsudoku import printing import saver import sudoku_generator_gui import sudoku_maker import timer from defaults import (APPNAME, APPNAME_SHORT, AUTHORS, COPYRIGHT, DESCRIPTION, DOMAIN, IMAGE_DIR, LICENSE, MIN_NEW_PUZZLES, UI_DIR, VERSION, WEBSITE, WEBSITE_LABEL) from gtk_goodies import gconf_wrapper, Undo, dialog_extras from simple_debug import simple_debug, options
ICON_FACTORY = gtk.IconFactory() STOCK_PIXBUFS = {} for filename, stock_id in [('footprints.png', 'tracks'), ]: try: pb = gtk.gdk.pixbuf_new_from_file(os.path.join(IMAGE_DIR, filename)) except gobject.GError, e: print 'Failed to load pixbuf: %s' % e continue 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, ""), ])
def inactivate_new_game_etc (fun): def inactivate_new_game_etc_ (ui, *args, **kwargs): paths = [ '/MenuBar/Game/New', '/MenuBar/Game/Print', # undo/redo is handled elsewhere as it can't simply be turned on/off. '/MenuBar/Edit/Clear', '/MenuBar/Edit/ClearNotes', '/MenuBar/View/ToggleToolbar', '/MenuBar/Tools/ShowPossible', '/MenuBar/Tools/AutofillCurrentSquare', '/MenuBar/Tools/Autofill', '/MenuBar/Tools/AlwaysShowPossible', '/MenuBar/Tools/ShowImpossibleImplications', '/MenuBar/Tools/Tracker', '/MenuBar/Game/PuzzleInfo', ] 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 inactivate_new_game_etc_
class UI (gconf_wrapper.GConfWrapper): ui = '''<ui> <menubar name="MenuBar"> <menu name="Game" action="Game"> <menuitem action="New"/> <separator/> <menuitem action="PuzzleInfo"/> <separator/> <menuitem action="Print"/> <menuitem action="PrintMany"/> <separator/> <menuitem action="Close"/> </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="New"/> <toolitem action="Print"/> <separator/> <toolitem action="Undo"/> <toolitem action="Redo"/> <separator/> <toolitem action="ShowPossible"/> <toolitem action="AutofillCurrentSquare"/> <separator/> <toolitem action="ToggleHighlight"/> <toolitem action="Tracker"/> </toolbar> </ui>'''
initial_prefs = {'group_size':9, 'always_show_hints':False, 'difficulty':0.0, 'minimum_number_of_new_puzzles':MIN_NEW_PUZZLES, 'highlight':False, 'bg_color':'black', 'show_tracker':False, 'width': 700, 'height': 675, 'auto_save_interval':60 # auto-save interval in seconds... }
@simple_debug def __init__ (self, run_selector = True): """run_selector means that we start our regular game.
For testing purposes, it will be convenient to hand a run_selector=False to this method to avoid running the dialog and allow a tester to set up a game programmatically. """ gconf_wrapper.GConfWrapper.__init__(self, gconf_wrapper.GConf('gnome-sudoku') ) self.setup_gui() self.timer = timer.ActiveTimer(self.w) self.won = False # add the accelerator group to our toplevel window self.worker_connections = [] self.is_fullscreen = False
# setup sudoku maker... self.sudoku_maker = sudoku_maker.SudokuMaker() self.sudoku_tracker = saver.SudokuTracker() # generate puzzles while our use is working... self.show() if run_selector: self.do_stop() if self.select_game(): # If this return True, the user closed... self.quit = True else: self.quit = False # Generate puzzles in background... if self.gconf['generate_puzzles_in_background']: gobject.timeout_add_seconds(1, lambda *args: self.start_worker_thread() and True)
@inactivate_new_game_etc def select_game (self): self.tb.hide() self.update_statusbar() choice = game_selector.NewOrSavedGameSelector().run_swallowed_dialog(self.swallower) if not choice: return True self.timer.start_timing() if choice[0] == game_selector.NewOrSavedGameSelector.NEW_GAME: self.gsd.change_grid(choice[1], 9) self.update_statusbar() if choice[0] == game_selector.NewOrSavedGameSelector.SAVED_GAME: saver.open_game(self, choice[1]) self.update_statusbar() if self.gconf['show_toolbar']: self.tb.show() if self.gconf['always_show_hints']: self.gsd.update_all_hints() if self.gconf['highlight']: self.gsd.toggle_highlight(True)
def show (self): self.gsd.show() self.w.show()
def setup_gui (self): 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), ('Close', gtk.STOCK_CLOSE, None, '<Control>w', _('Close Sudoku'), self.quit_cb), ('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', gtk.STOCK_FULLSCREEN, None, 'F11', None, self.full_screen_cb), ('Generator', None, _('_Generate new puzzles'), None, _('Generate new puzzles.'), self.generate_puzzle_gui, ), ('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), ]) 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'), self.stop_dancer), ('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), ]) 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 entry in self.gsd.__entries__.values(): Undo.UndoableGenericWidget(entry, self.history, set_method = 'set_value_from_undo', pre_change_signal = 'value-about-to-change' ) Undo.UndoableGenericWidget(entry, 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 bgcol = self.gconf['bg_color'] if bgcol != '': self.gsd.set_bg_color(bgcol)
def setup_autosave (self): gobject.timeout_add_seconds(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 = dialog_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() self.vb.pack_end(self.statusbar, fill = False, expand = False) self.w.add(self.vb)
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) try: if n_new_puzzles < self.gconf['minimum_number_of_new_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() except gconf_wrapper.GConfError: pass # assume we have enough new puzzles return True
@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)
def stop_dancer (self, *args): if hasattr(self, 'dancer'): self.dancer.stop_dancing() delattr(self, 'dancer')
@simple_debug def you_win_callback (self, grid): if hasattr(self, 'dancer'): return 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} import dancer self.dancer = dancer.GridDancer(self.gsd) self.dancer.start_dancing() dialog_extras.show_message(_("You win!"), label = _("You win!"), sublabel = sublabel )
@simple_debug def initialize_prefs (self): for k, v in self.initial_prefs.items(): try: self.gconf[k] except: self.gconf[k] = v
@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.stop_dancer() self.gsd.grid = None self.tracker_ui.reset() 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): self.w.hide() if (self.gsd.grid and self.gsd.grid.is_changed() and (not self.won)): self.save_game(self) 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 # make sure we really go away before doing our saving -- # otherwise we appear sluggish. while gtk.events_pending(): gtk.main_iteration() self.stop_worker_thread() # allow KeyboardInterrupts, which calls quit_cb outside the main loop try: gtk.main_quit() except RuntimeError: pass
@simple_debug def save_game (self, *args): self.sudoku_tracker.save_game(self)
def full_screen_cb (self, *args): 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( self.do_clear, #action self.undo_clear, #inverse self.history #history ) clearer.perform()
# add a check to stop the dancer if she is dancing def do_clear (self, *args): self.cleared.append(self.gsd.reset_grid()) self.stop_dancer()
# add a check for finish in the undo to clear def undo_clear (self, *args): for entry in self.cleared.pop(): self.gsd.add_value(*entry) if self.gsd.grid.check_for_completeness(): self.gsd.emit('puzzle-finished')
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( self.do_auto_fill, self.undo_auto_fill, self.history ) self.autofiller.perform()
def do_auto_fill (self, *args): self.autofilled.append(self.gsd.auto_fill()) if self.gconf['always_show_hints']: self.gsd.update_all_hints()
def undo_auto_fill (self, *args): for entry in self.autofilled.pop(): self.gsd.remove(entry[0], entry[1], do_removal = True) if self.gconf['always_show_hints']: self.gsd.update_all_hints()
@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 tracker_toggle_cb (self, widg): if widg.get_active(): 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 set_statusbar_value (self, status): if not hasattr(self, 'sbid'): self.sbid = self.statusbar.get_context_id('game_info') self.statusbar.pop(self.sbid) self.statusbar.push(self.sbid, status)
def update_statusbar (self, *args): if not self.gsd.grid: self.set_statusbar_value(" ") return True
puzzle = self.gsd.grid.virgin.to_string() puzzle_diff = self.sudoku_maker.get_difficulty(puzzle)
tot_string = _("Playing %(difficulty)s puzzle.") % {'difficulty':puzzle_diff.value_string()} tot_string += " " + "(%1.2f)" % puzzle_diff.value
self.set_statusbar_value(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 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) return True
@simple_debug def show_about (self, *args): about = gtk.AboutDialog() about.set_transient_for(self.w) 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): try: gtk.show_uri(self.w.get_screen(), "ghelp:gnome-sudoku", gtk.get_current_event_time()) except gobject.GError, error: # FIXME: This should create a pop-up dialog print _('Unable to display help: %s') % str(error)
@simple_debug def print_game (self, *args): printing.print_sudokus([self.gsd], self.w)
@simple_debug def print_multiple_games (self, *args): gp = printing.GamePrinter(self.sudoku_maker, 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.builder = gtk.Builder() self.builder.set_translation_domain(DOMAIN) self.builder.add_from_file(os.path.join(UI_DIR, 'tracker.ui')) self.main_ui = main_ui self.vb = self.builder.get_object('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.builder.get_object('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.builder.get_object(widget_name)) self.builder.get_object('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): tracker_id = self.main_ui.gsd.create_tracker() pixbuf = self.pixbuf_transform_color( STOCK_PIXBUFS['tracks'], 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, pixbuf, _("Tracker %s") % (tracker_id + 1)] ) )
@simple_debug def pixbuf_transform_color (self, pixbuf, color): """Return new pixbuf with color changed to color""" pixbuf_str = pixbuf.get_pixels() pixbuf_str_new = ""
for alpha in pixbuf_str[3::4]: pixbuf_str_new += chr(int(color[0]*255)) pixbuf_str_new += chr(int(color[1]*255)) pixbuf_str_new += chr(int(color[2]*255)) pixbuf_str_new += alpha
return gtk.gdk.pixbuf_new_from_data(pixbuf_str_new, gtk.gdk.COLORSPACE_RGB, True, 8, pixbuf.get_width(), pixbuf.get_height(), pixbuf.get_rowstride())
@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(*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(*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, 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()
|