def create_widgets(self):
     """Fills the frame with various buttons."""
     # Fill grid (dictionary) with cell buttons.
     grid = {}
     for i in range(BOARDSIZE):
         for j in range(BOARDSIZE):
             grid[(i, j)] = Cell(False)
             cell_btn = Button(self, text="", relief=GROOVE, width=2)
             cell_btn.bind("<Button-1>", self.click_cell_btn)
             cell_btn.grid(row=i, column=j)
             # Store buttons and their coordinates in a dictionary.
             self.cell_btns[i, j] = cell_btn
        
     # Initialize Board with dead cells.    
     self.board = Board(grid, BOARDSIZE)
     
     # Place error label.
     self.error_lbl = Label(self, text="", foreground="red")
     self.error_lbl.grid(columnspan=15, column=2, row=(BOARDSIZE + 1))
       
     # Place reset button.
     reset_btn = Button(self, text="Reset", command=self.click_reset_btn)
     reset_btn.grid(columnspan=2, pady=10, column=5, row=(BOARDSIZE + 2))
     
     # Place load file button.
     loadfile_btn = Button(self, text="Load File...", command=self.click_loadfile_btn)
     loadfile_btn.grid(columnspan=4, column=4, row=(BOARDSIZE + 3))
     
     # Place generation step button.
     step_btn = Button(self, text="Step for one Generation", command=self.click_step_btn)
     step_btn.grid(columnspan=6, column=10, row=(BOARDSIZE + 2))
     
     # Place save file button.
     savefile_btn = Button(self, text="Save to File...", command=self.click_savefile_btn)
     savefile_btn.grid(columnspan=4, column=10, row=(BOARDSIZE + 3))
     
     # Place number of generations label.
     num_of_gens_lbl = Label(self, text="# of Generations:")
     num_of_gens_lbl.grid(columnspan=4, column=4, row=(BOARDSIZE + 4))
             
     # Place number of generations entry box.
     self.counter = StringVar(value="<#>")
     self.num_of_gens_box = Entry(self, width=4, textvariable=self.counter)
     self.num_of_gens_box.grid(columnspan=4, column=7, row=(BOARDSIZE + 4)) 
     
     # Place go button for multiple generation advancement.
     go_btn = Button(self, text="Go!", command=self.click_go_btn)
     go_btn.grid(columnspan=2, pady=10, column=10, row=(BOARDSIZE + 4))
 def click_step_btn(self):
     """Registers step_btn clicks
     
     Checks whether each cell on the Board will live in the "next
     generation" based on Board.would_cell_live(). Creates new Board
     filled with next generation Cells and displays the new Board.
        
     """
     # Clear any previous error messages on action.
     self.clear_error_lbl()
     # Need to store future cell states in new dictionary so as not to base
     # would_cell_live on updated values during iterations.
     new_board = {}   
     for i in range(BOARDSIZE):
         for j in range(BOARDSIZE):
             if self.board.would_cell_live(i, j):
                 new_board[(i, j)] = Cell(True)
                 self.cell_btns[(i, j)].configure(text="X", background="medium sea green")
             else:
                 new_board[(i, j)] = Cell(False)
                 self.cell_btns[(i, j)].configure(text="", background="SystemButtonFace")
     # Construct new board reflecting new generation of cells.
     self.board = Board(new_board, BOARDSIZE) 
class GameFrame (Frame):
    """GUI representation of Game of Life."""
    def __init__(self, parent):
        """Initialize frame."""
        # Initialize parent Frame.
        Frame.__init__(self, parent)
        self.cell_btns = {}
        self.grid()   
        self.create_widgets()
            
    def create_widgets(self):
        """Fills the frame with various buttons."""
        # Fill grid (dictionary) with cell buttons.
        grid = {}
        for i in range(BOARDSIZE):
            for j in range(BOARDSIZE):
                grid[(i, j)] = Cell(False)
                cell_btn = Button(self, text="", relief=GROOVE, width=2)
                cell_btn.bind("<Button-1>", self.click_cell_btn)
                cell_btn.grid(row=i, column=j)
                # Store buttons and their coordinates in a dictionary.
                self.cell_btns[i, j] = cell_btn
           
        # Initialize Board with dead cells.    
        self.board = Board(grid, BOARDSIZE)
        
        # Place error label.
        self.error_lbl = Label(self, text="", foreground="red")
        self.error_lbl.grid(columnspan=15, column=2, row=(BOARDSIZE + 1))
          
        # Place reset button.
        reset_btn = Button(self, text="Reset", command=self.click_reset_btn)
        reset_btn.grid(columnspan=2, pady=10, column=5, row=(BOARDSIZE + 2))
        
        # Place load file button.
        loadfile_btn = Button(self, text="Load File...", command=self.click_loadfile_btn)
        loadfile_btn.grid(columnspan=4, column=4, row=(BOARDSIZE + 3))
        
        # Place generation step button.
        step_btn = Button(self, text="Step for one Generation", command=self.click_step_btn)
        step_btn.grid(columnspan=6, column=10, row=(BOARDSIZE + 2))
        
        # Place save file button.
        savefile_btn = Button(self, text="Save to File...", command=self.click_savefile_btn)
        savefile_btn.grid(columnspan=4, column=10, row=(BOARDSIZE + 3))
        
        # Place number of generations label.
        num_of_gens_lbl = Label(self, text="# of Generations:")
        num_of_gens_lbl.grid(columnspan=4, column=4, row=(BOARDSIZE + 4))
                
        # Place number of generations entry box.
        self.counter = StringVar(value="<#>")
        self.num_of_gens_box = Entry(self, width=4, textvariable=self.counter)
        self.num_of_gens_box.grid(columnspan=4, column=7, row=(BOARDSIZE + 4)) 
        
        # Place go button for multiple generation advancement.
        go_btn = Button(self, text="Go!", command=self.click_go_btn)
        go_btn.grid(columnspan=2, pady=10, column=10, row=(BOARDSIZE + 4))
        
    def click_cell_btn(self, event):
        """Registers a right-mouse click on a particular cell button
        
        Registers a right-mouse click on a particular cell button and
        alternates the cell's internal and displayed states.
        
        """
        # Clear any previous error messages on action.
        self.clear_error_lbl()
        
        cell = event.widget
        # Store grid coordinates (dictionary)
        info = cell.grid_info()
        # If cell is alive, kill it and update display...
        if cell["text"] == "X":
            self.board.set_cell(Cell(False), int(info["row"]), int(info["column"]))
            cell.configure(text="", background="SystemButtonFace")
        # ...otherwise set cell to alive and update display.
        else:
            self.board.set_cell(Cell(True), int(info["row"]), int(info["column"]))
            cell.configure(text="X", background="medium sea green")
        
    def click_reset_btn(self):
        """Registers reset_btn clicks
        
        Resets the board to all False (dead) cells.
        
        """
        # Clear any previous error messages on action.
        self.clear_error_lbl()
        
        for i in range(BOARDSIZE):
            for j in range(BOARDSIZE):
                self.board.set_cell(Cell(False), i, j)
                self.cell_btns[(i, j)].configure(text="", background="SystemButtonFace")
    
    def click_loadfile_btn(self):
        """Registers loadfile_btn clicks
        
        Prompts user for file and displays reads contents into internal
        board and displays cell states.
           
        """
        # Clear any previous error messages on action.
        self.clear_error_lbl()
        
        f = askopenfile(filetypes=[("TXT Files", "*.txt")], mode="rU")
        read = f.readlines()     
        # Pass list to Validation object. Returns True if valid, False otherwise.   
        if not Validation(read).validate_lines():
            self.error_lbl.config(text="Invalid file.")
            return
        # File is valid. Sore in 2-dim list representing board.
        grid = [] 
        for i in range (len(read)):    
            lines = read[i].rstrip()
            grid.append(list(lines))
        f.close()
        
        # This block determines what type of Cell to place based on file text
        for i in range(BOARDSIZE):
            for j in range(BOARDSIZE):
            # Swap "X" with living Cell object and update display.
                if grid[i][j] == 'X':
                    self.board.set_cell(Cell(True), i, j)
                    self.cell_btns[(i, j)].configure(text="X", background="medium sea green")
                # Swap "." with dead Cell object and update display.
                else:
                    self.board.set_cell(Cell(False), i, j)
                    self.cell_btns[(i, j)].configure(text="", background="SystemButtonFace")
        
    def click_savefile_btn(self):
        """Registers savefile_btn clicks
        
        Prompts user for filename and writes internal Board to said file.
           
        """
        f = open(asksaveasfilename(filetypes=[("TXT Files", "*.txt")], defaultextension=".txt"), "w")
        build_string = ""
        for i in range(BOARDSIZE):
            if i > 0:
                build_string += "\n"
            for j in range(BOARDSIZE):
                build_string += (str(self.board.get_cell(i, j)))
        # Last character of last line must be "\n".
        build_string += "\n"
        f.writelines(build_string)
        f.close()
    
    def click_step_btn(self):
        """Registers step_btn clicks
        
        Checks whether each cell on the Board will live in the "next
        generation" based on Board.would_cell_live(). Creates new Board
        filled with next generation Cells and displays the new Board.
           
        """
        # Clear any previous error messages on action.
        self.clear_error_lbl()
        # Need to store future cell states in new dictionary so as not to base
        # would_cell_live on updated values during iterations.
        new_board = {}   
        for i in range(BOARDSIZE):
            for j in range(BOARDSIZE):
                if self.board.would_cell_live(i, j):
                    new_board[(i, j)] = Cell(True)
                    self.cell_btns[(i, j)].configure(text="X", background="medium sea green")
                else:
                    new_board[(i, j)] = Cell(False)
                    self.cell_btns[(i, j)].configure(text="", background="SystemButtonFace")
        # Construct new board reflecting new generation of cells.
        self.board = Board(new_board, BOARDSIZE) 
    
    def click_go_btn(self):
        """Registers go_btn clicks
        
        Executes the click_step_btn method a number of times equal to
        self.counter value entered in self.num_of_gens_box. This means
        the cells/board iterate through the number of next generations
        provided by the user.
        
        """
        # Clear any previous error messages on action.
        self.clear_error_lbl()
        
        try:
            # Get the number of iterations to execute.
            count = int(self.counter.get())
        except ValueError as error:
            self.error_lbl.config(text=error)
        # Input is valid.
        else:
            self.clear_error_lbl()
            while count > 0:
                count -= 1
                self.click_step_btn()
                # Decrement displayed counter in self.num_of_gens_box.
                self.counter.set(str(count))
                # Flush buffer to show countdown.
                self.master._root().update_idletasks()
                
    def clear_error_lbl(self):
        self.error_lbl.config(text="")